Quantcast
Viewing latest article 23
Browse Latest Browse All 165

Chunk generation optimization?

I'm making a Minecraft clone, and the chunks take ages to load. The probable way to optimize this is by using the Greedy Meshing algorithm. As you can see: ![alt text][1] The chunks that are generated are not using this algorithm. [1]: /storage/temp/202246-screenshot-41-min.png If someone could help me implement this algorithm into my code, that'd be *amazing.* here is my code: using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; using Extensions; /// /// A chunk is a cubic structure of fixed side that contains individual blocks. /// Chunks are generated by the TerrainGenerator. /// [RequireComponent(typeof(TextureStitcher))] public class Chunk { /// /// Individual blockNames contained within the chunk. /// public BaseBlock[,,] blocks; /// /// (x,z) size of the chunk. /// public static int chunkSize = 16; /// /// y-size of the chunk. /// public static int chunkHeight = 350; /// /// Private reference to the game object first created by BuildMesh(). /// public GameObject chunkGameObject; /// /// Whether the mesh was already built or not. /// public bool meshBuilt = false; /// /// x-position of the chunk. /// public int x; /// /// z-position of the chunk. /// public int z; /// /// Used to build a quad's triangles. /// private int[] identityQuad = new int[] { 0, 1, 3, 1, 2, 3 }; public Chunk() { this.blocks = new BaseBlock[chunkSize, chunkHeight, chunkSize]; } /// /// Builds the chunk's mesh. /// public void BuildMesh() { if (this.chunkGameObject == null) this.SpawnGameObject(); Mesh mesh = new Mesh(); List vertices = new List(); List uvs = new List(); List triangles = new List(); this.BuildMeshFaces(vertices, uvs, triangles); mesh.vertices = vertices.ToArray(); mesh.uv = uvs.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); this.chunkGameObject.GetComponent().mesh = mesh; this.chunkGameObject.GetComponent().sharedMesh = mesh; this.chunkGameObject.GetComponent().material = CachedResources.Load("PhysicsMaterials/FrictionLess"); this.meshBuilt = true; } private enum ThreadStatus { STARTED, FINISHED }; private ThreadStatus status = ThreadStatus.STARTED; /// /// Allows to build a chunk mesh almost entirely asynchronously. /// This has to be run on the main thread! /// Mesh building is done in parallel, spawning a new thread. GameObject instantiation & Unity API calls are executed by the coroutine /// on the main thread. /// public IEnumerator BuildMeshAsync() { if (this.chunkGameObject == null) this.SpawnGameObject(); Mesh mesh = new Mesh(); List vertices = new List(); List uvs = new List(); List triangles = new List(); Thread meshBuildingThread = new Thread(() => { this.BuildMeshFaces(vertices, uvs, triangles); this.status = ThreadStatus.FINISHED; }); meshBuildingThread.Start(); while (this.status == ThreadStatus.STARTED) yield return new WaitForEndOfFrame(); mesh.vertices = vertices.ToArray(); mesh.uv = uvs.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); this.chunkGameObject.GetComponent().mesh = mesh; this.chunkGameObject.GetComponent().sharedMesh = mesh; this.chunkGameObject.GetComponent().material = CachedResources.Load("PhysicsMaterials/FrictionLess"); this.meshBuilt = true; } /// /// Given the list of vertices, UVs and triangles, this builds the whole chunk's faces writing into the given parameters. /// void BuildMeshFaces(List vertices, List uvs, List triangles) { int builtFaces = 0; for (int i = 0; i < chunkSize; i++) for (int j = 0; j < chunkHeight; j++) for (int k = 0; k < chunkSize; k++) { // Determine block adjacency with air. For each adjacent block face, render the face. // If the block itself is air, don't render anything if (this.blocks[i,j,k] == null || this.blocks[i,j,k].blockName == "air") continue; // Top face adjacency if (j >= 0 && j <= chunkHeight - 1) if (j == chunkHeight - 1 || this.blocks[i, j + 1, k]?.blockName == "air" || this.blocks[i, j + 1, k]?.blockName == "leaves" || this.blocks[i, j + 1, k]?.blockName == "glass") // Always render the top most face, OR if the top-adjacent block is "air". this.AddFace(i, j, k, builtFaces++, "top", CubeMeshFaces.top, vertices, uvs, triangles); // Bottom face adjacency if (j >= 0 && j < chunkHeight) if (j == 0 || this.blocks[i, j - 1, k]?.blockName == "air" || this.blocks[i, j - 1, k]?.blockName == "leaves" || this.blocks[i, j - 1, k]?.blockName == "glass") this.AddFace(i, j, k, builtFaces++, "bottom", CubeMeshFaces.bottom, vertices, uvs, triangles); // West face adjacency if (i >= 0 && i < chunkSize) if (i == 0 || this.blocks[i - 1, j, k]?.blockName == "air" || this.blocks[i - 1, j, k]?.blockName == "leaves" || this.blocks[i - 1, j, k]?.blockName == "glass") this.AddFace(i, j, k, builtFaces++, "west", CubeMeshFaces.west, vertices, uvs, triangles); // East face adjacency if (i >= 0 && i <= chunkSize) if (i == chunkSize - 1 || this.blocks[i + 1, j, k]?.blockName == "air" || this.blocks[i + 1, j, k]?.blockName == "leaves" || this.blocks[i + 1, j, k]?.blockName == "glass") this.AddFace(i, j, k, builtFaces++, "east", CubeMeshFaces.east, vertices, uvs, triangles); // Front face adjacency if (k >= 0 && k < chunkSize) if (k == 0 || this.blocks[i, j, k - 1]?.blockName == "air" || this.blocks[i, j, k - 1]?.blockName == "leaves" || this.blocks[i, j, k - 1]?.blockName == "glass") this.AddFace(i, j, k, builtFaces++, "front", CubeMeshFaces.front, vertices, uvs, triangles); // Back face adjacency if (k >= 0 && k <= chunkSize) if (k == chunkSize - 1 || this.blocks[i, j, k + 1]?.blockName == "air" || this.blocks[i, j, k + 1]?.blockName == "leaves" || this.blocks[i, j, k + 1]?.blockName == "glass") this.AddFace(i, j, k, builtFaces++, "back", CubeMeshFaces.back, vertices, uvs, triangles); } } /// /// Generates and spawns the chunk's GameObject. /// private void SpawnGameObject() { // Instantiate the GameObject (and implictly add it to the scene). this.chunkGameObject = new GameObject("Chunk"); // Get the stitched texture. Texture2D texture = TextureStitcher.instance.StitchedTexture; // Add mesh filter and renderer. this.chunkGameObject.AddComponent(); this.chunkGameObject.AddComponent(); this.chunkGameObject.AddComponent(); this.chunkGameObject.AddComponent(); this.chunkGameObject.tag = "chunk"; this.chunkGameObject.GetComponent().material = Resources.Load("ChunkCutout"); this.chunkGameObject.GetComponent().material.mainTexture = texture; this.chunkGameObject.transform.position = new Vector3(this.x * chunkSize, 0, this.z * chunkSize); } /// /// Given the block's (i,j,k) vector, the number of built faces, face to build, vertices, uvs and triangles references, /// this appends a new face mesh's data on the vertices, uvs and triangles references. /// void AddFace(int i, int j, int k, int builtFaces, string faceName, Vector3[] face, List vertices, List uvs, List triangles) { string textureName = this.blocks[i,j,k].blockName; if (this.blocks[i,j,k].textureName != "default") textureName = this.blocks[i,j,k].textureName; if (this.blocks[i,j,k].hasSidedTextures) { if (TextureStitcher.instance.TextureUVs.ContainsKey(System.String.Format("{0}_{1}", textureName, faceName))) textureName = System.String.Format("{0}_{1}", textureName, faceName); else textureName = System.String.Format("{0}_{1}", textureName, "side"); } vertices.AddRange(face.Add((i,j,k))); uvs.AddRange(TextureStitcher.instance.TextureUVs[textureName].ToArray()); triangles.AddRange(this.identityQuad.Add(builtFaces * 4)); } public void Destroy() { GameObject.Destroy(this.chunkGameObject); } /// /// Recalculates the chunk mesh's normals. /// public void RecalculateNormals() { this.chunkGameObject.GetComponent().mesh.RecalculateNormals(); } }

Viewing latest article 23
Browse Latest Browse All 165

Trending Articles