Skip to main content

Overview

Threebox supports loading external 3D models in multiple formats through the tb.loadObj() method. Once loaded, models become interactive objects with built-in methods for positioning, animation, selection, and dragging.

Supported Formats

GLTF/GLB

Industry-standard format with animation and PBR material support

FBX

Autodesk format popular in game development and animation

OBJ/MTL

Classic 3D format with separate material files

Collada (DAE)

Open format supporting complex scenes and animations

Basic Usage

const options = {
  obj: '/models/soldier.glb',
  type: 'gltf',
  scale: 1,
  units: 'meters',
  rotation: { x: 90, y: 0, z: 0 }
};

tb.loadObj(options, function(model) {
  model.setCoords([-122.4194, 37.7749]);
  tb.add(model);
});

Format-Specific Loading

GLTF/GLB Models

const options = {
  type: 'gltf',
  obj: '/models/robot.gltf',
  bin: '/models/robot.bin',  // Optional binary data
  units: 'meters',
  scale: 5,
  rotation: { x: 90, y: 0, z: 0 },
  defaultAnimation: 0  // Play first animation by default
};
GLTF models can include animations. Use defaultAnimation to specify which animation plays by default, or control animations programmatically after loading.

FBX Models

const options = {
  type: 'fbx',
  obj: '/models/character.fbx',
  units: 'meters',
  scale: 0.01,  // FBX models often need scaling
  normalize: true,  // Recommended for FBX
  rotation: { x: 90, y: 0, z: 0 }
};

tb.loadObj(options, function(model) {
  model.setCoords(origin);
  model.castShadow = true;
  tb.add(model);
});

OBJ/MTL Models

const options = {
  type: 'mtl',
  obj: '/models/building.obj',
  mtl: '/models/building.mtl',  // Material file
  units: 'meters',
  scale: 1,
  rotation: { x: 0, y: 0, z: 0 }
};

Collada (DAE) Models

const options = {
  type: 'dae',
  obj: '/models/scene.dae',
  units: 'meters',
  scale: 1,
  rotation: { x: 90, y: 0, z: 0 }
};

Configuration Options

Required Parameters

ParameterTypeDescription
type'mtl' | 'gltf' | 'glb' | 'fbx' | 'dae'Model format type
objstringURL path to the model file
callbackfunctionCallback function receiving the loaded model

Optional Parameters

ParameterTypeDefaultDescription
mtlstringnullURL to material file (OBJ format only)
binstringnullURL to binary file (GLTF format only)
units'scene' | 'meters''scene'Coordinate system for model vertices
scalenumber | {x, y, z}1Model scale (uniform or per-axis)
rotationnumber | {x, y, z}0Initial rotation in degrees
anchorstring'bottom-left'Pivot point for positioning and rotation
adjustment{x, y, z}{x:0, y:0, z:0}Fine-tune center position
normalizebooleantrueNormalize specular values (recommended for FBX/GLTF)
bboxbooleantrueEnable bounding box display
tooltipbooleantrueEnable tooltip on hover
raycastedbooleantrueInclude in raycasting for selection
clonebooleantrueClone textures for new instances
defaultAnimationnumber0Default animation index
withCredentialsbooleanfalseSend credentials with HTTP requests

Positioning and Anchoring

Anchor Points

The anchor parameter determines the model’s pivot point:
const options = {
  obj: '/models/building.glb',
  type: 'gltf',
  anchor: 'center',  // 'top', 'bottom', 'left', 'right', 'top-left', etc.
  units: 'meters'
};
Use anchor: 'bottom-left' (default) for most ground-level objects, and anchor: 'center' for objects that float or need centered rotation.

Adjustment for Off-Center Models

Many 3D models have misaligned centers. Use adjustment to correct this:
const options = {
  obj: '/models/car.glb',
  type: 'gltf',
  anchor: 'bottom-left',
  adjustment: { x: 0.5, y: 0.5, z: 0 }  // Shift center by these ratios
};

Advanced Configuration

Using GeoJSON Features

Store model metadata in GeoJSON feature properties:
const feature = {
  type: 'Feature',
  geometry: {
    type: 'Point',
    coordinates: [-122.4194, 37.7749, 100]
  },
  properties: {
    height: 50,
    name: 'Building A',
    camera: [-122.4194, 37.7749],
    zoom: 18,
    pitch: 60,
    bearing: 45
  }
};

const options = {
  type: 'gltf',
  obj: '/models/building.glb',
  feature: feature,
  units: 'meters'
};

tb.loadObj(options, function(model) {
  model.setCoords(feature.geometry.coordinates);
  tb.add(model);
});

Controlling Scale

const options = {
  obj: '/models/tree.glb',
  type: 'gltf',
  scale: 10  // 10x original size
};

Rotation

const options = {
  obj: '/models/soldier.glb',
  type: 'gltf',
  rotation: { x: 90, y: 0, z: 0 }  // Rotate 90° on X-axis
};

Model Caching

Threebox automatically caches the first instance of each model URL. Subsequent loads return duplicates from cache:
// First load - fetched from server and cached
tb.loadObj(options, function(model1) {
  model1.setCoords([-122.4194, 37.7749]);
  tb.add(model1);
});

// Second load - duplicated from cache (faster)
tb.loadObj(options, function(model2) {
  model2.setCoords([-122.4200, 37.7750]);
  tb.add(model2);
});
Set clone: false to create a new instance without cloning textures. This uses more memory but may be needed for models with complex animations.

Server Configuration

3D model file extensions require MIME type configuration on your web server.
<system.webServer>
  <staticContent>
    <remove fileExtension=".glb" />
    <mimeMap fileExtension=".glb" mimeType="model/gltf-binary" />
    <remove fileExtension=".gltf" />
    <mimeMap fileExtension=".gltf" mimeType="model/gltf+json" />
    <remove fileExtension=".fbx" />
    <mimeMap fileExtension=".fbx" mimeType="application/octet-stream" />
    <remove fileExtension=".dae" />
    <mimeMap fileExtension=".dae" mimeType="application/vnd.oipf.dae.svg+xml" />
  </staticContent>
</system.webServer>
http {
  include /etc/nginx/mime.types;
  types {
    model/gltf+json gltf;
    model/gltf-binary glb;
    application/octet-stream fbx;
    application/vnd.oipf.dae.svg+xml dae;
  }
}
model/gltf+json gltf
model/gltf-binary glb
application/octet-stream fbx
application/vnd.oipf.dae.svg+xml dae

Complete Example

map.on('style.load', function () {
  map.addLayer({
    id: 'custom_layer',
    type: 'custom',
    renderingMode: '3d',
    onAdd: function (map, gl) {
      window.tb = new Threebox(
        map,
        gl,
        { 
          defaultLights: true,
          enableSelectingObjects: true,
          enableDraggingObjects: true,
          enableRotatingObjects: true,
          enableTooltips: true
        }
      );

      const options = {
        obj: '/models/soldier.glb',
        type: 'gltf',
        scale: 20,
        units: 'meters',
        rotation: { x: 90, y: 0, z: 0 },
        anchor: 'bottom-left',
        adjustment: { x: 0, y: 0, z: 0 }
      };

      tb.loadObj(options, function (model) {
        const soldier = model.setCoords([-122.4194, 37.7749, 0]);
        
        // Add event listeners
        soldier.addEventListener('SelectedChange', onSelectedChange, false);
        soldier.addEventListener('ObjectDragged', onDraggedObject, false);
        
        tb.add(soldier);
      });
    },
    render: function (gl, matrix) {
      tb.update();
    }
  });
});

function onSelectedChange(e) {
  console.log('Model selected:', e.detail.selected);
}

function onDraggedObject(e) {
  console.log('Model dragged to:', e.detail.coordinates);
}

Next Steps

Primitives

Create spheres, lines, and tubes

Materials & Lighting

Configure materials and scene lighting

Build docs developers (and LLMs) love