Skip to main content

Building 3D Model Viewers with xeokit-webcomponents

· 7 min read
Marcin Feszter
Software Engineer @ Creoox

Integrating 3D models into web applications can be a complex task, often requiring extensive knowledge of WebGL, 3D rendering pipelines, and model loading formats. However, with the advent of Web Components, this process has become significantly simpler. @xeokit/xeokit-webcomponents leverages this power, providing a set of easy-to-use, framework-agnostic custom elements for displaying and interacting with 3D models, particularly useful for BIM and IFC data.

In this blog post, we'll explore how to get started with @xeokit/xeokit-webcomponents and create a basic 3D model viewer.

What are Web Components?

Web Components are a set of W3C specifications that allow you to create reusable custom elements with their own encapsulated functionality and styling. They are native to the browser and work with any JavaScript framework, making them a highly flexible solution for building UI components.

Why @xeokit/xeokit-webcomponents?

@xeokit/xeokit-webcomponents builds upon the powerful xeokit-sdk to provide a declarative way to create 3D viewers. Key benefits include:

  • Simplicity: No need to write complex JavaScript for basic viewer setup.
  • Declarative API: Define your viewer and models directly in your HTML.
  • Framework Agnostic: Works seamlessly with React, Vue, Angular, or plain JavaScript.
  • Rich Features: Out-of-the-box support for model loading (XKT, glTF, IFC via plugins), tree views, fast navigation, and more.
  • Performance: Leverages the highly optimized xeokit-sdk for efficient 3D rendering.

Getting Started

Let's dive into an example to see how incredibly easy it is to set up a 3D viewer.

1. Include the Package

The simplest way to include @xeokit/xeokit-webcomponents in your project is via a CDN. Add the following <script> tag to the <head> of your HTML file:

<script type="module"
src="https://cdn.jsdelivr.net/npm/@xeokit/xeokit-webcomponents/dist/index.min.js"></script>

2. Create Your Viewer

Now, within your <body>, you can define your 3D viewer using the <xeo-viewer> custom element.

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3D Viewer</title>
<script type="module"
src="https://cdn.jsdelivr.net/npm/@xeokit/xeokit-webcomponents/dist/index.min.js"></script>
</head>

<body>
<div id="app">
<xeo-viewer id="my-viewer" transparent="true" edges="false" dtxEnabled="false" colorTextureEnabled="false">
<!-- Models and UI components will go here -->
</xeo-viewer>
</div>
</body>

</html>

In this example:

  • <xeo-viewer>: This is the core component that encapsulates the 3D canvas and the xeokit-sdk viewer instance.
  • id: A unique ID for the viewer instance.
  • transparent: (Optional) Control the viewer background transparency (Default: false).
  • edges: (Optional) Show edges (default false)
  • dtxEnabled: (Optional) Whether data texture-based scene representation (DTX) is enabled (default false)
  • colorTextureEnabled: (Optional) Whether basic color texture rendering is enabled (default false)

3. Load 3D Models

You can load 3D models by nesting <xeo-model> elements inside your <xeo-viewer>. The src attribute specifies the URL to your model. @xeokit/xeokit-webcomponents supports various formats, including .xkt (xeokit's optimized format) and .ifc/.gltf/.glb/.las/.laz/.obj/.stl/.bim. <xeo-model> will try to get model format from src attribute, you can prefix it with format; to directly specify your model. For example: src="las;https://some.host/my-model-file". IFC will be automatically converted to xkt format in your browser by our WASM cx-converter.

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3D Viewer</title>
<script type="module"
src="https://cdn.jsdelivr.net/npm/@xeokit/xeokit-webcomponents/dist/index.min.js"></script>
</head>

<body>
<div id="app">
<xeo-viewer id="my-viewer" transparent="true">
<xeo-model src="https://sos-ch-gva-2.exo.io/creoox-public/example-no-1.xkt" bounding-box="true" show-edges="true"></xeo-model>
<xeo-model src="las;https://sos-ch-gva-2.exo.io/creoox-public/example-no-1.laz"></xeo-model>
</xeo-viewer>
</div>
</body>

</html>
  • src: The URL to your 3D model.
  • bounding-box: (Optional) Displays the bounding box of the loaded model. This is useful for debugging or visualizing model extents. (Default: false)
  • show-edges: (Optional) Whether or not to show edges (Default: false)
  • dtx-enabled: (Optional) Whether data texture scene representation (DTX) is enabled (Default: false)
  • color-texture-enabled: (Optional) Whether basic color texture rendering is enabled (Default: false)
  • sao-enabled: (Optional) Whether SAO is enabled (Default: false)
  • rotation: (Optional) Initial rotation of model (Default: [0, 0, 0])
  • fp64: (Optional) Whether floating point precision for point cloud models is enabled (Default: false)

4. Add UI Components (Tree Views)

@xeokit/xeokit-webcomponents also provides UI components that interact with the viewer. A common requirement for BIM models is a tree view to navigate the model hierarchy.

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3D Viewer</title>
<script type="module"
src="https://cdn.jsdelivr.net/npm/@xeokit/xeokit-webcomponents/dist/index.min.js"></script>
</head>

<body>
<div id="app">
<xeo-viewer id="viewer-1" transparent="true">
<xeo-tree-view-panel width="350px" bgColor="rgba(239,207,227,0.6)" positionRight>
<xeo-tree-view hierarchy="containment" autoExpandDepth="3"></xeo-tree-view>
<xeo-tree-view hierarchy="types" autoExpandDepth="1"></xeo-tree-view>
<xeo-tree-view hierarchy="storeys" autoExpandDepth="2"></xeo-tree-view>
</xeo-tree-view-panel>
<xeo-model src="https://sos-ch-gva-2.exo.io/creoox-public/example-no-1.xkt" bounding-box="true"></xeo-model>
<xeo-model src="las;https://sos-ch-gva-2.exo.io/creoox-public/example-no-1.laz"></xeo-model>
</xeo-viewer>
</div>
</body>

</html>

Here we've added:

  • <xeo-tree-view-panel>: A container for one or more tree views.
    • width: Sets the width of the panel.
    • bgColor: Sets the background color using an RGBA value for transparency.
    • positionRight: Positions the panel to the right of the viewer.
    • positionLeft: Positions the panel to the left of the viewer.
  • <xeo-tree-view>: Renders a tree view of the loaded models. You can have multiple xeo-tree-view elements, each displaying a different hierarchy.
    • hierarchy: Defines the hierarchy type. Common values include:
      • "containment": Based on IFC spatial containment.
      • "types": Grouped by IFC types.
      • "storeys": Grouped by IFC storeys.
    • autoExpandDepth: Specifies how many levels of the tree should be automatically expanded on load.

5. Other Components (Fast Navigation)

Another useful component is <xeo-fast-nav>, which optimizes rendering for better performance during camera movements.

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3D Viewer</title>
<script type="module"
src="https://cdn.jsdelivr.net/npm/@xeokit/xeokit-webcomponents/dist/index.min.js"></script>
</head>

<body>
<div id="app">
<xeo-viewer id="viewer-1" transparent="true">
<xeo-tree-view-panel width="350px" bgColor="rgba(239,207,227,0.6)" positionRight>
<xeo-tree-view hierarchy="containment" autoExpandDepth="3"></xeo-tree-view>
<xeo-tree-view hierarchy="types" autoExpandDepth="1"></xeo-tree-view>
<xeo-tree-view hierarchy="storeys" autoExpandDepth="2"></xeo-tree-view>
</xeo-tree-view-panel>
<xeo-fast-nav scale-canvas-resolution="true" scale-canvas-resolution-factor="0.5"></xeo-fast-nav>
<xeo-model src="https://sos-ch-gva-2.exo.io/creoox-public/example-no-1.xkt" bounding-box="true"></xeo-model>
<xeo-model src="las;https://sos-ch-gva-2.exo.io/creoox-public/example-no-1.laz"></xeo-model>
</xeo-viewer>
</div>
</body>

</html>
  • scale-canvas-resolution-factor: Reduces the canvas resolution during fast navigation to improve frame rates. A value of 0.5 means the canvas will be rendered at half its normal resolution.

Interacting with the Viewer

While the declarative API is great for setup, you might want to programmatically interact with the viewer. You can access the underlying xeokit.Viewer instance through the xeo-viewer element.

document.addEventListener('DOMContentLoaded', () => {
const viewerElement = document.getElementById('viewer-1');

if (viewerElement) {
const handleViewerReady = () => {
const viewer = viewerElement.viewer; // Access the xeokit.Viewer instance

if (viewer) {
viewer.camera.eye = [-10, 5, 10];
viewer.camera.look = [0, 0, 0];
viewer.camera.up = [0, 1, 0];

viewer.scene.on("picked", (event) => {
const pickedEntity = event.entity;
if (pickedEntity) {
console.log("Picked object ID:", pickedEntity.id);
// You can also access pickedEntity.json, pickedEntity.type, etc.
}
});

// Disconnect the observer once the viewer is found to prevent unnecessary checks
observer.disconnect();
}
};

// Use a MutationObserver to watch for changes to the 'viewer' property
const observer = new MutationObserver((mutationsList) => {
console.log({mutationsList});
for (const mutation of mutationsList) {
if (
mutation.type === 'attributes' &&
mutation.attributeName === 'viewer' // Assuming 'viewer' property could be reflected as an attribute,
// though it's more likely to be a property, which MutationObserver directly watches for in childList or subtree.
// For direct property changes, a custom event might be more appropriate,
// but for now, we check on any attribute or subtree change that might indicate readiness.
) {
handleViewerReady();
} else if (mutation.type === 'childList' || mutation.type === 'subtree') {
// This captures general DOM changes, which might include when properties are set
// internally by the Web Component, especially if it adds elements.
handleViewerReady();
}
}
});

console.log({observer});

observer.observe(viewerElement, { attributes: true, subtree: true, attributeFilter: [] }); // Observe all attributes and subtree
handleViewerReady();

}
});

Conclusion

@xeokit/xeokit-webcomponents provides an incredibly straightforward and powerful way to embed interactive 3D model viewers into any web application. By leveraging standard Web Components, it offers flexibility and ease of use, making 3D visualization accessible to a wider range of web developers.

Whether you're building a BIM viewer, a product configurator, or just want to display 3D assets on your website, this package can significantly reduce development time and complexity. Give it a try in your next project!

For more advanced features and customization, refer to the official @xeokit/xeokit-webcomponents documentation and the underlying xeokit-sdk documentation.