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