예제 #1
0
        /// <summary>
        /// Rebuild the MeshData for the given chunk. This is a heavy operation which takes a few frames to complete,
        /// so is best handled asynchronously.
        /// </summary>
        /// <param name="chunkIndex">The chunk index.</param>
        public void RebuildMesh(Vector2I chunkIndex)
        {
            TerrainChunk chunk = this.terrain.GetChunk(chunkIndex);

            // Initialise the arrays of shared indices
            var sharedIndices = new SharedIndices(Metrics.ChunkWidth, Metrics.ChunkWidth);

            // Clear the mesh data
            chunk.Mesh.Data.Clear();

            // Create the mesh for each cell in the chunk
            var chunkOrigin = Metrics.GetChunkOrigin(chunkIndex);
            for (int z = -1; z < Metrics.ChunkDepth; z++)
            {
                for (int x = chunkOrigin.X; x < chunkOrigin.X + Metrics.ChunkWidth; x++)
                {
                    for (int y = chunkOrigin.Y; y < chunkOrigin.Y + Metrics.ChunkHeight; y++)
                    {
                        this.CreateMeshCell(new Vector3I(x, y, z), chunk.Mesh.Data, sharedIndices);
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Create the cell at the given position.
        /// </summary>
        /// <param name="pos">The position.</param>
        /// <param name="mesh">The mesh data.</param>
        /// <param name="sharedIndices">The shared indices for the chunk.</param>
        private void CreateMeshCell(Vector3I pos, MeshData mesh, SharedIndices sharedIndices)
        {
            // Get the voxels and light at each corner of the cell
            var corners = new byte[8];
            var light = new Color[8];
            for (int i = 0; i < corners.Length; i++)
            {
                Vector3I cornerPos = pos + MarchingCubes.CornerVector[i];
                TerrainPoint point = this.terrain.GetPoint(cornerPos);
                if (point != null)
                {
                    corners[i] = point.GetDensity(cornerPos.Z);
                    light[i] = point.Light.ToColor();
                }
                else
                {
                    corners[i] = TerrainPoint.DensityMax;
                    light[i] = Color.black;
                }
            }

            // Get the case code
            byte caseCode = MarchingCubes.GetCaseCode(corners);
            if ((caseCode ^ ((corners[7] >> 7) & 0xFF)) == 0)
            {
                // For these cases there is no triangulation
                return;
            }
            else if ((caseCode & 0xCC) == 0xCC)
            {
                // For this case the surface is fully facing away from the camera (behind the terrain)
                return;
            }

            // Look up the geometry and vertex data for this case code
            CellGeometry geometry = MarchingCubes.CellGeometry[MarchingCubes.CellClass[caseCode]];
            ushort[] vertexData = MarchingCubes.VertexData[caseCode];

            // Calculate the mask which indicates whether vertices can be shared for a given direction
            Vector3I chunkPos = Metrics.WorldToChunk(pos);
            byte directionMask = (byte)((chunkPos.X > 0 ? 1 : 0) | ((chunkPos.Z > 0 ? 1 : 0) << 1) | ((chunkPos.Y > 0 ? 1 : 0) << 2));

            // Get the indices for each vertex in this cell, creating the vertices if necessary (otherwise use shared)
            var actualIndices = new ushort[geometry.Indices.Length];
            for (int i = 0; i < geometry.VertexCount; i++)
            {
                // Determine which edge this vertex lies on
                byte edge = (byte)(vertexData[i] >> 8);

                // Get the corner indices for the edge's end points
                byte cornerA = (byte)((vertexData[i] >> 4) & 0x0F);
                byte cornerB = (byte)(vertexData[i] & 0x0F);

                // Get the direction and index for reusing indices of a shared cell
                byte sharedDirection = (byte)(edge >> 4);
                byte sharedIndex = (byte)(edge & 0xF);

                // Check if a vertex should be re-used rather than creating a new one
                int actualIndex = -1;
                if (cornerB != 7 && (sharedDirection & directionMask) == sharedDirection)
                {
                    actualIndex = sharedIndices.GetIndexInDirection(chunkPos, sharedDirection, sharedIndex);
                }

                // Check if a new vertex should be created
                if (actualIndex == -1)
                {
                    this.CreateVertex(pos, mesh, corners, light, cornerA, cornerB);
                    actualIndex = mesh.LatestVertexIndex();
                }

                // Cache this vertex index so it can be used by other cells
                if ((sharedDirection & 8) != 0)
                {
                    sharedIndices[chunkPos, sharedIndex] = mesh.LatestVertexIndex();
                }

                actualIndices[i] = (ushort)actualIndex;
            }

            // Add the triangle indices to the mesh
            for (int t = 0; t < geometry.TriangleCount; t++)
            {
                // Step through the 3 vertices of this triangle
                int indexBase = t * 3;
                for (int i = 0; i < 3; i++)
                {
                    mesh.Indices.Add(actualIndices[geometry.Indices[indexBase + i]]);
                }
            }
        }