Example #1
0
        // 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();
        }