3d/voxelRenderer3D.js

Voxel Renderer & Terrain Gen

Native .vox file parser with InstancedMesh rendering, procedural terrain generation with GLB export, and the shared asset pipeline to Decentraland.

Voxel Renderer & Terrain Gen

Two modules that form the DN studio’s asset pipeline: voxelRenderer3D.js parses MagicaVoxel .vox files and renders them with instanced meshes; terrainGen3D.js generates procedural terrain and exports GLB files that load directly in Decentraland.

Include

<script src="/js/dn/3d/voxelRenderer3D.js"></script>
<script src="/js/dn/3d/terrainGen3D.js"></script>

voxelRenderer3D.js

Native binary .vox parser. No external library required. Renders each unique color as one THREE.InstancedMesh, grouping thousands of voxels into a handful of draw calls.

Load a .vox File

const renderer = new VoxelRenderer3D(scene);

renderer.loadVox('assets/pet-body.vox').then(group => {
  group.position.set(0, 0, 0);
  scene.add(group);
});

loadVox() returns a Promise<THREE.Group> containing one InstancedMesh per color. The group is centered at origin.

Scale

MagicaVoxel voxels are 1 unit each by default, so a 16×16×16 model is 16 meters wide. Scale down for world-space use:

group.scale.setScalar(0.0625); // 1/16 — makes each voxel 6.25cm, model is 1m cube

Pet Renderer Pattern

The DN pet renderer uses VoxelRenderer3D to assemble modular pets from trait-keyed GLB parts:

trait set (species: wolf, color: blue, hat: pirate)

load wolf-body.vox, wolf-tail.vox, hat-pirate.vox

VoxelRenderer3D.loadVox() for each part

position + attach to THREE.Group at joint points

render as companion following the player

terrainGen3D.js

Generates procedural terrain using dual noise layers. Three output modes: mesh (smooth subdivided geometry), slab (voxel-like flat-top cubes), and cubes (true voxel). Exports GLB for use in DCL or other tools.

Constructor

const terrain = new TerrainGen3D({
  width:        64,           // tiles wide
  depth:        64,           // tiles deep
  tileSize:     1,            // meters per tile (1 matches DCL convention)
  maxHeight:    8,            // maximum elevation in meters
  seed:         'world-a',    // string or number seed

  // Noise layers
  noiseScale:   0.05,         // base noise frequency
  noiseScale2:  0.2,          // detail noise frequency
  noiseBlend:   0.3,          // 0=all base, 1=all detail

  mode:         'mesh',       // 'mesh' | 'slab' | 'cubes'
  material:     mat,          // THREE.Material (optional, defaults to vertex-color Lambert)
});

Methods

MethodDescription
generate()Generate the terrain. Returns a THREE.Mesh.
addToScene(scene)Generate + add to scene in one call.
exportGLB()Export to GLB. Returns a Blob. Trigger a download or send to DCL.
getHeightAt(x, z)Sample terrain height at world coordinates. Use for placing objects on the surface.

GLB Export

const blob = terrain.exportGLB();
const url  = URL.createObjectURL(blob);
const a    = document.createElement('a');
a.href = url;
a.download = 'terrain.glb';
a.click(); // triggers download

The Asset Pipeline — Web to Decentraland

This is the key architectural insight: DN 3D and Decentraland share coordinates (Y-up, 1 unit = 1 meter). A GLB exported here loads in DCL without conversion.

Sketch / AI image

MagicaVoxel (.vox)  ←—— voxelRenderer3D.js previews in browser

Blender (VoxImporter add-on → Decimate → clean mesh)

        GLB
       /   \
      /     \
Web 3D      DCL
scene3D     GltfContainer
     (same file, both runtimes)

Terrain path (no Blender needed):

terrainGen3D.js → exportGLB() → terrain.glb → DCL GltfContainer

The terrain builder at /games/terrain-test/ already implements this — generate terrain, download GLB, drop it in a DCL scene folder.

Voxel asset path (Blender step needed):

MagicaVoxel → .vox → Blender VoxImporter → Decimate modifier → .glb → DCL

The Decimate step reduces poly count from voxel geometry to something DCL-friendly (target: under 500 polys per prop, under 5000 for terrain chunks).

DCL GLB Requirements

When exporting any GLB for Decentraland import:

  1. Collider mesh: each mesh needs a companion with _collider suffix (e.g., terrain_collider). Without it, players fall through.
  2. Y=0 at bottom: the lowest point of the geometry must sit at Y=0. Terrain below Y=0 causes DCL to reject the import.
  3. Scale: 1 unit = 1 meter in DCL. A 64-tile terrain at tileSize: 1 is 64m × 64m — fits inside a 4×4 parcel world.