Namespace scene

xeokit 3D Scene Representation


The SDK's buildable, viewable, importable and exportable 3D scene representation


Overview

The xeokit SDK represents models in a scene graph that include the model's 3D objects, geometries, and materials. This scene graph works on both the browser and NodeJS platforms and can be used to create models, convert between model formats, and provide content for the SDK's model Viewer.

To elaborate further:



  • SceneTextureSets are collections of textures that are shared among SceneMeshes and are organized into texture atlasses to optimize rendering efficiency on GPUs.
  • Each SceneMesh can be assigned to only one SceneObject, whereas each SceneGeometry and SceneTextureSet can be allocated to an unlimited number of SceneMeshes.

Installation

npm install @xeokit/sdk

Usage


First we'll import the modules we'll need:

import {Scene} from "@xeokit/sdk/scene";
import {TrianglesPrimitive, LinearEncoding, LinearFilter, ClampToEdgeWrapping} from "@xeokit/sdk/constants";
import {Viewer} from "@xeokit/sdk/viewer";
import {WebGLRenderer} from "@xeokit/sdk/webglrenderer";
import {CameraControl} from "@xeokit/sdk/cameracontrol";

Next, we'll create a Scene:

const theScene = new Scene();

If we're coding in the browser, we can view our Scene by attaching it to a Viewer, as shown below. The Viewer will then provide an interactive 3D view of our Scene. As we create and destroy objects, they will appear and disaapear in the view.

Our minimal browser Viewer gets a WebGLRenderer, to adapt it to use the browser's WebGL graphics API for 3D viewing. Our Viewer also gets a single View to make it draw to a specified canvas in the page. The View also gets a CameraControl, so we can interact with it using mouse and touch input.

Note that a Scene does not need to be attached to a Viewer. A Scene functions as a stand-alone data structure, and is agnostic of Viewer. In the browser, we typically use Scene with a Viewer. In a NodeJS script, that has no need to draw anything, we would typically use Scene without a Viewer.

const myViewer = new Viewer({
id: "myViewer",
scene,
renderer: new WebGLRenderer({})
});

const view1 = myViewer.createView({
id: "myView",
elementId: "myView1"
});

view1.camera.eye = [0,0,-100];
view1.camera.look = [0,0,0];
view1.camera.up = [0,1,0];

const myCameraControl = new CameraControl({
view: view1
});

Next, we'll create a SceneModel that will model the simple table object shown in the image above. Our SceneModel will contain five SceneObjects, five SceneMeshes, one SceneGeometry and one SceneTexture.

When we've finished constructing our SceneModel, we'll call SceneModel.build. After that, our SceneModel appears in our Viewer and we can interact with it.

const sceneModel = theScene.createModel({
id: "theModel"
});

if (sceneModel instanceof SDKError) {

// Most SDK methods return an SDKError when
// something goes wrong.

// We'll use some SDKErrors in this example
// to demonstrate where we can use them.

console.error(sceneModel.message);

} else {

const geometry = sceneModel.createGeometry({
id: "boxGeometry",
primitive: TrianglesPrimitive,
positions: [ // Floats
1, 1, 1, -1, 1, 1,
-1, -1, 1, 1, -1, 1, 1,
-1, -1, 1, 1, -1, -1, 1, -1, -1,
-1, -1
],
indices: [
0, 1, 2, 0, 2, 3, 4, 5, 6, 4,
6, 7, 8, 9, 10, 8, 10, 11, 12,
13, 14, 12, 14, 15, 16, 17, 18,
16, 18, 19, 20, 21, 22, 20, 22, 23
]
});

if (geometry instanceof SDKError) {
console.error(geometry.message);
}

const texture = sceneModel.createTexture({
id: "colorTexture",
src: "./assets/sample_etc1s.ktx2",
preloadColor: [1, 0, 0, 1],
flipY: false,
encoding: LinearEncoding,
magFilter: LinearFilter,
minFilter: LinearFilter,
wrapR: ClampToEdgeWrapping,
wrapS: ClampToEdgeWrapping,
wrapT: ClampToEdgeWrapping,
});

if (texture instanceof SDKError) {
console.error(texture.message);
}

const theTextureSet = sceneModel.createTextureSet({
id: "theTextureSet",
colorTextureId: "colorTexture"
});

if (theTextureSet instanceof SDKError) {
console.error(theTextureSet.message);
}

const redLegMesh = sceneModel.createLayerMesh({
id: "redLegMesh",
geometryId: "boxGeometry",
position: [-4, -6, -4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [1, 0.3, 0.3],
textureSetId: "theTextureSet"
});

if (redLegMesh instanceof SDKError) {
console.error(redLegMesh.message);
}

const greenLegMesh = sceneModel.createLayerMesh({
id: "greenLegMesh",
geometryId: "boxGeometry",
position: [4, -6, -4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [0.3, 1.0, 0.3],
textureSetId: "theTextureSet"
});

const blueLegMesh = sceneModel.createLayerMesh({
id: "blueLegMesh",
geometryId: "boxGeometry",
position: [4, -6, 4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [0.3, 0.3, 1.0],
textureSetId: "theTextureSet"
});

const yellowLegMesh = sceneModel.createLayerMesh({
id: "yellowLegMesh",
geometryId: "boxGeometry",
position: [-4, -6, 4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [1.0, 1.0, 0.0],
textureSetId: "theTextureSet"
});

const tableTopMesh = sceneModel.createLayerMesh({
id: "tableTopMesh",
geometryId: "boxGeometry",
position: [0, -3, 0],
scale: [6, 0.5, 6],
rotation: [0, 0, 0],
color: [1.0, 0.3, 1.0],
textureSetId: "theTextureSet"
});

// Create five SceneObjects, each using a SceneMesh.
// A SceneMesh belongs to exactly one SceneObject.

const redLegSceneObject = sceneModel.createObject({
id: "redLegObject",
meshIds: ["redLegMesh"]
});

if (redLegSceneObject instanceof SDKError) {
console.log(redLegSceneObject.message);
}

const greenLegSceneObject = sceneModel.createObject({
id: "greenLegObject",
meshIds: ["greenLegMesh"]
});

const blueLegSceneObject = sceneModel.createObject({
id: "blueLegObject",
meshIds: ["blueLegMesh"]
});

const yellowLegSceneObject = sceneModel.createObject({
id: "yellowLegObject",
meshIds: ["yellowLegMesh"]
});

const tableTopSceneObject = sceneModel.createObject({
id: "tableTopObject",
meshIds: ["tableTopMesh"]
});

sceneModel.build().then(()=> {

// SceneModel is ready for use

}).catch((err) => {
console.error(err);
});
}

Now that we've built our SceneModel, we can read all of its components.

const theSceneModel = theScene.models["theModel"];
const theTexture = theSceneModel.textures["theColorTexture"];
const theTextureSet = theSceneModel.textureSets["theTextureSet"];
const boxGeometry = theSceneModel.geometries["boxGeometry"];
const theTableTopMesh = theSceneModel.meshes["tableTopMesh"];
const theTableTopObject = theSceneModel.objects["tableTopObject"];
const theTableTopObjectAgain = theScene.objects["tableTopObject"];

The classes Scene, SceneModel, SceneObject, and SceneMesh each have an "aabb" property that contains their axis-aligned world-space 3D boundaries (AABB).

The SceneGeometry class has an "aabb" property that represents the geometry's local-space boundary.

const sceneAABB = theScene.aabb; // [xmin,ymin,zmin,xmax,ymax,zmax]
const sceneModelAABB = theSceneModel.aabb;
const sceneMeshAABB = theTableTopMesh.aabb;
const sceneObjectAABB = theTableTopObject.aabb;

When we created our SceneModel, the SceneModel.createGeometry method internally performed some on-the-fly compression and processing of our geometry parameters.

To speed up SceneModel creation, we may want to perform that compression in advance.

We can use the compressGeometryParams function to pre-compress the geometry parameters so that we can then use SceneModel.createGeometryCompressed instead, to create the geometry directly from the compressed parameters.

In the example below, we'll now use compressGeometryParams to compress a SceneGeometryParams into a SceneGeometryCompressedParams.

import {compressGeometryParams} from "@xeokit/sdk/compression";
import {TrianglesPrimitive} from "@xeokit/sdk/constants";

const geometryCompressedParams = compressGeometryParams({
id: "boxGeometry",
primitive: TrianglesPrimitive,
positions: [ // Floats
1, 1, 1, -1, 1, 1,
-1, -1, 1, 1, -1, 1, 1,
-1, -1, 1, 1, -1, -1, 1, -1, -1,
-1, -1
],
indices: [
0, 1, 2, 0, 2, 3, 4, 5, 6, 4,
6, 7, 8, 9, 10, 8, 10, 11, 12,
13, 14, 12, 14, 15, 16, 17, 18,
16, 18, 19, 20, 21, 22, 20, 22, 23
]
});

The value of our new SceneGeometryCompressedParams is shown below.

We can see that:

  • Vertex positions are now quantized to 16-bit integers
  • Edge indices are generated for our TrianglesPrimitive
  • Quantization range is given in axis-sligned bounding box aabb, to de-quantize the positions within the Viewer
{
id: "boxGeometry",
primitive: TrianglesPrimitive,
aabb: [
-1, -,1 -,1, 1, 1, 1
],
positionsCompressed: [
65525, 65525, 65525, 0, 65525, 65525,
0, 0, 65525, 65525, 0, 65525, 65525,
0, 0, 65525, 65525, 0, 0, 65525, 0, 0,
0, 0
],
indices: [
0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6,
0, 6, 1, 1, 6, 7, 1, 7, 2, 7, 4, 3, 7, 3, 2,
4, 7, 6, 4, 6, 5
],
edgeIndices: [
3, 4, 0, 4, 5, 0, 5, 6,
0, 6, 1, 1, 6, 7, 1, 7,
3, 2, 4, 7, 6, 4, 6
]
}

We could then create a SceneGeometry from the compressed parameters like this:

const geometry = sceneModel.createGeometryCompressed(geometryCompressedParams);

We can export SceneModels to several file formats.

For example, let's use saveDotBIM to save our SceneModel to .BIM format:

import {loadDotBIM} from "@xeokit/sdk/dotbim";

const fileData = saveDotBIM({ // ArrayBuffer
sceneModel
});

We can also import SceneModels from several file formats.

For example, let's use loadDotBIM to load a .BIM file into a new SceneModel:

import {loadDotBIM} from "@xeokit/sdk/dotbim";

const sceneModel2 = scene.createModel({
id: "mySceneModel2"
});

fetch(`model.bim`)
.then(response => {
response
.json()
.then(fileData => {
loadDotBIM({
fileData,
sceneModel2
})
.then(()=>{
sceneModel2.build();
})
.catch(err => {
console.error(err);
});
}).catch(err => {
console.error(err);
});
}).catch(err => {
console.error(err);
});

 const sceneModel2JSON = sceneModel2.toParams();

const sceneModel3 = sce.createSceneModel({
id: "mySceneModel3"
});

sceneModel3.fromParams(sceneModel2JSON);

sceneModel3.build();

 sceneModel3.destroy();

Classes

Scene
SceneGeometry
SceneMesh
SceneModel
SceneObject
SceneTexture
SceneTextureSet
SceneTile

Interfaces

GeometryView
RendererGeometry
RendererMesh
RendererModel
RendererObject
RendererTexture
RendererTextureSet
SceneGeometryCompressedParams
SceneGeometryParams
SceneMeshParams
SceneModelParams
SceneModelStats
SceneModelStreamLayerParams
SceneModelStreamParams
SceneObjectParams
SceneTextureParams
SceneTextureSetParams

Functions

buildMat4
compressGeometryParams
getSceneObjectGeometry