Exemple #1
0
        public void InitializeFromChunk(VoxelChunk chunk, GraphicsDevice graphics)
        {
            if (chunk == null)
            {
                return;
            }

            rebuildMutex.WaitOne();
            if (isRebuilding)
            {
                rebuildMutex.ReleaseMutex();
                return;
            }

            isRebuilding = true;
            rebuildMutex.ReleaseMutex();


            accumulatedVertices.Clear();
            accumulatedIndices.Clear();
            faceExists.Clear();
            drawFace.Clear();

            Voxel v           = chunk.MakeVoxel(0, 0, 0);
            Voxel voxelOnFace = chunk.MakeVoxel(0, 0, 0);

            Voxel[] manhattanNeighbors = new Voxel[4];
            for (int x = 0; x < chunk.SizeX; x++)
            {
                for (int y = 0; y < Math.Min(chunk.Manager.ChunkData.MaxViewingLevel + 1, chunk.SizeY); y++)
                {
                    for (int z = 0; z < chunk.SizeZ; z++)
                    {
                        v.GridPosition = new Vector3(x, y, z);


                        if (v.IsEmpty || !v.IsVisible)
                        {
                            continue;
                        }

                        BoxPrimitive primitive = VoxelLibrary.GetPrimitive(v.Type);

                        if (primitive == null)
                        {
                            continue;
                        }

                        BoxPrimitive.BoxTextureCoords uvs = primitive.UVs;

                        if (v.Type.HasTransitionTextures)
                        {
                            uvs = v.ComputeTransitionTexture(manhattanNeighbors);
                        }


                        Voxel worldVoxel = new Voxel();
                        for (int i = 0; i < 6; i++)
                        {
                            BoxFace face  = (BoxFace)i;
                            Vector3 delta = FaceDeltas[face];
                            faceExists[face] = chunk.IsCellValid(x + (int)delta.X, y + (int)delta.Y, z + (int)delta.Z);
                            drawFace[face]   = true;

                            if (faceExists[face])
                            {
                                voxelOnFace.GridPosition = new Vector3(x + (int)delta.X, y + (int)delta.Y, z + (int)delta.Z);
                                drawFace[face]           = voxelOnFace.IsEmpty || !voxelOnFace.IsVisible || (voxelOnFace.Type.CanRamp && voxelOnFace.RampType != RampType.None && IsSideFace(face) && ShouldDrawFace(face, voxelOnFace.RampType, v.RampType));
                            }
                            else
                            {
                                bool success = chunk.Manager.ChunkData.GetNonNullVoxelAtWorldLocation(new Vector3(x + (int)delta.X, y + (int)delta.Y, z + (int)delta.Z) + chunk.Origin, ref worldVoxel);
                                drawFace[face] = !success || worldVoxel.IsEmpty || !worldVoxel.IsVisible || (worldVoxel.Type.CanRamp && worldVoxel.RampType != RampType.None && IsSideFace(face) && ShouldDrawFace(face, worldVoxel.RampType, v.RampType));
                            }
                        }


                        for (int i = 0; i < 6; i++)
                        {
                            BoxFace face = (BoxFace)i;
                            if (!drawFace[face])
                            {
                                continue;
                            }
                            int faceIndex   = 0;
                            int faceCount   = 0;
                            int vertexIndex = 0;
                            int vertexCount = 0;
                            primitive.GetFace(face, uvs, out faceIndex, out faceCount, out vertexIndex, out vertexCount);
                            Vector2 texScale = uvs.Scales[i];

                            int indexOffset = accumulatedVertices.Count;
                            for (int vertOffset = 0; vertOffset < vertexCount; vertOffset++)
                            {
                                ExtendedVertex vert      = primitive.Vertices[vertOffset + vertexIndex];
                                VoxelVertex    bestKey   = VoxelChunk.GetNearestDelta(vert.Position);
                                Color          color     = v.Chunk.Data.GetColor(x, y, z, bestKey);
                                Vector3        offset    = Vector3.Zero;
                                Vector2        texOffset = Vector2.Zero;

                                if (v.Type.CanRamp && ShouldRamp(bestKey, v.RampType))
                                {
                                    offset = new Vector3(0, -v.Type.RampSize, 0);

                                    if (face != BoxFace.Top && face != BoxFace.Bottom)
                                    {
                                        texOffset = new Vector2(0, v.Type.RampSize * (texScale.Y));
                                    }
                                }


                                ExtendedVertex newVertex = new ExtendedVertex((vert.Position + v.Position + VertexNoise.GetNoiseVectorFromRepeatingTexture(vert.Position + v.Position) + offset),
                                                                              color,
                                                                              uvs.Uvs[vertOffset + vertexIndex] + texOffset, uvs.Bounds[faceIndex / 6]);
                                accumulatedVertices.Add(newVertex);
                            }

                            for (int idx = faceIndex; idx < faceCount + faceIndex; idx++)
                            {
                                int vertexOffset = primitive.Indices[idx];
                                accumulatedIndices.Add((short)(indexOffset + (vertexOffset - primitive.Indices[faceIndex])));
                            }
                        }
                    }
                }
            }


            Vertices = new ExtendedVertex[accumulatedVertices.Count];
            accumulatedVertices.CopyTo(Vertices);
            IndexBuffer = new IndexBuffer(graphics, typeof(short), accumulatedIndices.Count, BufferUsage.WriteOnly);
            IndexBuffer.SetData(accumulatedIndices.ToArray());

            ResetBuffer(graphics);
            isRebuilding = false;

            //chunk.PrimitiveMutex.WaitOne();
            chunk.NewPrimitive         = this;
            chunk.NewPrimitiveReceived = true;
            //chunk.PrimitiveMutex.ReleaseMutex();
        }
        private static void BuildVoxelTopFaceGeometry(
            RawPrimitive Into,
            VoxelChunk Chunk,
            Cache Cache,
            BoxPrimitive Primitive,
            VoxelHandle V,
            BoxPrimitive.BoxTextureCoords UVs,
            int i)
        {
            var face  = (BoxFace)i;
            var delta = FaceDeltas[i];

            var faceVoxel = new VoxelHandle(Chunk.Manager, V.Coordinate + GlobalVoxelOffset.FromVector3(delta));

            if (!IsFaceVisible(V, faceVoxel, face))
            {
                return;
            }

            var faceDescriptor = Primitive.GetFace(face);
            int exploredVerts  = 0;
            var vertexColors   = new VertexColorInfo[4];
            var vertexTint     = new Color[4];

            // Find all verticies to use for geometry later, and for the fringe
            var vertexPositions = new Vector3[4];

            for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; faceVertex++)
            {
                var vertex      = Primitive.Vertices[faceDescriptor.VertexOffset + faceVertex];
                var voxelVertex = Primitive.Deltas[faceDescriptor.VertexOffset + faceVertex];

                var rampOffset = Vector3.Zero;
                if (V.IsExplored && V.Type.CanRamp && ShouldRamp(voxelVertex, V.RampType))
                {
                    rampOffset = new Vector3(0, -V.Type.RampSize, 0);
                }

                var worldPosition = V.WorldPosition + vertex.Position + rampOffset;
                //worldPosition += VertexNoise.GetNoiseVectorFromRepeatingTexture(worldPosition);

                vertexPositions[faceVertex] = worldPosition;
            }

            if (V.IsExplored)
            {
                exploredVerts = 4;
            }
            else
            {
                for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; ++faceVertex)
                {
                    var  voxelVertex         = Primitive.Deltas[faceDescriptor.VertexOffset + faceVertex];
                    var  cacheKey            = GetCacheKey(V, voxelVertex);
                    bool anyNeighborExplored = true;

                    if (!Cache.ExploredCache.TryGetValue(cacheKey, out anyNeighborExplored))
                    {
                        anyNeighborExplored = VoxelHelpers.EnumerateVertexNeighbors2D(V.Coordinate, voxelVertex)
                                              .Select(c => new VoxelHandle(V.Chunk.Manager, c))
                                              .Any(n => n.IsValid && n.IsExplored);
                        Cache.ExploredCache.Add(cacheKey, anyNeighborExplored);
                    }


                    if (anyNeighborExplored)
                    {
                        exploredVerts += 1;
                    }
                }
            }

            for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; ++faceVertex)
            {
                var voxelVertex = Primitive.Deltas[faceDescriptor.VertexOffset + faceVertex];
                var cacheKey    = GetCacheKey(V, voxelVertex);

                VertexColorInfo vertexColor;
                if (!Cache.LightCache.TryGetValue(cacheKey, out vertexColor))
                {
                    vertexColor = CalculateVertexLight(V, voxelVertex, Chunk.Manager);
                    Cache.LightCache.Add(cacheKey, vertexColor);
                }

                vertexColors[faceVertex] = vertexColor;

                vertexTint[faceVertex] = new Color(1.0f, 1.0f, 1.0f, 1.0f);
                if (exploredVerts != 4)
                {
                    bool anyNeighborExplored = true;
                    if (!Cache.ExploredCache.TryGetValue(cacheKey, out anyNeighborExplored))
                    {
                        throw new InvalidProgramException("Failed cache lookup");
                    }

                    if (!anyNeighborExplored)
                    {
                        vertexTint[faceVertex] = new Color(0.0f, 0.0f, 0.0f, 1.0f);
                    }
                }

                vertexTint[faceVertex] = new Color(vertexTint[faceVertex].ToVector4() * V.Type.Tint.ToVector4());
            }

            if (exploredVerts != 0)
            {
                var baseUVs = UVs.Uvs[11]; // EW

                var baseUVBounds = new Vector4(baseUVs.X + 0.001f, baseUVs.Y + 0.001f, baseUVs.X + (1.0f / 16.0f) - 0.001f, baseUVs.Y + (1.0f / 16.0f) - 0.001f);

                // Draw central top tile.
                AddTopFaceGeometry(Into,
                                   Cache.AmbientValues, Primitive,
                                   faceDescriptor,
                                   vertexPositions,
                                   vertexColors,
                                   vertexTint,
                                   Vector2.One,
                                   baseUVs, baseUVBounds);

                if (V.GrassType != 0)
                {
                    BuildGrassFringeGeometry(Into, Chunk, Cache, Primitive, V, vertexColors,
                                             vertexTint, vertexPositions, faceDescriptor, exploredVerts);
                }
            }
            else
            {
                if (!Debugger.Switches.HideSliceTop)
                {
                    var indexOffset = Into.VertexCount;

                    for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; faceVertex++)
                    {
                        Into.AddVertex(new ExtendedVertex(
                                           vertexPositions[faceVertex] + VertexNoise.GetNoiseVectorFromRepeatingTexture(vertexPositions[faceVertex]),
                                           new Color(0, 0, 0, 255),
                                           new Color(0, 0, 0, 255),
                                           new Vector2(12.5f / 16.0f, 0.5f / 16.0f),
                                           new Vector4(12.0f / 16.0f, 0.0f, 13.0f / 16.0f, 1.0f / 16.0f)));
                    }

                    for (int idx = faceDescriptor.IndexOffset; idx < faceDescriptor.IndexCount +
                         faceDescriptor.IndexOffset; idx++)
                    {
                        ushort offset  = Primitive.Indexes[idx];
                        ushort offset0 = Primitive.Indexes[faceDescriptor.IndexOffset];
                        Into.AddIndex((short)(indexOffset + offset - offset0));
                    }
                }
            }
        }
        private static IEnumerable <ExtendedVertex> CreateWaterFace(Voxel voxel, BoxFace face, VoxelChunk chunk, int x, int y, int z, int totalDepth, bool top)
        {
            List <ExtendedVertex> toReturn = new List <ExtendedVertex>();
            int idx        = 0;
            int c          = 0;
            int vertOffset = 0;
            int numVerts   = 0;

            m_canconicalPrimitive.GetFace(face, m_canconicalPrimitive.UVs, out idx, out c, out vertOffset, out numVerts);

            for (int i = idx; i < idx + c; i++)
            {
                toReturn.Add(m_canconicalPrimitive.Vertices[m_canconicalPrimitive.Indices[i]]);
            }

            Vector3      origin          = chunk.Origin + new Vector3(x, y, z);
            List <Voxel> neighborsVertex = new List <Voxel>();

            for (int i = 0; i < toReturn.Count; i++)
            {
                VoxelVertex currentVertex = VoxelChunk.GetNearestDelta(toReturn[i].Position);
                chunk.GetNeighborsVertex(currentVertex, voxel, neighborsVertex);
                int   index             = chunk.Data.IndexAt(x, y, z);
                float averageWaterLevel = chunk.Data.Water[index].WaterLevel;
                float count             = 1.0f;
                float emptyNeighbors    = 0.0f;

                foreach (byte level in neighborsVertex.Select(vox => vox.WaterLevel))
                {
                    averageWaterLevel += level;
                    count++;

                    if (level < 1)
                    {
                        emptyNeighbors++;
                    }
                }

                averageWaterLevel = averageWaterLevel / count;

                float   averageWaterHeight = (float)averageWaterLevel / 255.0f;
                float   puddleness         = 0;
                Vector2 uv;

                float foaminess = emptyNeighbors / count;

                if (foaminess <= 0.5f)
                {
                    foaminess = 0.0f;
                }

                if (totalDepth < 5)
                {
                    foaminess  = 0.75f;
                    puddleness = 0;
                    uv         = new Vector2((toReturn[i].Position.X + origin.X) / 80.0f, (toReturn[i].Position.Z + origin.Z) / 80.0f);
                }
                else
                {
                    uv = new Vector2((toReturn[i].Position.X + origin.X) / 80.0f, (toReturn[i].Position.Z + origin.Z) / 80.0f);
                }
                Vector4 bounds = new Vector4(0, 0, 1, 1);

                if (chunk.Data.Water[index].IsFalling || !top)
                {
                    averageWaterHeight = 1.0f;
                }

                if (face == BoxFace.Top)
                {
                    toReturn[i] = new ExtendedVertex(toReturn[i].Position + origin + new Vector3(0, (averageWaterHeight * 0.4f - 1.0f), 0),
                                                     new Color(foaminess, puddleness, (float)totalDepth / 512.0f, 1.0f),
                                                     Color.White,
                                                     uv, bounds);
                }
                else
                {
                    Vector3 offset = Vector3.Zero;
                    switch (face)
                    {
                    case BoxFace.Back:
                    case BoxFace.Front:
                        uv        = new Vector2((Math.Abs(toReturn[i].Position.X + origin.X) / 80.0f), (Math.Abs(toReturn[i].Position.Y + origin.Y) / 80.0f));
                        foaminess = 1.0f;
                        offset    = new Vector3(0, -0.5f, 0);
                        break;

                    case BoxFace.Right:
                    case BoxFace.Left:
                        uv        = new Vector2((Math.Abs(toReturn[i].Position.Z + origin.Z) / 80.0f), (Math.Abs(toReturn[i].Position.Y + origin.Y) / 80.0f));
                        foaminess = 1.0f;
                        offset    = new Vector3(0, -0.5f, 0);
                        break;

                    case BoxFace.Top:
                        offset = new Vector3(0, -0.5f, 0);
                        break;
                    }

                    toReturn[i] = new ExtendedVertex(toReturn[i].Position + origin + offset, new Color(foaminess, 0.0f, 1.0f, 1.0f), Color.White, uv, bounds);
                }
            }

            return(toReturn);
        }
Exemple #4
0
        private static void CreateWaterFaces(
            VoxelHandle voxel,
            VoxelChunk chunk,
            int x, int y, int z,
            ExtendedVertex[] vertices,
            ushort[] Indexes,
            int startVertex,
            int startIndex)
        {
            // Reset the appropriate parts of the cache.
            cache.Reset();

            // These are reused for every face.
            var   origin           = voxel.WorldPosition;
            float centerWaterlevel = voxel.LiquidLevel;

            var  below       = VoxelHelpers.GetVoxelBelow(voxel);
            bool belowFilled = false;
            bool belowLiquid = below.IsValid && below.LiquidLevel > 0;
            bool belowRamps  = below.IsValid && below.RampType != RampType.None;

            if ((below.IsValid && !below.IsEmpty) || belowLiquid)
            {
                belowFilled = true;
            }

            float[] foaminess = new float[4];

            for (int i = 0; i < cache.drawFace.Length; i++)
            {
                if (!cache.drawFace[i])
                {
                    continue;
                }
                BoxFace face = (BoxFace)i;

                var faceDescriptor = primitive.GetFace(face);
                int indexOffset    = startVertex;

                for (int vertOffset = 0; vertOffset < faceDescriptor.VertexCount; vertOffset++)
                {
                    VoxelVertex currentVertex = primitive.Deltas[faceDescriptor.VertexOffset + vertOffset];

                    // These will be filled out before being used   lh  .
                    //float foaminess1;
                    foaminess[vertOffset] = 0.0f;
                    bool shoreLine = false;

                    Vector3 pos        = Vector3.Zero;
                    Vector3 rampOffset = Vector3.Zero;
                    var     uv         = primitive.UVs.Uvs[vertOffset + faceDescriptor.VertexOffset];
                    // We are going to have to reuse some vertices when drawing a single so we'll store the position/foaminess
                    // for quick lookup when we find one of those reused ones.
                    // When drawing multiple faces the Vertex overlap gets bigger, which is a bonus.
                    if (!cache.vertexCalculated[(int)currentVertex])
                    {
                        float count             = 1.0f;
                        float emptyNeighbors    = 0.0f;
                        float averageWaterLevel = centerWaterlevel;

                        var vertexSucc = VoxelHelpers.VertexNeighbors[(int)currentVertex];

                        // Run through the successors and count up the water in each voxel.
                        for (int v = 0; v < vertexSucc.Length; v++)
                        {
                            var neighborVoxel = new VoxelHandle(chunk.Manager, voxel.Coordinate + vertexSucc[v]);
                            if (!neighborVoxel.IsValid)
                            {
                                continue;
                            }

                            // Now actually do the math.
                            count++;
                            if (neighborVoxel.LiquidLevel < 1)
                            {
                                emptyNeighbors++;
                            }
                            if (neighborVoxel.LiquidType == LiquidType.None && !neighborVoxel.IsEmpty)
                            {
                                shoreLine = true;
                            }
                        }

                        foaminess[vertOffset] = emptyNeighbors / count;

                        if (foaminess[vertOffset] <= 0.5f)
                        {
                            foaminess[vertOffset] = 0.0f;
                        }
                        // Check if it should ramp.
                        else if (!shoreLine)
                        {
                            //rampOffset.Y = -0.4f;
                        }

                        pos = primitive.Vertices[vertOffset + faceDescriptor.VertexOffset].Position;
                        if ((currentVertex & VoxelVertex.Top) == VoxelVertex.Top)
                        {
                            if (belowFilled)
                            {
                                pos.Y -= 0.6f;// Minimum ramp position
                            }
                            var neighbors = VoxelHelpers.EnumerateVertexNeighbors2D(voxel.Coordinate, currentVertex)
                                            .Select(c => new VoxelHandle(chunk.Manager, c))
                                            .Where(h => h.IsValid)
                                            .Select(h => MathFunctions.Clamp((float)h.LiquidLevel / 8.0f, 0.25f, 1.0f));

                            if (neighbors.Count() > 0)
                            {
                                if (belowFilled)
                                {
                                    pos.Y *= neighbors.Average();
                                }
                            }
                        }
                        else
                        {
                            uv.Y -= 0.6f;
                        }

                        pos += VertexNoise.GetNoiseVectorFromRepeatingTexture(voxel.WorldPosition +
                                                                              primitive.Vertices[vertOffset + faceDescriptor.VertexOffset].Position);

                        if (!belowFilled)
                        {
                            pos = (pos - Vector3.One * 0.5f);
                            pos.Normalize();
                            pos *= 0.35f;
                            pos += Vector3.One * 0.5f;
                        }
                        else if ((belowLiquid || belowRamps) && IsBottom(currentVertex))
                        {
                            if (belowRamps)
                            {
                                pos -= Vector3.Up * 0.5f;
                            }
                            else
                            {
                                pos -= Vector3.Up * 0.8f;
                            }
                        }

                        pos += origin + rampOffset;
                        // Store the vertex information for future use when we need it again on this or another face.
                        cache.vertexCalculated[(int)currentVertex] = true;
                        cache.vertexFoaminess[(int)currentVertex]  = foaminess[vertOffset];
                        cache.vertexPositions[(int)currentVertex]  = pos;
                    }
                    else
                    {
                        // We've already calculated this one.  Time for a cheap grab from the lookup.
                        foaminess[vertOffset] = cache.vertexFoaminess[(int)currentVertex];
                        pos = cache.vertexPositions[(int)currentVertex];
                    }

                    vertices[startVertex].Set(pos,
                                              new Color(foaminess[vertOffset], 0.0f, 1.0f, 1.0f),
                                              Color.White,
                                              uv,
                                              new Vector4(0, 0, 1, 1));

                    startVertex++;
                }

                bool flippedQuad = foaminess[1] + foaminess[3] >
                                   foaminess[0] + foaminess[2];

                for (int idx = faceDescriptor.IndexOffset; idx < faceDescriptor.IndexCount + faceDescriptor.IndexOffset; idx++)
                {
                    ushort offset  = flippedQuad ? primitive.FlippedIndexes[idx] : primitive.Indexes[idx];
                    ushort offset0 = flippedQuad ? primitive.FlippedIndexes[faceDescriptor.IndexOffset] : primitive.Indexes[faceDescriptor.IndexOffset];

                    Indexes[startIndex] = (ushort)(indexOffset + offset - offset0);
                    startIndex++;
                }
            }
            // End cache.drawFace loop
        }
        private static void BuildVoxelFaceGeometry(
            RawPrimitive Into,
            VoxelChunk Chunk,
            Cache Cache,
            BoxPrimitive Primitive,
            VoxelHandle V,
            Color Tint,
            BoxPrimitive.BoxTextureCoords UVs,
            Matrix VertexTransform,
            int i,
            bool ApplyLighting)
        {
            var face  = (BoxFace)i;
            var delta = FaceDeltas[i];

            var faceVoxel = new VoxelHandle(Chunk.Manager, V.Coordinate + GlobalVoxelOffset.FromVector3(delta));

            if (!IsFaceVisible(V, faceVoxel, face))
            {
                return;
            }

            var faceDescriptor = Primitive.GetFace(face);
            var indexOffset    = Into.VertexCount;

            for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; faceVertex++)
            {
                var vertex      = Primitive.Vertices[faceDescriptor.VertexOffset + faceVertex];
                var voxelVertex = Primitive.Deltas[faceDescriptor.VertexOffset + faceVertex];
                var vertexColor = new VertexColorInfo
                {
                    SunColor     = 255,
                    AmbientColor = 255,
                    DynamicColor = 255,
                };

                if (ApplyLighting)
                {
                    var cacheKey = GetCacheKey(V, voxelVertex);

                    if (!Cache.LightCache.TryGetValue(cacheKey, out vertexColor))
                    {
                        vertexColor = CalculateVertexLight(V, voxelVertex, Chunk.Manager);
                        Cache.LightCache.Add(cacheKey, vertexColor);
                    }

                    Cache.AmbientValues[faceVertex] = vertexColor.AmbientColor;
                }

                var rampOffset = Vector3.Zero;
                if (V.IsExplored && V.Type.CanRamp && ShouldRamp(voxelVertex, V.RampType))
                {
                    rampOffset = new Vector3(0, -V.Type.RampSize, 0);
                }

                var baseWorldPosition = V.WorldPosition + vertex.Position + rampOffset;
                var noise             = VertexNoise.GetNoiseVectorFromRepeatingTexture(baseWorldPosition);
                var localPosition     = Vector3.Transform(vertex.Position + rampOffset + noise, VertexTransform);

                Into.AddVertex(new ExtendedVertex(
                                   V.WorldPosition + localPosition,
                                   vertexColor.AsColor(),
                                   Tint,
                                   UVs.Uvs[faceDescriptor.VertexOffset + faceVertex],
                                   UVs.Bounds[faceDescriptor.IndexOffset / 6]));
            }

            bool flippedQuad = ApplyLighting && (Cache.AmbientValues[0] + Cache.AmbientValues[2] >
                                                 Cache.AmbientValues[1] + Cache.AmbientValues[3]);

            for (int idx = faceDescriptor.IndexOffset; idx < faceDescriptor.IndexCount +
                 faceDescriptor.IndexOffset; idx++)
            {
                ushort offset  = flippedQuad ? Primitive.FlippedIndexes[idx] : Primitive.Indexes[idx];
                ushort offset0 = flippedQuad ? Primitive.FlippedIndexes[faceDescriptor.IndexOffset] : Primitive.Indexes[faceDescriptor.IndexOffset];
                Into.AddIndex((short)(indexOffset + offset - offset0));
            }
        }
Exemple #6
0
        public void InitializeFromChunk(VoxelChunk chunk, GraphicsDevice graphics)
        {
            if (chunk == null)
            {
                return;
            }

            rebuildMutex.WaitOne();
            if (isRebuilding)
            {
                rebuildMutex.ReleaseMutex();
                return;
            }

            isRebuilding = true;
            rebuildMutex.ReleaseMutex();
            int[] ambientValues = new int[4];
            int   maxIndex      = 0;
            int   maxVertex     = 0;
            Voxel v             = chunk.MakeVoxel(0, 0, 0);
            Voxel voxelOnFace   = chunk.MakeVoxel(0, 0, 0);

            Voxel[]      manhattanNeighbors = new Voxel[4];
            BoxPrimitive bedrockModel       = VoxelLibrary.GetPrimitive("Bedrock");
            Voxel        worldVoxel         = new Voxel();

            if (Vertices == null)
            {
                Vertices = new ExtendedVertex[1024];
            }

            if (Indexes == null)
            {
                Indexes = new ushort[512];
            }

            for (int y = 0; y < Math.Min(chunk.Manager.ChunkData.MaxViewingLevel + 1, chunk.SizeY); y++)
            {
                for (int x = 0; x < chunk.SizeX; x++)
                {
                    for (int z = 0; z < chunk.SizeZ; z++)
                    {
                        v.GridPosition = new Vector3(x, y, z);


                        if ((v.IsExplored && v.IsEmpty) || !v.IsVisible)
                        {
                            continue;
                        }

                        BoxPrimitive primitive = VoxelLibrary.GetPrimitive(v.Type);
                        if (v.IsExplored && primitive == null)
                        {
                            continue;
                        }
                        if (!v.IsExplored)
                        {
                            primitive = bedrockModel;
                        }

                        Color tint = v.Type.Tint;
                        BoxPrimitive.BoxTextureCoords uvs = primitive.UVs;

                        if (v.Type.HasTransitionTextures && v.IsExplored)
                        {
                            uvs = v.ComputeTransitionTexture(manhattanNeighbors);
                        }

                        for (int i = 0; i < 6; i++)
                        {
                            BoxFace face  = (BoxFace)i;
                            Vector3 delta = FaceDeltas[(int)face];
                            faceExists[(int)face] = chunk.IsCellValid(x + (int)delta.X, y + (int)delta.Y, z + (int)delta.Z);
                            drawFace[(int)face]   = true;

                            if (faceExists[(int)face])
                            {
                                voxelOnFace.GridPosition = new Vector3(x + (int)delta.X, y + (int)delta.Y, z + (int)delta.Z);
                                drawFace[(int)face]      = (voxelOnFace.IsExplored && voxelOnFace.IsEmpty) || !voxelOnFace.IsVisible ||
                                                           (voxelOnFace.Type.CanRamp && voxelOnFace.RampType != RampType.None && IsSideFace(face) &&
                                                            ShouldDrawFace(face, voxelOnFace.RampType, v.RampType));
                            }
                            else
                            {
                                bool success = chunk.Manager.ChunkData.GetNonNullVoxelAtWorldLocation(new Vector3(x + (int)delta.X, y + (int)delta.Y, z + (int)delta.Z) + chunk.Origin, ref worldVoxel);
                                drawFace[(int)face] = !success || (worldVoxel.IsExplored && worldVoxel.IsEmpty) || !worldVoxel.IsVisible ||
                                                      (worldVoxel.Type.CanRamp && worldVoxel.RampType != RampType.None &&
                                                       IsSideFace(face) &&
                                                       ShouldDrawFace(face, worldVoxel.RampType, v.RampType));
                            }
                        }


                        for (int i = 0; i < 6; i++)
                        {
                            BoxFace face = (BoxFace)i;
                            if (!drawFace[(int)face])
                            {
                                continue;
                            }


                            int faceIndex   = 0;
                            int faceCount   = 0;
                            int vertexIndex = 0;
                            int vertexCount = 0;
                            primitive.GetFace(face, uvs, out faceIndex, out faceCount, out vertexIndex, out vertexCount);
                            Vector2 texScale = uvs.Scales[i];

                            int indexOffset = maxVertex;
                            for (int vertOffset = 0; vertOffset < vertexCount; vertOffset++)
                            {
                                ExtendedVertex vert    = primitive.Vertices[vertOffset + vertexIndex];
                                VoxelVertex    bestKey = primitive.Deltas[vertOffset + vertexIndex];
                                Color          color   = v.Chunk.Data.GetColor(x, y, z, bestKey);
                                ambientValues[vertOffset] = color.G;
                                Vector3 offset    = Vector3.Zero;
                                Vector2 texOffset = Vector2.Zero;

                                if (v.Type.CanRamp && ShouldRamp(bestKey, v.RampType))
                                {
                                    offset = new Vector3(0, -v.Type.RampSize, 0);

                                    if (face != BoxFace.Top && face != BoxFace.Bottom)
                                    {
                                        texOffset = new Vector2(0, v.Type.RampSize * (texScale.Y));
                                    }
                                }

                                if (maxVertex >= Vertices.Length)
                                {
                                    ExtendedVertex[] newVertices = new ExtendedVertex[Vertices.Length * 2];
                                    Vertices.CopyTo(newVertices, 0);
                                    Vertices = newVertices;
                                }

                                Vertices[maxVertex] = new ExtendedVertex(vert.Position + v.Position +
                                                                         VertexNoise.GetNoiseVectorFromRepeatingTexture(
                                                                             vert.Position + v.Position) + offset,
                                                                         color,
                                                                         tint,
                                                                         uvs.Uvs[vertOffset + vertexIndex] + texOffset,
                                                                         uvs.Bounds[faceIndex / 6]);
                                maxVertex++;
                            }

                            bool flippedQuad = ambientValues[0] + ambientValues[2] >
                                               ambientValues[1] + ambientValues[3];
                            for (int idx = faceIndex; idx < faceCount + faceIndex; idx++)
                            {
                                if (maxIndex >= Indexes.Length)
                                {
                                    ushort[] indexes = new ushort[Indexes.Length * 2];
                                    Indexes.CopyTo(indexes, 0);
                                    Indexes = indexes;
                                }

                                ushort vertexOffset  = flippedQuad ? primitive.FlippedIndexes[idx] : primitive.Indexes[idx];
                                ushort vertexOffset0 = flippedQuad? primitive.FlippedIndexes[faceIndex] : primitive.Indexes[faceIndex];
                                Indexes[maxIndex] =
                                    (ushort)((int)indexOffset + (int)((int)vertexOffset - (int)vertexOffset0));
                                maxIndex++;
                            }
                        }
                    }
                }
            }
            MaxIndex  = maxIndex;
            MaxVertex = maxVertex;
            GenerateLightmap(chunk.Manager.ChunkData.Tilemap.Bounds);
            isRebuilding = false;

            //chunk.PrimitiveMutex.WaitOne();
            chunk.NewPrimitive         = this;
            chunk.NewPrimitiveReceived = true;
            //chunk.PrimitiveMutex.ReleaseMutex();
        }
Exemple #7
0
        private static void CreateWaterFaces(Voxel voxel,
                                             VoxelChunk chunk,
                                             int x, int y, int z,
                                             ExtendedVertex[] vertices,
                                             int startVertex)
        {
            // Reset the appropriate parts of the cache.
            cache.Reset();

            // These are reused for every face.
            Vector3 origin           = chunk.Origin + new Vector3(x, y, z);
            int     index            = chunk.Data.IndexAt(x, y, z);
            float   centerWaterlevel = chunk.Data.Water[chunk.Data.IndexAt(x, y, z)].WaterLevel;

            for (int faces = 0; faces < cache.drawFace.Length; faces++)
            {
                if (!cache.drawFace[faces])
                {
                    continue;
                }
                BoxFace face = (BoxFace)faces;

                // Let's get the vertex/index positions for the current face.
                int idx         = 0;
                int vertexCount = 0;
                int vertOffset  = 0;
                int numVerts    = 0;
                primitive.GetFace(face, primitive.UVs, out idx, out vertexCount, out vertOffset, out numVerts);

                for (int i = 0; i < vertexCount; i++)
                {
                    // Used twice so we'll store it for later use.
                    int         primitiveIndex = primitive.Indexes[i + idx];
                    VoxelVertex currentVertex  = primitive.Deltas[primitiveIndex];

                    // These two will be filled out before being used.
                    float   foaminess;
                    Vector3 pos;

                    // We are going to have to reuse some vertices when drawing a single so we'll store the position/foaminess
                    // for quick lookup when we find one of those reused ones.
                    // When drawing multiple faces the Vertex overlap gets bigger, which is a bonus.
                    if (!cache.vertexCalculated[(int)currentVertex])
                    {
                        float count             = 1.0f;
                        float emptyNeighbors    = 0.0f;
                        float averageWaterLevel = centerWaterlevel;

                        List <Vector3> vertexSucc = VoxelChunk.VertexSuccessors[currentVertex];

                        // Run through the successors and count up the water in each voxel.
                        for (int v = 0; v < vertexSucc.Count; v++)
                        {
                            Vector3 succ = vertexSucc[v];
                            // We are going to use a lookup key so calculate it now.
                            int key = VoxelChunk.SuccessorToEuclidianLookupKey(succ);

                            // If we haven't gotten this Voxel yet then retrieve it.
                            // This allows us to only get a particular voxel once a function call instead of once per vertexCount/per face.
                            if (!cache.retrievedNeighbors[key])
                            {
                                Voxel neighbor = cache.neighbors[key];
                                cache.validNeighbors[key]     = voxel.GetNeighborBySuccessor(succ, ref neighbor, false);
                                cache.retrievedNeighbors[key] = true;
                            }
                            // Only continue if it's a valid (non-null) voxel.
                            if (!cache.validNeighbors[key])
                            {
                                continue;
                            }

                            // Now actually do the math.
                            Voxel vox = cache.neighbors[key];
                            averageWaterLevel += vox.WaterLevel;
                            count++;
                            if (vox.WaterLevel < 1)
                            {
                                emptyNeighbors++;
                            }
                        }

                        averageWaterLevel = averageWaterLevel / count;

                        float averageWaterHeight = averageWaterLevel / WaterManager.maxWaterLevel;
                        foaminess = emptyNeighbors / count;

                        if (foaminess <= 0.5f)
                        {
                            foaminess = 0.0f;
                        }

                        pos    = primitive.Vertices[primitiveIndex].Position;
                        pos.Y *= averageWaterHeight;
                        pos   += origin;

                        // Store the vertex information for future use when we need it again on this or another face.
                        cache.vertexCalculated[(int)currentVertex] = true;
                        cache.vertexFoaminess[(int)currentVertex]  = foaminess;
                        cache.vertexPositions[(int)currentVertex]  = pos;
                    }
                    else
                    {
                        // We've already calculated this one.  Time for a cheap grab from the lookup.
                        foaminess = cache.vertexFoaminess[(int)currentVertex];
                        pos       = cache.vertexPositions[(int)currentVertex];
                    }

                    switch (face)
                    {
                    case BoxFace.Back:
                    case BoxFace.Front:
                        vertices[i + startVertex].Set(pos,
                                                      new Color(foaminess, 0.0f, 1.0f, 1.0f),
                                                      Color.White,
                                                      new Vector2(pos.X, pos.Y),
                                                      new Vector4(0, 0, 1, 1));
                        break;

                    case BoxFace.Right:
                    case BoxFace.Left:
                        vertices[i + startVertex].Set(pos,
                                                      new Color(foaminess, 0.0f, 1.0f, 1.0f),
                                                      Color.White,
                                                      new Vector2(pos.Z, pos.Y),
                                                      new Vector4(0, 0, 1, 1));
                        break;

                    case BoxFace.Top:
                        vertices[i + startVertex].Set(pos,
                                                      new Color(foaminess, 0.0f, 1.0f, 1.0f),
                                                      Color.White,
                                                      new Vector2(pos.X, pos.Z),
                                                      new Vector4(0, 0, 1, 1));
                        break;
                    }
                }
                startVertex += 6;
            }
        }
Exemple #8
0
        private static void CreateWaterFace(Voxel voxel,
                                            BoxFace face,
                                            VoxelChunk chunk,
                                            int x, int y, int z,
                                            bool top,
                                            ExtendedVertex[] vertices,
                                            int startVertex)
        {
            int idx         = 0;
            int vertexCount = 0;
            int vertOffset  = 0;
            int numVerts    = 0;

            primitive.GetFace(face, primitive.UVs, out idx, out vertexCount, out vertOffset, out numVerts);

            for (int i = idx; i < idx + vertexCount; i++)
            {
                vertices[i + startVertex - idx] = primitive.Vertices[primitive.Indexes[i]];
            }

            Vector3      origin          = chunk.Origin + new Vector3(x, y, z);
            List <Voxel> neighborsVertex = new List <Voxel>();

            for (int i = 0; i < vertexCount; i++)
            {
                VoxelVertex currentVertex = VoxelChunk.GetNearestDelta(vertices[i + startVertex].Position);
                chunk.GetNeighborsVertex(currentVertex, voxel, neighborsVertex);
                int   index             = chunk.Data.IndexAt(x, y, z);
                float averageWaterLevel = chunk.Data.Water[index].WaterLevel;
                float count             = 1.0f;
                float emptyNeighbors    = 0.0f;

                foreach (byte level in neighborsVertex.Select(vox => vox.WaterLevel))
                {
                    averageWaterLevel += level;
                    count++;

                    if (level < 1)
                    {
                        emptyNeighbors++;
                    }
                }

                averageWaterLevel = averageWaterLevel / count;

                float averageWaterHeight = (float)averageWaterLevel / 8.0f;
                float foaminess          = emptyNeighbors / count;

                if (foaminess <= 0.5f)
                {
                    foaminess = 0.0f;
                }

                /*
                 * if(chunk.Data.Water[index].IsFalling || !top)
                 * {
                 *  averageWaterHeight = 1.0f;
                 * }
                 */

                Vector3 pos = vertices[i + startVertex].Position;
                pos.Y *= averageWaterHeight;
                pos   += origin;

                switch (face)
                {
                case BoxFace.Back:
                case BoxFace.Front:
                    vertices[i + startVertex].Set(pos,
                                                  new Color(foaminess, 0.0f, 1.0f, 1.0f),
                                                  Color.White,
                                                  new Vector2(pos.X, pos.Y),
                                                  new Vector4(0, 0, 1, 1));
                    break;

                case BoxFace.Right:
                case BoxFace.Left:
                    vertices[i + startVertex].Set(pos,
                                                  new Color(foaminess, 0.0f, 1.0f, 1.0f),
                                                  Color.White,
                                                  new Vector2(pos.Z, pos.Y),
                                                  new Vector4(0, 0, 1, 1));
                    break;

                case BoxFace.Top:
                    vertices[i + startVertex].Set(pos,
                                                  new Color(foaminess, 0.0f, 1.0f, 1.0f),
                                                  Color.White,
                                                  new Vector2(pos.X, pos.Z),
                                                  new Vector4(0, 0, 1, 1));
                    break;
                }
            }
        }
Exemple #9
0
        private static void BuildVoxelTopFaceGeometry(
            RawPrimitive Into,
            VoxelChunk Chunk,
            Cache Cache,
            BoxPrimitive Primitive,
            VoxelHandle V,
            BoxPrimitive.BoxTextureCoords UVs)
        {
            if (!IsFaceVisible(V, BoxFace.Top, Chunk.Manager, out var _))
            {
                return;
            }

            var faceDescriptor = Primitive.GetFace(BoxFace.Top);
            int exploredVerts  = 0;
            var vertexColors   = new VertexColorInfo[4];
            var vertexTint     = new Color[4];

            // Find all verticies to use for geometry later, and for the fringe
            var vertexPositions = new Vector3[4];

            for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; faceVertex++)
            {
                var vertex      = Primitive.Vertices[faceDescriptor.VertexOffset + faceVertex];
                var voxelVertex = Primitive.VertexClassifications[faceDescriptor.VertexOffset + faceVertex];

                var rampOffset = Vector3.Zero;
                if (V.IsExplored && V.Type.CanRamp && ShouldRamp(voxelVertex, V.RampType))
                {
                    rampOffset = new Vector3(0, -0.5f, 0);
                }

                var worldPosition = V.WorldPosition + vertex.Position + rampOffset;
                //worldPosition += VertexNoise.GetNoiseVectorFromRepeatingTexture(worldPosition);

                vertexPositions[faceVertex] = worldPosition;
            }

            // Figure out if this vertex is adjacent to any explored voxel.
            if (V.IsExplored)
            {
                exploredVerts = 4;
            }
            else
            {
                for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; ++faceVertex)
                {
                    var voxelVertex         = Primitive.VertexClassifications[faceDescriptor.VertexOffset + faceVertex];
                    var cacheKey            = GetCacheKey(V, voxelVertex);
                    var anyNeighborExplored = true;

                    if (!Cache.ExploredCache.TryGetValue(cacheKey, out anyNeighborExplored))
                    {
                        anyNeighborExplored = VoxelHelpers.EnumerateVertexNeighbors2D(V.Coordinate, voxelVertex)
                                              .Select(c => new VoxelHandle(V.Chunk.Manager, c))
                                              .Any(n => n.IsValid && n.IsExplored);
                        Cache.ExploredCache.Add(cacheKey, anyNeighborExplored);
                    }

                    if (anyNeighborExplored)
                    {
                        exploredVerts += 1;
                    }
                }
            }

            for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; ++faceVertex)
            {
                var voxelVertex = Primitive.VertexClassifications[faceDescriptor.VertexOffset + faceVertex];
                var cacheKey    = GetCacheKey(V, voxelVertex);

                VertexColorInfo vertexColor;
                if (!Cache.LightCache.TryGetValue(cacheKey, out vertexColor))
                {
                    vertexColor = CalculateVertexLight(V, voxelVertex, Chunk.Manager);
                    Cache.LightCache.Add(cacheKey, vertexColor);
                }

                vertexColors[faceVertex] = vertexColor;

                vertexTint[faceVertex] = new Color(1.0f, 1.0f, 1.0f, 1.0f);

                // Turn face solid black if there are no explored neighbors - this is an optimization; it means we do not need to apply a solid black decal.
                if (exploredVerts != 4)
                {
                    var anyNeighborExplored = true;
                    if (!Cache.ExploredCache.TryGetValue(cacheKey, out anyNeighborExplored))
                    {
                        throw new InvalidProgramException("Failed cache lookup");
                    }

                    if (!anyNeighborExplored)
                    {
                        vertexTint[faceVertex] = new Color(0.0f, 0.0f, 0.0f, 1.0f);
                    }
                }

                vertexTint[faceVertex] = new Color(vertexTint[faceVertex].ToVector4() * V.Type.Tint.ToVector4());
            }

            if (exploredVerts != 0)
            {
                var baseUVs = UVs.Uvs[11]; // EW

                var baseUVBounds = new Vector4(baseUVs.X + 0.001f, baseUVs.Y + 0.001f, baseUVs.X + (1.0f / 16.0f) - 0.001f, baseUVs.Y + (1.0f / 16.0f) - 0.001f);

                // Draw the base voxel
                AddTopFaceGeometry(Into,
                                   Cache.AmbientValues, Primitive,
                                   faceDescriptor,
                                   vertexPositions,
                                   vertexColors,
                                   vertexTint,
                                   Vector2.One,
                                   baseUVs, baseUVBounds);

                if (V.GrassType != 0)
                {
                    BuildGrassFringeGeometry(Into, Chunk, Cache, Primitive, V, vertexColors, vertexTint, vertexPositions, faceDescriptor);
                }

                if (V.DecalType != 0)
                {
                    BuildDecalGeometry(Into, Chunk, Cache, Primitive, V, vertexColors, vertexTint, vertexPositions, faceDescriptor, exploredVerts);
                }
            }
            else
            {
                // Apparently being unexplored hides all grass and decals. Is that the behavior we actually want?

                if (!Debugger.Switches.HideSliceTop)
                {
                    var indexOffset = Into.VertexCount;

                    for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; faceVertex++)
                    {
                        Into.AddVertex(new ExtendedVertex(
                                           vertexPositions[faceVertex] + VertexNoise.GetNoiseVectorFromRepeatingTexture(vertexPositions[faceVertex]),
                                           new Color(0, 0, 0, 255),
                                           new Color(0, 0, 0, 255),
                                           new Vector2(12.5f / 16.0f, 0.5f / 16.0f),
                                           new Vector4(12.0f / 16.0f, 0.0f, 13.0f / 16.0f, 1.0f / 16.0f)));
                    }

                    for (int idx = faceDescriptor.IndexOffset; idx < faceDescriptor.IndexCount +
                         faceDescriptor.IndexOffset; idx++)
                    {
                        ushort offset  = Primitive.Indexes[idx];
                        ushort offset0 = Primitive.Indexes[faceDescriptor.IndexOffset];
                        Into.AddIndex((short)(indexOffset + offset - offset0));
                    }
                }
            }
        }
Exemple #10
0
        private static void BuildVoxelFaceGeometry(
            RawPrimitive Into,
            VoxelChunk Chunk,
            Cache Cache,
            BoxPrimitive Primitive,
            VoxelHandle V,
            Color Tint,
            BoxPrimitive.BoxTextureCoords UVs,
            Matrix VertexTransform,
            BoxFace BoxFace,
            bool ApplyLighting)
        {
            if (!IsFaceVisible(V, BoxFace, Chunk.Manager, out var neighbor))
            {
                return;
            }

            var faceDescriptor = Primitive.GetFace(BoxFace);
            var indexOffset    = Into.VertexCount;

            for (int faceVertex = 0; faceVertex < faceDescriptor.VertexCount; faceVertex++)
            {
                var vertex      = Primitive.Vertices[faceDescriptor.VertexOffset + faceVertex];
                var voxelVertex = Primitive.VertexClassifications[faceDescriptor.VertexOffset + faceVertex];
                var vertexColor = new VertexColorInfo
                {
                    SunColor     = 255,
                    AmbientColor = 255,
                    DynamicColor = 255,
                };

                if (ApplyLighting)
                {
                    var cacheKey = GetCacheKey(V, voxelVertex);

                    if (!Cache.LightCache.TryGetValue(cacheKey, out vertexColor))
                    {
                        vertexColor = CalculateVertexLight(V, voxelVertex, Chunk.Manager);
                        Cache.LightCache.Add(cacheKey, vertexColor);
                    }

                    Cache.AmbientValues[faceVertex] = vertexColor.AmbientColor;

                    if (!V.IsExplored && !neighbor.IsValid) // Turns the outside of the world black when it's not explored.
                    {
                        Tint = new Color(0.0f, 0.0f, 0.0f, 1.0f);
                    }
                }

                var rampOffset = Vector3.Zero;
                if (V.IsExplored && V.Type.CanRamp && ShouldRamp(voxelVertex, V.RampType))
                {
                    rampOffset = new Vector3(0, -0.5f, 0);
                }

                var baseWorldPosition = V.WorldPosition + vertex.Position + rampOffset;
                var noise             = VertexNoise.GetNoiseVectorFromRepeatingTexture(baseWorldPosition);
                var localPosition     = Vector3.Transform(vertex.Position + rampOffset + noise, VertexTransform);

                Into.AddVertex(new ExtendedVertex(
                                   V.WorldPosition + localPosition,
                                   vertexColor.AsColor(),
                                   Tint,
                                   UVs.Uvs[faceDescriptor.VertexOffset + faceVertex],
                                   UVs.Bounds[faceDescriptor.IndexOffset / 6]));
            }

            // Sometimes flip the quad to smooth out lighting.
            bool flippedQuad = ApplyLighting && (Cache.AmbientValues[0] + Cache.AmbientValues[2] >
                                                 Cache.AmbientValues[1] + Cache.AmbientValues[3]);

            for (int idx = faceDescriptor.IndexOffset; idx < faceDescriptor.IndexCount +
                 faceDescriptor.IndexOffset; idx++)
            {
                ushort offset  = flippedQuad ? Primitive.FlippedIndexes[idx] : Primitive.Indexes[idx];
                ushort offset0 = flippedQuad ? Primitive.FlippedIndexes[faceDescriptor.IndexOffset] : Primitive.Indexes[faceDescriptor.IndexOffset];
                Into.AddIndex((short)(indexOffset + offset - offset0));
            }
        }