Skip to content

XKT Format

xeolabs edited this page Jul 17, 2019 · 21 revisions

See also:

Introduction

The .xkt format is xeokit's native binary format, which may be loaded using the XKTLoaderPlugin.

This page describes the .xkt format, with the intention of helping developers to write their own tools for exporting models to .xkt.

The xeokit-gltf-to-xkt tool provides a means to convert our glTF files to .xkt. It's also a reference implementation intended to help developers create their own .xkt exporters.

Contents

XKT V1.0

Overview

The table below lists the elements within V1.0 of the .xkt file format.

For convence we're using a symbolic name, eg. index_size, for each element.

Some elements are deflated using zlib. These are flagged in the fourth column.

Element Type Description zlib-Deflated
version Uint32 The .xkt file format version. This is the first four bytes in the file.
index_size Uint32 Byte size of the index. The index is is the following block, which provides a table of the sizes of certain subsequent elements within the file.
positions_size Uint32 Byte size of deflated positions. This is the start of the index.
normals_size Uint32 Byte size of deflated normals.
indices_size Uint32 Byte size of deflated indices.
edge_indices_size Uint32 Byte size of deflated edge_indices.
mesh_positions_size Uint32 Byte size of deflated mesh_positions.
mesh_normals_size Uint32 Byte size of deflated mesh_normals.
mesh_indices_size Uint32 Byte size of deflated mesh_indices.
mesh_edge_indices_size Uint32 Byte size of deflated mesh_edge_indices.
mesh_colors_size Uint32 Byte size of deflated mesh_colors.
entity_ids_size Uint32 Byte size of deflated entity_ids.
entity_meshes_size Uint32 Byte size of deflated entity_meshes.
entity_is_objects_size Uint32 Byte size of deflated entity_is_objects.
positions_decode_matrix_size Uint32 Byte size of deflated positions_decode_matrix. This is the end of the index.
positions Uint16[] Quantized positions for all meshes. Yes
normals Uint8[] Oct-encoded normals for all meshes. Yes
indices Uint32[] Geometry triangle indices for all meshes. Has three elements per triangle. Yes
edge_indices Uint32[] Geometry edge indices for all meshes. Has two elements per edge. Yes
mesh_positions Uint32[] For each mesh, base index of a portion in positions. Yes
mesh_normals Uint32[] For each mesh, base index of a portion in normals. Yes
mesh_indices Uint32[] For each mesh, base index of a portion in indices Yes
mesh_edge_indices Uint32[] For each mesh, base index of a portion in edge_indices Yes
mesh_colors Uint8[] For each mesh, an RGBA color. Has four elements per color, each in range [0..255]. The fourth element, alpha, is opacity. Yes
entity_ids String ID for each entity, as a string-encoded JSON array of strings Yes
entity_meshes Uint32[] For each entity, base index of a portion in mesh_positions, mesh_normals, mesh_indices and mesh_colors. Yes
entity_is_objects_size Uint8[] For each entity, a flag indicating whether or not it represents an object Yes
positions_decode_matrix Float32[] De-quantization matrix to decompress positions Yes

zlib Deflation

Note the last column in the table above, which indicates that some of the elements are deflated using zlib. The xeokit-gltf-to-xkt tool and the XKTLoaderPlugin plugin both use pako.js, which is a JavaScript port of zlib, to deflate and inflate.

When loading .xkt, XKTLoaderPlugin inflates those elements before parsing them.

Geometry Arrays

The positions, normals, indices and edge_indices arrays are the concatenation of the geometries for all the meshes in the model.

Both positions and normals are in World space.

The positions array is quantized to 16-bit integers, and will be dequantized in xeokit's shaders using positions_decode_matrix. The normals array is oct-encoded to 8-bit integers, and will be also decoded in xeokit's shaders. For an example of geometry quantization and oct-encoding using JavaScript and WebGL, see the mesh-quantization-example demo by @tsherif. You can also find an example within the source code of xeokit-gltf-to-xkt.

The indices array defines triangles, with three elements per triangle.

The edge_indices array defines the edges that xeokit draws for wireframe views, with two elements per edge. An .xkt exporter needs to generate those edge indices from the geometries, using the algorithm demonstrated in buildEdgesindices.js (a file within xeokit-gltf-to-xkt).

Implicit Mesh Order

There is an implicit order in which meshes appear in the geometry arrays, and mesh_positions, mesh_normals and mesh_indices indicate which portion of the geometry arrays is used for each mesh. These rely on the implicit mesh order.

The first position used by mesh meshIdx is:

let i = mesh_positions[ meshIdx ];
let x = positions[ i + 0 ];
let y = positions[ i + 1 ];
let z = positions[ i + 2 ];

The last position used by mesh meshIdx is:

let i2 = mesh_positions[ meshIdx + 1 ] - 1;
let x2 = positions[ i2 + 0 ];
let y2 = positions[ i2 + 1 ];
let z2 = positions[ i2 + 2 ];

Recall that the position is quantized to 16-bit integers. To de-quantize it, we would need to multiply it by positions_decode_matrix.

Indices

The indices array indexes positions and normals, to define the geometry primitives, which are currently assumed to be triangles.

In the snippet below, we'll obtain the quantized World-space 3D positions of the vertices of the first triangle for mesh meshIdx:

let indicesBaseIdx = mesh_indices[ meshIdx ];
let positionsBaseIdx = mesh_positions[ meshIdx ];

let a = indices[ indicesBaseIdx + 0 ];
let b = indices[ indicesBaseIdx + 1 ];
let c = indices[ indicesBaseIdx + 2 ];

let ax = positions[ positionsBaseIdx + (a * 3) + 0];
let ay = positions[ positionsBaseIdx + (a * 3) + 1];
let az = positions[ positionsBaseIdx + (a * 3) + 2];

let bx = positions[ positionsBaseIdx + (b * 3) + 0];
let by = positions[ positionsBaseIdx + (b * 3) + 1];
let bz = positions[ positionsBaseIdx + (b * 3) + 2];

let cx = positions[ positionsBaseIdx + (c * 3) + 0];
let cy = positions[ positionsBaseIdx + (c * 3) + 1];
let cz = positions[ positionsBaseIdx + (c * 3) + 2];

Note how mesh_indices contains a base index for each mesh to indicate its portion of indices, and mesh_positions contains a base index for each mesh to indicate its portion of positions. We use mesh_positions to offset each index to align it with the meshes portion in positions.

Meshes

In xeokit, an entity can have multiple meshes. For example, an entity representing a window could have a mesh representing the frame, another representing the pane, another for the handle, and so on.

The entity_meshes array contains a base index into mesh_positions, mesh_normals, mesh_indices and mesh_colors for each entity.

Let's extend the previous snippet to obtain the quantized World-space 3D positions of the vertices of the first triangle within the first mesh belonging to the entity at entityIdx:

let meshBaseIdx = entity_meshes[ entityIdx ];

let indicesBaseIdx = mesh_indices[ meshBaseIdx ];
let positionsBaseIdx = mesh_positions[ meshBaseIdx ];

let a = indices[ indicesBaseIdx + 0 ];
let b = indices[ indicesBaseIdx + 1 ];
let c = indices[ indicesBaseIdx + 2 ];

let ax = positions[ positionsBaseIdx + (a * 3) + 0];
let ay = positions[ positionsBaseIdx + (a * 3) + 1];
let az = positions[ positionsBaseIdx + (a * 3) + 2];

let bx = positions[ positionsBaseIdx + (b * 3) + 0];
let by = positions[ positionsBaseIdx + (b * 3) + 1];
let bz = positions[ positionsBaseIdx + (b * 3) + 2];

let cx = positions[ positionsBaseIdx + (c * 3) + 0];
let cy = positions[ positionsBaseIdx + (c * 3) + 1];
let cz = positions[ positionsBaseIdx + (c * 3) + 2];

Entity IDs

Each entity has a string ID, which we can get like so:

let entityId = entity_ids[ entityIdx ];

Improvements Needed

Geometry reuse

Currently there is no geometry reuse in .xkt. Each geometry instance is transformed into World-space and concatenated to the geometry arrays, as if it was a separate geometry. This will be addressed in the next version of the .xkt format.

Even without geometry reuse, however, the geometry quantization, oct-encoding and zlib deflation still make the .xkt format an efficient option.

Clone this wiki locally