add ChunkVertexGenerator

This commit is contained in:
Gered 2013-08-25 19:50:37 -04:00
parent e1b5cb9c0c
commit d20837e199
2 changed files with 266 additions and 5 deletions

View file

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

View file

@ -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)