autodesk-forgeautodesk-viewerbim

How to initialize Autodesk Forge 3D viewer correctly from BIM BCF Topic Viewpoint?


I am trying to initialize the Autodesk Forge 3D viewer correctly based on BIM topic-visualization data from a BCF file.

I have uploaded the 3D model (IFC) to Autodesk API, it has been processed and i can visualize/navigate the model successfully using the Autodesk Forge 3D viewer.

I have used the same 3D model (IFC) in Nemetschek SOLIBRI to create a Topic and exported it using the BIM BCF 2.1 standard (see https://github.com/buildingSMART/BCF-XML).

XML-Data from VisualizationInfo:

<PerspectiveCamera>
  <CameraViewPoint>
      <X>2.803843040759871</X>
      <Y>14.568845808384443</Y>
      <Z>0.8249055320631105</Z>
  </CameraViewPoint>
  <CameraDirection>
      <X>0.4898262677194313</X>
      <Y>-0.8652456579090667</Y>
      <Z>0.1068652371988122</Z>
  </CameraDirection>
  <CameraUpVector>
      <X>-0.05264688190667085</X>
      <Y>0.09299722978166312</Y>
      <Z>0.9942735142195238</Z>
  </CameraUpVector>
  <FieldOfView>60.0</FieldOfView>
</PerspectiveCamera>

My attempt to set the same camera/view on Autodesk Forge 3D viewer:

viewer.restoreState(JSON.parse(`{
  "viewport": {
    "eye": [2.803843040759871, 14.568845808384443, 0.8249055320631105],
    "target": [0.4898262677194313, -0.8652456579090667, 0.1068652371988122],
    "up": [-0.05264688190667085, 0.09299722978166312, 0.9942735142195238],
    "projection": "perspective",
    "isOrthographic": false,
    "fieldOfView": 60.0
  }
}`));

Result: A Forge 3D viewer camera view which does NOT match the one seen in SOLIBRI nor the one saved as snapshot-png in the BCF file.

Maybe the IFC-model in SOLIBRI and the Autodesk-processed-IFC-model have different coordinate-systems or what could be the error?


Solution

  • The CameraDirection value is not the target parameter of the Forge viewer. You have to use the following formula to calculate it yourself.

    {Target} = {CameraViewPoint} + {CameraDirection} * {CurrentFocalLength}

    The full camera mapping of your issue view will be:

    var lengthScale = 1000; //! Use viwer.model.getUnitString(), the model I loaded is in `mm`, and BCF camera definition is in `m`
    // or var lengthScale = Autodesk.Viewing.Private.convertUnits('m', viewer.model.getUnitString(), 1, 1);
    var eye = new THREE.Vector3( 2.803843040759871 * lengthScale, 14.568845808384443 * lengthScale, 0.8249055320631105 * lengthScale );
    var sightVec = new THREE.Vector3( 0.4898262677194313, -0.8652456579090667,  0.1068652371988122 ).multiplyScalar( viewer.navigation.getFocalLength() );
    var target = eye.clone().add( sightVec )
    var up = new THREE.Vector3( -0.05264688190667085, 0.09299722978166312, 0.9942735142195238 );
    
    //Since Forge Viewer will apply a global offset to the whole model
    //var offsetMatrix = viewer.model.getData().placementWithOffset;
    var offsetMatrix = viewer.model.getModelToViewerTransform();
    var offsetEye = eye.applyMatrix4(offsetMatrix); 
    var offsetTarget = target.applyMatrix4(offsetMatrix);
    var fov = 60; //!<< from BCF
    
    var cameraView = {
        aspect: viewer.getCamera().aspect,
        isPerspective: true,
        fov: fov,
        position: offsetEye,
        target: offsetTarget,
        up: up,
        orthoScale: 1
    };
    
    viwer.impl.setViewFromCamera( cameraView ); 
    

    Appendix Converting viewer camera back to BCF viewpoint

    let scale = Autodesk.Viewing.Private.convertUnits(viewer.model.getUnitString(), 'm', 1, 1);
    let invOffsetMatrix = viewer.model.getInverseModelToViewerTransform();
    let eye = viewer.navigation.getPosition().clone();
    let sightDir = viewer.navigation.getEyeVector().clone().normalize();
    let upVec = viewer.navigation.getCameraUpVector().clone();
    
    let offsetEye = eye.applyMatrix4(invOffsetMatrix).multiplyScalar(scale);
    let fov = viewer.navigation.getCamera().fov;
    
    let bcfDoc = document.implementation.createDocument('', '', null);
    let bcfDocPI = bcfDoc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"');
    bcfDoc.insertBefore(bcfDocPI, bcfDoc.firstChild);
    
    let visualizationInfoElem = bcfDoc.createElement('VisualizationInfo');
    bcfDoc.appendChild(visualizationInfoElem);
    
    let perspectiveCameraElem = bcfDoc.createElement('PerspectiveCamera');
    visualizationInfoElem.appendChild(perspectiveCameraElem);
    
    let cameraViewPointElem = bcfDoc.createElement('CameraViewPoint');
    perspectiveCameraElem.appendChild(cameraViewPointElem);
    
    let cameraViewPointXElem = bcfDoc.createElement('X');
    cameraViewPointXElem.textContent = offsetEye.x.toFixed(14);
    cameraViewPointElem.appendChild(cameraViewPointXElem);
    
    let cameraViewPointYElem = bcfDoc.createElement('Y');
    cameraViewPointYElem.textContent = offsetEye.y.toFixed(14);
    cameraViewPointElem.appendChild(cameraViewPointYElem);
    
    let cameraViewPointZElem = bcfDoc.createElement('Z');
    cameraViewPointZElem.textContent = offsetEye.z.toFixed(14);
    cameraViewPointElem.appendChild(cameraViewPointZElem);
    
    let cameraDirectionElem = bcfDoc.createElement('CameraDirection');
    perspectiveCameraElem.appendChild(cameraDirectionElem);
    
    let cameraDirectionXElem = bcfDoc.createElement('X');
    cameraDirectionXElem.textContent = sightDir.x.toFixed(14);
    cameraDirectionElem.appendChild(cameraDirectionXElem);
    
    let cameraDirectionYElem = bcfDoc.createElement('Y');
    cameraDirectionYElem.textContent = sightDir.y.toFixed(14);
    cameraDirectionElem.appendChild(cameraDirectionYElem);
    
    let cameraDirectionZElem = bcfDoc.createElement('Z');
    cameraDirectionZElem.textContent = sightDir.z.toFixed(14);
    cameraDirectionElem.appendChild(cameraDirectionZElem);
    
    let cameraUpVectorElem = bcfDoc.createElement('CameraUpVector');
    perspectiveCameraElem.appendChild(cameraUpVectorElem);
    
    let cameraUpVectorXElem = bcfDoc.createElement('X');
    cameraUpVectorXElem.textContent = upVec.x.toFixed(14);
    cameraUpVectorElem.appendChild(cameraUpVectorXElem);
    
    let cameraUpVectorYElem = bcfDoc.createElement('Y');
    cameraUpVectorYElem.textContent = upVec.y.toFixed(14);
    cameraUpVectorElem.appendChild(cameraUpVectorYElem);
    
    let cameraUpVectorZElem = bcfDoc.createElement('Z');
    cameraUpVectorZElem.textContent = upVec.z.toFixed(14);
    cameraUpVectorElem.appendChild(cameraUpVectorZElem);
    
    let fieldOfViewElem = bcfDoc.createElement('FieldOfView');
    perspectiveCameraElem.appendChild(fieldOfViewElem);
    fieldOfViewElem.textContent = fov.toFixed(14);
    
    let bcfVpConents = new XMLSerializer().serializeToString(bcfDoc);
    

    Enjoy it!