ImFusion Web SDK

babylon.js

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

Here we show a simplified version of babylon.js' Area Lights demo, integrated into the SDK's views.

babylon.js defaults to a left-handed coordinate system, so set the scene to right-handed to match the SDK. It also has no single "reset GL state" call, so we reset its caches and gl.CULL_FACE each frame.

ts
const gl = imf.canvas.getContext('webgl2')!;

// ThinEngine's constructor calls resize(), which overwrites canvas.width/height,
// so capture the SDK's drawing-buffer size and restore it afterwards.
const bufferWidth = imf.canvas.width;
const bufferHeight = imf.canvas.height;

const engine = new ThinEngine(gl, true, { preserveDrawingBuffer: true }, false);
imf.canvas.width = bufferWidth;
imf.canvas.height = bufferHeight;

const scene = new Scene(engine);
scene.autoClear = false;
scene.useRightHandedSystem = true;

// Reset GL state
engine.wipeCaches(true);
gl.disable(gl.CULL_FACE);

In render, map the SDK matrices to the camera:

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 babylonGlObject = {
  render: (viewState: GlViewState) => {
    // babylon.js viewports are normalized to [0, 1]
    const imfViewport = viewState.viewport();
    camera.viewport = new Viewport(
      imfViewport.x / imf.canvas.width,
      imfViewport.y / imf.canvas.height,
      imfViewport.width / imf.canvas.width,
      imfViewport.height / imf.canvas.height,
    );

    // Transpose for the row-major vs column-major difference in matrix storage.
    // Invert to get viewToWorld from the worldToView matrix the SDK provides.
    const modelViewMatrix = Matrix.FromArray(viewState.modelViewMatrix()).transpose().invert();

    const scale = Vector3.Zero();
    const translation = Vector3.Zero();
    const quaternion = Quaternion.Zero();
    modelViewMatrix.decompose(scale, quaternion, translation);
    camera.position = translation;
    camera.rotationQuaternion = quaternion;

    const projectionMatrix = Matrix.FromArray(viewState.projectionMatrix()).transpose();
    camera.freezeProjectionMatrix(projectionMatrix);

    scene.render(false);

    // Reset GL state
    engine.wipeCaches(true);
    gl.disable(gl.CULL_FACE);
  },

  bounds: () => bounds,
};

const customGlObject = imf.createCustomGlObject(babylonGlObject, 'glObjectBabylonJS');

Add it to the views:

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