From d20837e1997104b53b2190e4cd793840e3124894 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 25 Aug 2013 19:50:37 -0400 Subject: [PATCH] add ChunkVertexGenerator --- .../TileMap/ChunkVertexGenerator.cs | 249 ++++++++++++++++++ Blarg.GameFramework/TileMap/TileChunk.cs | 22 +- 2 files changed, 266 insertions(+), 5 deletions(-) diff --git a/Blarg.GameFramework/TileMap/ChunkVertexGenerator.cs b/Blarg.GameFramework/TileMap/ChunkVertexGenerator.cs index b3a4e72..275df96 100644 --- a/Blarg.GameFramework/TileMap/ChunkVertexGenerator.cs +++ b/Blarg.GameFramework/TileMap/ChunkVertexGenerator.cs @@ -1,4 +1,6 @@ using System; +using Blarg.GameFramework.Graphics; +using Blarg.GameFramework.TileMap.Meshes; namespace Blarg.GameFramework.TileMap { @@ -8,8 +10,255 @@ namespace Blarg.GameFramework.TileMap { } + private VertexBuffer MakeVertexBuffer() + { + var buffer = new VertexBuffer(Framework.GraphicsDevice, + VertexAttributeDeclarations.TextureColorNormalPosition3D, + 128, + BufferObjectUsage.Static); + return buffer; + } + public void Generate(TileChunk chunk) { + VertexBuffer mesh = null; + VertexBuffer alphaMesh = null; + + for (int y = 0; y < chunk.Height; ++y) + { + for (int z = 0; z < chunk.Depth; ++z) + { + for (int x = 0; x < chunk.Width; ++x) + { + var tile = chunk.Get(x, y, z); + if (tile.TileIndex == Tile.NO_TILE) + continue; + + var tileMesh = chunk.TileMap.TileMeshes.Get(tile); + + // choose the right vertex buffer to add this tile's mesh + // to and, if necessary, create the buffer + VertexBuffer buffer; + if (tileMesh.Alpha) + { + if (alphaMesh == null) + alphaMesh = MakeVertexBuffer(); + buffer = alphaMesh; + } + else + { + if (mesh == null) + mesh = MakeVertexBuffer(); + buffer = mesh; + } + + // "world/tilemap space" position that this tile is at + Point3 position; + position.X = x + (int)chunk.Position.X; + position.Y = y + (int)chunk.Position.Y; + position.Z = z + (int)chunk.Position.Z; + + Matrix4x4 transform = Matrix4x4.Identity; + Tile.GetTransformationFor(tile, ref transform); + + // tile color + Color color; + if (tile.HasCustomColor) + color = Color.FromInt(tile.Color); + else + color = tileMesh.Color; + + if (tileMesh is CubeTileMesh) + HandleCubeMesh(buffer, x, y, z, tile, chunk, (CubeTileMesh)tileMesh, position, transform, color); + else + HandleGenericMesh(buffer, x, y, z, tile, chunk, tileMesh, position, transform, color); + } + } + } + } + + private void HandleCubeMesh(VertexBuffer buffer, + int x, + int y, + int z, + Tile tile, + TileChunk chunk, + CubeTileMesh mesh, + Point3 tileMapPosition, + Matrix4x4 transform, + Color color) + { + // determine what's next to each cube face + Tile left = chunk.GetWithinSelfOrNeighbourSafe(x - 1, y, z); + Tile right = chunk.GetWithinSelfOrNeighbourSafe(x + 1, y, z); + Tile forward = chunk.GetWithinSelfOrNeighbourSafe(x, y, z - 1); + Tile backward = chunk.GetWithinSelfOrNeighbourSafe(x, y, z + 1); + Tile down = chunk.GetWithinSelfOrNeighbourSafe(x, y - 1, z); + Tile up = chunk.GetWithinSelfOrNeighbourSafe(x, y + 1, z); + + // evaluate each face's visibility and add it's vertices if needed one at a time + if ((left == null || left.TileIndex == Tile.NO_TILE || !chunk.TileMap.TileMeshes.Get(left).IsOpaque(TileMesh.SIDE_RIGHT)) && mesh.HasFace(TileMesh.SIDE_LEFT)) { + // left face is visible + AddMesh(buffer, + tile, + mesh, + chunk, + tileMapPosition, + transform, + color, + mesh.LeftFaceVertexOffset, + TileMesh.CUBE_VERTICES_PER_FACE); + } + if ((right == null || right.TileIndex == Tile.NO_TILE || !chunk.TileMap.TileMeshes.Get(right).IsOpaque(TileMesh.SIDE_LEFT)) && mesh.HasFace(TileMesh.SIDE_RIGHT)) { + // right face is visible + AddMesh(buffer, + tile, + mesh, + chunk, + tileMapPosition, + transform, + color, + mesh.RightFaceVertexOffset, + TileMesh.CUBE_VERTICES_PER_FACE); + } + if ((forward == null || forward.TileIndex == Tile.NO_TILE || !chunk.TileMap.TileMeshes.Get(forward).IsOpaque(TileMesh.SIDE_BACK)) && mesh.HasFace(TileMesh.SIDE_FRONT)) { + // front face is visible + AddMesh(buffer, + tile, + mesh, + chunk, + tileMapPosition, + transform, + color, + mesh.FrontFaceVertexOffset, + TileMesh.CUBE_VERTICES_PER_FACE); + } + if ((backward == null || backward.TileIndex == Tile.NO_TILE || !chunk.TileMap.TileMeshes.Get(backward).IsOpaque(TileMesh.SIDE_FRONT)) && mesh.HasFace(TileMesh.SIDE_BACK)) { + // back face is visible + AddMesh(buffer, + tile, + mesh, + chunk, + tileMapPosition, + transform, + color, + mesh.BackFaceVertexOffset, + TileMesh.CUBE_VERTICES_PER_FACE); + } + if ((down == null || down.TileIndex == Tile.NO_TILE || !chunk.TileMap.TileMeshes.Get(down).IsOpaque(TileMesh.SIDE_TOP)) && mesh.HasFace(TileMesh.SIDE_BOTTOM)) { + // bottom face is visible + AddMesh(buffer, + tile, + mesh, + chunk, + tileMapPosition, + transform, + color, + mesh.BottomFaceVertexOffset, + TileMesh.CUBE_VERTICES_PER_FACE); + } + if ((up == null || up.TileIndex == Tile.NO_TILE || !chunk.TileMap.TileMeshes.Get(up).IsOpaque(TileMesh.SIDE_BOTTOM)) && mesh.HasFace(TileMesh.SIDE_TOP)) { + // top face is visible + AddMesh(buffer, + tile, + mesh, + chunk, + tileMapPosition, + transform, + color, + mesh.TopFaceVertexOffset, + TileMesh.CUBE_VERTICES_PER_FACE); + } + } + + private void HandleGenericMesh(VertexBuffer buffer, + int x, + int y, + int z, + Tile tile, + TileChunk chunk, + TileMesh mesh, + Point3 tileMapPosition, + Matrix4x4 transform, + Color color) + { + bool visible = false; + + // visibility determination. we check for at least one + // adjacent empty space / non-opaque tile + Tile left = chunk.GetWithinSelfOrNeighbourSafe(x - 1, y, z); + Tile right = chunk.GetWithinSelfOrNeighbourSafe(x + 1, y, z); + Tile forward = chunk.GetWithinSelfOrNeighbourSafe(x, y, z - 1); + Tile backward = chunk.GetWithinSelfOrNeighbourSafe(x, y, z + 1); + Tile down = chunk.GetWithinSelfOrNeighbourSafe(x, y - 1, z); + Tile up = chunk.GetWithinSelfOrNeighbourSafe(x, y + 1, z); + + // null == empty space (off the edge of the entire map) + if ((left == null || left.IsEmptySpace || !chunk.TileMap.TileMeshes.Get(left).IsOpaque(TileMesh.SIDE_RIGHT)) || + (right == null || right.IsEmptySpace || !chunk.TileMap.TileMeshes.Get(right).IsOpaque(TileMesh.SIDE_LEFT)) || + (forward == null || forward.IsEmptySpace || !chunk.TileMap.TileMeshes.Get(forward).IsOpaque(TileMesh.SIDE_BACK)) || + (backward == null || backward.IsEmptySpace || !chunk.TileMap.TileMeshes.Get(backward).IsOpaque(TileMesh.SIDE_FRONT)) || + (up == null || up.IsEmptySpace || !chunk.TileMap.TileMeshes.Get(up).IsOpaque(TileMesh.SIDE_BOTTOM)) || + (down == null || down.IsEmptySpace || !chunk.TileMap.TileMeshes.Get(down).IsOpaque(TileMesh.SIDE_TOP)) + ) + visible = true; + + if (visible) + AddMesh(buffer, tile, mesh, chunk, tileMapPosition, transform, color, 0, mesh.Vertices.NumElements); + } + + protected void AddMesh(VertexBuffer buffer, + Tile tile, + TileMesh sourceMesh, + TileChunk chunk, + Point3 position, + Matrix4x4? transform, + Color color, + int firstVertex, + int numVertices) + { + // adjust position by the tilemesh offset. TileMesh's are modeled using the + // origin (0,0,0) as the center and are 1 unit wide/deep/tall. So, their + // max extents are from -0.5,-0.5,-0.5 to 0.5,0.5,0.5. For rendering + // purposes in a chunk, we want the extents to be 0,0,0 to 1,1,1. This + // adjustment fixes that + Vector3 offset = TileMesh.OFFSET; + offset.X += (float)position.X; + offset.Y += (float)position.Y; + offset.Z += (float)position.Z; + + var sourceVertices = sourceMesh.Vertices; + sourceVertices.MoveTo(firstVertex); + + // copy vertices + for (int i = 0; i < numVertices; ++i) { + Vector3 v = sourceVertices.GetCurrentPosition3D(); + Vector3 n = sourceVertices.GetCurrentNormal(); + + if (transform != null) + { + var transformMatrix = transform.Value; + // need to transform the vertex + normal first before copying it + v = Matrix4x4.Transform(transformMatrix, v); + n = Matrix4x4.TransformNormal(transformMatrix, n); + } + + // translate vertex into "tilemap space" + v += offset; + + // copy to destination + buffer.SetCurrentPosition3D(v); + buffer.SetCurrentNormal(n); + + // just directly copy the tex coord as-is + buffer.SetCurrentTexCoord(sourceVertices.GetCurrentTexCoord()); + + // color is the same for the entire mesh + buffer.SetCurrentColor(color); + + sourceVertices.MoveNext(); + buffer.MoveNext(); + } } } } diff --git a/Blarg.GameFramework/TileMap/TileChunk.cs b/Blarg.GameFramework/TileMap/TileChunk.cs index 3811c45..0138324 100644 --- a/Blarg.GameFramework/TileMap/TileChunk.cs +++ b/Blarg.GameFramework/TileMap/TileChunk.cs @@ -14,10 +14,11 @@ namespace Blarg.GameFramework.TileMap readonly int _depth; readonly Vector3 _position; readonly BoundingBox _bounds; - readonly TileMap _tileMap; VertexBuffer _mesh; VertexBuffer _alphaMesh; + public readonly TileMap TileMap; + public Tile[] Data { get { return _data; } @@ -93,7 +94,7 @@ namespace Blarg.GameFramework.TileMap if (tileMap == null) throw new ArgumentNullException("tileMap"); - _tileMap = tileMap; + TileMap = tileMap; _x = x; _y = y; _z = z; @@ -119,12 +120,23 @@ namespace Blarg.GameFramework.TileMap generator.Generate(this); } + internal void SetMeshes(VertexBuffer mesh, VertexBuffer alphaMesh) + { + if (_mesh != null) + _mesh.Dispose(); + _mesh = mesh; + + if (_alphaMesh != null) + _alphaMesh.Dispose(); + _alphaMesh = alphaMesh; + } + public Tile GetWithinSelfOrNeighbour(int x, int y, int z) { int checkX = x + _x; int checkY = y + _y; int checkZ = z + _z; - return _tileMap.Get(checkX, checkY, checkZ); + return TileMap.Get(checkX, checkY, checkZ); } public Tile GetWithinSelfOrNeighbourSafe(int x, int y, int z) @@ -132,10 +144,10 @@ namespace Blarg.GameFramework.TileMap int checkX = x + _x; int checkY = y + _y; int checkZ = z + _z; - if (!_tileMap.IsWithinBounds(checkX, checkY, checkZ)) + if (!TileMap.IsWithinBounds(checkX, checkY, checkZ)) return null; else - return _tileMap.Get(checkX, checkY, checkZ); + return TileMap.Get(checkX, checkY, checkZ); } public override Tile Get(int x, int y, int z)