public static BoxPrimitive CreatePrimitive(GraphicsDevice graphics, Texture2D textureMap, int width, int height, Point top, Point sides, Point bottom) { BoxPrimitive.BoxTextureCoords coords = new BoxPrimitive.BoxTextureCoords(textureMap.Width, textureMap.Height, width, height, sides, sides, top, bottom, sides, sides); BoxPrimitive cube = new BoxPrimitive(graphics, 1.0f, 1.0f, 1.0f, coords); return cube; }
public static void CreateTransitionUVs(GraphicsDevice graphics, Texture2D textureMap, int width, int height, Point top, Point sides, Point bottom, Dictionary<TransitionTexture, BoxPrimitive.BoxTextureCoords> transitionTextures) { for(int i = 0; i < 16; i++) { Point topPoint = new Point(top.X + i, top.Y); transitionTextures[(TransitionTexture)i] = new BoxPrimitive.BoxTextureCoords(textureMap.Width, textureMap.Height, width, height, sides, sides, topPoint, bottom, sides, sides); } }
public void GetBottomFace(BoxPrimitive.BoxTextureCoords uvs, out int idx, out int count) { idx = 18; count = 6; }
public void GetFrontFace(BoxPrimitive.BoxTextureCoords uvs, out int idx, out int count) { idx = 0; count = 6; }
public void GetTopFace(BoxPrimitive.BoxTextureCoords uvs, out int idx, out int count) { idx = 12; count = 6; }
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 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)); } }
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)); } }
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].Position = vert.Position + v.Position + VertexNoise.GetNoiseVectorFromRepeatingTexture( vert.Position + v.Position) + offset; Vertices[maxVertex].Color = color; Vertices[maxVertex].VertColor = tint; Vertices[maxVertex].TextureCoordinate = uvs.Uvs[vertOffset + vertexIndex] + texOffset; Vertices[maxVertex].TextureBounds = 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(PlayState.ChunkManager.ChunkData.Tilemap.Bounds); isRebuilding = false; //chunk.PrimitiveMutex.WaitOne(); chunk.NewPrimitive = this; chunk.NewPrimitiveReceived = true; //chunk.PrimitiveMutex.ReleaseMutex(); }