Skip to content

Creating custom shapes ​

Extend BlockShapeBase, register an instance on the view, then name its id in a block definition. This shape is a flat carpet lying just above the floor of its cell:

ts
import {
  BlockShapeBase,
  Face,
  VoxelDocument,
  VoxelView,
  defineFace,
  type FaceDefinition
} from "@jolly-pixel/voxel.renderer";

class Carpet extends BlockShapeBase {
  readonly id = "carpet";
  readonly collisionHint = "none";
  readonly faces: readonly FaceDefinition[] = [
    defineFace({
      face: Face.PosY,
      normal: [0, 1, 0],
      vertices: [
        [0, 0.0625, 1],
        [1, 0.0625, 1],
        [1, 0.0625, 0],
        [0, 0.0625, 0]
      ]
    })
  ];
}

const document = new VoxelDocument({ layers: ["Ground"] });
const view = new VoxelView(document, {
  shapes: [new Carpet()]
});

document.defineBlock({
  id: 10,
  name: "Red carpet",
  shapeId: "carpet",
  collidable: false,
  defaultTexture: { col: 0, row: 0 }
});

view.shapes.register(new Carpet()) works too, for a view that already exists.

defineFace() fills in what the face leaves out. The face sits inside the cell, so it is never culled against a neighbour, and its UVs are the projection of its vertices, so it samples the whole tile. BlockShapeBase derives occludes() from the faces: the carpet covers no side of its cell, so it hides nothing behind it.

The BlockShape reference covers the face format, default culling, the UV convention and the built-in shapes.