/// <summary>
            /// Places objects on the Tile if positions have
            /// already been computed.
            /// </summary>
            public void PlaceObjects()
            {
                //Get TerraSettings instance
                TerraConfig config = UnityEngine.Object.FindObjectOfType <TerraConfig>();
                int         length = config.Generator.Length;

                if (Positions != null && config != null)
                {
                    foreach (PositionsContainer p in Positions)
                    {
                        ObjectContainer container = GetContainerForType(p.ObjectPlacementData);
                        Transform       parent    = GetParent();

                        if (container != null)
                        {
                            foreach (Vector3 pos in p.Positions)
                            {
                                GameObject go = container.GetObject(parent);
                                p.ObjectPlacementData.TransformGameObject(go, pos);

                                //Translate object back into place (terrain origin is centered)
                                go.transform.position =
                                    new Vector3(go.transform.position.x - length / 2f, go.transform.position.y, go.transform.position.z - length / 2f);

                                PlacedObjects.Add(go);
                            }
                        }
                    }
                }
            }
        public TerraGUI(TerraConfig config)
        {
            this._config = config;

            _biomeList    = new ReorderableBiomeList(config);
            _biomeDetails = new List <KeyValuePair <BiomeData, DetailSubList> >();
        }
Пример #3
0
        /// <summary>
        /// Works the same as <see cref="GenerateCoroutine"/> without the
        /// yield instructions. Runs synchronously.
        /// </summary>
        /// <param name="remapMin"></param>
        /// <param name="remapMax"></param>
        private void GenerateEditor(float remapMin = 0f, float remapMax = 1f)
        {
            TerraConfig.Log($"Started tile {GridPosition.ToString()}");

            //Make & set heightmap
            MeshManager.CalculateHeightmap(GridPosition, remapMin, remapMax);

            MeshManager.SetTerrainHeightmap();
            MeshManager.SetVisible();

            //Create TilePaint object
            TilePaint painter = new TilePaint(this);

            //Make biomemap
            int[,] map = painter.GetBiomeMap();

            //Paint terrain
            painter.Paint(map);

            //Apply details to terrain
            // ReSharper disable once IteratorMethodResultIsIgnored
            ApplyDetails(painter, map);
            MeshManager.SetVisible(true);

            TerraConfig.Log("Completed tile " + GridPosition);
        }
Пример #4
0
        /// <summary>
        /// Uses raycast hit information to check whether a grass
        /// vertex can be placed at this location in the world.
        /// Checks against grass height and angle information found
        /// in TerraSettings.
        /// </summary>
        /// <param name="hit">Raycast hit</param>
        /// <returns>true if this vertex should be placed here</returns>
        private bool CanPlaceAt(RaycastHit hit)
        {
            float       height = hit.point.y;
            float       angle  = Vector3.Angle(Vector3.up, hit.normal);
            TerraConfig set    = TerraConfig.Instance;

            bool passesHeight = height >= set.Grass.GrassMinHeight && height <= set.Grass.GrassMaxHeight;
            bool passesAngle  = angle >= set.Grass.GrassAngleMin && angle <= set.Grass.GrassAngleMax;

            if (set.Grass.GrassConstrainHeight && set.Grass.GrassConstrainAngle)
            {
                return(passesHeight && passesAngle);
            }
            else if (set.Grass.GrassConstrainHeight)
            {
                return(passesHeight);
            }
            else if (set.Grass.GrassConstrainAngle)
            {
                return(passesAngle);
            }
            else
            {
                return(true);
            }
        }
        /// <summary>
        /// Adds a <see cref="UnityEngine.Terrain"/> component to this <see cref="Tile"/>'s
        /// gameobject and sets it up according to <see cref="TerraConfig"/>.
        /// Overwrites <see cref="ActiveTerrain"/> if it already exists.
        /// </summary>
        public void AddTerrainComponent()
        {
            //Destory current Terrain instance if it exists
            if (ActiveTerrain != null)
            {
#if UNITY_EDITOR
                UnityEngine.Object.DestroyImmediate(ActiveTerrain);
#else
                UnityEngine.Object.Destroy(ActiveTerrain);
#endif
            }

            TerraConfig         conf   = TerraConfig.Instance;
            int                 length = conf.Generator.Length;
            UnityEngine.Terrain t      = _tile.gameObject.AddComponent <UnityEngine.Terrain>();

            t.terrainData      = new TerrainData();
            t.terrainData.size = new Vector3(length, conf.Generator.Amplitude, length);
            t.allowAutoConnect = true;
            t.drawInstanced    = true;

            TerrainCollider tc = _tile.gameObject.AddComponent <TerrainCollider>();
            tc.terrainData = t.terrainData;

            t.materialTemplate = conf.Generator.TerrainMaterial != null ? conf.Generator.TerrainMaterial : GetDefaultTerrainMaterial();
        }
            /// <summary>
            /// Places objects on the Tile if positions have
            /// already been computed.
            /// </summary>
            public void PlaceObjects()
            {
                //Get TerraSettings instance
                TerraConfig config = UnityEngine.Object.FindObjectOfType <TerraConfig>();

                if (Positions != null && config != null)
                {
                    foreach (PositionsContainer p in Positions)
                    {
                        ObjectContainer container = GetContainerForType(p.ObjectPlacementData);
                        Transform       parent    = GetParent();

                        if (container != null)
                        {
                            foreach (Vector3 pos in p.Positions)
                            {
                                GameObject go = container.GetObject(parent);
                                p.ObjectPlacementData.TransformGameObject(go, pos, config.Generator.Length, Tile.transform.position);

                                PlacedObjects.Add(go);
                            }
                        }
                    }
                }
            }
 /// <summary>
 /// Creates an ObjectPlacerPreview instance with the passed
 /// TerraSettings (for sampling different placement settings) and
 /// a Mesh to place the objects on.
 /// </summary>
 /// <param name="config">TerraSettings</param>
 /// <param name="m">A preview mesh to draw on top of</param>
 public ObjectPlacerPreview(TerraConfig config, Mesh m)
 {
     _config     = config;
     PreviewMesh = m;
     Placer      = new ObjectRenderer();
     CreateParentGO();
 }
    private Dictionary <int, Rect> _positions;    //Cached positions at last repaint

    public ReorderableBiomeList(TerraConfig config) : base(config.BiomesData, null, MAX_HEIGHT)
    {
        _config = config;

        _positions    = new Dictionary <int, Rect>();
        _materialList = new List <KeyValuePair <BiomeData, ReorderableMaterialList> >();
        _objectList   = new List <KeyValuePair <BiomeData, ReorderableObjectList> >();
    }
Пример #9
0
        /// <summary>
        /// Generates a map of constructed from connected biome nodes. Instead of
        /// returning a biome map of size [resolution, resolution], it returns one
        /// of size [resolution + kernel size, resolution + kernel size] where
        /// kernel size is defined in <code>BlurUtils.GetGaussianKernelSize()</code>
        /// </summary>
        /// <param name="config"></param>
        /// <param name="position"></param>
        /// <param name="resolution"></param>
        /// <param name="deviation"></param>
        /// <returns></returns>
        public int[,] GetGaussianBlurrableBiomeMap(TerraConfig config, GridPosition position, int resolution, int deviation)
        {
            int[,] biomeMap = GetBiomeMap(config, position, resolution);
            int kernelSize = BlurUtils.GetGaussianKernelSize(deviation);
            int newRes     = resolution + kernelSize;

            int[,] blurrableBiomeMap = new int[newRes, newRes];

            // Fill in area surrounding the center biome map
        }
Пример #10
0
        private IEnumerator GenerateCoroutine(Action onComplete, float remapMin = 0f, float remapMax = 1f)
        {
            TerraConfig conf = TerraConfig.Instance;

            TerraConfig.Log("Started tile " + GridPosition);

            //Make & set heightmap
            bool madeHm = false;

            MeshManager.CalculateHeightmapAsync(remapMin, remapMax, () => madeHm = true);
            while (!madeHm)
            {
                yield return(null);
            }

            MeshManager.SetTerrainHeightmap();
            MeshManager.SetVisible();

            //Create TilePaint object
            TilePaint painter = new TilePaint(this);

            //Make biomemap
            bool madeBm = false;

            int[,] map = null;
            conf.Worker.Enqueue(() => map = painter.GetBiomeMap(), () => madeBm = true);
            while (!madeBm)
            {
                yield return(null); //Skip frame until biomemap made
            }
            //Paint terrain
            bool madePaint = false;

            yield return(StartCoroutine(painter.PaintAsync(map, () => madePaint = true)));

            while (!madePaint)
            {
                yield return(null);
            }

            //Apply details to terrain
            bool madeDetails = false;

            yield return(StartCoroutine(ApplyDetails(painter, map, () => madeDetails = true)));

            while (!madeDetails)
            {
                yield return(null);
            }

            MeshManager.SetVisible(true);

            TerraConfig.Log("Completed tile " + GridPosition);
            onComplete();
        }
        /// <summary>
        /// Creates a new ObjectPlacer that uses mesh information provided
        /// by TerraSettings to calculate where to place objects on meshes.
        /// Optionally disable observing TerrainTiles if you wish to
        /// manage the placement of tiles manually rather than displaying
        /// and hiding when a Tile activates or deactivates.
        /// </summary>
        /// <param name="observeTiles">Observe Tile events?</param>
        public ObjectRenderer(bool observeTiles = true)
        {
            _config        = TerraConfig.Instance;
            ObserveTiles   = observeTiles;
            ObjectsToPlace = _config.ObjectData;
            Pool           = new ObjectPool(this);

            if (ObserveTiles)
            {
                TerraEvent.OnTileActivated   += OnTerrainTileActivate;
                TerraEvent.OnTileDeactivated += OnTerrainTileDeactivate;
            }
        }
Пример #12
0
        /// <summary>
        /// Calculates the min and maximum values to use when applying a heightmap
        /// remap. This sets <see cref="RemapMax"/> and <see cref="RemapMin"/>.
        /// </summary>
        public void CalculateHeightmapRemap()
        {
            bool      shouldProfile = TerraConfig.Instance.EditorState.ShowDebugMessages;
            Stopwatch sw            = null;

            if (shouldProfile)
            {
                sw = new Stopwatch();
                sw.Start();
            }

            float min       = float.PositiveInfinity;
            float max       = float.NegativeInfinity;
            int   res       = Config.Generator.RemapResolution;
            var   generator = Config.Graph.GetEndGenerator();

            for (int x = 0; x < res; x++)
            {
                for (int z = 0; z < res; z++)
                {
                    float value = generator.GetValue(x / (float)res, z / (float)res, 0f);

                    if (value > max)
                    {
                        max = value;
                    }
                    if (value < min)
                    {
                        min = value;
                    }
                }
            }

            //Set remap values for instance
            _remapMin = min;
            _remapMax = max;

            if (shouldProfile)
            {
                sw.Stop();
                TerraConfig.Log("CalculateHeightmapRemap took " + sw.ElapsedMilliseconds + "ms to complete. " +
                                "New min=" + min + " New max=" + max);
            }
        }
Пример #13
0
        /// <summary>
        /// Sets the heightmap to the current LOD asynchronously
        /// </summary>
        public IEnumerator UpdateHeightmapAsync(Action onComplete, float remapMin, float remapMax)
        {
            MeshManager.Lod = GetLodLevel();

            bool updatedHm = false;

            TerraConfig.Log("Updating heightmap start");
            MeshManager.CalculateHeightmapAsync(remapMin, remapMax, () => {
                MeshManager.SetTerrainHeightmap();
                updatedHm = true;
            });

            while (!updatedHm)
            {
                yield return(null);
            }

            if (onComplete != null)
            {
                onComplete();
            }
        }
Пример #14
0
        /// <summary>
        /// Sets the <see cref="UnityEngine.Terrain"/> component's heightmap to this instances'
        /// <see cref="Heightmap"/>. If <see cref="UnityEngine.Terrain"/> hasn't been created,
        /// it is added as a component.
        /// </summary>
        /// <remarks>Since this method creates and adds a <see cref="UnityEngine.Terrain"/>
        /// component, it is not thread safe.</remarks>
        /// <param name="heightmap">Optionally use the passed heightmap instead of
        /// <see cref="TileMesh"/>'s <see cref="Heightmap"/></param>
        public void SetTerrainHeightmap(float[,] heightmap = null)
        {
            float[,] hm = heightmap ?? Heightmap;

            if (hm == null)
            {
                return;
            }
            if (ActiveTerrain == null)
            {
                AddTerrainComponent();
            }

            //ReSharper disable once PossibleNullReferenceException
            TerrainData td     = ActiveTerrain.terrainData;
            TerraConfig conf   = TerraConfig.Instance;
            int         length = conf.Generator.Length;

            td.heightmapResolution = HeightmapResolution;
            td.SetHeights(0, 0, hm);
            td.size = new Vector3(length, conf.Generator.Amplitude, length);
            ActiveTerrain.Flush();
        }
Пример #15
0
        /// <summary>
        /// Creates a heightmap of resolution <see cref="HeightmapResolution"/> asynchronously.
        /// If a <see cref="Heightmap"/> of the same resolution or higher has already been
        /// created, this method does nothing.
        /// A heightmap is 2D array of floats that represents the Y values (or heights)
        /// of to-be created vertices in 3D space.
        /// </summary>
        /// <param name="remapMin">Optionally linear transform the heightmap from [min, max] to [0, 1]</param>
        /// <param name="remapMax">Optionally linear transform the heightmap from [min, max] to [0, 1]</param>
        /// <param name="onComplete">Called when the heightmap has been created</param>
        public void CalculateHeightmapAsync(float remapMin = 0f, float remapMax = 1f, Action onComplete = null)
        {
            _lastGeneratedLodLevel = _tile.GetLodLevel();
            Lod = _lastGeneratedLodLevel;
            bool shouldProfile = TerraConfig.Instance.EditorState.ShowDebugMessages;

            Stopwatch wsw = null;

            if (shouldProfile)
            {
                wsw = new Stopwatch();
                wsw.Start();
            }
            TerraConfig.Instance.Worker.Enqueue(() => {
                Stopwatch sw = null;
                if (shouldProfile)
                {
                    sw = new Stopwatch();
                    Profiler.BeginThreadProfiling("Heightmap Workers", "Tile " + _tile.GridPosition);
                    sw.Start();
                }
                CalculateHeightmap(null, remapMin, remapMax);

                if (shouldProfile)
                {
                    sw.Stop();
                    TerraConfig.Log("Tile " + _tile.GridPosition + " heightmap time: " + sw.ElapsedMilliseconds);
                    Profiler.EndThreadProfiling();
                }
            }, () => {
                if (shouldProfile)
                {
                    wsw.Stop();
                    MTDispatch.Instance().Enqueue(() => { TerraConfig.Log("CalculateHM worker time elapsed " + wsw.ElapsedMilliseconds); onComplete(); });
                }
            });
        }
Пример #16
0
 /// <summary>
 /// Generates a map of biomes constructed from connected biome nodes.
 /// </summary>
 /// <param name="config">TerraConfig instance for pulling length & spread</param>
 /// <param name="position">Position of in Terra grid units of this map</param>
 /// <param name="resolution">Resolution of this biome map</param>
 public int[,] GetBiomeMap(TerraConfig config, GridPosition position, int resolution)
 {
     return(GetBiomeMap(position, config.Generator.Length, config.Generator.Spread, resolution));
 }
Пример #17
0
 // Use this for initialization
 void Start()
 {
     TerraConfig config = FindObjectOfType <TerraConfig>();
     //m = settings.CustomMaterial;
 }
 public TerraGUI(TerraConfig config)
 {
     this._config = config;
 }
Пример #19
0
        /// <summary>
        /// Updates tiles that are surrounding the tracked GameObject
        /// asynchronously. When calling this method using
        /// <see cref="MonoBehaviour.StartCoroutine(IEnumerator)"/>,
        /// tiles are generated once per frame
        /// </summary>
        public IEnumerator UpdateTiles()
        {
            List <GridPosition> nearbyPositions = GetTilePositionsFromRadius();
            List <GridPosition> newPositions    = Cache.GetNewTilePositions(nearbyPositions);
            List <Tile>         toRegenerate    = new List <Tile>();

            RemoveOldPositions(ref nearbyPositions, ref toRegenerate);

            //Add new positions
            _queuedTiles = newPositions.Count + toRegenerate.Count;

            foreach (GridPosition pos in newPositions)
            {
                if (_isGenerating && Application.isPlaying) //Wait and generate one tile per frame
                {
                    yield return(null);
                }

                Tile cached = Cache.GetCachedTileAtPosition(pos);

                //Attempt to pull from cache, generate if not available
                if (cached != null)
                {
                    if (cached.IsHeightmapLodValid())   //Cached tile is valid, use it
                    {
                        AddTile(cached);
                        _queuedTiles--;

                        continue;
                    }

                    TerraConfig.Log("Cached tile " + cached + " has heightmap res=" + cached.MeshManager.HeightmapResolution +
                                    ". requested res=" + cached.GetLodLevel().Resolution + ". Regenerating.");

                    toRegenerate.Add(cached);
                    Cache.AddActiveTile(cached);
                    continue;
                }

                _isGenerating = true;
                CreateTileAt(pos, tile => {
                    _queuedTiles--;

                    if (_queuedTiles == 0)
                    {
                        UpdateNeighbors(newPositions.ToArray(), false);
                    }

                    _isGenerating = false;
                });
            }

            //Regenerate tiles with outdated heightmaps
            for (int i = 0; i < toRegenerate.Count; i++)
            {
                Tile t = toRegenerate[i];
                TerraConfig.Log("Active tile " + t + " has heightmap res=" + t.MeshManager.HeightmapResolution +
                                ". requested res=" + t.GetLodLevel().Resolution + ". Regenerating.");

                //Generate one tile per frame
                if (Application.isPlaying)
                {
                    yield return(null);
                }

                if (Application.isPlaying)
                {
                    Config.StartCoroutine(t.UpdateHeightmapAsync(() => {
                        UpdateNeighbors(new[] { t.GridPosition }, false);
                        _queuedTiles--;
                    }, RemapMin, RemapMax));
                }
                else
                {
                    t.UpdateHeightmap(RemapMin, RemapMax);
                }
            }

            //If tiles were updated synchronously, notify queue completion
            if (newPositions.Count > 0 && _queuedTiles == 0)
            {
                UpdateNeighbors(newPositions.ToArray(), false);
            }
        }
 public ReorderableMaterialList(TerraConfig config, DetailData detail) : base(detail.SplatsData, null, MAX_HEIGHT)
 {
     _detail = detail;
 }
 public ObjectPlacer(TerraConfig config)
 {
     Config = config;
     //ObjectsToPlace = Settings.ObjectData;
 }
 public DetailSubList(TerraConfig config, DetailData details)
 {
     ObjectsList   = new ReorderableObjectList(config, details);
     MaterialsList = new ReorderableMaterialList(config, details);
 }
 void Start()
 {
     TerraConfig t = FindObjectOfType <TerraConfig>();
     //m = t.CustomMaterial;
 }
    private Dictionary <int, Rect> _positions;    //Cached positions at last repaint

    public ReorderableObjectList(TerraConfig config, DetailData detail) : base(detail.ObjectData, null, MAX_HEIGHT)
    {
        _positions = new Dictionary <int, Rect>();
    }