public static void UpdateDetailLayer(TerrainData _terrain_data)
        {
            //_terrain_data.SetDetailResolution( TerrainDetailResolution, TerrainPatchDetail );

            _terrain_data.wavingGrassStrength = 0.25f;
            _terrain_data.wavingGrassAmount   = 0.25f;

            DetailPrototype[] _details = new DetailPrototype[10];

            int _index = 0;

            _details[_index] = new DetailPrototype();
            _details[_index].prototypeTexture = (Texture2D)Resources.Load("Grass/Grass01");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1.5f;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 1;
            _details[_index] = new DetailPrototype();
            _details[_index].prototypeTexture = (Texture2D)Resources.Load("Grass/Grass02");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1.5f;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 2;
            _details[_index] = new DetailPrototype();
            _details[_index].prototypeTexture = (Texture2D)Resources.Load("Grass/Grass03");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1.5f;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 3;
            _details[_index] = new DetailPrototype();
            _details[_index].prototypeTexture = (Texture2D)Resources.Load("Grass/Flower01");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1f;
            _details[_index].healthyColor     = Color.white;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 4;
            _details[_index] = new DetailPrototype();
            _details[_index].prototypeTexture = (Texture2D)Resources.Load("Grass/Flower02");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1f;
            _details[_index].healthyColor     = Color.yellow;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 5;
            _details[_index] = new DetailPrototype();
            _details[_index].usePrototypeMesh = true;
            _details[_index].prototype        = (GameObject)Resources.Load("Bushes/Fern");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1f;
            _details[_index].healthyColor     = Color.white;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 6;
            _details[_index] = new DetailPrototype();
            _details[_index].usePrototypeMesh = true;
            _details[_index].prototype        = (GameObject)Resources.Load("Bushes/Bush1");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1f;
            _details[_index].healthyColor     = Color.white;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 7;
            _details[_index] = new DetailPrototype();
            _details[_index].usePrototypeMesh = true;
            _details[_index].prototype        = (GameObject)Resources.Load("Bushes/Bush2");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1f;
            _details[_index].healthyColor     = Color.white;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 8;
            _details[_index] = new DetailPrototype();
            _details[_index].usePrototypeMesh = true;
            _details[_index].prototype        = (GameObject)Resources.Load("Bushes/Bush3");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1f;
            _details[_index].healthyColor     = Color.white;
            _details[_index].renderMode       = DetailRenderMode.Grass;

            _index           = 9;
            _details[_index] = new DetailPrototype();
            _details[_index].usePrototypeMesh = true;
            _details[_index].prototype        = (GameObject)Resources.Load("Bushes/Bush4");
            _details[_index].minWidth         = 0.25f;
            _details[_index].maxWidth         = 1f;
            _details[_index].minHeight        = 0.25f;
            _details[_index].maxHeight        = 1f;
            _details[_index].healthyColor     = Color.white;
            _details[_index].renderMode       = DetailRenderMode.Grass;


            _terrain_data.detailPrototypes = _details;

            TerrainDetailResolution = _terrain_data.detailResolution;

            int[,] _density_map_00 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_01 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_02 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_03 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_04 = new int[TerrainDetailResolution, TerrainDetailResolution];

            int[,] _density_map_bush_00 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_bush_01 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_bush_02 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_bush_03 = new int[TerrainDetailResolution, TerrainDetailResolution];
            int[,] _density_map_bush_04 = new int[TerrainDetailResolution, TerrainDetailResolution];

            float _x_pos_m = (float)_terrain_data.size.x / (float)TerrainDetailResolution;
            float _y_pos_m = (float)_terrain_data.size.z / (float)TerrainDetailResolution;

            if (TerrainGrassDensity > 0 || TerrainMeshDensity > 0)
            {
                for (int a = 0; a < TerrainDetailResolution; a++)
                {
                    for (int b = 0; b < TerrainDetailResolution; b++)
                    {
                        float   _x      = MathTools.Normalize(a * _x_pos_m, 0, _terrain_data.size.x);
                        float   _y      = MathTools.Normalize(b * _y_pos_m, 0, _terrain_data.size.z);
                        Vector3 _ground = _terrain_data.GetInterpolatedNormal(_y, _x);
                        float   _angle  = Mathf.Abs(Vector3.Angle(_ground, Vector3.up));

                        float _f = 5;
                        //float _m = Mathf.Clamp01( 1 - ( _angle / TerrainGrassAngle ) );

                        if (TerrainGrassDensity > 0)
                        {
                            _density_map_00[a, b] = Mathf.RoundToInt(UnityEngine.Random.Range(TerrainGrassDensity * 0.5f, TerrainGrassDensity) * MathTools.NormalizeRange(_angle, -_f, TerrainGrassAngle + _f, _f));

                            _f = 5;
                            _density_map_01[a, b] = Mathf.RoundToInt(UnityEngine.Random.Range(TerrainGrassDensity * 0.25f, TerrainGrassDensity * 0.3f) * MathTools.NormalizeRange(_angle, 0, TerrainGrassAngle + _f, _f));

                            _f = 3;
                            _density_map_02[a, b] = Mathf.RoundToInt(UnityEngine.Random.Range(TerrainGrassDensity * 0.15f, TerrainGrassDensity * 0.25f) * MathTools.NormalizeRange(_angle, -_f * 0.5f, TerrainGrassAngle + _f, _f));

                            _f = 2;
                            _density_map_03[a, b] = Mathf.RoundToInt(UnityEngine.Random.Range(TerrainGrassDensity * 0.1f, TerrainGrassDensity * 0.5f) * MathTools.NormalizeRange(_angle, -_f, (TerrainGrassAngle * 0.25f) + _f, _f));

                            _f = 2;
                            _density_map_04[a, b] = Mathf.RoundToInt(UnityEngine.Random.Range(TerrainGrassDensity * 0.1f, TerrainGrassDensity * 0.5f) * MathTools.NormalizeRange(_angle, -_f, (TerrainGrassAngle * 0.25f) + _f, _f));
                        }

                        if (TerrainMeshDensity > 0)
                        {
                            _f = 5;
                            _density_map_bush_00[a, b] = Mathf.RoundToInt((UnityEngine.Random.Range(0, Mathf.RoundToInt(100 / TerrainMeshDensity)) == 0 ? 1:0) * MathTools.NormalizeRange(_angle, (TerrainGrassAngle * 0.5f) - _f, (TerrainGrassAngle * 2f) + _f, _f));

                            _f = 2;
                            _density_map_bush_01[a, b] = Mathf.RoundToInt((UnityEngine.Random.Range(0, Mathf.RoundToInt(500 / TerrainMeshDensity)) == 0 ? 1:0) * MathTools.NormalizeRange(_angle, _f, (TerrainMeshAngle * 1.5f) + _f, _f));

                            _f = 2;
                            _density_map_bush_03[a, b] = Mathf.RoundToInt((UnityEngine.Random.Range(0, Mathf.RoundToInt(500 / TerrainMeshDensity)) == 0 ? 1:0) * MathTools.NormalizeRange(_angle, _f, (TerrainMeshAngle * 1.5f) + _f, _f));

                            _f = 2;
                            _density_map_bush_03[a, b] = Mathf.RoundToInt((UnityEngine.Random.Range(0, Mathf.RoundToInt(500 / TerrainMeshDensity)) == 0 ? 1:0) * MathTools.NormalizeRange(_angle, _f, (TerrainMeshAngle * 1.5f) + _f, _f));

                            _f = 2;
                            _density_map_bush_04[a, b] = Mathf.RoundToInt((UnityEngine.Random.Range(0, Mathf.RoundToInt(500 / TerrainMeshDensity)) == 0 ? 1:0) * MathTools.NormalizeRange(_angle, _f, (TerrainMeshAngle * 1.5f) + _f, _f));
                        }
                    }
                }
            }

            _terrain_data.SetDetailLayer(0, 0, 0, _density_map_00);
            _terrain_data.SetDetailLayer(0, 0, 1, _density_map_01);
            _terrain_data.SetDetailLayer(0, 0, 2, _density_map_02);
            _terrain_data.SetDetailLayer(0, 0, 3, _density_map_03);
            _terrain_data.SetDetailLayer(0, 0, 4, _density_map_04);

            _terrain_data.SetDetailLayer(0, 0, 5, _density_map_bush_00);
            _terrain_data.SetDetailLayer(0, 0, 6, _density_map_bush_01);
            _terrain_data.SetDetailLayer(0, 0, 7, _density_map_bush_02);
            _terrain_data.SetDetailLayer(0, 0, 8, _density_map_bush_03);
            _terrain_data.SetDetailLayer(0, 0, 9, _density_map_bush_04);
        }
        public static void UpdateSplatmap(TerrainData _terrain_data)
        {
            SplatPrototype[] _terrain_texture = new SplatPrototype[6];
            _terrain_texture[0]          = new SplatPrototype();
            _terrain_texture[0].texture  = (Texture2D)Resources.Load("Textures/Grass (Meadows)");
            _terrain_texture[0].tileSize = new Vector2(5, 5);

            _terrain_texture[1]          = new SplatPrototype();
            _terrain_texture[1].texture  = (Texture2D)Resources.Load("Textures/Grass (Forest)");
            _terrain_texture[1].tileSize = new Vector2(5, 5);

            _terrain_texture[2]          = new SplatPrototype();
            _terrain_texture[2].texture  = (Texture2D)Resources.Load("Textures/Grass (Rock)");
            _terrain_texture[2].tileSize = new Vector2(15, 15);

            _terrain_texture[3]          = new SplatPrototype();
            _terrain_texture[3].texture  = (Texture2D)Resources.Load("Textures/Cliff (Grassy)");
            _terrain_texture[3].tileSize = new Vector2(15, 15);

            _terrain_texture[4]          = new SplatPrototype();
            _terrain_texture[4].texture  = (Texture2D)Resources.Load("Textures/Cliff (Layered Rock Grass)");
            _terrain_texture[4].tileSize = new Vector2(25, 25);

            _terrain_texture[5]          = new SplatPrototype();
            _terrain_texture[5].texture  = (Texture2D)Resources.Load("Textures/Sand (Beach)");
            _terrain_texture[5].tileSize = new Vector2(15, 15);

            _terrain_data.splatPrototypes = _terrain_texture;

            // Splatmap data is stored internally as a 3d array of floats, so declare a new empty array ready for your custom splatmap data:
            float[,,] splatmapData = new float[_terrain_data.alphamapWidth, _terrain_data.alphamapHeight, _terrain_data.alphamapLayers];

            //float _max_debug_value =0;
            for (int y = 0; y < _terrain_data.alphamapHeight; y++)
            {
                for (int x = 0; x < _terrain_data.alphamapWidth; x++)
                {
                    // Normalise x/y coordinates to range 0-1
                    float y_01 = (float)y / (float)_terrain_data.alphamapHeight;
                    float x_01 = (float)x / (float)_terrain_data.alphamapWidth;

                    // Sample the height at this location (note GetHeight expects int coordinates corresponding to locations in the heightmap array)
                    //float _height = _terrain_data.GetInterpolatedHeight(y_01,x_01);

                    // Calculate the normal of the terrain (note this is in normalised coordinates relative to the overall terrain dimensions)
                    Vector3 _normal = _terrain_data.GetInterpolatedNormal(y_01, x_01);

                    // Calculate the angle deviation of the terrain (normal 0)
                    float _angle = Mathf.Abs(Vector3.Angle(_normal, Vector3.up));
                    //float _angle_multiplier = Mathf.Clamp01( _angle / 90 );

                    // Calculate the steepness of the terrain
                    float _steepness = _terrain_data.GetSteepness(y_01, x_01);

                    //float _waterlevel = 10;

                    // Setup an array to record the mix of texture weights at this point
                    float[] splatWeights = new float[_terrain_data.alphamapLayers];

                    // CHANGE THE RULES BELOW TO SET THE WEIGHTS OF EACH TEXTURE ON WHATEVER RULES YOU WANT

                    // BASIC FOREST - Texture[0] has constant influence
                    splatWeights[0] = 0.5f;

                    // GRASS MEADOWS - Texture[1] stronger on flatter terrain
                    splatWeights[1] = 1f - Mathf.Clamp01(_steepness * _steepness / (_terrain_data.heightmapHeight / 5.0f));

                    // BASIC CORRECTION - reduce the Texture[0] according to the MEADOW value
                    //splatWeights[0] = Mathf.Clamp01( splatWeights[0] - ( splatWeights[1] / 2 ) );

                    // MEADOW CORRECTION - splatWeights[1] will be also required on flatt hills
                    //splatWeights[1] = ( splatWeights[1] > Mathf.Clamp01( ( _steepness * Mathf.Clamp01(-_normal.z) ) - _height ) ? splatWeights[1] : Mathf.Clamp01( ( _steepness * Mathf.Clamp01(-_normal.z) ) - _height ) );


                    // BOULDERS - Texture[2] will be required only on flat hillslopes
                    splatWeights[2] = MathTools.NormalizeRange(_angle, 4, 18, 6);                                                      //* ( _normal.z > 0 ? 1f : 0f );

                    splatWeights[3] = MathTools.NormalizeRange(_angle, 10, 45, 6) * Mathf.Clamp01(_normal.z * ((90 - _angle) * 0.1f)); // ( _normal.z > 0 ? 1f : 0f );// Mathf.Clamp01( ( _steepness * Mathf.Clamp01(_normal.z) ) - ( _height * _height) );//Mathf.Clamp01( Mathf.Clamp01( _normal.z *  _normal.z ) );//1.0f - Mathf.Clamp01(steepness*steepness/(_terrain_data.heightmapHeight/5.0f));

                    splatWeights[1] = Mathf.Clamp01(splatWeights[1] - (splatWeights[3] * splatWeights[3]));

                    // ROCK - Texture[4] will be required on cliffy surfaces over 35 degrees
                    splatWeights[4] = MathTools.NormalizeRange(_angle, 35, 90, 10);                      // ( _normal.z > 0 ? 1f : 0f );// Mathf.Clamp01( ( _steepness * Mathf.Clamp01(_normal.z) ) - ( _height * _height) );//Mathf.Clamp01( Mathf.Clamp01( _normal.z *  _normal.z ) );//1.0f - Mathf.Clamp01(steepness*steepness/(_terrain_data.heightmapHeight/5.0f));

                    splatWeights[2] = (splatWeights[2] == 0 ? MathTools.NormalizeRange(_angle, 30, 45, 5) : splatWeights[2]);

                    //if( _height > _max_debug_value )
                    //	_max_debug_value = _height;

                    float _sum = splatWeights.Sum();

                    // Loop through each terrain texture
                    for (int _i = 0; _i < _terrain_data.alphamapLayers; _i++)
                    {
                        // Normalize so that sum of all texture weights = 1
                        splatWeights[_i] /= _sum;

                        // Assign this point to the splatmap array
                        splatmapData[x, y, _i] = splatWeights[_i];
                    }
                }
            }

            //Debug.Log( _max_debug_value );

            // Finally assign the new splatmap to the terrainData:
            _terrain_data.SetAlphamaps(0, 0, splatmapData);
        }