/// <summary>
 /// Initialise the extension
 /// </summary>
 public override void Initialise()
 {
     m_textureHM = new UnityHeightMap(m_grassMask);
     if (m_normaliseMask && m_textureHM.HasData())
     {
         m_textureHM.Normalise();
     }
     if (m_invertMask && m_textureHM.HasData())
     {
         m_textureHM.Invert();
     }
     if (m_flipMask && m_textureHM.HasData())
     {
         m_textureHM.Flip();
     }
 }
        /// <summary>
        /// Called after the spawn is complete
        /// </summary>
        /// <param name="spawnRule">The rule that generated this call</param>
        /// <param name="spawnInfo">The spawninfo structure for this call</param>
        public override void PostSpawn(SpawnRule spawnRule, ref SpawnInfo spawnInfo)
        {
            //See if we can load the texture
            if (m_textureHM == null || !m_textureHM.HasData())
            {
                return;
            }

            //Get the terrain
            Terrain t = Gaia.TerrainHelper.GetTerrain(spawnInfo.m_hitLocationWU);

            if (t == null)
            {
                return;
            }

            //Get the cached detail maps
            List <HeightMap> detailMaps = spawnInfo.m_spawner.GetDetailMaps(spawnInfo.m_hitTerrain.GetInstanceID());

            if (detailMaps == null || m_grassIndex >= detailMaps.Count)
            {
                return;
            }

            //Make some speedy calculations
            float   widthWU = spawnInfo.m_hitTerrain.terrainData.size.x;
            float   depthWU = spawnInfo.m_hitTerrain.terrainData.size.z;
            float   radiusWU = spawnRule.GetMaxScaledRadius(ref spawnInfo) * m_scaleMask;
            float   xStartWU = spawnInfo.m_hitLocationWU.x - (radiusWU / 2f);
            float   zStartWU = spawnInfo.m_hitLocationWU.z - (radiusWU / 2f);
            float   xEndWU = xStartWU + radiusWU;
            float   zEndWU = zStartWU + radiusWU;
            float   stepWU = 0.5f;
            Vector3 locationWU = Vector3.zero;
            float   xRotNU = 0f, zRotNU = 0f;
            float   grassRange = (float)(m_maxGrassStrength - m_minGrassStrenth);

            for (float x = xStartWU; x < xEndWU; x += stepWU)
            {
                for (float z = zStartWU; z < zEndWU; z += stepWU)
                {
                    //Need to rotate x,z around the pivot by the rotation angle
                    locationWU = new Vector3(x, spawnInfo.m_hitLocationWU.y, z);
                    locationWU = Gaia.Utils.RotatePointAroundPivot(locationWU, spawnInfo.m_hitLocationWU, new Vector3(0f, spawnInfo.m_spawnRotationY, 0f));

                    //Now normalise the result
                    xRotNU = (locationWU.x / widthWU) + 0.5f;
                    zRotNU = (locationWU.z / depthWU) + 0.5f;

                    //Drop out if out of bounds
                    if (xRotNU < 0f || xRotNU >= 1f || zRotNU < 0f || zRotNU > 1f)
                    {
                        continue;
                    }

                    detailMaps[m_grassIndex][zRotNU, xRotNU] = Mathf.Clamp((m_textureHM[(x - xStartWU) / radiusWU, (z - zStartWU) / radiusWU] * grassRange) + m_minGrassStrenth, 0f, 15f);
                }
            }
        }
        /// <summary>
        /// Call this to get the degree of completion in range 0..1.
        /// </summary>
        /// <param name="spawner">The spawner this is for</param>
        /// <param name="objSpawned">The object that was spawned in the spawn step (if relevant)</param>
        /// <returns>Return the completion in range of 0 (not started) .. 1 (completed)</returns>
        public override void PostSpawn(SpawnRule spawnRule, ref SpawnInfo spawnInfo)
        {
            //See if we can load the texture
            if (m_textureHM == null || !m_textureHM.HasData())
            {
                return;
            }

            //Get the terrain
            Terrain t = Gaia.TerrainHelper.GetTerrain(spawnInfo.m_hitLocationWU);

            if (t == null)
            {
                return;
            }

            //Get the cached texture maps
            List <HeightMap> txtMaps = spawnInfo.m_spawner.GetTextureMaps(spawnInfo.m_hitTerrain.GetInstanceID());

            if (txtMaps == null || m_textureIndex >= txtMaps.Count)
            {
                return;
            }

            //Make some speedy calculations
            float   widthWU = spawnInfo.m_hitTerrain.terrainData.size.x;
            float   depthWU = spawnInfo.m_hitTerrain.terrainData.size.z;
            float   radiusWU = spawnRule.GetMaxScaledRadius(ref spawnInfo) * m_scaleMask;
            float   xStartWU = spawnInfo.m_hitLocationWU.x - (radiusWU / 2f);
            float   zStartWU = spawnInfo.m_hitLocationWU.z - (radiusWU / 2f);
            float   xEndWU = xStartWU + radiusWU;
            float   zEndWU = zStartWU + radiusWU;
            float   stepWU = 0.5f;
            Vector3 locationWU = Vector3.zero;
            float   xRotNU = 0f, zRotNU = 0f;
            float   currStrength = 0f, newStrength = 1f;

            for (float x = xStartWU; x < xEndWU; x += stepWU)
            {
                for (float z = zStartWU; z < zEndWU; z += stepWU)
                {
                    //Need to rotate x,z around the pivot by the rotation angle
                    locationWU = new Vector3(x, spawnInfo.m_hitLocationWU.y, z);
                    locationWU = Gaia.GaiaUtils.RotatePointAroundPivot(locationWU, spawnInfo.m_hitLocationWU, new Vector3(0f, spawnInfo.m_spawnRotationY, 0f));

                    //Now normalise the result
                    xRotNU = (locationWU.x / widthWU) + 0.5f;
                    zRotNU = (locationWU.z / depthWU) + 0.5f;

                    //Drop out if out of bounds
                    if (xRotNU < 0f || xRotNU >= 1f || zRotNU < 0f || zRotNU > 1f)
                    {
                        continue;
                    }

                    //Only interested in increasing values
                    currStrength = txtMaps[m_textureIndex][zRotNU, xRotNU];
                    newStrength  = m_textureHM[(x - xStartWU) / radiusWU, (z - zStartWU) / radiusWU];
                    if (newStrength > currStrength)
                    {
                        float delta      = newStrength - currStrength;
                        float theRest    = 1f - currStrength;
                        float adjustment = 0f;
                        if (theRest != 0f)
                        {
                            adjustment = 1f - (delta / theRest);
                        }

                        for (int idx = 0; idx < txtMaps.Count; idx++)
                        {
                            if (idx == m_textureIndex)
                            {
                                txtMaps[idx][zRotNU, xRotNU] = newStrength;
                            }
                            else
                            {
                                txtMaps[idx][zRotNU, xRotNU] *= adjustment;
                            }
                        }
                    }
                }
            }

            spawnInfo.m_spawner.SetTextureMapsDirty();
        }