Skip to content

VoxelDocument ​

The voxel data of a world: layers, the block registry, blockset links, the undo source and the command stream. It creates no Three.js objects; pair it with a VoxelView to draw it.

ts
import { VoxelDocument } from "@jolly-pixel/voxel.renderer";

const document = new VoxelDocument({
  layers: ["Ground"],
  blocks: [
    {
      id: 1,
      name: "Grass",
      shapeId: "cube",
      collidable: true,
      faceTextures: {},
      defaultTexture: { col: 0, row: 0 }
    }
  ]
});

document.world.setVoxel("Ground", { position: { x: 0, y: 0, z: 0 }, blockId: 1 });

Layer, voxel and object edits go through document.world.

Options ​

OptionTypeDefaultDescription
chunkSizenumber16Chunk edge in voxels. Must be a power of two, otherwise a RangeError is thrown.
layersstring[][]Layer names added in order; the last one ends up on top.
blocksBlockDefinition[][]Registered before any command is applied.
blocksetsIterable<BlocksetDefinition>Blockset links declared up front.
materialGroupsIterable<MaterialGroupJSON>[]Material groups the blocks can name. Invalid entries are skipped.
blendGroupsIterable<BlendGroupJSON>[]Blend groups the blocks can name. Invalid entries are skipped.
loggerVoxelLoggerno-op
onCommandVoxelCommandListenerSubscribed to "command" before any command is applied.

Properties ​

ts
class VoxelDocument {
  readonly world: VoxelWorld;
  readonly blocks: BlockRegistry;
  readonly blocksets: BlocksetList;
  readonly materialGroups: MaterialGroupList;
  readonly blendGroups: BlendGroupList;
  readonly chunkSize: number;
}

Events ​

EventArgumentsWhen
command(command, context)After every applied command, local or replayed. See context.
loadedAfter load() put the new world in place.

load() and direct blocks or blocksets mutations emit no command.

Methods ​

Commands ​

ts
applyCommand(command: VoxelCommand, options?: VoxelApplyOptions): boolean;

interface VoxelApplyOptions {
  origin?: "local" | "remote" | "replay";
  clientId?: string | null;
}

Applies a command and emits it once on "command" with options.origin (default "local") and options.clientId. A sync client applies a peer's command as "remote" with the peer's clientId, and its own pending commands, applied or rolled back around a peer's, as "replay". Returns false and emits nothing when the command changed nothing. See commands for what each command carries once applied.

The block and group methods below are shorthands for applyCommand():

MethodCommandReturns false when
defineBlock(def)block-definedthe registry did not change. An existing ID is overwritten.
defineBlocks(defs)one block-defined eachreturns nothing
removeBlock(blockId)block-removedthe ID is unknown. IDs are never reused.
moveBlock(blockId, toIndex)block-movedthe ID is unknown or the move changes nothing
defineMaterialGroup(group)material-group-definedthe finish is invalid or equal to the current one
removeMaterialGroup(groupId)material-group-removedthe group is unknown
defineBlendGroup(group)blend-group-definedthe settings are invalid or equal to the current ones
removeBlendGroup(groupId)blend-group-removedthe group is unknown
addBlockset(blockset)blockset-addedthe list did not change. Without slot, the lowest free slot is assigned.
removeBlockset(blocksetId)blockset-removedthe blockset is unknown

A tile reference without blocksetId gets the first declared blockset. To declare a blockset locally without a command, use VoxelView.loadBlockset().

Queries ​

ts
blockAt(position: THREE.Vector3Like): ResolvedBlockDefinition | undefined;
blockPropertiesAt(position: THREE.Vector3Like): BlockProperties | undefined;

blockAt() returns the definition of the visible voxel at position, or undefined for air or an unregistered block. The result is the stored object; do not mutate it. blockPropertiesAt() returns a copy of that block's custom properties, or {}.

Save and load ​

ts
save(): VoxelWorldJSON;
load(data: VoxelWorldJSON, options?: VoxelLoadOptions): void;
dispose(): void;

interface VoxelLoadOptions {
  mergeLayers?: boolean | VoxelMergeAllLayersOptions;
  blocksets?: Iterable<BlocksetDefinition>;
}

save() writes the world and its blockset links in the serialized format.

load() replaces the world and the blockset list and emits loaded. It emits no command and leaves blocks and the groups alone. The document keeps its own chunkSize. options.blocksets are declared after the snapshot. mergeLayers collapses the layers as world.mergeAllLayers() does; an except name with no matching layer logs a warning.

dispose() clears the blockset list and every listener.