Skip to content

Saving and Loading BCF Viewpoints in xeokit

Lindsay Kay edited this page Dec 7, 2018 · 1 revision

Contents

Introduction

The BIM Collaboration Format (BCF) is a buildingSMART standard that defines an interface for exchanging information between BIM software systems, usually to raise issues, provide answers and make comments about a building model.

In this tutorial you'll learn how to use the xeokit SDK's BCFViewpointsPlugin to save a JSON BCF viewpoint from one Viewer, and then load it into a second Viewer.

The BCFViewpointsPlugin conforms to the BCF Version 2.1 specification.

Saving a Viewpoint

In the first example, we'll use a GLTFModelsPlugin to load the Schependomlaan model from a glTF file and a ClipsPlugin to slice it in half to create a cross-section view. Then, when the model has loaded, we'll arrange the camera, select eight elements, and use the BCFViewpointsPlugin to save a BCF viewpoint.

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

import {GLTFModelsPlugin} from 
    "../../../src/viewer/plugins/GLTFModelsPlugin/GLTFModelsPlugin.js";

import {ClipsPlugin} from 
    "../../../src/viewer/plugins/ClipsPlugin/ClipsPlugin.js";

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

// Create a xeokit Viewer
const viewer = new Viewer({
    canvasId: "myCanvas"
});

// Add a GLTFModelsPlugin
const glTFModels = new GLTFModelsPlugin(viewer);

// Add a ClipsPlugin
const clips = new ClipsPlugin(viewer);

// Add a BCFViewpointsPlugin
const bcfViewpoints = new BCFViewpointsPlugin(viewer);

// Load a glTF model
const model = glTFModels.load({
    id: "myModel",
    src: "./../../models/gltf/schependomlaan/schependomlaan.gltf",
    edges: true
});

// Create a clipping plane
clips.createClip({
    id: "myClip",
    pos: [0, 0, 0],
    dir: [0.5, 0.0, 0.5]
});

// When the model has loaded, arrange the camera, 
// select eight objects and save a BCF viewpoint
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.setSelected([
        "myModel#product-d5af753d-e8ff-467d-951c-bc66b940831a-body",
        "myModel#product-4e6cfbe8-2c4a-4b2e-92d8-dbaeeefe19f3-body.1",
        "myModel#product-4d959014-d715-4be0-9646-04ddb9384fe7-body",
        "myModel#product-42df0fcb-3410-43c8-af51-84653eecbfa3-body",
        "myModel#product-83ba020a-23c6-4a17-aa77-750345793211-body.0",
        "myModel#product-05f386ae-4fb9-420a-8bc7-c7b76aa264e6-body",
        "myModel#product-bc6847c1-5f7c-48ce-8f58-7834d0f8cc1c-body",
        "myModel#product-0e80f652-d76e-47f8-9beb-d1163367ad9e-body.3"
    ], true);

    const viewpoint = bcfViewpoints.getViewpoint();

    console.log(JSON.stringify(viewpoint, null, "  "));
});

Our scene appears as shown below. Note the cross-section view created by the ClipsPlugin, and the eight selected objects rendered green.

Viewpoint JSON

The JSON BCF viewpoint we captured is shown below. The viewpoint contains camera state for perspective and orthographic projects, the clip plane, the selection state of those eight elements we selected, and a data URI containing a snapshot of the Viewer's canvas (truncated for brevity).

{
  "perspective_camera": {
        "camera_view_point": {"x": -2.36, "y": 18.96, "z": -26.12},
        "camera_direction": {"x": 10.97, "y": 5.82, "z": -11.22},
        "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": 10.97, "y": 5.82, "z": -11.22},
        "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": "product-d5af753d-e8ff-467d-951c-bc66b940831a-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-4d959014-d715-4be0-9646-04ddb9384fe7-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-42df0fcb-3410-43c8-af51-84653eecbfa3-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-05f386ae-4fb9-420a-8bc7-c7b76aa264e6-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-bc6847c1-5f7c-48ce-8f58-7834d0f8cc1c-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            }
        ]
    },
    "snapshot": {
        "snapshot_type": "png",
        "snapshot_data": "data:image/png;base64,iVBORw0KGgoA..."
    }
}

Loading a Viewpoint

In our second example, we'll create another Viewer with a GLTFModelsPlugin that loads the same Schependomlaan glTF, and a BCFViewpointsPlugin that loads our JSON BCF viewpoint once the model has loaded.

Note that the second Viewer does not need a ClipsPlugin, since the BCFViewpointsPlugin creates the xeogl.Clip directly within the Viewer's xeogl.Scene. The ClipsPlugin just provides a layer of convenience around that functionality, along with the ability to capture and restore xeogl.Clip instances to and from Viewer bookmarks.

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

import {GLTFModelsPlugin} from 
    "../../../src/viewer/plugins/GLTFModelsPlugin/GLTFModelsPlugin.js";

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

const bcfViewpoint = {
    "perspective_camera": {
        "camera_view_point": {"x": -2.36, "y": 18.96, "z": -26.12},
        "camera_direction": {"x": 10.97, "y": 5.82, "z": -11.22},
        "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": 10.97, "y": 5.82, "z": -11.22},
        "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": "product-d5af753d-e8ff-467d-951c-bc66b940831a-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-4d959014-d715-4be0-9646-04ddb9384fe7-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-42df0fcb-3410-43c8-af51-84653eecbfa3-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-05f386ae-4fb9-420a-8bc7-c7b76aa264e6-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            },
            {
                "ifc_guid": "product-bc6847c1-5f7c-48ce-8f58-7834d0f8cc1c-body",
                "originating_system": "xeogl",
                "authoring_tool_id": "xeogl"
            }
        ]
    },
    "snapshot": {
        "snapshot_type": "png",
        "snapshot_data": "data:image/png;base64,iVBORw0KGgoA..."    
    }
};

// Create a xeokit Viewer
const viewer = new Viewer({
    canvasId: "myCanvas"
});

// Add a GLTFModelsPlugin
const glTFModels = new GLTFModelsPlugin(viewer);

// Add a BCFViewpointsPlugin
const bcfViewpoints = new BCFViewpointsPlugin(viewer);

// Load a glTF model
const model = glTFModels.load({
    id: "myModel",
    src: "./../../models/gltf/schependomlaan/schependomlaan.gltf",
    edges: true
});

// When the model has loaded, load the BCF viewpoint
model.on("loaded", () => {
    bcfViewpoints.setViewpoint(bcfViewpoint);
});

Conclusion

In this tutorial, we created a Viewer that loads a glTF model using a GLTFModelsPlugin, with a ClipsPlugin that creates a cross-section view of the model, and a BCFViewpointsPlugin that saves the view to a JSON BCF viewpoint. We then created a second Viewer, also with a GLTFModelsPlugin that loads the model, and a BCFViewpointsPlugin that loads the JSON BCF viewpoint to restore the view that was saved from the first Viewer.