Skip to content

xeokit v0.5 Release Notes

Lindsay Kay edited this page May 15, 2020 · 1 revision

Overview

This xeokit SDK v0.5.0 release includes: support for larger BIM models, 2D plan views and mini-maps, ability to save and restore viewer state, improved BCF support, plus numerous enhancements and bug fixes. Many thanks to UniZite, BIMData, BIMSpot and D-Studio for helping with this release.

Contents

Contributors

Special thanks to:

Load Large Models Faster

Geometry Reuse in .XKT Format

xeokit's compressed binary .XKT model format is now even more efficient to load and render, thanks to the efforts of Toni Marti at UniZite.

We're calling this .XKT format V2. The xeokit-gltf-to-xkt conversion tool and XKTLoaderPlugin are also updated to support .XKT V2.

To achieve this performance improvement, Toni extended the .XKT format to reuse geometries within the model. A case where we would reuse geometries is the windows in a building, where we would have a single geometry representing the shape of a window, which is reused by all the window objects.

This has several benefits:

  • reduces file size, making models load faster,
  • consumes less browser and GPU memory, allowing bigger models,
  • consumes less GPU bandwidth, and
  • renders more efficiently using WebGL hardware instancing.

The table below shows the reductions in file size, time to convert from glTF, and loading time:

EElsbWBXkAA-KXO

Storey Plan Views

The new StoreyPlansPlugin provides a flexible set of building blocks with which we can build a variety of UX for navigating the storeys within our BIM models.

The plugin supports most of the UX flavors found in existing BIM viewers. There are two general flavors:

  1. navigate plan views within the main viewer canvas (usually 2D orthographic), and
  2. interactive mini-maps to help us navigate storeys in the viewer canvas (usually in first-person mode).

2D Plan Views

We can use StoreyViewsPlugin to set up views (often orthographic plan views) of building storeys within the main 3D view.

Mini-maps

We can also use StoreyViewsPlugin to generate mini-maps that you can use to track your current position within a storey, or fly to the location you click on.

Peek-2019-10-22-10-57

Transformable Models

We can now rotate, scale and translate .xkt and glTF models as we load them. This lets us load multiple models, or even multiple copies of the same model, and position them apart from each other.

Previously, GLTFLoaderPlugin only supported these transformations when configured with performance:false.

Screenshot from 2019-09-03 17-51-50

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

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

viewer.camera.eye = [-5.13, 16.83, 39.46];
viewer.camera.look = [22.20, 1.86, 4.44];
viewer.camera.up = [0.19, 0.94, -0.25];

const xktLoader = new XKTLoaderPlugin(viewer);

var i = 0;

xktLoader.load({
        src: "./models/xkt/duplex/duplex.xkt",
        metaModelSrc: "./metaModels/duplex/metaModel.json",
        edges: true,
        scale: [0.5, 0.5, 0.5],
        position: [i++ * 10, 0, 0]
    })
    .on("loaded", () => {
        xktLoader.load({
                src: "./models/xkt/duplex/duplex.xkt",
                metaModelSrc: "./metaModels/duplex/metaModel.json",
                edges: true,
                scale: [0.5, 0.5, 0.5],
                position: [i++ * 10, 0, 0]
            })
            .on("loaded", () => {
                xktLoader.load({
                        src: "./models/xkt/duplex/duplex.xkt",
                        metaModelSrc: "./metaModels/duplex/metaModel.json",
                        edges: true,
                        scale: [0.5, 0.5, 0.5],
                        position: [i++ * 10, 0, 0]
                    })
                    .on("loaded", () => {
                        xktLoader.load({
                                src: "./models/xkt/duplex/duplex.xkt",
                                metaModelSrc:
                                    "./metaModels/duplex/metaModel.json",
                                edges: true,
                                scale: [0.5, 0.5, 0.5],
                                position: [i++ * 10, 0, 0]
                            })
                            .on("loaded", () => {
                                xktLoader.load({
                                    src: "./models/xkt/duplex/duplex.xkt",
                                    metaModelSrc:
                                        "./metaModels/duplex/metaModel.json",
                                    edges: true,
                                    scale: [0.5, 0.5, 0.5],
                                    position: [i++ * 10, 0, 0]
                                });
                            });
                    });
            });
    });

CLI glTF to XKT Converter

The xeokit-gltf-to-xkt conversion tool can now be run from the command line, thanks to Hugo Duroux at BIMData.

This means that we can now use scripts to fully automate the conversion of IFC, COLLADA and glTF models to xeokit's optimized binary .xkt format

Picking Enhancements

Picking is where we select objects, either at given canvas coordinates or with an arbitrarily-positioned 3D ray. This release supports two more options for picking:

  1. option to pick invisible entities, and
  2. option to provide a matrix when ray-picking, as an alternative way to indicate the ray.

In the example below, we'll pick whatever Entity intersects the given ray, even if the Entity is currently invisible:

const pickResult = myViewer.scene.pick({
    pickInvisible: true,   // Picking both visible and invisible Entitys
    origin: [10,10,10],
    dir: [-10, -10, -10]
});

if (pickResult) { // Picked an Entity with the ray
    // ....
}

In the second example, we'll pick the Entity that intersects a ray, which we'll implicitely provide as a 4x4 matrix:

const pickMatrix = math.lookAtMat4v([0,10,0], [0,-1,0], [0,0,-1]); // Eye, look and up vectors
const pickResult = myViewer.scene.pick({
    pickMatrix: pickMatrix
});

if (pickResult) { // Picked an Entity with the ray
    // ....
}

Canvas Snapshot Improvements

When taking a canvas snapshot, xeokit now 1) temporarily resizes the canvas to the target width and height, 2) takes the snapshot, then 3) restores the canvas size. This allows us to take snapshots that have a different aspect ratio to the canvas, and also improves their sharpness, since xeokit no longer needs to scale the captured image to the target width and height.

For example, to take a square snapshot of a panorama-shaped canvas:

const imageData = viewer.getSnapshot({
    format: "png",
    width: 200,
    height: 200
});

Screenshot from 2019-09-25 19-22-56

Support BCF view_setup_hints

BCFViewpointsPlugin now saves and loads view_setup_hints in BCF viewpoints.

When saving a viewpoint, we can selectively save properties into view_setup_hints like this:

const bcfViewpoints = new BCFViewpintsPlugin(viewer);

const viewpoint = bcfViewpoints.getViewpoint({ // Options
    spacesVisible: false, // Don't force IfcSpace types visible in viewpoint (default)
    spaceBoundariesVisible: false, // Don't show IfcSpace boundaries in viewpoint (default) 
    openingsVisible: false // Don't force IfcOpening types visible in viewpoint (default)
});

When loading a viewpoint, BCFViewpointplugin will load those properties if they are found in the viewpoint.

Save and Restore Viewer State

New memento classes allow us to save and restore the state of the Viewer. These are useful when we need to temporarily switch to some view of our model, then switch back to the previous view when finished.

The StoreyViewsPlugin (see above) uses these mementos to transition to and from isolated views of building storeys. When switching to an isolated view, the plugin first saves the current camera and object states to mementos before showing the view. When the user wants to transition back to the previous view, StoreyViewsPlugin then restores the camera and objects from the mementos.

There are two memento classes: CameraMemento and ObjectsMemento.

CameraMemento

In the example below, we'll create a Viewer and use an XKTLoaderPlugin to load a .xkt model. When the model has loaded, we'll save a snapshot of the Camera state in a CameraMemento. Then we'll move the Camera, and then we'll restore its original state again from the CameraMemento.

 import {Viewer} from "../src/viewer/Viewer.js";
 import {CameraMemento} from "../src/scene/mementos/CameraMemento.js";
 
 const viewer = new Viewer({
      canvasId: "myCanvas"
 });
 
 // Load a model
 const xktLoader = new XKTLoaderPlugin(viewer);
 
 const model = xktLoader.load({
     id: "myModel",
     src: "./models/xkt/schependomlaan/schependomlaan.xkt"
 });
 
 // Set camera
 viewer.camera.eye = [-2.56, 8.38, 8.27];
 viewer.camera.look = [13.44, 3.31, -14.83];
 viewer.camera.up = [0.10, 0.98, -0.14];
 
 model.on("loaded", () => {
 
    // Model has loaded
 
    // Save memento of camera state
    const cameraMemento = new CameraMemento();
 
    cameraMemento.saveCamera(viewer.scene);
 
    // Move the camera
    viewer.camera.eye = [45.3, 2.00, 5.13];
    viewer.camera.look = [0.0, 5.5, 10.0];
    viewer.camera.up = [0.10, 0.98, -0.14];
 
    // Restore the camera state again
    objectsMemento.restoreCamera(viewer.scene);
});

ObjectsMemento

In the example below, we'll create a Viewer and use an XKTLoaderPlugin to load an .xkt model. When the model has loaded, we'll hide a couple of Entitys and save a snapshot of the visual states of all the Entitys in an ObjectsMemento. Then we'll show all the Entitys again, and then we'll restore the visual states of all the Entitys again from the ObjectsMemento, which will hide those two Entitys again.

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

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

// Load a model
const xktLoader = new XKTLoaderPlugin(viewer);

const model = xktLoader.load({
    id: "myModel",
    src: "./models/xkt/schependomlaan/schependomlaan.xkt"
});

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

    // Model has loaded
    // Hide a couple of objects
    viewer.scene.objects["0u4wgLe6n0ABVaiXyikbkA"].visible = false;
    viewer.scene.objects["3u4wgLe3n0AXVaiXyikbYO"].visible = false;

    // Save memento of all object states, which includes those two hidden objects
    const objectsMemento = new ObjectsMemento();

    objectsMemento.saveObjects(viewer.scene);

    // Show all objects
    viewer.scene.setObjectsVisible(viewer.scene.objectIds, true);

    // Restore the objects states again, which involves hiding those two objects again
   objectsMemento.restoreObjects(viewer.scene);
});

Settable CameraControl 3D Pivot Position

So we can do things like automatically pivot about the center of the whole scene:

const aabb = myViewer.scene.getAABB(myViewer.scene.visibleObjectIds);
myViewer.cameraFlight.flyTo({
    aabb: aabb
});
myViewer.cameraControl.pivotPos = math.getAABB3Center(aabb, math.vec3());

NavCube Enhancements

Option for NavCube to view-fit to visible objects only

NavCubePlugin now supports the option to fit the axis, corner and edge-aligned views to visible object-Entitys only.

const navCube = new NavCubePlugin(viewer, {
    canvasID: "myNavCubeCanvas",
    //...
    fitVisible: true  
});

Customizable NavCube Colors

NavCubePlugin now supports custom colors:

 new NavCubePlugin(viewer, {
       
    //...
    // Custom color configurations
    // We can optionally supply a uniform color for the whole cube:
    color: "#99FF99",      // Default value

    // We can also optionally supply a separate color per face,
    // which will override our uniform color, if we supplied that:
    frontColor: "#55FF55", // Default values
    backColor: "#55FF55",
    leftColor: "#FF5555",
    rightColor: "#FF5555",
    topColor: "#5555FF",
    bottomColor: "#5555FF",

    // We can also supply a color to highlight NavCube regions
    // as we hover the pointer over them:
    hoverColor: "rgba(0,0.5,0,0.4)" // Default value
 });

Enable backface culling by default

Backface culling is now enabled by default. This means that triangles that are facing away from the Camera not be rendered, unless we configure it. It also means that you'll your triangle meshes will have holes if their triangles have irregular vertex windings.