openbim-ifcx-geometry
Renderer-neutral geometry and viewer helpers for IFC5 / IFCX: transforms, meshes, curves, point clouds, presentation, a flat render scene, and GLB export.
| Crate | openbim-ifcx-geometry 0.1.1 on main |
|---|---|
| Latest release | 0.1.1 (2026-10-03) |
| Distributed as | crates.io openbim-ifcx-geometry |
| Install | cargo add openbim-ifcx-geometry |
| Requires | Rust 1.88.0 |
| License | MIT |
| API documentation | rustdoc · docs.rs |
| Source | crates/openbim-ifcx-geometry/ |
About
From the crate's README.md:
Renderer-neutral geometry and viewer helpers for IFC5 / IFCX. It depends on openbim-ifcx and never on a renderer or GPU API.
Implemented for the ifcx_alpha draft:
| Module | Attribute | Result |
|---|---|---|
transform | usd::xformop | Affine Transform (USD row-vector layout, translation in the last row) and world_from_parent |
mesh | usd::usdgeom::mesh | TriangleMesh: positions, u32 indices, flat face normals |
curves | usd::usdgeom::basiscurves | Linear Polylines, or Unsupported for other curve types |
points | points::array, points::base64, pcd::base64 | PointCloud: positions and optional linear RGB colours; PCD ascii, binary, binary_compressed |
presentation | usd::usdgeom::visibility, bsi::ifc::presentation::*, gltf::material | NodePresentation per node; is_visible, resolve_basic_material, resolve_mesh_material apply the reference viewer's precedence over a node's ancestors |
glb | a RenderScene | to_glb: one binary glTF 2.0 file with shared meshes, LINES, POINTS (with COLOR_0), PBR materials, node names set to IFCX paths, and the origin and Z-up to Y-up rotation on a root node |
scene | all of the above, over a composed tree | RenderScene::from_composition: instances (node path for picking, f64 world transform, f32 render matrix, material), shared f32 mesh, line, and point buffers stored once per shared attribute value, a material table, f64 world bounds, a render origin, and per-value warnings; the walk is bounded by SceneOptions::max_visits and max_path_bytes, and coordinates outside f32 range are left out with a warning |
Each decoder takes an attribute's serde_json::Value and returns a typed DecodeError for malformed input. Coordinates are f64 because IFCX files carry georeferenced coordinates; convert to f32 for rendering after applying world transforms.
use openbim_ifcx::{compose, flatten_owned, IfcxFile};
use openbim_ifcx_geometry::{RenderScene, SceneOptions};
let file = IfcxFile::from_json_slice(&std::fs::read("model.ifcx")?)?;
let scene = RenderScene::from_composition(&compose(&flatten_owned(file.data))?, &SceneOptions::default());
for instance in &scene.instances {
// instance.path, instance.geometry, instance.matrix, scene.materials[instance.material]
}Buffers hold vertices relative to a per-buffer anchor, and instance matrices map to world coordinates minus scene.origin, so georeferenced models keep sub-millimetre precision in f32.
To look at an IFCX file in any glTF viewer:
cargo run --release -p openbim-ifcx-geometry --example ifcx2glb -- model.ifcx model.glb
# layers, weakest first; --z-up keeps IFCX axes, --local leaves the origin off the root
cargo run --release -p openbim-ifcx-geometry --example ifcx2glb -- base.ifcx overlay.ifcx out.glb
# resolve imports from disk; --mirror maps a URI prefix to an offline copy
cargo run --release -p openbim-ifcx-geometry --example ifcx2glb -- \
--resolve-imports --mirror https://ifcx.dev/=mirror/ifcx.dev model.ifcx model.glbWithout --resolve-imports, imports are ignored. With it, every layer's imports load through openbim_ifcx::layers::FsResolver, each layer after the layers it imports (a layer overrides its imports, the next layer overrides both); nothing is fetched from the network.
scripts/gltf-validate.sh (opt-in, needs Node.js) exports the fixtures and, with IFCX_UPSTREAM_DIR, every upstream example, and checks them with the Khronos glTF validator. See the repository capabilities, the documentation with a Rust guide, and the viewer, which shows an IFCX file in the browser.
Licensed under MIT.
Public API
The crate root, as rustdoc shows it. Follow a name to its rustdoc entry.
| Module | Summary |
|---|---|
curves | Polylines from usd::usdgeom::basiscurves. |
error | The error shared by every attribute decoder in this crate. |
glb | Binary glTF 2.0 (GLB) export of a RenderScene. |
math | Small vector helpers shared by the decoders. |
mesh | Indexed triangle meshes from usd::usdgeom::mesh. |
points | Point clouds from points::array, points::base64, and pcd::base64 (ifcx_alpha). |
presentation | Visibility, colour, opacity, and glTF PBR materials (ifcx_alpha). |
scene | A flat, renderer-neutral render scene over a composed node tree. |
transform | Local and world transforms from usd::xformop. |
| Item | Kind | Summary |
|---|---|---|
AlphaMode | enum | glTF alphaMode. |
Attributes | trait | A node's attributes keyed by namespaced attribute id, as in a file's schemas (for example points::array or gltf::material). |
BasicMaterial | struct | Colour and opacity from bsi::ifc::presentation. |
Bounds | struct | An axis-aligned box in f64. |
CurveGeometry | enum | The result of decoding a curves attribute that is well formed. |
DecodeError | enum | Why an attribute value could not be decoded into geometry. |
GeometryKind | enum | The kind of geometry an instance draws. |
GeometryRef | enum | What an instance draws: an index into one of the scene's buffer lists. |
GlbError | enum | Why a scene could not be written as GLB. |
GlbOptions | struct | Options for to_glb. |
GltfMaterial | struct | A gltf::material value with glTF 2.0 defaults filled in. |
Instance | struct | One drawn geometry of one node at one path. |
is_visible | function | Whether a node is drawn, given whether its parent is (true for a root) and the node's own visibility. An invisible node hides its subtree; a descendant cannot become visible again. |
LineBuffer | struct | Shared line segments, from the linear curves of usd::usdgeom::basiscurves. |
Material | enum | The material a mesh is drawn with. |
MeshBuffer | struct | A shared triangle mesh, from usd::usdgeom::mesh. |
NodePresentation | struct | One node's own presentation attributes, before any inheritance. |
NormalTexture | struct | A normal map reference. The texture is recorded, not loaded. |
OcclusionTexture | struct | An occlusion map reference. The texture is recorded, not loaded. |
Origin | enum | Where the render origin of a scene lies. |
PointBuffer | struct | Shared points, from pcd::base64, points::array, or points::base64. |
PointCloud | struct | Decoded points of one node, in the node's local coordinates. |
Polyline | struct | One connected line strip in the node's local coordinates. |
RenderScene | struct | A flat render scene; see the module docs. |
resolve_basic_material | function | Colour and opacity for a node, from the node and its ancestors given nearest first. The nearest diffuseColor wins, and opacity comes from that same node (default 1); an opacity on a node without diffuseColor is not used. Used for curves, and for meshes without a glTF material. |
resolve_mesh_material | function | The material for a mesh node, from the node and its ancestors given nearest first. The nearest gltf::material wins over any bsi::ifc::presentation attribute; without one, see resolve_basic_material. |
SceneOptions | struct | Options for RenderScene::from_composition. |
SceneWarning | struct | A value skipped while building a scene; see the module docs. |
SceneWarningKind | enum | Why part of a node was left out of a scene. |
to_glb | function | Writes scene as a binary glTF 2.0 file. |
Transform | struct | An affine 4×4 transform in USD's row-vector layout. |
TriangleMesh | struct | An indexed triangle mesh in the node's local coordinates. |
UnsupportedCurve | struct | A curve type this crate does not turn into polylines. |
Vec3 | type alias | A point or direction in 3D, x, y, z. |
Visibility | enum | USD visibility token of one node. |
world_from_parent | function | World transform of a node from its parent's world transform and its own local transform, if it has one. A node without a local transform inherits its parent's world transform unchanged. Use Transform::IDENTITY as the parent of a root. |
Examples
ifcx2glb:cargo run -p openbim-ifcx-geometry --example ifcx2glb
Within the workspace
- Depends on
openbim-ifcx - Used by
openbim-ifcx-binding-core
Changes
Unreleased, on main:
Changed
ifcx2glb --resolve-importsstacks each layer after the layers it imports, followingopenbim-ifcx's new layer order (buildingSMART/IFC5-development#144), so a layer overrides its imports. The library API is unchanged (#36).
Latest release, 0.1.1 (2026-10-03):
No breaking change. Requires openbim-ifcx 0.1.1.
Added
ifcx2glb --resolve-importsloads every layer'simportsthroughopenbim_ifcx::layers::FsResolverbefore exporting, stacked as upstream'sifcx composedoes;--mirror PREFIX=DIR(repeatable) maps a URI prefix such ashttps://ifcx.dev/to an offline copy. The example now flattens withflatten_owned(#42, #43).- Test fixture
imports-panel-type.ifcx, whose panels inherit a type only its import defines, andtests/imports.rs, which exports it with the import resolved (#42). - Dev-dependency on
openbim-ifcxwith featurefs, for the example and its test. SceneOptions::max_visits(default 10 million) andSceneOptions::max_path_bytes(default 1 GiB), with builders andDEFAULT_*constants, bound the render-scene walk. When one is reached the walk stops with aSceneWarningKind::LimitReachedwarning (#41).SceneWarningKind::OutOfRangefor geometry and instances whose coordinates the scene'sf32buffers and matrices cannot hold (#41).
Fixed
RenderScene::from_compositionno longer visits an exponential number of paths: composition shares sub-trees, so 40 nodes naming the next one twice as children described 2^40 instances. Deep trees with long child names no longer build instance paths without bound. Found while fuzzing (#41).- Coordinates beyond
f32range (for example a translation of5e299) no longer produce infinite render matrices, buffers, or origins, whichto_glbwrote as JSONnull; such geometry and instances are left out with anOutOfRangewarning.to_glbwrites an infinitealphaCutoffasf32::MAXand a NaN one as0.5. Found by fuzzing (#41). PointCloud::from_pcdreturnsInvalidPcdfor ASCII data whoseCOUNTvalues overflowusizewhen summed, instead of panicking with overflow checks on (or reading the wrong column without). Found while fuzzing (#41).
Full history: crates/openbim-ifcx-geometry/CHANGELOG.md, and every release of every crate on the project changelog.