/// <summary>
        /// Creates splatmaps which are (by default) applied to terrain splat shaders.
        /// </summary>
        /// <param name="applySplats">When true, the generated splat is automatically applied to the terrain.
        /// Otherwise, it can be applied by calling ApplySplatmapsToShader</param>
        /// <param name="debug">When true, the generated alpha maps are saved to the disk.
        /// They're located at [Your project's root dir]/SplatImages/</param>
        /// <returns>Created alphamap textures</returns>
        public List <Texture2D> GenerateSplatmaps(bool applySplats = true)
        {
            //Ensure correct shader is set
            SetFirstPassShader(true);

            //Set amount of required maps
            List <Texture2D> maps = new List <Texture2D>();

            for (int i = 0; i < Mathf.CeilToInt(SplatSettings.Count / 4f); i++)
            {
                maps.Add(new Texture2D(AlphaMapResolution, AlphaMapResolution));
            }

            //Sample weights and fill in textures
            for (int x = 0; x < AlphaMapResolution; x++)
            {
                for (int y = 0; y < AlphaMapResolution; y++)
                {
                    MeshSampler.MeshSample sample = Sampler.SampleAt(y / (float)AlphaMapResolution, x / (float)AlphaMapResolution);
                    AddWeightsToTextures(CalculateWeights(sample), ref maps, y, x);
                }
            }

            if (TerraDebug.WRITE_SPLAT_TEXTURES)
            {
                TerrainTile tile = TerrainObject.GetComponent <TerrainTile>();

                string tileName = tile != null ?
                                  "Tile[" + tile.Position.x + "_" + tile.Position.y + "]" :
                                  "Tile[0_0]";
                string folderPath = Application.dataPath + "/SplatImages/";
                if (!Directory.Exists(folderPath))
                {
                    Directory.CreateDirectory(folderPath);
                }

                for (var i = 0; i < maps.Count; i++)
                {
                    byte[] bytes = maps[i].EncodeToPNG();
                    string name  = "Splat" + i + "_" + tileName + ".png";
                    File.WriteAllBytes(folderPath + name, bytes);
                }
            }

            //Apply set pixel values to textures
            maps.ForEach(t => t.Apply());
            if (applySplats)
            {
                ApplySplatmapsToShaders(maps);
            }

            return(maps);
        }
Beispiel #2
0
        /// <summary>
        /// First creates a poisson grid based on the passed density.
        /// Positions are then filtered based on the passed object placement
        /// type taking into account height and angle constraints.
        ///
        /// Unlike the <c>GetFilteredGrid(ObjectPlacementType, float)</c> method
        /// this method samples from the passed Mesh rather than pulling
        /// mesh information from TerraSettings.
        /// </summary>
        /// <param name="m">Mesh to sample height and angle values from</param>
        /// <param name="type">object placement type to sample</param>
        /// <returns>List of vectors within the grid and sample constraints</returns>
        public List <Vector3> GetFilteredGrid(Mesh m, ObjectPlacementType type)
        {
            MeshSampler    sampler = new MeshSampler(m, Settings.MeshResolution);
            List <Vector2> grid    = GetPoissonGrid(type.Spread / 10);
            List <Vector3> toAdd   = new List <Vector3>();

            foreach (Vector2 pos in grid)
            {
                MeshSampler.MeshSample sample = sampler.SampleAt(pos.x, pos.y);

                if (type.ShouldPlaceAt(sample.Height, sample.Angle))
                {
                    Vector3 newPos = new Vector3(pos.x, sample.Height, pos.y);
                    toAdd.Add(newPos);
                }
            }

            return(toAdd);
        }
        /// <summary>
        /// First creates a poisson grid based on the passed density.
        /// Positions are then filtered based on the passed object placement
        /// type taking into account height and angle constraints.
        ///
        /// Unlike the <c>GetFilteredGrid(ObjectPlacementType, float)</c> method
        /// this method samples from the passed Mesh rather than pulling
        /// mesh information from TerraSettings.
        /// </summary>
        /// <param name="m">Mesh to sample height and angle values from</param>
        /// <param name="objectPlacementData">object placement type to sample</param>
        /// <returns>List of vectors within the grid and sample constraints</returns>
        public List <Vector3> GetFilteredGrid(Mesh m, ObjectPlacementData objectPlacementData)
        {
            MeshSampler    sampler = /**new MeshSampler(m, Settings.Generator.MeshResolution);*/ new MeshSampler(m, 128);          //TODO Resolution from TerraSettings
            List <Vector2> grid    = GetPoissonGrid(objectPlacementData.Spread / 10);
            List <Vector3> toAdd   = new List <Vector3>();

            foreach (Vector2 pos in grid)
            {
                MeshSampler.MeshSample sample = sampler.SampleAt(pos.x, pos.y);

                if (objectPlacementData.ShouldPlaceAt(sample.Height, sample.Angle))
                {
                    Vector3 newPos = new Vector3(pos.x, sample.Height, pos.y);
                    toAdd.Add(newPos);
                }
            }

            return(toAdd);
        }
        /// <summary>
        /// Calculates the weights that can be used to create a splatmap
        /// based on the passed sample and splats.
        /// </summary>
        /// <param name="sample">Sample to base calculation on</param>
        /// <param name="splat">Splat setting to base calculation on</param>
        /// <returns>Weight values in the same order of the </returns>
        float[] CalculateWeights(MeshSampler.MeshSample sample)
        {
            float height = sample.Height;
            float angle  = sample.Angle;

            float[] weights = new float[SplatSettings.Count];

            var orderMap = new Dictionary <PlacementType, int>()
            {
                { PlacementType.ElevationRange, 0 },
                { PlacementType.Angle, 1 }
            };
            List <SplatSetting> ordered = SplatSettings
                                          .OrderBy(s => orderMap[s.PlacementType]) //Order elevation before angle
                                                                                   //.OrderBy(s => s.MinRange)                //Order lower ranges
                                                                                   //.OrderBy(s => s.IsMinHeight)             //Order min height first
                                          .ToList();

            for (int i = 0; i < SplatSettings.Count; i++)
            {
                SplatSetting splat = ordered[i];

                float min = splat.IsMinHeight ? float.MinValue : splat.MinRange;
                float max = splat.IsMaxHeight ? float.MaxValue : splat.MaxRange;

                switch (splat.PlacementType)
                {
                case PlacementType.Angle:
                    if (angle > splat.AngleMin && angle < splat.AngleMax)
                    {
                        float factor = Mathf.Clamp01(((angle - splat.AngleMin)) / splat.Blend);
                        weights[i] = factor;
                    }

                    break;

                case PlacementType.ElevationRange:
                    if (height > min && height < max)
                    {
                        if (i > 0)                                   //Can blend up
                        {
                            float factor = Mathf.Clamp01((splat.Blend - (height - min)) / splat.Blend);
                            weights[i - 1] = factor;
                            weights[i]     = 1 - factor;
                        }
                        else
                        {
                            weights[i] = 1f;
                        }
                    }

                    break;
                }
            }

            //Normalize weights
            float sum = weights.Sum();

            for (var i = 0; i < weights.Length; i++)
            {
                weights[i] /= sum;
            }

            return(weights);
        }