189 lines
8.8 KiB
Java
189 lines
8.8 KiB
Java
package com.blarg.gdx.tilemap3d;
|
|
|
|
import com.badlogic.gdx.graphics.Color;
|
|
import com.badlogic.gdx.graphics.VertexAttributes;
|
|
import com.badlogic.gdx.graphics.g3d.utils.MeshBuilder;
|
|
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
|
|
import com.badlogic.gdx.math.Matrix4;
|
|
import com.badlogic.gdx.math.Vector3;
|
|
import com.blarg.gdx.graphics.Vertices;
|
|
import com.blarg.gdx.tilemap3d.tilemesh.CubeTileMesh;
|
|
import com.blarg.gdx.tilemap3d.tilemesh.TileMesh;
|
|
|
|
public class ChunkVertexGenerator {
|
|
protected final MeshBuilder.VertexInfo vertex = new MeshPartBuilder.VertexInfo();
|
|
|
|
final MeshBuilder builder = new MeshBuilder();
|
|
final MeshBuilder alphaBuilder = new MeshBuilder();
|
|
|
|
final TileCoord tmpPosition = new TileCoord();
|
|
final Color tmpColor = new Color();
|
|
final Vector3 tmpOffset = new Vector3();
|
|
|
|
public void generate(TileChunk chunk) {
|
|
TileMap tileMap = chunk.tileMap;
|
|
|
|
builder.begin(
|
|
VertexAttributes.Usage.Position |
|
|
VertexAttributes.Usage.Color |
|
|
VertexAttributes.Usage.Normal |
|
|
VertexAttributes.Usage.TextureCoordinates
|
|
);
|
|
alphaBuilder.begin(
|
|
VertexAttributes.Usage.Position |
|
|
VertexAttributes.Usage.Color |
|
|
VertexAttributes.Usage.Normal |
|
|
VertexAttributes.Usage.TextureCoordinates
|
|
);
|
|
|
|
for (int y = 0; y < chunk.getHeight(); ++y) {
|
|
for (int z = 0; z < chunk.getDepth(); ++z) {
|
|
for (int x = 0; x < chunk.getWidth(); ++x) {
|
|
Tile tile = chunk.get(x, y, z);
|
|
if (tile.tile == Tile.NO_TILE)
|
|
continue;
|
|
|
|
TileMesh mesh = chunk.tileMap.tileMeshes.get(tile);
|
|
|
|
// "world/tilemap space" position that this tile is at
|
|
tmpPosition.x = x + (int)chunk.getPosition().x;
|
|
tmpPosition.y = y + (int)chunk.getPosition().y;
|
|
tmpPosition.z = z + (int)chunk.getPosition().z;
|
|
|
|
Matrix4 transform = Tile.getTransformationFor(tile);
|
|
|
|
// tile color
|
|
if (tile.hasCustomColor())
|
|
tmpColor.set(tile.color);
|
|
else
|
|
tmpColor.set(mesh.color);
|
|
|
|
if (mesh instanceof CubeTileMesh) {
|
|
CubeTileMesh cubeMesh = (CubeTileMesh)mesh;
|
|
|
|
// 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.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(left).isOpaque(TileMesh.SIDE_RIGHT)) && cubeMesh.hasFace(TileMesh.SIDE_LEFT)) {
|
|
// left face is visible
|
|
if (cubeMesh.alpha)
|
|
addMesh(alphaBuilder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.leftFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
else
|
|
addMesh(builder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.leftFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
}
|
|
if ((right == null || right.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(right).isOpaque(TileMesh.SIDE_LEFT)) && cubeMesh.hasFace(TileMesh.SIDE_RIGHT)) {
|
|
// right face is visible
|
|
if (cubeMesh.alpha)
|
|
addMesh(alphaBuilder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.rightFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
else
|
|
addMesh(builder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.rightFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
}
|
|
if ((forward == null || forward.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(forward).isOpaque(TileMesh.SIDE_BACK)) && cubeMesh.hasFace(TileMesh.SIDE_FRONT)) {
|
|
// front face is visible
|
|
if (cubeMesh.alpha)
|
|
addMesh(alphaBuilder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.frontFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
else
|
|
addMesh(builder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.frontFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
}
|
|
if ((backward == null || backward.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(backward).isOpaque(TileMesh.SIDE_FRONT)) && cubeMesh.hasFace(TileMesh.SIDE_BACK)) {
|
|
// back face is visible
|
|
if (cubeMesh.alpha)
|
|
addMesh(alphaBuilder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.backFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
else
|
|
addMesh(builder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.backFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
}
|
|
if ((down == null || down.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(down).isOpaque(TileMesh.SIDE_TOP)) && cubeMesh.hasFace(TileMesh.SIDE_BOTTOM)) {
|
|
// bottom face is visible
|
|
if (cubeMesh.alpha)
|
|
addMesh(alphaBuilder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.bottomFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
else
|
|
addMesh(builder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.bottomFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
}
|
|
if ((up == null || up.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(up).isOpaque(TileMesh.SIDE_BOTTOM)) && cubeMesh.hasFace(TileMesh.SIDE_TOP)) {
|
|
// top face is visible
|
|
if (cubeMesh.alpha)
|
|
addMesh(alphaBuilder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.topFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
else
|
|
addMesh(builder, cubeMesh, chunk, tmpPosition, transform, tmpColor, cubeMesh.topFaceVertexOffset, TileMesh.CUBE_VERTICES_PER_FACE);
|
|
}
|
|
} else {
|
|
boolean 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.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(left).isOpaque(TileMesh.SIDE_RIGHT)) ||
|
|
(right == null || right.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(right).isOpaque(TileMesh.SIDE_LEFT)) ||
|
|
(forward == null || forward.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(forward).isOpaque(TileMesh.SIDE_BACK)) ||
|
|
(backward == null || backward.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(backward).isOpaque(TileMesh.SIDE_FRONT)) ||
|
|
(up == null || up.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(up).isOpaque(TileMesh.SIDE_BOTTOM)) ||
|
|
(down == null || down.tile == Tile.NO_TILE || !tileMap.tileMeshes.get(down).isOpaque(TileMesh.SIDE_TOP))
|
|
)
|
|
visible = true;
|
|
|
|
if (visible) {
|
|
if (mesh.alpha)
|
|
addMesh(alphaBuilder, mesh, chunk, tmpPosition, transform, tmpColor, 0, mesh.getVertices().count());
|
|
else
|
|
addMesh(builder, mesh, chunk, tmpPosition, transform, tmpColor, 0, mesh.getVertices().count());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
chunk.mesh.setMesh(builder.end());
|
|
chunk.alphaMesh.setMesh(alphaBuilder.end());
|
|
}
|
|
|
|
protected void addMesh(MeshBuilder builder, TileMesh sourceMesh, TileChunk chunk, TileCoord position, Matrix4 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
|
|
tmpOffset.set(TileMesh.OFFSET);
|
|
tmpOffset.x += (float)position.x;
|
|
tmpOffset.y += (float)position.y;
|
|
tmpOffset.z += (float)position.z;
|
|
|
|
Vertices sourceVertices = sourceMesh.getVertices();
|
|
sourceVertices.moveTo(firstVertex);
|
|
|
|
// copy vertices
|
|
for (int i = 0; i < numVertices; ++i) {
|
|
copyVertex(builder, sourceMesh, sourceVertices, tmpOffset, transform, color, chunk);
|
|
sourceVertices.moveNext();
|
|
}
|
|
}
|
|
|
|
protected void copyVertex(MeshBuilder builder, TileMesh sourceMesh, Vertices sourceVertices, Vector3 positionOffset, Matrix4 transform, Color color, TileChunk chunk) {
|
|
sourceVertices.getVertex(vertex);
|
|
|
|
// transform if applicable... (this will probably just be per-tile rotation)
|
|
if (transform != null) {
|
|
vertex.position.mul(transform);
|
|
vertex.normal.mul(transform);
|
|
}
|
|
|
|
// translate vertex into "world/tilemap space"
|
|
vertex.position.add(positionOffset);
|
|
|
|
builder.vertex(vertex);
|
|
}
|
|
}
|