Hi,
I have not written an entry in the wiki about custom shapes, it is still on my to-do list. I do of course welcome contributions
I did however add a lot of javadoc explaining the inner workings of the mesh construction.
For a custom shape to be useable by Blocks, you need to register it with the ShapeRegistry:
ShapeRegistry shapeRegistry = BlocksConfig.getInstance().getShapeRegistry();
shapeRegistry.register("my_custom_shape", new CustomShape());
A new shape must implement the Shape interface. This interface has 1 method (Shape#add(Vec3i, Chunk, ChunkMesh)
) that is called for each block in a chunk when the mesh is generated. Where:
- Vec3i : location of the block inside the chunk
- Chunk: the chunk that contains the block
- ChunkMesh: the current âmeshâ of the chunk. This is the object where you should append your shape to.
The ChunkMesh holds lists of all the vertex positions, indexes, normals, tangents, ⌠for the mesh construction of the chunk. You should add the vertices, uvâs, normals, ⌠of your shape to these lists.
Iâll add this example of a shape that renders a block as a horizontal plane square.
A --- B
| \ |
| \ |
| \ |
C --- D
A horizontal square will have these positions:
- (-0.5f, 0, -0.5f) A
- (0.5f, 0, -0.5f) B
- (-0.5f, 0, 0.5f) C
- (0.5f, 0, 0.5f) D
these indexes (the 2 triangles):
- (1, 0, 3) (BAD)
- (0, 2, 3) (ACD)
and letâs say itâs pointing up, so we have these 4 normals:
- (0, 1, 0)
- (0, 1, 0)
- (0, 1, 0)
- (0, 1, 0)
and to end the uv coordinates:
- (1, 1)
- (0, 1)
- (1, 0)
- (0, 0)
you can also add the tangents but these are optional. Blocks will generate missing tangents.
Implementing this shape in blocks will be something like this:
public class Square implements Shape {
@Override
public void add(Vec3i location, Chunk chunk, ChunkMesh chunkMesh) {
// get the scale of the blocks. The vertex positions of the shape should take this into account
float blockScale = BlocksConfig.getInstance().getBlockScale();
// we need to offset the positions of the shape so they are at the correct location in the chunk.
// the Shape class has a helper method that will calculate the position of the vertex based on the location of the block in the chunk and the block scale.
// this is exactly the same as doing: new Vector3f(0.5f, 0, -0.5f).addLocal(location.x, location.y, location.z).multLocal(blockScale);
Vector3f p1 = Shape.createVertex(new Vector3f(0.5f, 0, -0.5f), location, blockScale);
Vector3f p2 = Shape.createVertex(new Vector3f(-0.5f, 0, -0.5f), location, blockScale);
Vector3f p3 = Shape.createVertex(new Vector3f(0.5f, 0, 0.5f), location, blockScale);
Vector3f p4 = Shape.createVertex(new Vector3f(-0.5f, 0, 0.5f), location, blockScale);
int startIndex = chunkMesh.getPositions().size();
// add the vertices
chunkMesh.getPositions().add(p1);
chunkMesh.getPositions().add(p2);
chunkMesh.getPositions().add(p3);
chunkMesh.getPositions().add(p4);
// connect the vertices to create triangles
chunkMesh.getIndices().add(startIndex);
chunkMesh.getIndices().add(startIndex + 1);
chunkMesh.getIndices().add(startIndex + 2);
chunkMesh.getIndices().add(startIndex + 1);
chunkMesh.getIndices().add(startIndex + 3);
chunkMesh.getIndices().add(startIndex + 2);
// only create normals, tangents, uv's, ... for non-collision meshes
if (!chunkMesh.isCollisionMesh()) {
chunkMesh.getNormals().add(new Vector3f(0.0f, 1.0f, 0.0f));
chunkMesh.getNormals().add(new Vector3f(0.0f, 1.0f, 0.0f));
chunkMesh.getNormals().add(new Vector3f(0.0f, 1.0f, 0.0f));
chunkMesh.getNormals().add(new Vector3f(0.0f, 1.0f, 0.0f));
chunkMesh.getUvs().add(new Vector2f(1.0f, 1.0f));
chunkMesh.getUvs().add(new Vector2f(0.0f, 1.0f));
chunkMesh.getUvs().add(new Vector2f(1.0f, 0.0f));
chunkMesh.getUvs().add(new Vector2f(0.0f, 0.0f));
}
}
}
To complete the example, iâll add a âwooden plateâ block that uses this shape:
ShapeRegistry shapeRegistry = BlocksConfig.getInstance().getShapeRegistry();
shapeRegistry.register("plate", new Square());
Block woodenPlate = Block.builder()
.name("wooden_plate")
.type("oak_planks")
.shape("plate")
.solid(true)
.build();
BlockRegistry blockRegistry = BlocksConfig.getInstance().getBlockRegistry();
blockRegistry.register(woodenPlate);
You can take a look at the current available shapes and implementations for more input.