using System; using System.Collections; using UnityEditor.PackageManager; using UnityEngine; using UnityEngine.UIElements; using static UnityEditor.PlayerSettings; public static class MeshGenerator { public static MeshData GenerateTerrainMesh(float[,] heightMap, float heightMultiplier, AnimationCurve _heightCurve, int levelOfDetail, bool useFlatShading) { AnimationCurve heightCurve = new AnimationCurve(_heightCurve.keys); int meshSimplificationIncrement = (levelOfDetail == 0) ? 1 : levelOfDetail * 2; int borderedSize = heightMap.GetLength(0); int meshSize = borderedSize - 2 * meshSimplificationIncrement; int meshSizeUnsimplified = borderedSize - 2; float topLeftX = (meshSizeUnsimplified - 1) / -2f; float topLeftZ = (meshSizeUnsimplified - 1) / 2f; int verticesPerLine = (meshSize - 1) / meshSimplificationIncrement + 1; MeshData meshData = new MeshData(verticesPerLine, useFlatShading); int[,] vertexIndicesMap = new int[borderedSize, borderedSize]; int meshVertexIndex = 0; int borderVertexIndex = -1; for (int y = 0; y < borderedSize; y += meshSimplificationIncrement) { for (int x = 0; x < borderedSize; x += meshSimplificationIncrement) { bool isBorderVertex = y == 0 || y == borderedSize - 1 || x == 0 || x == borderedSize - 1; if (isBorderVertex) { vertexIndicesMap[x, y] = borderVertexIndex; borderVertexIndex--; } else { vertexIndicesMap[x, y] = meshVertexIndex; meshVertexIndex++; } } } for (int y = 0; y < borderedSize; y += meshSimplificationIncrement) { for (int x = 0; x < borderedSize; x += meshSimplificationIncrement) { int vertexIndex = vertexIndicesMap[x, y]; Vector2 percent = new Vector2((x - meshSimplificationIncrement) / (float)meshSize, (y - meshSimplificationIncrement) / (float)meshSize); float height = heightCurve.Evaluate(heightMap[x, y]) * heightMultiplier; Vector3 vertexPosition = new Vector3(topLeftX + percent.x * meshSizeUnsimplified, height, topLeftZ - percent.y * meshSizeUnsimplified); /* try { ForestData.spawnable.Enqueue(vertexPosition); } catch (Exception e) { Debug.Log(e); }*/ meshData.AddVertex(vertexPosition, percent, vertexIndex); if (x < borderedSize - 1 && y < borderedSize - 1) { int a = vertexIndicesMap[x, y]; int b = vertexIndicesMap[x + meshSimplificationIncrement, y]; int c = vertexIndicesMap[x, y + meshSimplificationIncrement]; int d = vertexIndicesMap[x + meshSimplificationIncrement, y + meshSimplificationIncrement]; meshData.AddTriangle(a, d, c); meshData.AddTriangle(d, a, b); } vertexIndex++; } } meshData.Finish(); return meshData; } } public class MeshData { Vector3[] vertices; int[] triangles; Vector2[] uvs; Vector3[] bakedNormals; Vector3[] borderVertices; int[] borderTriangles; int triangleIndex; int borderTriangleIndex; bool useFlatShading; public MeshData(int verticesPerLine, bool useFlatShading) { this.useFlatShading = useFlatShading; vertices = new Vector3[verticesPerLine * verticesPerLine]; uvs = new Vector2[verticesPerLine * verticesPerLine]; triangles = new int[(verticesPerLine - 1) * (verticesPerLine - 1) * 6]; borderVertices = new Vector3[verticesPerLine * 4 + 4]; borderTriangles = new int[24 * verticesPerLine]; } public void AddVertex(Vector3 vertexPosition, Vector2 uv, int vertexIndex) { if (vertexIndex < 0) { borderVertices[-vertexIndex - 1] = vertexPosition; } else { vertices[vertexIndex] = vertexPosition; uvs[vertexIndex] = uv; } } public void AddTriangle(int a, int b, int c) { if (a < 0 || b < 0 || c < 0) { borderTriangles[borderTriangleIndex] = a; borderTriangles[borderTriangleIndex + 1] = b; borderTriangles[borderTriangleIndex + 2] = c; borderTriangleIndex += 3; } else { triangles[triangleIndex] = a; triangles[triangleIndex + 1] = b; triangles[triangleIndex + 2] = c; triangleIndex += 3; } } Vector3[] CalculateNormals() { Vector3[] vertexNormals = new Vector3[vertices.Length]; int triangleCount = triangles.Length / 3; for (int i = 0; i < triangleCount; i++) { int normalTriangleIndex = i * 3; int vertexIndexA = triangles[normalTriangleIndex]; int vertexIndexB = triangles[normalTriangleIndex + 1]; int vertexIndexC = triangles[normalTriangleIndex + 2]; Vector3 triangleNormal = SurfaceNormalFromIndices(vertexIndexA, vertexIndexB, vertexIndexC); vertexNormals[vertexIndexA] += triangleNormal; vertexNormals[vertexIndexB] += triangleNormal; vertexNormals[vertexIndexC] += triangleNormal; } int borderTriangleCount = borderTriangles.Length / 3; for (int i = 0; i < borderTriangleCount; i++) { int normalTriangleIndex = i * 3; int vertexIndexA = borderTriangles[normalTriangleIndex]; int vertexIndexB = borderTriangles[normalTriangleIndex + 1]; int vertexIndexC = borderTriangles[normalTriangleIndex + 2]; Vector3 triangleNormal = SurfaceNormalFromIndices(vertexIndexA, vertexIndexB, vertexIndexC); if (vertexIndexA >= 0) { vertexNormals[vertexIndexA] += triangleNormal; } if (vertexIndexB >= 0) { vertexNormals[vertexIndexB] += triangleNormal; } if (vertexIndexC >= 0) { vertexNormals[vertexIndexC] += triangleNormal; } } for (int i = 0; i < vertexNormals.Length; i++) { vertexNormals[i].Normalize(); } return vertexNormals; } Vector3 SurfaceNormalFromIndices(int indexA, int indexB, int indexC) { Vector3 pointA = (indexA < 0) ? borderVertices[-indexA - 1] : vertices[indexA]; Vector3 pointB = (indexB < 0) ? borderVertices[-indexB - 1] : vertices[indexB]; Vector3 pointC = (indexC < 0) ? borderVertices[-indexC - 1] : vertices[indexC]; Vector3 sideAB = pointB - pointA; Vector3 sideAC = pointC - pointA; return Vector3.Cross(sideAB, sideAC).normalized; } public void Finish() { if (useFlatShading) { FlatShading(); } else { BakeNormals(); } } void BakeNormals() { bakedNormals = CalculateNormals(); } void FlatShading() { Vector3[] flatShadedVertices = new Vector3[triangles.Length]; Vector2[] flatShadedUvs = new Vector2[triangles.Length]; for (int i = 0; i < triangles.Length; i++) { flatShadedVertices[i] = vertices[triangles[i]]; flatShadedUvs[i] = uvs[triangles[i]]; triangles[i] = i; } vertices = flatShadedVertices; uvs = flatShadedUvs; } public Mesh CreateMesh() { Mesh mesh = new Mesh(); mesh.vertices = vertices; mesh.triangles = triangles; mesh.uv = uvs; if (useFlatShading) { mesh.RecalculateNormals(); } else { mesh.normals = bakedNormals; } return mesh; } }