Namespace viewer

xeokit Viewer


The SDK's browser-based 3D/2D Viewer for xeokit Scenes


This module allows you to display and interact with a Scene in the browser.

If you’re new to xeokit’s model representation, start with:

This Viewer module focuses on the interactive layer: Views, Cameras, picking, emphasis effects (highlight/selection/x-ray), section planes, lighting, and rendering modes.


Installation

npm install @xeokit/sdk

Tutorial

This tutorial walks through a minimal setup and then adds common interaction features step by step.


import { Scene } from "@xeokit/sdk/scene";
import { Viewer } from "@xeokit/sdk/viewer";
import { WebGLRenderer } from "@xeokit/sdk/webglrenderer";
import { CameraControl } from "@xeokit/sdk/cameracontrol";

import {
OrthoProjectionType,
PerspectiveProjectionType,
TrianglesPrimitive,
LinearEncoding,
LinearFilter
} from "@xeokit/sdk/constants";

Start by creating a Scene. The Scene is the SDK’s in-memory scene graph: it owns the model representation (SceneModels, objects, meshes, geometries, textures) and is where model content is created, updated, imported, and exported. For a deeper introduction to Scene content and building/importing models, see @xeokit/sdk/scene.

Next, create a Viewer. The Viewer is the browser-facing facade for interactive viewing: it manages one or more Views (canvases), user interaction state (camera control, picking, emphasis effects, section planes, render modes, etc), and it provides a Viewer-centric event stream that reflects interactions and viewer-side changes.

Finally, attach a WebGLRenderer. The WebGLRenderer is the rendering backend that listens to changes and draws the Scene into the View canvases using the browser’s WebGL API.

The runtime event flow is:

  • The Scene emits events when its content changes (models loaded, objects created/destroyed, transforms updated, etc).
  • The Viewer receives those Scene changes (via the attached Scene) and emits Viewer events as user interaction and view configuration changes occur (camera moves, section planes added, objects highlighted/selected/x-rayed, etc).
  • The WebGLRenderer listens to both streams to keep GPU resources in sync and re-render Views whenever something changes.
const scene = new Scene();

const viewer = new Viewer({
scene
});

const renderer = new WebGLRenderer({
viewer
});

A Viewer renders into one or more Views. Each View has its own canvas, camera, and per-object visual state.

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

if (!view1Result.ok) {
console.error(view1Result.error);
// handle error...
}

const view1 = view1Result.value;

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

view1.camera.eye = [-3.93, 2.85, 27.01];
view1.camera.look = [4.40, 3.72, 8.89];
view1.camera.up = [-0.01, 0.99, 0.03];

view1.projectionType = OrthoProjectionType;
// view1.projectionType = PerspectiveProjectionType;

view1.perspectiveProjection.fov = 60.0;
view1.orthoProjection.scale = 1.0;

Configure Camera.worldAxis | worldAxis to match your domain’s coordinate system. This affects navigation behavior when using CameraControl.

+Y up (+X right, -Z forward):

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

+Z up (+X right, -Y forward):

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

For a full walkthrough on creating and importing geometry, see @xeokit/sdk/scene. Below is a tiny example that creates a model with a few objects so you can try interactions immediately:

const sceneModelResult = scene.createModel({
id: "myModel"
});

if (!sceneModelResult.ok) {
console.error(sceneModelResult.error);
// handle error...
}

const sceneModel = sceneModelResult.value;

sceneModel.createGeometry({
id: "myGeometry",
primitive: TrianglesPrimitive,
positions: [202, 202, 202, 200, 202, 202 ...],
indices: [0, 1, 2, 0, 2, 3 ...]
});

sceneModel.createTexture({
id: "myColorTexture",
src: "myTexture",
encoding: LinearEncoding,
magFilter: LinearFilter,
minFilter: LinearFilter
});

sceneModel.createTextureSet({
id: "myTextureSet",
colorTextureId: "myColorTexture"
});

sceneModel.createLayerMesh({
id: "myMesh1",
geometryId: "myGeometry",
textureSetId: "myTextureSet"
});

sceneModel.createLayerMesh({
id: "myMesh2",
geometryId: "myGeometry",
textureSetId: "myTextureSet"
});

sceneModel.createLayerMesh({
id: "myMesh3",
geometryId: "myGeometry",
textureSetId: "myTextureSet"
});

sceneModel.createObject({
id: "myObject1",
meshIds: ["myMesh1"]
});

sceneModel.createObject({
id: "myObject2",
meshIds: ["myMesh2"]
});

sceneModel.createObject({
id: "myObject3",
meshIds: ["myMesh3"]
});

Toggle effects in batches:

view1.setObjectsHighlighted(["myObject1"], true);
view1.setObjectsSelected(["myObject2"], true);
view1.setObjectsXRayed(["myObject3"], true);
view1.setObjectsColorized(["myObject1"], [1, 0, 0]);

Or set state directly on a ViewObject:

view1.objects["myObject1"].highlighted = true;
view1.objects["myObject2"].selected = true;
view1.objects["myObject3"].xrayed = true;

view1.objects["myObject1"].colorize = [1, 0, 0];
view1.objects["myObject1"].colorize = null; // clear

Create a SectionPlane:

const sectionPlaneResult = view1.createSectionPlane({
id: "sectionPlane1",
pos: [0, 0, 0],
dir: [-1, -1, -1]
});

if (!sectionPlaneResult.ok) {
console.error(sectionPlaneResult.error);
// handle error...
}

const sectionPlane1 = sectionPlaneResult.value;

Animate it:

sectionPlane1.dir = [-1, -1, 1];
sectionPlane1.pos = [1, 0, 0];

Keep specific objects unaffected:

view1.setObjectsClippable(["myObject1"], false);
// or:
view1.objects["myObject1"].clippable = false;

view1.clearLights();

const lightResult = view1.createDirLight({
id: "dirLight1",
dir: [-1, -1, -1],
color: [0.9, 0.9, 0.9],
intensity: 0.9
});

if (!lightResult.ok) {
console.error(lightResult.error);
// handle error...
}

view1.createPointLight({
id: "pointLight1",
pos: [-100, 10, -100],
color: [0.9, 0.9, 1.0],
intensity: 1.0,
constantAttenuation: 0.8,
linearAttenuation: 0.9,
quadraticAttenuation: 0.9
});

view1.createAmbientLight({
id: "ambientLight1",
color: [0.5, 0.5, 0.6],
intensity: 0.7
});

Multiple Views can render the same Scene with different camera positions and visual states.

const view2Result = viewer.createView({
id: "myView2",
elementId: "myView2"
});

if (!view2Result.ok) {
console.error(view2Result.error);
// handle error...
}

const view2 = view2Result.value;

view2.camera.eye = [-3.933, 2.855, 27.018];
view2.camera.look = [4.400, 3.724, 8.899];
view2.camera.up = [-0.018, 0.999, 0.039];

const cameraControl2 = new CameraControl({
view: view2
});

view2.objects["myObject1"].highlighted = true;

ViewLayers let you apply operations to groups of objects (visibility, selection, clipping, etc). ViewLayers are created in a View and are populated based on the layerId of the underlying SceneObjects.

const environmentLayerRes = view1.createLayer({
id: "myEnvironmentViewLayer"
});

if (!environmentLayerRes.ok) {
console.error(environmentLayerRes.error);
}

const environmentLayer = environmentLayerRes.value;

Hide everything in a layer:

environmentLayer.setObjectsVisible(environmentLayer.objectIds, false);

Use BCF to exchange viewpoints with other BIM tools. For background and semantic context, see @xeokit/sdk/data.

import { saveBCFViewpoint, loadBCFViewpoint } from "@xeokit/sdk/bcf";

const bcfViewpointResult = saveBCFViewpoint({
view: view1,
excludeViewLayerIds: ["myEnvironmentViewLayer"]
});

if (!bcfViewpointResult.ok) {
console.log("Error saving BCF viewpoint:", bcfViewpointResult.error);
}

const bcfViewpoint = bcfViewpointResult.value;

const loadBCFResult = loadBCFViewpoint({
bcfViewpoint,
view: view1,
excludeViewLayerIds: ["myEnvironmentViewLayer"]
});

if (!loadBCFResult.ok) {
console.log("Error loading BCF viewpoint:", loadBCFResult.error);
}

Rendering modes let you enable/disable effects as a group (eg. quality vs performance).

import { FastRender, QualityRender } from "@xeokit/sdk/constants";

view1.edges.renderModes = [QualityRender];
view1.sao.renderModes = [QualityRender];
view1.resolutionScale.renderModes = [FastRender];

view1.renderMode = FastRender;
view1.renderMode = QualityRender;
view1.renderMode = FastRender;

Use Viewer.toParams and Viewer.fromParams to copy Viewer configuration between instances (Views, Cameras, SectionPlanes, Lights, materials, and more).

const viewerParamsResult = viewer.toParams();
const viewerParams = viewerParamsResult.value;

const viewer2 = new Viewer({
scene: new Scene()
});

const renderer2 = new WebGLRenderer({
viewer: viewer2
});

const fromParamsResult = viewer2.fromParams(viewerParams);
if (!fromParamsResult.ok) {
console.error("Error loading ViewerParams:", fromParamsResult.error);
}

Classes

AmbientLight
Camera
CustomProjection
DirLight
Edges
EmphasisMaterial
FrustumProjection
LinesMaterial
OrthoProjection
PerspectiveProjection
PickResult
PointLight
PointsMaterial
ResolutionScale
SAO
SectionPlane
SnapshotResult
Texturing
View
Viewer
ViewerEvents
ViewLayer
ViewObject

Interfaces

AmbientLightParams
CameraParams
CustomProjectionParams
DirLightParams
EdgesParams
EmphasisMaterialParams
FrustumProjectionParams
OrthoProjectionParams
PerspectiveProjectionParams
PickParams
PointLightParams
PointsMaterialParams
Projection
ResolutionScaleParams
SAOParams
SectionPlaneParams
SnapshotFinishedEvent
SnapshotParams
SnapshotStartedEvent
TickParams
ViewerParams
ViewLayerParams
ViewParams