Пример #1
0
        /// <summary>
        /// Gets the shared indices at the position in the direction relative to the given position.
        /// </summary>
        /// <param name="pos">The position from which the direction is applied.</param>
        /// <param name="direction">The bitmask indicating the directional of the shared cell.</param>
        /// <param name="index">The index of the vertex index.</param>
        /// <returns>The vertex index.</returns>
        public int GetIndexInDirection(Vector3I pos, byte direction, byte index)
        {
            // Offset the position by the direction mask
            pos.X -= direction & 0x01;
            pos.Z -= (direction >> 1) & 0x01;
            pos.Y -= (direction >> 2) & 0x01;

            return this[pos, index];
        }
Пример #2
0
        /// <summary>
        /// Calculate the normal at the given position.
        /// </summary>
        /// <param name="pos">The position.</param>
        /// <returns>The normal.</returns>
        private Vector3 CalculateNormal(Vector3I pos)
        {
            byte x0 = this.terrain.GetDensity(pos - Vector3I.UnitX);
            byte x1 = this.terrain.GetDensity(pos + Vector3I.UnitX);
            byte y0 = this.terrain.GetDensity(pos - Vector3I.UnitY);
            byte y1 = this.terrain.GetDensity(pos + Vector3I.UnitY);
            byte z0 = this.terrain.GetDensity(pos - Vector3I.UnitZ);
            byte z1 = this.terrain.GetDensity(pos + Vector3I.UnitZ);

            Vector3 normal = new Vector3((x1 - x0) * 0.5f, (y1 - y0) * 0.5f, (z1 - z0) * 0.5f);
            normal.Normalize();
            return normal;
        }
Пример #3
0
 /// <summary>
 /// Gets the point at the given world position.
 /// </summary>
 /// <param name="pos">The world position.</param>
 /// <returns>The point.</returns>
 public TerrainPoint GetPoint(Vector3I pos)
 {
     TerrainChunk chunk;
     if (this.chunks.TryGetValue(Metrics.ChunkIndex(pos.X, pos.Y), out chunk))
     {
         return chunk.GetPoint(Metrics.WorldToChunk(pos));
     }
     else
     {
         return null;
     }
 }
Пример #4
0
 /// <summary>
 /// Gets the density at the given world position.
 /// </summary>
 /// <param name="pos">The world position.</param>
 /// <returns>The density.</returns>
 public byte GetDensity(Vector3I pos)
 {
     TerrainChunk chunk;
     if (this.chunks.TryGetValue(Metrics.ChunkIndex(pos.X, pos.Y), out chunk))
     {
         return chunk.GetDensity(Metrics.WorldToChunk(pos));
     }
     else
     {
         return TerrainPoint.DensityMax;
     }
 }
Пример #5
0
 /// <summary>
 /// Determine if the vector is equal.
 /// </summary>
 /// <param name="other">The vector to test.</param>
 /// <returns>True if the vectors are equal.</returns>
 public bool Equals(Vector3I other)
 {
     return other.X == this.X && other.Y == this.Y && other.Z == this.Z;
 }
Пример #6
0
 /// <summary>
 /// Gets the point at the given chunk position.
 /// </summary>
 /// <param name="pos">The chunk position.</param>
 /// <returns>The point.</returns>
 public TerrainPoint GetPoint(Vector3I pos)
 {
     return this.Points[pos.X, pos.Y];
 }
Пример #7
0
 /// <summary>
 /// Gets the density at the given chunk position.
 /// </summary>
 /// <param name="pos">The chunk position.</param>
 /// <returns>The density.</returns>
 public byte GetDensity(Vector3I pos)
 {
     TerrainPoint point = this.Points[pos.X, pos.Y];
     if (point != null)
     {
         return point.GetDensity(pos.Z);
     }
     else
     {
         return TerrainPoint.DensityMax;
     }
 }
Пример #8
0
        /// <summary>
        /// Create the vertex for the given point.
        /// </summary>
        /// <param name="pos">The position.</param>
        /// <param name="mesh">The mesh.</param>
        /// <param name="corners">The density at each corner of the cell.</param>
        /// <param name="light">The light data for cell corners.</param>
        /// <param name="cornerA">The first corner index of the of the edge on which the vertex lies.</param>
        /// <param name="cornerB">The second corner index of the of the edge on which the vertex lies.</param>
        private void CreateVertex(
            Vector3I pos,
            MeshData mesh,
            byte[] corners,
            Color[] light,
            byte cornerA,
            byte cornerB)
        {
            // Calculate the position of the two end points between which the vertex lies
            Vector3I pAI = pos + MarchingCubes.CornerVector[cornerA];
            Vector3I pBI = pos + MarchingCubes.CornerVector[cornerB];
            Vector3 pA = new Vector3(pAI.X, pAI.Y, pAI.Z);
            Vector3 pB = new Vector3(pBI.X, pBI.Y, pBI.Z);

            // Get the end point densities
            byte densityA = corners[cornerA];
            byte densityB = corners[cornerB];

            // Calculate the interpolation ratio
            float ratio = this.CalculateRatio(densityA, densityB);

            // Interpolate the vertex position between the two end points
            Vector3 point = this.InterpolatePoint(pA, pB, ratio);

            // Calculate the normals at each end point and then interpolate the two normals
            Vector3 nA = this.CalculateNormal(pAI);
            Vector3 nB = this.CalculateNormal(pBI);
            Vector3 normal = this.InterpolatePoint(nA, nB, ratio);

            // Interpolate the color value between the two end points
            Color color = this.InterpolateColor(light[cornerA], light[cornerB], ratio);

            // Add the vertex to the mesh
            mesh.Vertices.Add(point);
            mesh.Normals.Add(normal);
            mesh.Light.Add(color);
        }
Пример #9
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]]);
                }
            }
        }
Пример #10
0
 /// <summary>
 /// Gets or sets the shared vertex index at the given position.
 /// </summary>
 /// <param name="pos">The position.</param>
 /// <param name="index">The index of the vertex index.</param>
 /// <returns>The vertex index.</returns>
 public int this[Vector3I pos, byte index]
 {
     get { return this.indices[pos.Z & 1][pos.X, pos.Y][index]; }
     set { this.indices[pos.Z & 1][pos.X, pos.Y][index] = value; }
 }
Пример #11
0
 /// <summary>
 /// Convert the world coordinates into chunk coordinates.
 /// </summary>
 /// <param name="worldPos">The position.</param>
 /// <returns>The position in chunk coordinates.</returns>
 public static Vector3I WorldToChunk(Vector3I worldPos)
 {
     return new Vector3I(
         worldPos.X & (Metrics.ChunkWidth - 1),
         worldPos.Y & (Metrics.ChunkHeight - 1),
         worldPos.Z);
 }