Skip to content

Updates in xeogl V0.7

Lindsay Kay edited this page Mar 20, 2018 · 48 revisions

screenshot from 2018-02-01 02-02-50

[Run demo]

Contents

Overview

Oh no, even more API changes in V0.7!

But good ones, especially if you're using xeogl with large models. This release is a partial rewrite, with new features and optimizations that allow xeogl to work with larger models.

In case you need the previous version of xeogl: https://github.com/xeolabs/xeogl/tree/v0.6

This (pre) release introduces API changes that break backward compatibility. Hopefully, these notes can help migrate your apps.

I'm still updating the examples and API docs, so if you see anything I've missed, or any breakages, please let me know through the issue tracker.

New Features

Gamma correction

Within its shaders, xeogl now performs shading calculations in linear space.

By default, a Scene expects color Textures to be in pre-multiplied gamma space, so will convert those to linear space before they are used in shaders. Other textures are always expected to be in linear space.

By default, the Scene will also gamma-correct its rendered output.

You can configure the Scene to expect all those color textures to be linear space, so that it does not gamma-correct them:

scene.gammaInput = false;

You would still need to gamma-correct the output, though, if it's going straight to the canvas, so normally we would leave that enabled:

scene.gammaOutput = true;

Ghost and Highlight Effects

We can now dynamically apply ghost and highlight effects to entities and models, to reveal and emphasize objects of interest. We can also configure these effects differently for individual entities.

Floor plan Gearbox Adam
house gearbox adam

Lightweight CAD Materials

We now have a simple flat-shaded LambertMaterial, which renders more efficiently than the Phong and PBR materials. These are preferable for models with large numbers of objects.

screenshot from 2018-01-31 00-31-11

You can also load a GLTFModel with the option to convert the materials in the glTF to LambertMaterial as they are loaded:

var model = new xeogl.GLTFModel({
    src: "models/gltf/modern_office/scene.gltf",
    lambertMaterials: true // Default is false
});

Since LambertMaterials don't use textures, that's going to load faster because the GLTFModel will not bother loading any textures. Note that this only derives the LambertMaterials from the glTF material base colors, so it won't work properly wherever base color is provided as a texture.

Quantized Geometry

Geometries are now automatically quantized to reduce memory and GPU bus usage. Usually, geometry attributes such as positions and normals are stored as 32-bit floating-point numbers. Quantization compresses those attributes to 16-bit integers represented on a scale between the minimum and maximum values. Decompression is then done on the GPU, via a simple matrix multiplication in the vertex shader.

Disabling

Since each normal vector is oct-encoded into two 8-bit unsigned integers, this can cause them to lose precision, which may affect the accuracy of any operations that rely on them being perfectly perpendicular to their surfaces. In such cases, you may need to disable quantization for your geometries and models:

// Disable geometry quantization when loading a Model
var model = new xeogl.GLTFModel({
    src: "models/gltf/modern_office/scene.gltf",
    quantizeGeometry: false // Default is true
});

// Disable quantization when creating a Geometry
 var entity = new xeogl.Entity({
    geometry: new xeogl.TeapotGeometry({
        quantized: false // Default is true
    }),
    material: new xeogl.PhongMaterial({
        diffuse: [0.2, 0.2, 1.0]
    })
 });

Geometry Batching

Geometries are now automatically combined into the same vertex buffer objects (VBOs) so that we reduce the number of VBO binds done by WebGL on each frame. VBO binds are expensive, so this really makes a difference when we have large numbers of Entities that share similar Materials (as is often the case in CAD rendering).

Disabling

Since combined VBOs need to be rebuilt whenever we destroy a Geometry, we can disable this optimization for individual Models and Geometries when we know that we'll be continually creating and destroying them.

// Disable VBO combination for a GLTFModel
var model = new xeogl.GLTFModel({
    src: "models/gltf/modern_office/scene.gltf",
    combinedGeometry: false // Default is true
});

// Disable VBO combination for an individual Geometry
 var entity = new xeogl.Entity({
    geometry: new xeogl.TeapotGeometry({
        combined: false // Default is true
    }),
    material: new xeogl.PhongMaterial({
        diffuse: [0.2, 0.2, 1.0]
    })
 });

Improved camera control

I replaced InputControl with CameraControl, which has various improvements, including:

  • Touch handling
  • Easing, inertia etc
  • Better zooming

Pick masking

We can now use the Scene pick() method's include and exclude options to mask which Entities we attempt to pick.

This is useful for picking through things, to pick only the Entities of interest.

Including entities

To pick only Entities "gearbox#77.0" and "gearbox#79.0", picking through any other Entities that are in the way, as if they weren't there:

var hit = scene.pick({
    canvasPos: [23, 131],
    include: ["gearbox#77.0", "gearbox#79.0"]
});

if (hit) {
     // Entity will always be either "gearbox#77.0" or "gearbox#79.0"
     var entity = hit.entity;
}

Excluding entities

To pick any pickable Entity, except for "gearbox#77.0" and "gearbox#79.0", picking through those Entities if they happen to be in the way:

var hit = scene.pick({
    canvasPos: [23, 131],
    exclude: ["gearbox#77.0", "gearbox#79.0"]
});

if (hit) {
     // Entity will never be "gearbox#77.0" or "gearbox#79.0"
     var entity = hit.entity;
}

API changes

Camera changes

One Camera per Scene

Now we have exactly one Camera per Scene, which we get off the Scene component:

var camera = myScene.camera;
camera.eye = [-10,0,0];

Camera control components will now always use the Scene's singleton Camera (and thus can no longer be configured with a Camera component):

var myCameraFlight = new xeogl.CameraFlightAnimation();
myCameraFlight.flyTo(myEntity, function() { ... });

Incorporated lookat transform

The Camera component now manages the viewing transform matrix internally and is therefore no longer attached to a Transform (ie. a Lookat). You can now access the viewing transform's eye, look and up vectors directly on the Camera component:

camera.eye = [-10,0,0];
camera.look = [-10,0,0];

// Get the view matrix
var viewMatrix = camera.matrix;
var viewNormalMatrix = camera.normalMatrix;

// Listen for view matrix updates
camera.on("matrix", function(matrix) { ... });

Navigation methods

You can also navigate the camera using methods on the Camera component:

camera.orbitYawY(0.5);
camera.pan([2, 0, 0]);
camera.zoom(-6);

The Camera also now has fixed components for each projection type, as permanently attached child components. You can switch the Camera between those projection types while updating the configurations on each projection component at any time. This means that you no longer need to detach and reattach projection transform components on the Camera every time you switch projection type.

camera.perspective.near = 0.4;
camera.perspective.fov = 45;

camera.ortho.near = 0.8;
camera.ortho.far = 1000;

camera.frustum.left = -1.0;
camera.frustum.right = 1.0;
camera.frustum.far = 1000.0;

camera.projection = "perspective"; // Switch to perspective
camera.projection = "frustum"; // Switch to frustum
camera.projection = "ortho"; // Switch to ortho

// Get the projection matrix
var projMatrix = camera.projMatrix;

// Listen for projection matrix updates
camera.on("projMatrix", function(matrix) { ... });

Configurable World-space "up"

We can now easily switch the Camera between different directions for World-space "up". For example, to set the +Y axis as the direction considered "up" in the viewer's World space coordinate system (default):

camera.worldAxis = [
   1, 0, 0,    // Right
   0, 1, 0,    // Up
   0, 0,-1     // Forward
];

To set the +Z axis as "up":

camera.worldAxis = [
   1, 0, 0, // Right
   0, 0, 1, // Up
   0,-1, 0  // Forward
];

One Viewport per Scene

We now have exactly one Viewport per Scene (as opposed to being able to attach different Viewports to different Entities).

var viewport = myScene.viewport;
viewport.boundary = [0, 0, 500, 400];

If you need to render a Scene to multiple viewports, then you can do this sort of thing:

scene.passes = 2;
scene.on("rendering", function (e) {
    switch (e.pass) {
        case 0:
            scene.viewport.boundary = [0, 0, 200, 200];
            break;
        case 1:
            scene.viewport.boundary = [200, 0, 200, 200];
            break;
     }
});

One Lights per Scene

We now have exactly one Lights component per Scene (as opposed to being able to attach different Lights to different Entities).

var lights = myScene.lights;
lights.lights[0].color = [1,0,0];

One Clips per Scene

We now have exactly one Clips (set of user defined cross section planes) per Scene (as opposed to being able to attach different Clips to different Entities).

var clips = myScene.clips;

clips.clips = [
    new xeogl.Clip({
        id: "clip0",
        pos: [0.8, 0.8, 0.8],
        dir: [-1, -1, -1],
        active: true
    }),
    new xeogl.Clip({
        id: "clip1",
        pos: [0.8, 0.8, -0.8],
        dir: [-1, -1, 1],
        active: true
    }),
];
  • See Clips for more info.

Simplified Bounding Boxes

We now get axis-aligned and object-aligned boundaries directly from the Scene, Model, Entity and Geometry components:

var aabb = myScene.aabb; // World-space
var aabb2 = myModel.aabb; // World-space
var aabb3 = myEntity.aabb; // World-space
var obb = myEntity.obb; // World-space
var localAABB = myEntity.geometry.aabb; // Local-space
var localOBB = myEntity.geometry.obb; // Local-space

Fly the camera to a Scene, Model or Entity by just providing the component as a target to CameraFlightAnimation.flyTo():

var myCameraFlight = new xeogl.CameraFlightAnimation();
myCameraFlight.flyTo(myScene, function() { ... });
myCameraFlight.jumpTo(myEntity);
  • View and canvas-space boundaries are no longer available, but you can always calculate those from the Camera viewing matrix and the World-space boundaries.
  • The Boundary3D and Boundary2D component types have been removed from the API.

Removed Canvas Overlay

Less Change Events

In previous releases, I made every single property update on a component fire a change event when updated. This was because I intended xeogl to form the basis for a graphical scene editor GUI, but I've since realized that I can't have that AND high performance at the same time, since those events incur a certain amount of execution and memory overhead. Therefore, I removed those change events, with the exception of Camera, Clip and Light components, so that we can potentially have 3D helper components that show the states of those types of components and change appearance based on those events (eg. showing camera and light positions etc).

Removed Serialize-to-JSON

Previously we were able to dump the complete runtime state of any component (including a whole Scene) to a JSON object that could then be used to re-instantiate that component again, like this:

var json = myScene.json;
var myNewScene = new xeogl.Scene(json);

I had originally intended xeogl to form the basis for a graphical scene editor, and this feature was supposed to help the editor save and load your scenes. Since a GUI is no longer a focus of xeogl, I removed this feature because it became a maintenance bottleneck.