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
| Method | Description |
|---|---|
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:
- Collider mesh: each mesh needs a companion with
_collidersuffix (e.g.,terrain_collider). Without it, players fall through. - 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.
- Scale: 1 unit = 1 meter in DCL. A 64-tile terrain at
tileSize: 1is 64m × 64m — fits inside a 4×4 parcel world.