Blocks

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 :wink:
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.

2 Likes