Skip to main content

Textures in XKT Model Format

· 6 min read

See also: Textures in Scene Representations

Introduction

In this tutorial, we'll use xeokit's convert2xkt CLI tool to convert a binary glTF model with textures into xeokit's native XKT geometry format, which we'll then view in the browser using a xeokit Viewer.

The XKT format compresses models into a compact payload from which xeokit can load large numbers of objects over the Web in seconds, at full geometric precision.

XKT stores textures in such a way that, as they are loaded, they are transcoded within the browser straight to the optimal compressed format that’s supported by the user’s GPU. Transcoding consumes minimal browser resources, and the GPU footprint of the compressed textures is also minimal. This gives us a minimal memory spike while loading textures, and the ability to pack more more them onto our GPU.

To achieve this, XKT stores each texture in the KTX 2.0 Container Format (.ktx2) . Within each KTX2 container, the texture’s pixel data is is encoded in the UASTC Transmission Format. The UASTC format is designed to be efficiently transcoded into the optimal compressed format for the target GPU.

Internally, convert2xkt uses the Basis Universal Transcoder to convert the glTF textures into XKT textures. Likewise, XKTLoaderPlugin uses the Basis Transcoder to transcode the XKT textures to the GPU’s optimal format.

For our glTF model, we'll use an architectural model from Sketchfab. When that's converted and loaded into our viewer, it will look like the example below.

This model contains 571 visible objects, with 139642 triangles and 14 textures, and a xeokit Viewer can usually load it over a good Internet connection in ~2 seconds.

Work to do

Although xeokit’s shaders currently support bump mapping, they do not currently (as of v2.3) support the tangent-space normal maps used in glTF. Note the seat coverings in the screenshot - not bumpy. We didn’t consider that a show-stopper for the xeokit v3.2 release though, and since we also had photogrammetry applications eagerly awaiting textures in XKT, which don’t need bumpiness, we plan to adjust the shaders in a subsequent minor release.

Run this example

image-20240605-162449.png

Creating an XKT File Containing Textures

We’ll now use xeokit’s convert2xkt CLI tool to convert our binary glTF file with textures into an XKT file. We’ll make sure we install the latest version of @xeokit/xeokit-convert:

npm i @xeokit/xeokit-convert

Let’s print out the help info - note the new -t option we’re using to convert glTF textures:

node convert2xkt.js -h

Usage: convert2xkt [options]

Options:
-v, --version output the version number
-s, --source [file] path to source file
-f, --format [string] source file format (optional); supported formats are gltf, ifc, laz, las, pcd, ply, stl and cityjson
-m, --metamodel [file] path to source metamodel JSON file (optional)
-i, --include [types] only convert these types (optional)
-x, --exclude [types] never convert these types (optional)
-r, --rotatex rotate model 90 degrees about X axis (for las and cityjson)
-g, --disablegeoreuse disable geometry reuse (optional)
-z, --mintilesize [number] minimum diagonal tile size (optional, default 500)
-t, --disabletextures ignore textures (optional)
-n, --disablenormals ignore normals (optional)
-o, --output [file] path to target .xkt file; creates directories on path automatically if not existing
-l, --log enable logging (optional)
-h, --help display help for command


XKT version: 10

Now we’ll go ahead and convert our binary glTF file to XKT, using the -l option to log progress and see what’s going on:

node convert2xkt.js -s HousePlan.glb -o HousePlan.textures.glb.xkt -l

[convert2xkt] Running convert2xkt v1.1.0...
[convert2xkt] Reading input file: HousePlan.glb
[convert2xkt] Input file size: 28650.37 kB
[convert2xkt] Input file parsed OK. Building XKT document...
[convert2xkt] XKT document built OK. Writing to XKT file...
[convert2xkt] Converted to: XKT v10
[convert2xkt] XKT size: 19848.02 kB
[convert2xkt] XKT textures size: 18596.97kB
[convert2xkt] Compression ratio: 1.44
[convert2xkt] Conversion time: 70.20 s
[convert2xkt] Converted metaobjects: 0
[convert2xkt] Converted property sets: 0
[convert2xkt] Converted drawable objects: 571
[convert2xkt] Converted geometries: 571
[convert2xkt] Converted textures: 14
[convert2xkt] Converted textureSets: 11
[convert2xkt] Converted triangles: 139642
[convert2xkt] Converted vertices: 191234
[convert2xkt] Converted UVs: 10854
[convert2xkt] Converted normals: 191234
[convert2xkt] Writing XKT file: HousePlan.textures.glb.xkt
[convert2xkt] Done.

Looking at the tools’s log output, you’ll note that our XKT file is actually larger than original glTF file. That’s because each texture’s pixel data is stored in the UASTC Transmission Format, which XKTLoaderPlugin will transcode to the GPU’s optimal compressed format using its Basis Transcoder. In other words, we get the benefits of faster loading and lower GPU texture footprint for the penalties of a larger XKT file and the extra time the convert2xkt tool takes to create its textures.

Loading an XKT File Containing Textures

XKTLoaderPlugin uses a KTX2TextureTranscoder to load textures in XKT files (XKT v10+). An XKTLoaderPlugin has its own default KTX2TextureTranscoder, configured to load the Basis Codec from the CDN.

If we wish, we could override that with our own KTX2TextureTranscoder instance that's configured to load the Codec locally.

In the example below, we'll create a Viewer and add an XKTLoaderPlugin configured with a KTX2TextureTranscoder that loads the Codec from our local file system. Then we'll use the XKTLoaderPlugin to load an XKT file that contains KTX2 textures, which the plugin will transcode using its KTX2TextureTranscoder.

We'll configure our KTX2TextureTranscoder to load the Basis Codec from a local directory. If we were happy with loading the Codec from our CDN (in other words, our app will always have an Internet connection) then we could just leave out the KTX2TextureTranscoder altogether, and let the XKTLoaderPlugin use its internal default KTX2TextureTranscoder, which is configured to load the Codec from the CDN.

We'll stick with loading our own local copy of the Codec though, in case we want to run our app without an Internet connection.

Run this example

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

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];

const textureTranscoder = new KTX2TextureTranscoder({
viewer,
transcoderPath: "./../dist/basis/" // <------ Path to Basis transcoder
});

const xktLoader = new XKTLoaderPlugin(viewer, {
textureTranscoder // <<------------- Transcodes KTX2 textures in XKT files
});

const sceneModel = xktLoader.load({
id: "myModel",
src: "./HousePlan.xkt" // <<------ XKT file with KTX2 textures
});