ImFusion Web SDK

three.js

Integrate three.js rendering into ImFusion Web SDK views with a CustomGlObject.

Here we show a simplified version of three.js' lights / arealight demo, integrated with the SDK's views.

Set up three.js so it does not interfere with the SDK: disable auto-clear so it never wipes the SDK's output, and reset the GL state after each frame.

ts
const renderer = new THREE.WebGLRenderer({
  context: imf.canvas.getContext('webgl2')!,
  preserveDrawingBuffer: true,
  precision: 'mediump',
  antialias: true,
});
renderer.autoClear = false;

From these definitions, we can implement the render and bounds functions required by the CustomGlObject interface.

ts
// Here you would compute an axis aligned bounding box of your CustomGlObject in world space.
// This information is used by the ImFusion Web SDK to compute bounds for the whole rendered scene
// and corresponding clipping planes. For demo purposes we return invalid bounds, which tells the
// ImFusion Web SDK to ignore this CustomGlObject in those computations.
const bounds = new imf.bindings.AlignedBox();

const threejsGlObject = {
  render: (viewState: GlViewState) => {
    const imfViewport = viewState.viewport();
    renderer.setViewport(imfViewport.x, imfViewport.y, imfViewport.width, imfViewport.height);

    // Matrix4.set expects row-major order, which matches the ImFusion Web SDK convention
    const imfProjectionMatrix = new THREE.Matrix4();
    imfProjectionMatrix.set(...viewState.projectionMatrix());

    // The SDK provides worldToView; invert it to get the camera transform
    const imfModelViewMatrix = new THREE.Matrix4();
    imfModelViewMatrix.set(...viewState.modelViewMatrix()).invert();

    const position = new THREE.Vector3();
    const rotationQuaternion = new THREE.Quaternion();
    const scale = new THREE.Vector3();
    imfModelViewMatrix.decompose(position, rotationQuaternion, scale);
    camera.position.set(position.x, position.y, position.z);
    camera.quaternion.set(rotationQuaternion.x, rotationQuaternion.y, rotationQuaternion.z, rotationQuaternion.w);
    camera.scale.set(scale.x, scale.y, scale.z);
    camera.projectionMatrix.copy(imfProjectionMatrix);
    camera.projectionMatrixInverse.copy(imfProjectionMatrix).invert();

    renderer.render(scene, camera);
    renderer.resetState();
  },

  bounds: () => bounds,
};

const customGlObject = imf.createCustomGlObject(threejsGlObject, 'glObjectThreeJS');

This defines our CustomGlObject that we can then add to the Display:

ts
imf.display.mainSagittalView().addObject(customGlObject);
imf.display.main3dView().addObject(customGlObject);