Skip to main content

HelloWorldSchema

Source

Copying and pasting? We've got you covered! You can find the full source code of this tutorial here.

Hello World Schema 📋


Developers who want to understand or debug how an IFC file maps to the Fragment binary format have no way to inspect the internal schema without writing custom parsing code or reading raw binary data by hand. The Fragment format is built on a FlatBuffers schema, and the library exposes utilities to deserialize any loaded model back into a plain JavaScript object that mirrors that schema — making the internal structure readable without external tooling. This tutorial covers converting an IFC file to Fragment bytes using the importer; loading the result into a 3D scene; deserializing the raw FlatBuffers data into a plain object; displaying the full schema tree in an interactive JSON viewer; wiring expand/collapse controls; loading from a local file picker or a sample wall model; and downloading the converted Fragment file. By the end, you'll have an IFC-to-Fragment schema explorer that lets you load any IFC file, visualize it in the viewport, and inspect its full Fragment binary structure as a browsable JSON tree.

🖖 Importing our Libraries

First things first, let's install all necessary dependencies to make this little example work:

npm install @thatopen/components @thatopen/ui @andypf/json-viewer stats.js pako flatbuffers three @thatopen/fragments @thatopen/components @thatopen/ui web-ifc
import * as FB from "flatbuffers";
import pako from "pako";
import "@andypf/json-viewer";
import * as OBC from "@thatopen/components";
import * as THREE from "three";
import Stats from "stats.js";
import * as BUI from "@thatopen/ui";
import * as FRAGS from "../../../../index";

🌎 Setting up a Simple Scene

Now we will create a simple scene with a camera, a renderer, and a world, as well as add some stats to keep an eye on the performance:

const components = new OBC.Components();
const worlds = components.get(OBC.Worlds);
const container = document.getElementById("viewer") as HTMLDivElement;

const world = worlds.create<
OBC.SimpleScene,
OBC.SimpleCamera,
OBC.SimpleRenderer
>();

world.scene = new OBC.SimpleScene(components);
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);

components.init();

world.scene.setup();

const stats = new Stats();
stats.showPanel(2);
document.body.append(stats.dom);
stats.dom.style.left = "0px";
stats.dom.style.zIndex = "unset";
world.renderer.onBeforeUpdate.add(() => stats.begin());
world.renderer.onAfterUpdate.add(() => stats.end());

🔥 Setting up fragments

Now we will set up Fragments for this app. If you are not familiar with Fragments, you can check out the other Fragments related tutorails in this documentation.

// `FragmentsModels.getWorker()` fetches the matching worker for this library version from unpkg and returns a blob URL.
// You can also pass your own URL to `new FragmentsModels(...)` if you'd rather host the worker yourself.
const workerUrl = await FRAGS.FragmentsModels.getWorker();
const fragments = new FRAGS.FragmentsModels(workerUrl);
world.camera.controls.addEventListener("control", () => fragments.update());

// Remove z fighting
fragments.models.materials.list.onItemSet.add(({ value: material }) => {
if (!("isLodMaterial" in material && material.isLodMaterial)) {
material.polygonOffset = true;
material.polygonOffsetUnits = 1;
material.polygonOffsetFactor = Math.random();
}
});

💅🏻 Setting up the UI

Now we will create a simple UI to display the schema:

BUI.Manager.init();

const jsonViewer = document.getElementById("json-viewer") as any;

const expandBtn = document.getElementById("expand-btn")!;
expandBtn.addEventListener("click", () => {
jsonViewer.expanded = true;
});

const collapseBtn = document.getElementById("collapse-btn")!;
collapseBtn.addEventListener("click", () => {
jsonViewer.expanded = false;
});

const loadFileBtn = document.getElementById("load-file-btn")!;
const loadWallBtn = document.getElementById("load-wall-btn")!;
const downloadBtn = document.getElementById("download-btn")!;

👾 Loading an IFC and extracting the schema

Now we will convert an IFC file to a Fragments file, load it in the scene, and add extract its schema to display it in the screen.

let model: FRAGS.FragmentsModel | null = null;
let result = null as any;

async function loadIfcFile(fileUrl: string, raw: boolean) {
// If there is a previous model, dispose it
if (model) {
model.dispose();
}

// Load the model

const ifcFile = await fetch(fileUrl);
const ifcBuffer = await ifcFile.arrayBuffer();
const typedArray = new Uint8Array(ifcBuffer);
const serializer = new FRAGS.IfcImporter();
serializer.wasm = {
absolute: true,
path: "https://unpkg.com/web-ifc@0.0.77/",
};

const bytes = await serializer.process({ bytes: typedArray, raw: true });

model = await fragments.load(bytes, {
modelId: performance.now().toString(),
camera: world.camera.three,
raw: true,
});

world.scene.three.add(model.object);
await fragments.update(true);

// Extract the schema
const byteBuffer = new FB.ByteBuffer(raw ? bytes : pako.inflate(bytes));
const readModel = FRAGS.Model.getRootAsModel(byteBuffer);
result = {};
FRAGS.getObject(readModel, result);

// Display the schema in the screen
jsonViewer.data = {};
jsonViewer.data = result;

// Update the viewer
window.dispatchEvent(new Event("resize"));
}

Finally, we just need to bind the UI to the loading logic:

loadFileBtn.addEventListener("click", () => {
const input = document.createElement("input");
input.type = "file";
input.accept = ".ifc";
input.onchange = (event) => {
const file = (event.target as HTMLInputElement).files?.[0];
if (file) {
const url = URL.createObjectURL(file);
loadIfcFile(url, true);
URL.revokeObjectURL(url);
}
};
input.click();
});

loadWallBtn.addEventListener("click", () => {
const url =
"https://thatopen.github.io/engine_fragment/resources/ifc/just_wall.ifc";
loadIfcFile(url, true);
});

downloadBtn.addEventListener("click", async () => {
if (!model) return;
const bytes = await model.getBuffer(true);
const a = document.createElement("a");
const file = new File([bytes], "small_test.frag");
const url = URL.createObjectURL(file);
a.href = url;
a.download = file.name;
a.click();
URL.revokeObjectURL(url);
});

🥳 Congratulations!

You have created an app that can extract Fragment schemas from IFC files. This should be useful for understanding how the fragment schema works and build your custom tools on top of Fragments!