// C# port adapted from https://www.giawa.com/journal-entry-6-greedy-mesh-optimization/ // I rewrote some parts for readability and so I got a better understanding of the code // Functional changes: // - backfaces and frontfaces are handled seperately so we can set the right normals and have backface culling // - faces are compared to check if they can be merged (e.g. if they have a different material they can't be merged) /// <summary> /// Generate a mesh from the given voxel data. Greedily creates quads from aligned faces. /// </summary> /// <typeparam name="T">The type of vertices to generate.</typeparam> /// <param name="blocks">A 3d grid containing the voxel data.</param> /// <param name="createQuad">The function used to create a quad. Arguments in order: /// - int[] p: position {x, y, z} /// - int[] du, dv: unit vectors that contain the direction of the quad /// - int w, h: width and height of the quad. w is in du direction and h in dv direction /// - VoxelFace face: extra data of the face that should be put in the vertices /// - bool backFace: determines direction of the normal /// If the order is different indices will not match up.</param> /// <param name="vertices">The resulting vertices generated by the algorithm.</param> /// <param name="indices">The indices that can be used together with <see cref="vertices"/> to render the quads.</param> /// <param name="cw">Determines quad winding. CCW when left at false, CW when set to true.</param> public static void Generate <T>(BlockContainer blocks, Func <int[], int[], int[], int, bool, VoxelFace, bool, IEnumerable <T> > createQuad, out T[] vertices, out int[] indices, bool cw = false) where T : IVertexType { // This greedy algorithm is converted from JavaScript to C# from this article: // http://0fps.wordpress.com/2012/06/30/meshing-in-a-minecraft-game/ // // The original source code can be found here: // https://github.com/mikolalysenko/mikolalysenko.github.com/blob/gh-pages/MinecraftMeshes/js/greedy.js var verts = new List <T>(); var inds = new List <int>(); int[] size = { blocks.SizeX, blocks.SizeY, blocks.SizeZ }; // loop over the three directions; x -> 0, y -> 1, z -> 2 for (var direction = 0; direction < 3; direction++) { // u and v are the other two directions var u = (direction + 1) % 3; var v = (direction + 2) % 3; // pos holds the current position in the grid var pos = new int[3]; // 1 for the current direction and 0 for others, unit vector of the axis we're handling var nextPos = new int[3]; // contains the rendering data for each face in the current layers, a layer being a slice of the grid; Note that this is linearized var backFaces = new VoxelFace[blocks.GetSize(u) * blocks.GetSize(v)]; var frontFaces = new VoxelFace[blocks.GetSize(u) * blocks.GetSize(v)]; nextPos[direction] = 1; // outer loop goes through all layers // we start at -1 because we check for faces *between* blocks and have to get the outer faces too for (pos[direction] = -1; pos[direction] < blocks.GetSize(direction); pos[direction]++) { var noBack = true; var noFront = true; // Get all faces that need to be rendered in the current layer (front and back seperately) for (pos[v] = 0; pos[v] < size[v]; pos[v]++) { for (pos[u] = 0; pos[u] < size[u]; pos[u]++) { // if this block is visible and the one behind it is not we need to render the backface of the current block // if this one is not visible but the one behind it is, we need to render the frontface of the 'behind' block var index = pos[v] * size[u] + pos[u]; var current = pos[direction] >= 0 && !blocks[(byte)pos[0], (byte)pos[1], (byte)pos[2]].IsEmpty; var behind = pos[direction] + 1 < blocks.GetSize(direction) && !blocks[(byte)(pos[0] + nextPos[0]), (byte)(pos[1] + nextPos[1]), (byte)(pos[2] + nextPos[2])].IsEmpty; if (current && !behind) { backFaces[index] = new VoxelFace(blocks[(byte)pos[0], (byte)pos[1], (byte)pos[2]]); noBack = false; } else if (!current && behind) { frontFaces[index] = new VoxelFace(blocks[(byte)(pos[0] + nextPos[0]), (byte)(pos[1] + nextPos[1]), (byte)(pos[2] + nextPos[2])]); noFront = false; } } } // Then process both layers to build quads if (!noFront) { ProcessLayer(frontFaces, createQuad, verts, inds, pos[direction] + 1, direction, size, cw, false); } if (!noBack) { ProcessLayer(backFaces, createQuad, verts, inds, pos[direction] + 1, direction, size, !cw, true); } } } vertices = verts.ToArray(); indices = inds.ToArray(); }