OpenCascade.js

Render with three.js

OCCT shape → GLB → three.js GLTFLoader with orbit controls and lights.

@taucad/opencascade.js produces GLB bytes through OCCT's XCAF + RWGltf_CafWriter pipeline. three.js consumes GLB via GLTFLoader. The whole flow is in-browser — no server round-trip.

End-to-end snippet

src/render.ts
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

export const renderGlb = (canvas: HTMLCanvasElement, glb: Uint8Array): (() => void) => {
  const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
  renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
  renderer.setPixelRatio(globalThis.devicePixelRatio);

  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0x111111);

  const camera = new THREE.PerspectiveCamera(45, canvas.clientWidth / canvas.clientHeight, 0.1, 5000);
  camera.position.set(100, 100, 100);

  scene.add(new THREE.AmbientLight(0xffffff, 0.5));
  const dir = new THREE.DirectionalLight(0xffffff, 1);
  dir.position.set(1, 1, 1);
  scene.add(dir);

  const controls = new OrbitControls(camera, renderer.domElement);
  controls.target.set(0, 0, 0);

  new GLTFLoader().parse(glb.buffer, '', (gltf) => scene.add(gltf.scene));

  renderer.setAnimationLoop(() => {
    controls.update();
    renderer.render(scene, camera);
  });

  return () => renderer.dispose();
};

Why XCAF (and not raw triangle arrays)

OCCT can hand you raw Poly_Triangulation arrays per face, but the XCAF + GLB path:

  • preserves per-face colors and PBR materials,
  • writes valid glTF metadata (asset version, extensions used),
  • handles multi-shape assemblies as separate primitives,
  • emits indexed buffers so three.js doesn't need to dedupe vertices.

See Export glTF / GLB for the full pipeline including PBR materials.

Common rendering issues

SymptomLikely causeFix
Faceted curves (no smooth shading)Mesh deflection too coarseTighten BRepMesh_IncrementalMesh linear deflection (e.g. 0.05)
Black modelNo lightsAdd ambient + directional light; check dir.position is non-zero
Model offset from originShape has a transform baked inVerify TopLoc_Location or apply shape.Moved(loc, false)
GLB loads but invisibleCamera inside modelcamera.position.set(100, 100, 100) and controls.target.set(0, 0, 0)

Helper libraries

For batch shape conversion (multi-shape assemblies, edges-as-fat-lines, hover-highlight), replicad-threejs-helper wraps the common patterns and works with @taucad/opencascade.js@beta directly.

On this page