Skip to content

BCF Viewpoints

xeolabs edited this page Jun 19, 2019 · 13 revisions

See also:

Introduction

BCF is an open standard that enables workflow communications between BIM software tools. An XML schema, called Building Collaboration Format (BCF), encodes messages that inform one BIM tool of issues found by another.

A BCF viewpoint captures a viewpoint of a model that highlights an issue. The viewpoint can then be loaded by another viewer to examine the issue.

The xeokit-sdk provides a BCFViewpointsPlugin component that saves and loads JSON-encoded viewpoints that conform to the BCF Version 2.1 specification.

Contents

Example

In the example below, we'll use a GLTFLoaderPlugin to load the Schependomlaan house model.

When the model has loaded, we'll slice it half with a SectionPlanesPlugin, position the Camera, select some objects, then save a BCF viewpoint using a BCFViewpointsPlugin.

Click the image below for a live demo of how the viewpoint is saved.

import {Viewer} from "../src/viewer/Viewer.js";
import {GLTFLoaderPlugin} from
    "../src/plugins/GLTFLoaderPlugin/GLTFLoaderPlugin.js";
import {SectionPlanesPlugin} from
    "../src/plugins/SectionPlanesPlugin/SectionPlanesPlugin.js";
import {BCFViewpointsPlugin} from
    "../src/plugins/BCFViewpointsPlugin/BCFViewpointsPlugin.js";

const viewer = new Viewer({
    canvasId: "myCanvas",
    transparent: true
});

const gltfLoader = new GLTFLoaderPlugin(viewer);
const sectionPlanes = new SectionPlanesPlugin(viewer);
const bcfViewpoints = new BCFViewpointsPlugin(viewer);

const model = gltfLoader.load({
    id: "myModel",
    src: "./models/gltf/schependomlaan/scene.gltf",
    metaModelSrc: "./metaModels/schependomlaan/metaModel.json"
});

sectionPlanes.createSectionPlane({
    id: "myClip",
    pos: [0, 0, 0],
    dir: [0.5, 0.0, 0.5]
});

model.on("loaded", () => {

    var scene = viewer.scene;
    var camera = scene.camera;

    camera.eye = [-2.37, 18.97, -26.12];
    camera.look = [10.97, 5.82, -11.22];
    camera.up = [0.36, 0.83, 0.40];

    scene.setObjectsSelected([
        "3b2U496P5Ebhz5FROhTwFH",
        "2MGtJUm9nD$Re1_MDIv0g2",
        "3IbuwYOm5EV9Q6cXmwVWqd",
        "3lhisrBxL8xgLCRdxNG$2v",
        "1uDn0xT8LBkP15zQc9MVDW"
    ], true);

    const viewpoint = bcfViewpoints.getViewpoint();
    const viewpointStr = JSON.stringify(viewpoint, null, 4);

    console.log(viewpointStr);
});

BCF Viewpoint Data

Our BCFViewpointsPlugin has saved the viewpoint to a JSON object, shown below.

The viewpoint JSON contains:

  • the position of the Camera for both perspective and orthographic projections (in BCF, the camera can be in a different position for each of these projections, but in xeokit it's in the same position),
  • the position and orientation of the SectionPlane that we created with our SectionPlanesPlugin,
  • the visibility and selection status of all the Nodes in the scene that represent objects (see Scene Graphs for how Nodes can be objects), and
  • a data URI containing a PNG snapshot of the canvas.
{
    "perspective_camera": {
        "camera_view_point": {"x": -2.36, "y": 18.96, "z": -26.12},
        "camera_direction":  {"x": 0.55, "y": -0.54, "z": 0.62},
        "camera_up_vector": {"x": 0.36, "y": 0.82, "z": 0.40},
        "field_of_view": 60
    },
    "orthogonal_camera": {
        "camera_view_point": {"x": -2.36, "y": 18.96, "z": -26.12},
        "camera_direction":  {"x": 0.55, "y": -0.54, "z": 0.62},
        "camera_up_vector": {"x": 0.36, "y": 0.82, "z": 0.40},
        "view_to_world_scale": 1
    },
    "lines": [],
    "bitmaps": [],
    "clipping_planes": [
        {
            "location": {"x": 0, "y": 0, "z": 0},
            "direction": {"x": 0.5, "y": 0, "z": 0.5}
        }
    ],
    "components": {
        "visibility": {
            "view_setup_hints": {
                "spaces_visible": false,
                "space_boundaries_visible": false,
                "openings_visible": false
            },
            "exceptions": [],
            "default_visibility": true
        },
        "selection": [
            {
                "ifc_guid": "3b2U496P5Ebhz5FROhTwFH",
                "originating_system": "xeokit",
                "authoring_tool_id": "xeokit"
            },
            {
                "ifc_guid": "2MGtJUm9nD$Re1_MDIv0g2",
                "originating_system": "xeokit",
                "authoring_tool_id": "xeokit"
            },
            {
                "ifc_guid": "3IbuwYOm5EV9Q6cXmwVWqd",
                "originating_system": "xeokit",
                "authoring_tool_id": "xeokit"
            },
            {
                "ifc_guid": "3lhisrBxL8xgLCRdxNG$2v",
                "originating_system": "xeokit",
                "authoring_tool_id": "xeokit"
            },
            {
                "ifc_guid": "1uDn0xT8LBkP15zQc9MVDW",
                "originating_system": "xeokit",
                "authoring_tool_id": "xeokit"
            }
        ]
    },
    "snapshot": {
        "snapshot_type": "png",
        "snapshot_data": "data:image/png;base64,iVBORw0KGgoAAAA..." // Truncated
    }
}

Loading a BCF Viewpoint

Now let's load our BCF viewpoint. We'll set up another viewer with a GLTFLoaderPlugin and a BCFViewpointsPlugin, and load the same Schependomlaan house model into it as before.

Note that our second view does not need a SectionPlanesPlugin because the BCFViewpointsPlugin will recreate the SectionPlane directly.

import {Viewer} from "../src/viewer/Viewer.js";
import {GLTFLoaderPlugin} from "../src/plugins/GLTFLoaderPlugin/GLTFLoaderPlugin.js";
import {BCFViewpointsPlugin} from "../src/plugins/BCFViewpointsPlugin/BCFViewpointsPlugin.js";

const viewer = new Viewer({
    canvasId: "myCanvas",
    transparent: true
});

const gltfLoader = new GLTFLoaderPlugin(viewer);
const bcfViewpoints = new BCFViewpointsPlugin(viewer);

const model = gltfLoader.load({
    id: "myModel",
    src: "./models/gltf/schependomlaan/scene.gltf",
    metaModelSrc: "./metaModels/schependomlaan/metaModel.json"
});

We'll assume that we've got the BCF viewpoint in a variable named myViewpoint.

Then, when the model has loaded, we'll use the BCFViewpointsPlugin to load our viewpoint.

model.on("loaded", () => {

    bcfViewpoints.setViewpoint(viewpoint);
});

And voilà - our viewer is restored to the state captured in the BCF viewpoint.

Click the image below for a live demo of how the viewpoint is loaded.

Handling BCF Incompatibility with xeokit's Camera

If you take a close look at the BCF viewpoint data's perspective_camera field, you'll notice that it's different from our original Camera#look.

xeokit's Camera#look is the current 3D point-of-interest (POI). A BCF viewpoint, however, has a direction vector instead of a POI, so BCFViewpointsPlugin#getViewpoint needs to save xeokit's POI as a normalized vector from Camera#eye to Camera#look, which unfortunately loses that positional information.

Loading the viewpoint with BCFViewpointsPlugin#setViewpoint will (by default) restore Camera#look to the viewpoint's camera position, offset by the normalized vector.

That's not the Camera#look we saved, but we can compensate by configuring setViewpoint to set Camera#look to the closest surface intersection on the direction vector. Internally, setViewpoint supports this option by firing a ray along the vector, and if that hits an Entity, sets Camera#look to ray's intersection point with the Entity's surface.

We can do this by restoring the viewpoint using a rayPick option:

model.on("loaded", () => {

    bcfViewpoints.setViewpoint(viewpoint, {
        rayCast: true
    });
});
Clone this wiki locally