-
Notifications
You must be signed in to change notification settings - Fork 294
BCF Viewpoints
See also:
- Viewing BIM Models Offline - loading BIM models from the file system
- Viewing Models from BIMServer - loading IFC models from BIMServer
- Section Planes - slicing models with section planes to reveal inner structures
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.
- Example
- BCF Viewpoint Data
- Loading a BCF Viewpoint
- Handling BCF Incompatibility with xeokit's Camera
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);
});
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
}
}
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.
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
});
});