void Start()
    {
        /*
         * if (!m_UsedCamera)
         *  m_UsedCamera = Camera.main;
         */

        for (int i = 0; i < m_ManagedTerrains.Length; i++)
        {
            TreeSystemTerrain terrain = m_ManagedTerrains[i];

            if (terrain.m_ManagedTerrain != null)
            {
                terrain.m_ManagedTerrain.drawTreesAndFoliage = false;
            }

            terrain.m_CellsStructured = new TreeSystemStructuredTrees[terrain.m_CellCount, terrain.m_CellCount];

            CullingGroup cullingGroup = new CullingGroup();

            BoundingSphere[] bounds = new BoundingSphere[terrain.m_Cells.Length];

            for (int j = 0; j < terrain.m_Cells.Length; j++)
            {
                // TODO: maybe allocate dinamically at runtime based on cell visibility to save memory
                terrain.m_Cells[j].m_InstanceData = new TreeSystemLODInstance[terrain.m_Cells[j].m_Instances.Length];

                // TODO: structure cell data

                // Create the culling group data
                bounds[j] = new BoundingSphere(terrain.m_Cells[j].m_BoundsSphere.m_CenterRadius);

                // Structure cell data
                RowCol pos = terrain.m_Cells[j].m_Position;
                terrain.m_CellsStructured[pos.m_Row, pos.m_Col] = terrain.m_Cells[j];
            }

            if (!m_Settings.m_UsedCamera)
            {
                cullingGroup.targetCamera = Camera.main;
            }
            else
            {
                cullingGroup.targetCamera = m_Settings.m_UsedCamera;
            }

            cullingGroup.SetBoundingSpheres(bounds);
            cullingGroup.SetBoundingSphereCount(bounds.Length);

            // Save the bounds just in case we might need them
            terrain.m_CullingGroupSpheres = bounds;
            terrain.m_CullingGroup        = cullingGroup;
        }

        if (m_Settings.m_ApplyTreeColliders)
        {
            SetApplyColliders(true);
        }
    }
    void Update()
    {
        m_DataIssuedMeshTrees    = 0;
        m_DataIssuedTerrainCells = 0;
        m_DataIssuedTerrains     = 0;
        m_DataIssuesDrawCalls    = 0;

        Camera camera = Camera.main;

        // Calculate planes and camera position
        ExtractPlanes(m_PlanesTemp, camera.projectionMatrix * camera.worldToCameraMatrix);
        m_CameraPosTemp = camera.transform.position;

        // Update the wind block data
        for (int proto = 0; proto < m_ManagedPrototypes.Length; proto++)
        {
            TreeSystemLODData[] lod = m_ManagedPrototypes[proto].m_LODData;
            for (int i = 0; i < lod.Length; i++)
            {
                lod[i].CopyBlock();
            }
        }

        float x, y, z;
        float treeDistSqr = m_Settings.m_MaxTreeDistance * m_Settings.m_MaxTreeDistance;

        for (int i = 0; i < m_ManagedTerrains.Length; i++)
        {
            TreeSystemTerrain terrain = m_ManagedTerrains[i];

            // Get closest point
            Vector3 pt = terrain.m_ManagedTerrainBounds.ClosestPoint(m_CameraPosTemp);

            // Check if terrain is within reach range
            x = pt.x - m_CameraPosTemp.x;
            y = pt.y - m_CameraPosTemp.y;
            z = pt.z - m_CameraPosTemp.z;

            float distToTerrain = x * x + y * y + z * z;

            if (distToTerrain < treeDistSqr)
            {
                // If the terrain is within tree range
                ProcessTerrain(terrain, ref treeDistSqr);
                m_DataIssuedTerrains++;
            }
        }

        if (Time.frameCount % 60 == 0)
        {
            Debug.Log("Issued terrains: " + m_DataIssuedTerrains + " issued cells: " + m_DataIssuedTerrainCells + " issued trees:" + m_DataIssuedMeshTrees + " issued draw calls:" + m_DataIssuesDrawCalls);
        }
    }
Exemple #3
0
    private void ProcessTerrain(TreeSystemTerrain terrain, ref float treeDistSqr, ref float shadowDistSqr)
    {
        TreeSystemStructuredTrees[] cells = terrain.m_Cells;
        CullingGroup culling = terrain.m_CullingGroup;

        float x, y, z;

        // TODO: calculate based on bounds index the cells that we'll iterate like the grass system
        // We won't require to iterate all the cells but the neighbors of the current cell only

        // TODO: only get the data from the culling group API at the moment

        // Go bounds by bounds
        for (int cellIdx = 0; cellIdx < cells.Length; cellIdx++)
        {
            TreeSystemStructuredTrees cell = cells[cellIdx];

            // If we don't have any tree skip this cell
            if (cell.m_Instances.Length <= 0)
            {
                continue;
            }

            // And also check if the bounds are visible
            if (culling.IsVisible(cellIdx) == false)
            {
                continue;
            }

            // Get closest point to cell
            Vector3 pt = cell.m_BoundsBox.ClosestPoint(m_CameraPosTemp);

            x = pt.x - m_CameraPosTemp.x;
            y = pt.y - m_CameraPosTemp.y;
            z = pt.z - m_CameraPosTemp.z;

            float distToCell = x * x + y * y + z * z;

            if (distToCell < treeDistSqr && GeometryUtility.TestPlanesAABB(m_PlanesTemp, cell.m_BoundsBox))
            {
                // TODO: the same process when we are going to have terrain sub-cells
                ProcessTerrainCell(cell, ref treeDistSqr, ref shadowDistSqr);

                // If it's visible
                m_DataIssuedTerrainCells++;
            }
            // TODO: See for the case in which we are at the intersection of 4 cells and the cells are not visible to the frustum
            // BUT they have trees that 'might' cast shadows inside the frustum. Do that with a special thing that only checks the trees
            // for the shadow distance, and that's all
            // else if (m_ApplyShadowPoppingCorrection && distToCell < shadowDistSqr) { }
        }
    }
    void Start()
    {
        for (int i = 0; i < m_ManagedTerrains.Length; i++)
        {
            TreeSystemTerrain terrain = m_ManagedTerrains[i];
            terrain.m_ManagedTerrain.drawTreesAndFoliage = false;

            for (int j = 0; j < terrain.m_Cells.Length; j++)
            {
                // TODO: maybe allocate dinamically at runtime based on cell visibility to save memory
                terrain.m_Cells[j].m_InstanceData = new TreeSystemLODInstance[terrain.m_Cells[j].m_Instances.Length];

                // TODO: structure cell data
            }
        }
    }
    private void ProcessTerrain(TreeSystemTerrain terrain, ref float treeDistSqr)
    {
        TreeSystemStructuredTrees[] cells = terrain.m_Cells;

        float x, y, z;

        // TODO: calculate based on bounds index the cells that we'll iterate like the grass system
        // We won't require to iterate all the cells but the neighbors of the current cell only

        // Go bounds by bounds
        for (int cellIdx = 0; cellIdx < cells.Length; cellIdx++)
        {
            TreeSystemStructuredTrees cell = cells[cellIdx];

            // If we don't have any tree skip this cell
            if (cell.m_Instances.Length <= 0)
            {
                continue;
            }

            // Get closest point to cell
            Vector3 pt = cell.m_Bounds.ClosestPoint(m_CameraPosTemp);

            x = pt.x - m_CameraPosTemp.x;
            y = pt.y - m_CameraPosTemp.y;
            z = pt.z - m_CameraPosTemp.z;

            float distToCell = x * x + y * y + z * z;

            if (distToCell < treeDistSqr && GeometryUtility.TestPlanesAABB(m_PlanesTemp, cell.m_Bounds))
            {
                // TODO: the same process when we are going to have terrain sub-cells

                ProcessTerrainCell(cell, ref treeDistSqr);

                // If it's visible
                m_DataIssuedTerrainCells++;
            }
        }
    }
    void Start()
    {
        if (!m_UsedCamera)
        {
            m_UsedCamera = Camera.main;
        }

        for (int i = 0; i < m_ManagedTerrains.Length; i++)
        {
            TreeSystemTerrain terrain = m_ManagedTerrains[i];
            terrain.m_ManagedTerrain.drawTreesAndFoliage = false;

            CullingGroup cullingGroup = new CullingGroup();

            BoundingSphere[] bounds = new BoundingSphere[terrain.m_Cells.Length];

            for (int j = 0; j < terrain.m_Cells.Length; j++)
            {
                // TODO: maybe allocate dinamically at runtime based on cell visibility to save memory
                terrain.m_Cells[j].m_InstanceData = new TreeSystemLODInstance[terrain.m_Cells[j].m_Instances.Length];

                // TODO: structure cell data

                // Create the culling group data
                bounds[j] = new BoundingSphere(terrain.m_Cells[j].m_BoundsSphere.m_CenterRadius);
            }

            cullingGroup.targetCamera = m_UsedCamera;

            cullingGroup.SetBoundingSpheres(bounds);
            cullingGroup.SetBoundingSphereCount(bounds.Length);

            // Save the bounds just in case we might need them
            terrain.m_CullingGroupSpheres = bounds;
            terrain.m_CullingGroup        = cullingGroup;
        }
    }
Exemple #7
0
    private void ProcessTerrain(TreeSystemTerrain terrain, ref float colliderDistSqr)
    {
        TreeSystemStructuredTrees[] cells = terrain.m_Cells;
        float x, y, z;

        for (int cellIdx = 0; cellIdx < cells.Length; cellIdx++)
        {
            TreeSystemStructuredTrees cell = cells[cellIdx];

            // Get closest point to cell
            Vector3 pt = cell.m_BoundsBox.ClosestPoint(m_CameraPosTemp);

            x = pt.x - m_CameraPosTemp.x;
            y = pt.y - m_CameraPosTemp.y;
            z = pt.z - m_CameraPosTemp.z;

            float distToCell = x * x + y * y + z * z;

            if (distToCell < colliderDistSqr)
            {
                ProcessTerrainCell(cell, ref colliderDistSqr);
            }
        }
    }
Exemple #8
0
    IEnumerator CollisionUpdate()
    {
        for (;;)
        {
            m_CameraPosTemp = m_WatchedTransform.position;

            float x = m_CameraPosTemp.x - m_LastPosition.x;
            float y = m_CameraPosTemp.y - m_LastPosition.y;
            float z = m_CameraPosTemp.z - m_LastPosition.z;

            float distWalked = x * x + y * y + z * z;

            // If we didn't walked enough, return
            if (distWalked > m_CollisionRefreshDistance * m_CollisionRefreshDistance)
            {
                // Update last position
                m_LastPosition = m_CameraPosTemp;

                // Reset counter
                m_DataIssuedActiveColliders = 0;

                // Reset all the cache's data
                foreach (TreeCollisionCache cache in m_Cache.Values)
                {
                    if (cache != null)
                    {
                        cache.Reset();
                    }
                }

                // Refresh eveything
                float collDistSqr = m_CollisionDistance * m_CollisionDistance;

                for (int i = 0; i < m_OwnerSystem.m_ManagedTerrains.Length; i++)
                {
                    TreeSystemTerrain terrain = m_OwnerSystem.m_ManagedTerrains[i];

                    // Get closest point
                    Vector3 pt = terrain.m_ManagedTerrainBounds.ClosestPoint(m_CameraPosTemp);

                    // Check if terrain is within reach range
                    x = pt.x - m_CameraPosTemp.x;
                    y = pt.y - m_CameraPosTemp.y;
                    z = pt.z - m_CameraPosTemp.z;

                    float distToTerrain = x * x + y * y + z * z;

                    // Enable/disable culling group execution based on terrain distance, since we don't want all of them running around
                    if (distToTerrain < collDistSqr)
                    {
                        ProcessTerrain(terrain, ref collDistSqr);
                    }
                }

                // Update the stats for active/cached colliders
                m_OwnerSystem.m_DataIssuedActiveColliders = m_DataIssuedActiveColliders;
            }

            yield return(new WaitForSeconds(WAIT_TIME));
        }
    }
    private void ProcessTerrain(TreeSystemTerrain terrain, ref float treeDistSqr, ref float shadowDistSqr)
    {
        // Get only the visible cells around the player
        int     cellsCount   = terrain.m_CellCount;
        Vector3 terrainLocal = terrain.m_ManagedTerrainWorldToLocal.MultiplyPoint3x4(m_CameraPosTemp);
        Vector3 terrSize     = terrain.m_ManagedTerrainSizes;

        RowCol gridPos;

        GetTerrainGridIndex(out gridPos, terrainLocal, cellsCount, terrSize);

        // Get neighbors
        GetNeightbors(m_TempNeighbors, gridPos.m_Row, gridPos.m_Col, terrain.m_CellsStructured, ref cellsCount);

        if (m_TempNeighbors.Count <= 0)
        {
            return;
        }

        CullingGroup culling      = terrain.m_CullingGroup;
        bool         useOcclusion = m_Settings.m_UseOcclusion;

        // float x, y, z;

        // TODO: calculate based on bounds index the cells that we'll iterate like the grass system
        // We won't require to iterate all the cells but the neighbors of the current cell only

        // TODO: only get the data from the culling group API at the moment

        // Go bounds by bounds
        for (int cellIdx = 0; cellIdx < m_TempNeighbors.Count; cellIdx++)
        {
            TreeSystemStructuredTrees cell = m_TempNeighbors[cellIdx];

            // If we don't have any tree skip this cell
            if (cell.m_Instances.Length <= 0)
            {
                continue;
            }

            // And also check if the bounds are visible
            if (useOcclusion && culling.IsVisible(cellIdx) == false)
            {
                continue;
            }

            // Get closest point to cell

            /*
             * Vector3 pt = cell.m_BoundsBox.ClosestPoint(m_CameraPosTemp);
             *
             * x = pt.x - m_CameraPosTemp.x;
             * y = pt.y - m_CameraPosTemp.y;
             * z = pt.z - m_CameraPosTemp.z;
             *
             * float distToCell = x * x + y * y + z * z;
             */

            float distToCell = cell.m_BoundsBox.SqrDistance(m_CameraPosTemp);

            if (distToCell < treeDistSqr && GeometryUtility.TestPlanesAABB(m_PlanesTemp, cell.m_BoundsBox))
            {
                // TODO: the same process when we are going to have terrain sub-cells
                ProcessTerrainCell(cell, ref treeDistSqr, ref shadowDistSqr);

                // If it's visible
                m_DataIssuedTerrainCells++;
            }
            // TODO: See for the case in which we are at the intersection of 4 cells and the cells are not visible to the frustum
            // BUT they have trees that 'might' cast shadows inside the frustum. Do that with a special thing that only checks the trees
            // for the shadow distance, and that's all
            // else if (m_ApplyShadowPoppingCorrection && distToCell < shadowDistSqr) { }
        }
    }
Exemple #10
0
    void Update()
    {
        m_DataIssuedMeshTrees        = 0;
        m_DataIssuedShadows          = 0;
        m_DataIssuedTerrainCells     = 0;
        m_DataIssuedTerrainCellsFull = 0;
        m_DataIssuedTerrains         = 0;
        m_DataIssuesDrawCalls        = 0;

        Camera camera = m_Settings.m_UsedCamera ? m_Settings.m_UsedCamera : Camera.main;

        // Calculate planes and camera position
        ExtractPlanes(m_PlanesTemp, camera.projectionMatrix * camera.worldToCameraMatrix);
        m_CameraPosTemp = camera.transform.position;

        // Update the wind block data
        for (int proto = 0; proto < m_ManagedPrototypes.Length; proto++)
        {
            TreeSystemLODData[] lod = m_ManagedPrototypes[proto].m_LODData;
            for (int i = 0; i < lod.Length; i++)
            {
                lod[i].CopyBlock();
            }
        }

        float treeDistSqr   = m_Settings.m_MaxTreeDistance * m_Settings.m_MaxTreeDistance;
        float shadowDistSqr = m_Settings.m_ShadowDrawDistance * m_Settings.m_ShadowDrawDistance;

        for (int i = 0; i < m_ManagedTerrains.Length; i++)
        {
            TreeSystemTerrain terrain = m_ManagedTerrains[i];

            // Get closest point

            /*
             * Vector3 pt = terrain.m_ManagedTerrainBounds.ClosestPoint(m_CameraPosTemp);
             *
             * // Check if terrain is within reach range
             * x = pt.x - m_CameraPosTemp.x;
             * y = pt.y - m_CameraPosTemp.y;
             * z = pt.z - m_CameraPosTemp.z;
             *
             * float distToTerrain = x * x + y * y + z * z;
             */

            float distToTerrain = terrain.m_ManagedTerrainBounds.SqrDistance(m_CameraPosTemp);

            // Enable/disable culling group execution based on terrain distance, since we don't want all of them running around
            if (distToTerrain < treeDistSqr)
            {
                if (terrain.m_CullingGroup.enabled == false)
                {
                    terrain.m_CullingGroup.enabled = true;
                }

                // If the terrain is within tree range
                ProcessTerrain(terrain, ref treeDistSqr, ref shadowDistSqr);
                m_DataIssuedTerrains++;
            }
            else
            {
                if (terrain.m_CullingGroup.enabled)
                {
                    terrain.m_CullingGroup.enabled = false;
                }
            }
        }

#if UNITY_EDITOR
#endif
    }
Exemple #11
0
    private TreeSystemTerrain ProcessTerrain(Terrain terrain, int cellSize, GameObject cellHolder)
    {
        TreeSystemTerrain systemTerrain = new TreeSystemTerrain();

        // Set system terrain data
        systemTerrain.m_ManagedTerrain       = terrain;
        systemTerrain.m_ManagedTerrainBounds = terrain.GetComponent <TerrainCollider>().bounds;
        systemTerrain.m_CellCount            = TerrainUtils.GetCellCount(terrain, cellSize);
        systemTerrain.m_CellSize             = cellSize;

        int cellCount;

        BoxCollider[,] collidersBox;
        SphereCollider[,] collidersSphere;

        // Gridify terrain
        TerrainUtils.Gridify(terrain, cellSize, out cellCount, out collidersBox, out collidersSphere, cellHolder, null);

        // Temporary structured data
        TreeSystemStructuredTrees[,] str           = new TreeSystemStructuredTrees[cellCount, cellCount];
        List <TreeSystemStoredInstance>[,] strInst = new List <TreeSystemStoredInstance> [cellCount, cellCount];
        List <TreeSystemStructuredTrees> list = new List <TreeSystemStructuredTrees>();

        // Insantiate the required data
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                TreeSystemStructuredTrees s = new TreeSystemStructuredTrees();

                // Set the bounds, all in world space
                s.m_BoundsBox    = collidersBox[r, c].bounds;
                s.m_BoundsSphere = new TreeSystemBoundingSphere(s.m_BoundsBox.center, collidersSphere[r, c].radius);

                // Set it's new position
                s.m_Position = new RowCol(r, c);

                str[r, c]     = s;
                strInst[r, c] = new List <TreeSystemStoredInstance>();

                list.Add(s);
            }
        }

        TreeInstance[]  terrainTreeInstances = terrain.terrainData.treeInstances;
        TreePrototype[] terrainTreeProto     = terrain.terrainData.treePrototypes;

        Vector3 sizes = terrain.terrainData.size;

        for (int i = 0; i < terrainTreeInstances.Length; i++)
        {
            GameObject proto = terrainTreeProto[terrainTreeInstances[i].prototypeIndex].prefab;

            if (ShouldUsePrefab(proto) < 0)
            {
                continue;
            }

            // Get bounds for that mesh
            Bounds b = proto.transform.Find(proto.name + "_LOD0").gameObject.GetComponent <MeshFilter>().sharedMesh.bounds;

            // Calculate this from normalized terrain space to terrain's local space so that our row/col info are correct.
            // Do the same when testing for cell row/col in which the player is, transform to terrain local space
            Vector3 pos = TerrainUtils.TerrainToTerrainPos(terrainTreeInstances[i].position, terrain);
            int     row = Mathf.Clamp(Mathf.FloorToInt(pos.x / sizes.x * cellCount), 0, cellCount - 1);
            int     col = Mathf.Clamp(Mathf.FloorToInt(pos.z / sizes.z * cellCount), 0, cellCount - 1);

            pos = TerrainUtils.TerrainToWorldPos(terrainTreeInstances[i].position, terrain);
            Vector3 scale = new Vector3(terrainTreeInstances[i].widthScale, terrainTreeInstances[i].heightScale, terrainTreeInstances[i].widthScale);
            float   rot   = terrainTreeInstances[i].rotation;
            int     hash  = TUtils.GetStableHashCode(proto.name);

            Matrix4x4 mtx = Matrix4x4.TRS(pos, Quaternion.Euler(0, rot * Mathf.Rad2Deg, 0), scale);

            TreeSystemStoredInstance inst = new TreeSystemStoredInstance();

            inst.m_TreeHash      = hash;
            inst.m_PositionMtx   = mtx;
            inst.m_WorldPosition = pos;
            inst.m_WorldScale    = scale;
            inst.m_WorldRotation = rot;
            inst.m_WorldBounds   = TUtils.LocalToWorld(ref b, ref mtx);

            strInst[row, col].Add(inst);
        }

        // Generate the mesh that contain all the billboards
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                if (strInst[r, c].Count <= 0)
                {
                    continue;
                }

                // Sort based on the tree hash so that we don't have to do many dictionary look-ups
                strInst[r, c].Sort((x, y) => x.m_TreeHash.CompareTo(y.m_TreeHash));

                // Set the new instances
                str[r, c].m_Instances = strInst[r, c].ToArray();

                // Build the meshes for each cell based on tree type
                List <TreeSystemStoredInstance> singleType = new List <TreeSystemStoredInstance>();
                int lastHash = strInst[r, c][0].m_TreeHash;

                foreach (TreeSystemStoredInstance inst in strInst[r, c])
                {
                    // If we have a new hash, consume all the existing instances
                    if (inst.m_TreeHash != lastHash)
                    {
                        TreeSystemPrototypeData data = GetPrototypeWithHash(lastHash);
                        BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                        singleType.Clear();

                        // Update the hash
                        lastHash = inst.m_TreeHash;
                    }

                    // Add them to a list and when the hash changes begin the next generation
                    singleType.Add(inst);
                }

                if (singleType.Count > 0)
                {
                    TreeSystemPrototypeData data = GetPrototypeWithHash(singleType[0].m_TreeHash);
                    BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                    singleType.Clear();
                }
            }
        }

        // Set the cells that contain the trees to the system terrain
        systemTerrain.m_Cells = list.ToArray();

        // Return it
        return(systemTerrain);
    }
Exemple #12
0
    // TODO: see from the list which trees fit in here
    private TreeSystemTerrain ProcessTerrain(Terrain terrain, int cellSize, GameObject cellHolder, List <GameObject> extraTrees)
    {
        TreeSystemTerrain systemTerrain = new TreeSystemTerrain();

        // Set system terrain data
        systemTerrain.m_ManagedTerrain             = terrain;
        systemTerrain.m_ManagedTerrainBounds       = terrain.GetComponent <TerrainCollider>().bounds;
        systemTerrain.m_ManagedTerrainLocalToWorld = terrain.transform.localToWorldMatrix;
        systemTerrain.m_ManagedTerrainWorldToLocal = terrain.transform.worldToLocalMatrix;
        systemTerrain.m_ManagedTerrainSizes        = terrain.terrainData.size;

        systemTerrain.m_CellCount = TerrainUtils.GetCellCount(terrain, cellSize);
        systemTerrain.m_CellSize  = cellSize;

        int cellCount;

        BoxCollider[,] collidersBox;
        SphereCollider[,] collidersSphere;

        // Gridify terrain
        TerrainUtils.Gridify(terrain, cellSize, out cellCount, out collidersBox, out collidersSphere, cellHolder, null);

        // Temporary structured data
        TreeSystemStructuredTrees[,] str           = new TreeSystemStructuredTrees[cellCount, cellCount];
        List <TreeSystemStoredInstance>[,] strInst = new List <TreeSystemStoredInstance> [cellCount, cellCount];
        List <TreeSystemStructuredTrees> list = new List <TreeSystemStructuredTrees>();

        // Insantiate the required data
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                TreeSystemStructuredTrees s = new TreeSystemStructuredTrees();

                // Set the bounds, all in world space
                s.m_BoundsBox    = collidersBox[r, c].bounds;
                s.m_BoundsSphere = new TreeSystemBoundingSphere(s.m_BoundsBox.center, collidersSphere[r, c].radius);

                // Set it's new position
                s.m_Position = new RowCol(r, c);

                str[r, c]     = s;
                strInst[r, c] = new List <TreeSystemStoredInstance>();

                list.Add(s);
            }
        }

        // Delete cells since they might cause physics problems
        if (m_DeleteCellsAfterGridify)
        {
            for (int i = 0; i < cellCount; i++)
            {
                for (int j = 0; j < cellCount; j++)
                {
                    DestroyImmediate(collidersBox[i, j].gameObject);
                }
            }
        }

        int treeInstancesCount = 0, treeExtraCount = 0;

        TreeInstance[]  terrainTreeInstances = terrain.terrainData.treeInstances;
        TreePrototype[] terrainTreeProto     = terrain.terrainData.treePrototypes;

        Vector3 sizes = terrain.terrainData.size;

        for (int i = 0; i < terrainTreeInstances.Length; i++)
        {
            GameObject proto = terrainTreeProto[terrainTreeInstances[i].prototypeIndex].prefab;

            if (ShouldUsePrefab(proto) < 0)
            {
                continue;
            }

            treeInstancesCount++;

            // Get bounds for that mesh
            Bounds b = proto.transform.Find(proto.name + "_LOD0").gameObject.GetComponent <MeshFilter>().sharedMesh.bounds;

            // Calculate this from normalized terrain space to terrain's local space so that our row/col info are correct.
            // Do the same when testing for cell row/col in which the player is, transform to terrain local space
            Vector3 pos = TerrainUtils.TerrainToTerrainPos(terrainTreeInstances[i].position, terrain);
            int     row = Mathf.Clamp(Mathf.FloorToInt(pos.x / sizes.x * cellCount), 0, cellCount - 1);
            int     col = Mathf.Clamp(Mathf.FloorToInt(pos.z / sizes.z * cellCount), 0, cellCount - 1);

            pos = TerrainUtils.TerrainToWorldPos(terrainTreeInstances[i].position, terrain);
            Vector3 scale = new Vector3(terrainTreeInstances[i].widthScale, terrainTreeInstances[i].heightScale, terrainTreeInstances[i].widthScale);
            float   rot   = terrainTreeInstances[i].rotation;
            int     hash  = TUtils.GetStableHashCode(proto.name);

            Matrix4x4 mtx = Matrix4x4.TRS(pos, Quaternion.Euler(0, rot * Mathf.Rad2Deg, 0), scale);

            TreeSystemStoredInstance inst = new TreeSystemStoredInstance();

            inst.m_TreeHash      = hash;
            inst.m_PositionMtx   = mtx;
            inst.m_WorldPosition = pos;
            inst.m_WorldScale    = scale;
            inst.m_WorldRotation = rot;
            inst.m_WorldBounds   = TUtils.LocalToWorld(ref b, ref mtx);

            strInst[row, col].Add(inst);
        }

        List <GameObject> containedTrees = new List <GameObject>();

        // Change if we're going to use something diff than 50 for max extent
        Bounds terrainExtendedBounds = systemTerrain.m_ManagedTerrainBounds;

        terrainExtendedBounds.Expand(new Vector3(0, 50, 0));

        // Same as a instance with minor diferences
        for (int i = 0; i < extraTrees.Count; i++)
        {
            GameObject treeInstance = extraTrees[i];

            // If the terrain contains the stuff
            if (terrainExtendedBounds.Contains(treeInstance.transform.position) == false)
            {
                continue;
            }

            treeExtraCount++;

            // Add the tree to the list of trees for removal
            containedTrees.Add(treeInstance);

            // Owner
            GameObject proto = GetPrefabOwner(treeInstance);

            // Get bounds for that mesh
            Bounds b = proto.transform.Find(proto.name + "_LOD0").gameObject.GetComponent <MeshFilter>().sharedMesh.bounds;

            // Calculate this from normalized terrain space to terrain's local space so that our row/col info are correct.
            // Do the same when testing for cell row/col in which the player is, transform to terrain local space
            Vector3 pos = TerrainUtils.TerrainToTerrainPos(TerrainUtils.WorldPosToTerrain(treeInstance.transform.position, terrain), terrain);

            int row = Mathf.Clamp(Mathf.FloorToInt(pos.x / sizes.x * cellCount), 0, cellCount - 1);
            int col = Mathf.Clamp(Mathf.FloorToInt(pos.z / sizes.z * cellCount), 0, cellCount - 1);

            pos = treeInstance.transform.position;
            Vector3 scale = treeInstance.transform.localScale;
            float   rot   = treeInstance.transform.rotation.eulerAngles.y * Mathf.Deg2Rad;

            // Set the hash
            int hash = TUtils.GetStableHashCode(proto.name);

            // Set the mtx
            Matrix4x4 mtx = Matrix4x4.TRS(pos, Quaternion.Euler(0, rot * Mathf.Rad2Deg, 0), scale);

            TreeSystemStoredInstance inst = new TreeSystemStoredInstance();

            inst.m_TreeHash      = hash;
            inst.m_PositionMtx   = mtx;
            inst.m_WorldPosition = pos;
            inst.m_WorldScale    = scale;
            inst.m_WorldRotation = rot;
            inst.m_WorldBounds   = TUtils.LocalToWorld(ref b, ref mtx);

            strInst[row, col].Add(inst);
        }

        // Remove the items from the extra trees
        foreach (GameObject tree in containedTrees)
        {
            extraTrees.Remove(tree);
        }

        // Generate the mesh that contain all the billboards
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                if (strInst[r, c].Count <= 0)
                {
                    continue;
                }

                // Sort based on the tree hash so that we don't have to do many dictionary look-ups
                strInst[r, c].Sort((x, y) => x.m_TreeHash.CompareTo(y.m_TreeHash));

                // Set the new instances
                str[r, c].m_Instances = strInst[r, c].ToArray();

                // Build the meshes for each cell based on tree type
                List <TreeSystemStoredInstance> singleType = new List <TreeSystemStoredInstance>();
                int lastHash = strInst[r, c][0].m_TreeHash;

                foreach (TreeSystemStoredInstance inst in strInst[r, c])
                {
                    // If we have a new hash, consume all the existing instances
                    if (inst.m_TreeHash != lastHash)
                    {
                        TreeSystemPrototypeData data = GetPrototypeWithHash(lastHash);

                        if (ShouldBuildBillboardBatch(data.m_TreePrototype))
                        {
                            BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                        }

                        singleType.Clear();

                        // Update the hash
                        lastHash = inst.m_TreeHash;
                    }

                    // Add them to a list and when the hash changes begin the next generation
                    singleType.Add(inst);
                }

                if (singleType.Count > 0)
                {
                    TreeSystemPrototypeData data = GetPrototypeWithHash(singleType[0].m_TreeHash);

                    if (ShouldBuildBillboardBatch(data.m_TreePrototype))
                    {
                        BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                    }

                    singleType.Clear();
                }
            }
        }

        // Set the cells that contain the trees to the system terrain
        systemTerrain.m_Cells = list.ToArray();

        // Print extraction data
        Debug.Log("Extracted for terrain: " + terrain.name + " instance trees: " + treeInstancesCount + " extra trees: " + treeExtraCount);

        // Return it
        return(systemTerrain);
    }