public void CollectMeshInfo(int maxLodLevel) { lodMeshInfos = new LodMeshInfo[maxLodLevel + 1]; for (int lod = 0; lod <= maxLodLevel; lod++) { lodMeshInfos[lod] = new LodMeshInfo(); if (layerType == LayerType.SingleMesh_Max4Layer) { lodMeshInfos[lod].layerIndices = new int[1]; lodMeshInfos[lod].layerIndices[0] = 0; } else { lodMeshInfos[lod].layerIndices = new int[trees[0].validNums.Length]; int maxIndex = trees[0].GetMaxValidNumIndex(); for (int i = 0; i < lodMeshInfos[lod].layerIndices.Length; i++) { lodMeshInfos[lod].layerIndices[i == maxIndex ? 0 : (i < maxIndex ? i + 1 : i)] = i; } } lodMeshInfos[lod].intVertices = TerrainToMeshTool.GetIntVertices(trees[lod]); lodMeshInfos[lod].vecNormals = TerrainToMeshTool.GetNormals(lodMeshInfos[lod].intVertices, terrainData); lodMeshInfos[lod].indices = TerrainToMeshTool.GetIndices(trees[lod], lodMeshInfos[lod].layerIndices); } }
public void CreateGridObjects() { TerrainData terrainData = terrain.terrainData; float[,] heights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight); Material matBase = new Material(Shader.Find("Diffuse")); matBase.mainTexture = TerrainToMeshTool.BakeBaseTexture(terrainData); List <Material> matAdd = new List <Material>(); List <Material> matFirst = new List <Material>(); for (int l = 0; l < terrainData.alphamapLayers; l++) { matAdd.Add(TerrainToMeshTool.GetMaterial(terrainData, l, false)); matFirst.Add(TerrainToMeshTool.GetMaterial(terrainData, l, true)); } int w = terrainData.heightmapWidth - 1; int gridNumX = w / gridSize; tiles = new TerrainToMeshTile[gridNumX * gridNumX]; for (int x = 0; x < gridNumX; x++) { for (int y = 0; y < gridNumX; y++) { GameObject objGrid = new GameObject("mesh_" + x + "_" + y); objGrid.transform.SetParent(this.transform, false); TerrainToMeshTile tile = objGrid.AddComponent <TerrainToMeshTile>(); tiles[y * gridNumX + x] = tile; tile.matBase = matBase; tile.matAdd = matAdd; tile.matFirst = matFirst; tile.roots = roots; tile.trees = new Node[roots.Length]; tile.lodLevel = -1; tile.terrainData = terrainData; tile.heights = heights; for (int i = 0; i < roots.Length; i++) { tile.trees[i] = roots[i].FindSizeNode(x * gridSize, y * gridSize, gridSize); } } } for (int x = 0; x < gridNumX; x++) { for (int y = 0; y < gridNumX; y++) { // 2 //1 3 // 0 tiles[y * gridNumX + x].adjacencies[0] = y > 0 ? tiles[(y - 1) * gridNumX + x] : null; tiles[y * gridNumX + x].adjacencies[2] = y < gridNumX - 1 ? tiles[(y + 1) * gridNumX + x] : null; tiles[y * gridNumX + x].adjacencies[1] = x > 0 ? tiles[y * gridNumX + x - 1] : null; tiles[y * gridNumX + x].adjacencies[3] = x < gridNumX - 1 ? tiles[y * gridNumX + x + 1] : null; } } Update(); }
public void UpdateChildren() { lodLevel = newLodLevel; while (transform.childCount > 0) { DestroyImmediate(transform.GetChild(0).gameObject); } SetNodeSkirts(trees[lodLevel]); if (lodLevel <= 1) { int[] layerIndices = new int[trees[0].validNums.Length]; int maxIndex = trees[0].GetMaxValidNumIndex(); for (int i = 0; i < trees[0].validNums.Length; i++) { layerIndices[i == maxIndex ? 0 : (i < maxIndex ? i + 1 : i)] = i; } Mesh mesh = TerrainToMeshTool.CreateMesh(trees[lodLevel], terrainData, heights, layerIndices); GameObject obj = new GameObject("layer_lod_" + lodLevel); MeshRenderer renderer = obj.AddComponent <MeshRenderer>(); MeshFilter filter = obj.AddComponent <MeshFilter>(); obj.transform.SetParent(transform, false); filter.sharedMesh = mesh; Material[] sharedMaterials = new Material[mesh.subMeshCount]; for (int i = 0; i < mesh.subMeshCount; i++) { sharedMaterials[i] = (i == 0) ? matFirst[layerIndices[i]] : matAdd [layerIndices[i]]; } renderer.sharedMaterials = sharedMaterials; } else { Mesh mesh = TerrainToMeshTool.CreateMesh(trees[lodLevel], terrainData, heights, new int[] { 0 }); GameObject obj = new GameObject("base_lod_" + lodLevel); MeshRenderer renderer = obj.AddComponent <MeshRenderer>(); MeshFilter filter = obj.AddComponent <MeshFilter>(); renderer.sharedMaterial = matBase; filter.sharedMesh = mesh; obj.transform.SetParent(transform, false); } }
public override void OnInspectorGUI() { base.OnInspectorGUI(); if (converter.terrain) { GUILayout.BeginHorizontal(); if (GUILayout.Button("测试")) { converter.bakedControlTexture = TerrainToMeshTool.BakeTextureIndex(converter.terrain.terrainData); } if (GUILayout.Button("生成分层网格")) { converter.trees = new LodNodeTree[converter.maxLodLevel + 1]; for (int i = 0; i <= converter.maxLodLevel; i++) { converter.trees[i] = new LodNodeTree(); float error = converter.minError * Mathf.Pow(Mathf.Pow(converter.maxError / converter.minError, 1.0f / (converter.maxLodLevel)), i); Node tempNode = CreateNode(error, converter.gridSize); List <byte> bytes = new List <byte>(); tempNode.ToBytes(bytes); converter.trees[i].tree = bytes.ToArray(); converter.trees[i].alphaLayers = tempNode.GetAlphaBytes(); } converter.LoadNodes(); converter.ClearChildren(); converter.CollectInfos(); if (converter.staticLodMesh) { converter.CreateStaticMeshes(); converter.ClearCollectedInfo(); } else { converter.Update(); } } GUILayout.EndHorizontal(); } EditorGUILayout.LabelField("内存", converter.GetMemorySize().ToString()); }
Node CreateNode(float maxError, int maxSize) { int w = converter.terrain.terrainData.heightmapWidth; int h = converter.terrain.terrainData.heightmapHeight; float[,] heights = converter.terrain.terrainData.GetHeights(0, 0, w, h); int aw = converter.terrain.terrainData.alphamapWidth; int ah = converter.terrain.terrainData.alphamapHeight; float[,,] alphamaps = converter.terrain.terrainData.GetAlphamaps(0, 0, aw, ah); Node root = new Node(0, 0, w - 1); AddNode(root); //统计不透明的格子数量 root.PostorderTraversal((Node node) => { node.validNums = new int[converter.terrain.terrainData.alphamapLayers]; for (int alphaLayer = 0; alphaLayer < node.validNums.Length; alphaLayer++) { if (node.size == 1) { node.validNums[alphaLayer] = TestAlphaMap(alphamaps, node.x * aw / (w - 1), node.y * ah / (h - 1), aw / (w - 1), alphaLayer) ? 1 : 0; } else { node.validNums[alphaLayer] = node.childs[0].validNums[alphaLayer] + node.childs[1].validNums[alphaLayer] + node.childs[2].validNums[alphaLayer] + node.childs[3].validNums[alphaLayer]; } } }); //合并格子 for (int m = 1; 1 << m < w; m++) { int step = 1 << m; if (step < maxSize) { root.TraversalSize(step, (Node node) => { bool allChildrenIsMerged = node.childs != null && node.childs[0].childs == null && node.childs[1].childs == null && node.childs[2].childs == null && node.childs[3].childs == null; if (allChildrenIsMerged) { float childErrorSum = node.childs[0].error + node.childs[1].error + node.childs[2].error + node.childs[3].error; float error = childErrorSum * 0.3f + GetHeightError(heights, node.x, node.y, node.size, out node.swapEdge) * converter.terrain.terrainData.size.y; if (error < maxError && CheckSourrond(root, node.x, node.y, node.size)) { node.error = error; node.childs = null; } } }); } } //为了消除T接缝,如果相邻格子比自己大,则靠近大格子的两个三角形要合并为一个 root.PreorderTraversal((Node node) => { if (node.childs != null) { //x - 1 if (node.childs[0].childs == null && node.childs[2].childs == null && TerrainToMeshTool.IsSizeLeaf(root, node.x - 1, node.y, node.size)) { node.mergeTriangle |= 1 << 1; } //y - 1 if (node.childs[0].childs == null && node.childs[1].childs == null && TerrainToMeshTool.IsSizeLeaf(root, node.x, node.y - 1, node.size)) { node.mergeTriangle |= 1 << 0; } //x + 1 if (node.childs[1].childs == null && node.childs[3].childs == null && TerrainToMeshTool.IsSizeLeaf(root, node.x + node.size + 1, node.y, node.size)) { node.mergeTriangle |= 1 << 3; } //y + 1 if (node.childs[2].childs == null && node.childs[3].childs == null && TerrainToMeshTool.IsSizeLeaf(root, node.x, node.y + node.size + 1, node.size)) { node.mergeTriangle |= 1 << 2; } } }); return(root); }
//创建某个级别的LOD的网格 public List <Renderer> CreateRenderersForOneLodLevel(int lod) { List <Renderer> lodRenderers = new List <Renderer>(10); if (lod >= 0 && lod < lodMeshInfos.Length) { lodRenderers.Clear(); if (lod < 2) { bool[] layerValid = new bool[lodMeshInfos[lod].layerIndices.Length]; if (layerType == LayerType.SubMesh) { for (int i = 0; i < lodMeshInfos[lod].layerIndices.Length; i++) { if (lodMeshInfos[lod].indices[lodMeshInfos[lod].layerIndices[i]].Length > 0) { layerValid[i] = true; } } Mesh mesh = TerrainToMeshTool.CreateMesh(terrainData, heights, lodMeshInfos[lod].intVertices, lodMeshInfos[lod].vecNormals, lodMeshInfos[lod].indices, -1); MeshRenderer renderer = CreateMeshRenderer_SubMesh(mesh, layerValid, lodMeshInfos[lod].layerIndices, lod); if (renderer) { lodRenderers.Add(renderer); } } else if (layerType == LayerType.SingleMesh_Max4Layer) { Mesh mesh = TerrainToMeshTool.CreateMesh(terrainData, heights, lodMeshInfos[lod].intVertices, lodMeshInfos[lod].vecNormals, lodMeshInfos[lod].indices, 0); MeshRenderer renderer = CreateMeshRenderer_SingleMesh(mesh, lod); if (renderer) { lodRenderers.Add(renderer); } } else if (layerType == LayerType.SingleMesh_LayerIndexTexture) { Mesh mesh = TerrainToMeshTool.CreateMesh(terrainData, heights, lodMeshInfos[lod].intVertices, lodMeshInfos[lod].vecNormals, lodMeshInfos[lod].indices, 0); MeshRenderer renderer = CreateMeshRenderer_SingleMesh_TextureIndex(mesh, lod); if (renderer) { lodRenderers.Add(renderer); } } else { for (int i = 0; i < lodMeshInfos[lod].layerIndices.Length; i++) { Mesh mesh = TerrainToMeshTool.CreateMesh(terrainData, heights, lodMeshInfos[lod].intVertices, lodMeshInfos[lod].vecNormals, lodMeshInfos[lod].indices, i); MeshRenderer renderer = CreateMeshRenderer_OneOfMultiMesh(mesh, lodMeshInfos[lod].layerIndices[i], i == 0, lod); if (renderer) { lodRenderers.Add(renderer); } } } } else { List <TerrainToMeshTool.VecInt3> intVertices = TerrainToMeshTool.GetIntVertices(trees[lod]); List <TerrainToMeshTool.VecNormal> vecNormals = TerrainToMeshTool.GetNormals(intVertices, terrainData); List <int[]> indices = TerrainToMeshTool.GetIndices(trees[lod], new int[] { 0 }); Mesh mesh = TerrainToMeshTool.CreateMesh(terrainData, heights, intVertices, vecNormals, indices, 0); GameObject obj = new GameObject("base_lod_" + lod); MeshRenderer renderer = obj.AddComponent <MeshRenderer>(); MeshFilter filter = obj.AddComponent <MeshFilter>(); renderer.sharedMaterial = matBase; filter.sharedMesh = mesh; obj.transform.SetParent(transform, false); lodRenderers.Add(renderer); } } return(lodRenderers); }
public void CollectInfos() { TerrainData terrainData = terrain.terrainData; Texture2D baseTexture = TerrainToMeshTool.BakeBaseTexture(terrain.terrainData); List <Material> matAdd = new List <Material>(); List <Material> matFirst = new List <Material>(); Material matBase = new Material(shaderBase); matBase.SetTexture("_MainTex", baseTexture); float[,] heights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight); Texture2DArray texArray = null; Material terrainMaterial = new Material(Shader.Find("Nature/Terrain/Diffuse")); if (layerType == LayerType.SingleMesh_LayerIndexTexture) { bakedControlTexture = TerrainToMeshTool.BakeTextureIndex(terrain.terrainData); { Texture firstTexture = terrainData.splatPrototypes[0].texture; texArray = new Texture2DArray(firstTexture.width, firstTexture.height, terrainData.splatPrototypes.Length, TextureFormat.ARGB32, true, true); for (int i = 0; i < terrainData.splatPrototypes.Length; i++) { texArray.SetPixels32(terrainData.splatPrototypes[i].texture.GetPixels32(), i); } texArray.Apply(); } if (terrainTextureIndexMaterial == null) { terrainTextureIndexMaterial = new Material(Shader.Find("Mobile/TerrainTextureIndex")); } else { terrainTextureIndexMaterial.shader = Shader.Find("Mobile/TerrainTextureIndex"); } terrainTextureIndexMaterial.SetTexture("_IndexControl", bakedControlTexture); terrainTextureIndexMaterial.SetTexture("_TexArray", texArray); terrainTextureIndexMaterial.SetFloat("_TexArrayNum", terrainData.splatPrototypes.Length); terrainTextureIndexMaterial.SetVectorArray("_ScaleOffset", GetScaleOffsets(terrainData)); } else if (layerType == LayerType.SingleMesh_Max4Layer) { bakedControlTexture = TerrainToMeshTool.BakeControlTexture(terrain.terrainData, roots[0], gridSize, 4); } else { for (int l = 0; l < terrainData.alphamapLayers; l++) { LayerProperty lp = l < layerProperties.Length ? layerProperties[l] : null; matAdd.Add(TerrainToMeshTool.GetMaterial(terrain, l, shaderAdd, lp)); matFirst.Add(TerrainToMeshTool.GetMaterial(terrain, l, shaderFirst, lp)); } } int w = terrainData.heightmapWidth - 1; int gridNumX = w / gridSize; tiles = new TerrainToMeshTile[gridNumX * gridNumX]; for (int x = 0; x < gridNumX; x++) { for (int y = 0; y < gridNumX; y++) { GameObject objGrid = new GameObject("mesh_" + x + "_" + y); objGrid.transform.SetParent(GetRootTransform(), false); TerrainToMeshTile tile = objGrid.AddComponent <TerrainToMeshTile>(); tiles[y * gridNumX + x] = tile; tile.matBase = matBase; tile.matAdd = matAdd; tile.matFirst = matFirst; tile.lodLevel = -1; tile.terrainData = terrainData; tile.heights = heights; tile.roots = roots; tile.trees = new Node[roots.Length]; tile.layerType = layerType; tile.lodPower = lodPower; tile.terrainMaterial = terrainMaterial; tile.bakedControlTexture = bakedControlTexture; tile.terrainTextureIndexMaterial = terrainTextureIndexMaterial; tile.texArray = texArray; for (int lod = 0; lod < roots.Length; lod++) { tile.trees[lod] = roots[lod].FindSizeNode(x * gridSize, y * gridSize, gridSize); TerrainToMeshTool.SetNodeSkirts(tile.trees[lod], tile.trees[lod]); } } } for (int x = 0; x < gridNumX; x++) { for (int y = 0; y < gridNumX; y++) { tiles[y * gridNumX + x].CollectMeshInfo(maxLodLevel); } } }