| name | thatopen-core-fragments |
| description | Use when working with ThatOpen's fragment system for BIM model storage, loading, querying, or understanding the optimized geometry pipeline. Prevents worker initialization failures and memory leaks from undisposed models. Covers FragmentsManager, FragmentsModel, ModelIdMap, worker initialization, coordinate alignment, raycast, getData, GUID mapping. Keywords: fragments, fragmentsmanager, modelidmap, worker, instanced mesh, flatbuffers, bim model, load, dispose, raycast, how models are stored, fragment format, model data structure.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires @thatopen/fragments 3.3.x. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
ThatOpen Fragment System
Overview
The fragment system is ThatOpen's optimized geometry pipeline for BIM models.
It converts IFC files into a GPU-friendly binary format built on FlatBuffers
and THREE.InstancedMesh, enabling fast rendering of large models with millions
of elements. The @thatopen/fragments package provides the binary format,
worker architecture, and core operations. FragmentsManager in
@thatopen/components orchestrates model lifecycle, raycasting, data queries,
and coordinate alignment.
Pipeline:
IFC File → web-ifc (WASM) → IfcLoader → FragmentsModel (binary .frag)
│
┌─────────┴─────────┐
│ Fragment[] │
│ (InstancedMesh) │
└────────────────────┘
Convert once, reload fast: ALWAYS convert IFC to fragments once via
IfcLoader, then store the binary .frag file. Subsequent loads skip WASM
parsing entirely and load the pre-converted fragment binary directly.
Critical Warnings
-
ALWAYS call FragmentsManager.init(workerURL) before ANY fragment
operation. The worker handles raycasting, data queries, and model loading
off the main thread. Omitting this causes silent failures or crashes.
-
ALWAYS dispose models via FragmentsManager.disposeModel(modelId) or
components.dispose() when done. Fragment models hold GPU buffers
(InstancedMesh geometry, textures) and worker state. Undisposed models
cause memory leaks that crash browser tabs.
-
NEVER skip coordinate alignment in multi-model scenarios. Models from
different origins will appear scattered in 3D space. Use
applyBaseCoordinateSystem() to align them to a common origin.
-
ALWAYS match the worker.mjs URL to your installed @thatopen/fragments
version. A version mismatch between the main-thread library and the
worker script causes deserialization failures.
Core Concepts
Fragment Binary Format
Fragments use Google FlatBuffers for zero-copy binary serialization:
| Layer | Content |
|---|
FragmentGroup (root) | Coordination matrix, IFC metadata, array of Fragments |
Fragment | ID, type (Mesh/InstancedMesh/Point/Line), geometry, transforms, colors |
Geometry | Position/normal/index arrays, groups, bounding box |
Transform | 4x4 matrix, local ID, express ID per instance |
File identifier: FRAG. Dependencies: flatbuffers, pako (compression),
earcut (triangulation).
Key advantage: Geometry arrays (position, index) map directly to GPU
buffers as zero-copy typed array views. No deserialization step.
GPU Instancing
Each Fragment wraps a THREE.InstancedMesh. Identical geometries (e.g., all
doors of the same type) share one GPU geometry buffer with per-instance
transform matrices. This reduces draw calls from thousands to dozens.
FragmentsModel
A FragmentsModel represents one loaded BIM model. It contains:
- An array of
Fragment objects (instanced meshes)
- The coordination matrix (world positioning)
- IFC metadata and property data
- GUID-to-localID mappings
Access loaded models via FragmentsManager.list: Map<string, FragmentsModel>.
FragmentsManager API
class FragmentsManager extends Component implements Disposable {
list: Map<string, FragmentsModel>;
initialized: boolean;
baseCoordinationModel: string;
baseCoordinationMatrix: THREE.Matrix4;
init(workerURL: string, options?): void;
raycast(config: {
camera: THREE.Camera,
mouse: THREE.Vector2,
dom: HTMLElement,
snappingClasses?: number[]
}): Promise<Result | undefined>;
highlight(style: MaterialDefinition, items?: ModelIdMap): Promise<void>;
resetHighlight(items?: ModelIdMap): Promise<>;
(: , config?): <<, []>>;
(: ): <.[]>;
(: ): <.[]>;
(: <>): <>;
(: ): <[]>;
(: ., ?: .): .;
(: ): ;
}
Events:
onFragmentsLoaded: Event<FragmentsModel> — fires after a model is loaded
onBeforeDispose: Event<FragmentsModel> — fires before model disposal
onDisposed: Event<void> — fires after FragmentsManager itself is disposed
ModelIdMap
The universal data structure for targeting items across models:
type ModelIdMap = Record<string, Set<number>>;
Used by: FragmentsManager, Hider, Classifier, BoundingBoxer, Highlighter,
and every component that operates on specific BIM elements.
ALWAYS use ModelIdMap to reference items. NEVER reference items by
expressID alone — expressIDs are only unique within a single model.
Worker Architecture
FragmentsManager offloads heavy operations (raycasting, data extraction,
model loading) to a dedicated web worker.
const fragments = components.get(OBC.FragmentsManager);
fragments.init("https://unpkg.com/@thatopen/fragments@3.3.6/dist/Worker/worker.mjs");
What runs in the worker:
- FlatBuffers deserialization
- Raycast intersection tests
- Property data extraction (getData)
- Position and bounding box calculations
- GUID-to-ID mapping
What stays on the main thread:
- THREE.InstancedMesh creation and scene graph management
- Highlight/resetHighlight (GPU material swaps)
- Coordinate alignment (matrix multiplication)
Coordinate Alignment
When loading multiple models, each may have a different world origin stored
in its coordination matrix.
fragments.baseCoordinationModel = firstModel.modelId;
fragments.baseCoordinationMatrix = firstModel.coordinationMatrix;
fragments.applyBaseCoordinateSystem(secondModel, secondModel.coordinationMatrix);
NEVER skip this step for multi-model federation. Models will appear at
wrong positions without alignment.
Data Operations
getData: Extract IFC Properties
const items: ModelIdMap = { [model.modelId]: new Set([42, 43, 44]) };
const data = await fragments.getData(items);
getPositions: Get 3D Coordinates
const positions = await fragments.getPositions(items);
getBBoxes: Get Bounding Boxes
const boxes = await fragments.getBBoxes(items);
GUID Mapping
Convert between IFC GlobalId (GUID) strings and ModelIdMap:
const items = await fragments.guidsToModelIdMap(["2O2Fr$t4X7Zf8NOew3FLOH"]);
const guids = await fragments.modelIdMapToGuids(items);
Highlight and Raycast
Raycasting
const result = await fragments.raycast({
camera: world.camera.three,
mouse: new THREE.Vector2(normalizedX, normalizedY),
dom: renderer.three.domElement,
snappingClasses: [IFCWALL, IFCSLAB]
});
if (result) {
console.log(result.modelId, result.localId, result.point);
}
Highlighting
const style: MaterialDefinition = {
color: new THREE.Color("#BCF124"),
opacity: 0.6
};
await fragments.highlight(style, items);
await fragments.resetHighlight(items);
IFC-to-Fragment Pipeline
The recommended workflow for production:
- First time: Convert IFC via IfcLoader, export binary
- Subsequent loads: Load the binary directly (10-100x faster)
const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup();
const model = await ifcLoader.load(ifcBytes, true, "MyBuilding");
Dependencies
| Package | Purpose |
|---|
@thatopen/fragments | Core fragment engine, FlatBuffers format, worker |
flatbuffers | Binary serialization (zero-copy reads) |
pako | Compression/decompression of fragment binaries |
earcut | Polygon triangulation for 2D profiles |
three (>=0.175) | 3D rendering, InstancedMesh, scene graph |
web-ifc (>=0.0.74) | IFC parsing (used by IfcLoader, not fragments directly) |
Quick Reference
| Task | Method |
|---|
| Initialize worker | fragments.init(workerURL) |
| Get loaded models | fragments.list |
| Raycast scene | fragments.raycast({camera, mouse, dom}) |
| Get element properties | fragments.getData(items) |
| Get element positions | fragments.getPositions(items) |
| Get bounding boxes | fragments.getBBoxes(items) |
| GUID to items | fragments.guidsToModelIdMap(guids) |
| Items to GUIDs | fragments.modelIdMapToGuids(items) |
| Highlight elements | fragments.highlight(style, items) |
| Reset highlights | fragments.resetHighlight(items) |
| Align coordinates | fragments.applyBaseCoordinateSystem(obj, matrix) |
| Dispose a model | fragments.disposeModel(modelId) |
| Dispose everything | components.dispose() |
Related Skills
thatopen-core-architecture — Component system, world setup, lifecycle
thatopen-syntax-ifc-loading — IfcLoader configuration and WASM setup
thatopen-syntax-properties — Deep property extraction from IFC data
thatopen-impl-viewer — Full viewer setup including fragments initialization
References