Load and create BCF topics
This page shows two viewer samples. The first sample loads one NBU Medical Clinic IFC model, loads an existing .bcf file, and opens one BCF topic. The second sample creates a BCF topic with a viewpoint using the ThatOpen managers exposed by the SDK, loads the generated .bcf bytes, and opens the created topic.
Load the IFC model before loading the BCF file, so topic viewpoints and component state can resolve against the model. Call viewer.select(topicGuid) only after the .bcf bytes have been added. When you generate BCF data yourself, export the BCF bytes and pass them to viewer.add() with a .bcf filename.
Load an existing BCF file
This sample loads the NBU Medical Clinic architecture IFC model, adds an existing .bcf file, and opens one coordination topic. The selected topic GUID comes from the BCF file; in an app, this value usually comes from the imported topic list.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; display: flex; flex-direction: column; }
bim-grid { flex: 1; }
</style>
</head>
<body>
<bim-grid id="ifc-viewer"></bim-grid>
<script type="module">
import { IfcViewer } from "https://cdn.flinker.app/ifc-viewer/v3.2.0/ifc-viewer.es.js";
const viewer = new IfcViewer("#ifc-viewer");
await viewer.ready;
const base = "https://raw.githubusercontent.com/flinker-app/ifc-sample-files/main/NBU_MedicalClinic/";
const ifcBytes = new Uint8Array(await (await fetch(base + "NBU_MedicalClinic_Arch-Optimized.ifc")).arrayBuffer());
await viewer.add("NBU_MedicalClinic_Arch-Optimized.ifc", ifcBytes);
const sourceBcfName = "NBU_MedicalClinic_BCF_Coordination_Sample.bcf";
const bcfBytes = new Uint8Array(await (await fetch(base + sourceBcfName)).arrayBuffer());
await viewer.add("coordination-sample.bcf", bcfBytes);
await viewer.select("1530757a-ae02-40df-a088-d5c1996ab010");
</script>
</body>
</html>
Create a BCF topic
This sample loads the NBU Medical Clinic architecture IFC model, creates a BCF topic in the browser with viewer.components and viewer.OBC, adds the generated .bcf bytes to the viewer, and opens the created topic.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; display: flex; flex-direction: column; }
bim-grid { flex: 1; }
</style>
</head>
<body>
<bim-grid id="ifc-viewer"></bim-grid>
<script type="module">
import { IfcViewer } from "https://cdn.flinker.app/ifc-viewer/v3.2.0/ifc-viewer.es.js";
const viewer = new IfcViewer("#ifc-viewer");
await viewer.ready;
await loadModel(
"NBU_MedicalClinic_Arch-Optimized.ifc",
"https://raw.githubusercontent.com/flinker-app/ifc-sample-files/main/NBU_MedicalClinic/NBU_MedicalClinic_Arch-Optimized.ifc"
);
const { bytes, topicGuid } = await createBCF("Clinic model overview");
await viewer.add("clinic-model-overview.bcf", bytes);
await viewer.select(topicGuid);
async function loadModel(name, url) {
const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
await viewer.add(name, bytes.slice());
return { name, bytes };
}
async function createBCF(title) {
const topics = viewer.components.get(viewer.OBC.BCFTopics);
const viewpoints = viewer.components.get(viewer.OBC.Viewpoints);
setupTopics(topics);
if (!viewpoints.isSetup) viewpoints.setup();
await frameModel();
const viewpoint = viewpoints.create();
viewpoint.world = viewer.world;
await viewpoint.updateCamera(false);
viewpoint.snapshot = "snapshot";
viewpoints.snapshots.set(viewpoint.snapshot, snapshot());
const topic = topics.create({ title, type: "Coordination", status: "Open" });
topic.viewpoints.add(viewpoint.guid);
try {
const blob = await topics.export([topic]);
return { topicGuid: topic.guid, bytes: new Uint8Array(await blob.arrayBuffer()) };
} finally {
topics.list.delete(topic.guid);
viewpoints.list.delete(viewpoint.guid);
viewpoints.snapshots.delete(viewpoint.snapshot);
}
}
function setupTopics(topics) {
if (!topics.isSetup) {
topics.setup({
version: "3",
author: "docs@example.com",
types: new Set(["Coordination"]),
statuses: new Set(["Open"])
});
}
topics.config.version = "3";
topics.config.author = "docs@example.com";
topics.config.types.add("Coordination");
topics.config.statuses.add("Open");
topics.config.includeSelectionTag = true;
}
async function frameModel() {
const box = await loadedModelBox();
const center = box.getCenter(new viewer.THREE.Vector3());
const direction = new viewer.THREE.Vector3(-0.58, -0.58, -0.58).normalize();
const position = center.clone().add(direction.multiplyScalar(-80));
viewer.world.camera.projection.set("Perspective");
await viewer.world.camera.controls.setLookAt(position.x, position.y, position.z, center.x, center.y, center.z, false);
viewer.world.camera.controls.update(0);
}
async function loadedModelBox() {
const box = new viewer.THREE.Box3();
for (const model of viewer.fragments.core.models.list.values()) {
const modelBox = await model.box;
if (modelBox && !modelBox.isEmpty()) box.union(modelBox);
}
return box;
}
function snapshot() {
return Uint8Array.from(
atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="),
char => char.charCodeAt(0)
);
}
</script>
</body>
</html>