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); }
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)); } }
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(); }
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; } }
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; } } }
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)); } } } }
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)); } }