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); } }
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; } }
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); } } }
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) { } } }
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 }
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); }
// 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); }