AgentStack
SKILL verified MIT Self-run

Crosstech Impl Bim Web Viewer

skill-impertio-studio-cross-tech-aec-claude-skill-package-crosstech-impl-bim-web-viewer · by Impertio-Studio

>

No reviews yet
0 installs
12 views
0.0% view→install

Install

$ agentstack add skill-impertio-studio-cross-tech-aec-claude-skill-package-crosstech-impl-bim-web-viewer

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Crosstech Impl Bim Web Viewer? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

crosstech-impl-bim-web-viewer

Technology Boundary

| Aspect | Side A: BIM/IFC Data | Side B: Web Browser | |--------|---------------------|---------------------| | Format | IFC STEP file (.ifc) — IFC4 / IFC4X3 | WebGL rendering via Three.js | | Coordinates | Z-up (IFC standard) | Y-up (Three.js / WebGL) | | Data model | EXPRESS schema, entity graph | JavaScript objects, typed arrays | | Units | Millimeters or meters (per IfcProject) | Three.js scene units (unitless) | | Size limit | No limit (files reach 500MB–1GB) | ~2GB WASM memory, ~4GB tab total |

The Bridge: @thatopen/components (v3.x) — converts IFC to Fragments format via web-ifc WASM, then renders through Three.js. Handles coordinate transformation, geometry tessellation, and entity-to-mesh mapping automatically.

Directionality: IFC → Web browser (one-way rendering + interactive inspection). Writing IFC from the browser is NOT covered by this skill.


Decision Tree: Which Approach?

Need a BIM web viewer?
├── Using standard BIM features (navigate, select, clip, inspect)?
│   └── YES → Use @thatopen/components (Option A)
├── Integrating into existing Three.js app with custom rendering?
│   └── YES → Use custom web-ifc + Three.js pipeline (Option B)
├── Need server-side IFC processing (validation, authoring)?
│   └── YES → Use IfcOpenShell on backend + web-ifc on frontend (Hybrid)
└── File size > 200MB?
    └── YES → ALWAYS pre-convert to Fragments on server (see Large Model Handling)

Option A: @thatopen/components Viewer (Recommended)

Installation

npm install @thatopen/components @thatopen/components-front @thatopen/fragments three web-ifc

CRITICAL: The Three.js version MUST match the version pinned by @thatopen/components. Run npm ls three and verify only ONE version is installed. Mismatched versions cause silent rendering failures.

Complete Minimal Viewer

import * as OBC from "@thatopen/components";
import * as OBCF from "@thatopen/components-front";
import * as THREE from "three";

// 1. Initialize component system
const components = new OBC.Components();

// 2. Create world (scene + camera + renderer)
const worlds = components.get(OBC.Worlds);
const world = worlds.create();

// 3. Attach to DOM
const container = document.getElementById("viewer")!;
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.SimpleCamera(components);
world.scene = new OBC.SimpleScene(components);

// 4. Configure IFC loader with WASM path
const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup({
  autoSetWasm: false,
  wasm: { path: "https://unpkg.com/web-ifc@0.0.77/", absolute: true },
});

// 5. Setup FragmentsManager — handles model lifecycle
const fragments = components.get(OBC.FragmentsManager);
fragments.list.onItemSet.add(({ value: model }) => {
  model.useCamera(world.camera.three);
  world.scene.three.add(model.object);
  fragments.core.update(true);
});

// 6. Add interaction components
const highlighter = components.get(OBCF.Highlighter);
highlighter.multiple = "ctrlKey";
highlighter.zoomToSelection = true;

const hider = components.get(OBC.Hider);

// 7. Load IFC file
const response = await fetch("/models/building.ifc");
const data = new Uint8Array(await response.arrayBuffer());
await ifcLoader.load(data, false, "my-model");

Key Components Reference

| Component | Package | Purpose | |-----------|---------|---------| | OBC.IfcLoader | components | IFC → Fragments conversion and loading | | OBC.FragmentsManager | components | Model lifecycle, Fragment caching, disposal | | OBC.Hider | components | Hide, show, isolate elements by ID or category | | OBCF.Highlighter | components-front | Selection highlighting via raycasting | | OBCF.Clipper | components-front | Section planes through models |

Selection and Property Inspection

// Highlight on click — returns selected element IDs
const highlighter = components.get(OBCF.Highlighter);
await highlighter.highlight("select");

// Highlight by EXPRESS ID programmatically
const idMap: OBC.ModelIdMap = {};
idMap[model.modelId] = new Set([101, 102, 103]);
await highlighter.highlightByID("select", idMap);

// Clear selection
await highlighter.clear("select");

Hide / Isolate Elements

const hider = components.get(OBC.Hider);

// Isolate walls only
const wallIds = model.getItemsOfCategories(["IFCWALL"]);
const wallMap: OBC.ModelIdMap = {};
wallMap[model.modelId] = wallIds;
await hider.isolate(wallMap);

// Show everything again
await hider.set(true);

Z-Fighting Prevention

ALWAYS apply polygon offset to Fragment materials to prevent z-fighting on coplanar BIM faces:

fragments.core.models.materials.list.onItemSet.add(({ value: material }) => {
  material.polygonOffset = true;
  material.polygonOffsetUnits = 1;
  material.polygonOffsetFactor = Math.random();
});

Option B: Custom web-ifc + Three.js Pipeline

Use this ONLY when integrating BIM data into an existing Three.js application where @thatopen/components cannot be adopted.

Core Pipeline

import * as THREE from "three";
import { IfcAPI } from "web-ifc";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

// Initialize web-ifc
const ifcApi = new IfcAPI();
ifcApi.SetWasmPath("/static/wasm/");  // MUST be called BEFORE Init()
await ifcApi.Init();

// Load IFC file
const ifcData = new Uint8Array(await fetch("model.ifc").then(r => r.arrayBuffer()));
const modelID = ifcApi.OpenModel(ifcData, { COORDINATE_TO_ORIGIN: true });

// Store expressID → mesh mapping for selection
const expressIdToMesh = new Map();

// Stream geometry and convert to Three.js meshes
ifcApi.StreamAllMeshes(modelID, (flatMesh) => {
  const geometries = flatMesh.geometries;
  for (let i = 0; i  {
  const mouse = new THREE.Vector2(
    (event.clientX / window.innerWidth) * 2 - 1,
    -(event.clientY / window.innerHeight) * 2 + 1
  );
  raycaster.setFromCamera(mouse, camera);
  const hits = raycaster.intersectObjects(scene.children);
  if (hits.length > 0) {
    const expressID = hits[0].object.userData.expressID;
    const entity = ifcApi.GetLine(modelID, expressID, true);  // flatten=true
    displayProperties(entity);
  }
});

Section Planes (Custom)

const clipPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 5);
renderer.clippingPlanes = [clipPlane];
renderer.localClippingEnabled = true;

// Update plane position interactively
function setClipHeight(y: number) {
  clipPlane.constant = y;
}

Coordinate System Rules

| Source | Coordinate System | Action Required | |--------|-------------------|-----------------| | web-ifc geometry output | Y-up (transformed internally) | NONE — render directly | | IfcOpenShell geometry | Z-up (IFC native) | ALWAYS rotate: matrix.makeRotationX(-Math.PI / 2) | | COORDINATE_TO_ORIGIN: true | Centered at origin | Real-world coordinates are LOST — store separately if needed | | COORDINATE_TO_ORIGIN: false | Real-world offset | Model may appear far from origin — camera setup required |


Large Model Handling (>100MB IFC)

Memory Constraints

| Constraint | Limit | |-----------|-------| | WASM linear memory | ~2GB practical maximum | | Browser tab total | 2–4GB before crash | | 8GB RAM laptop | ~1.7GB practical tab limit |

Performance Strategy

  1. ALWAYS pre-convert to Fragments on server for files >200MB. First-load IFC parsing is the bottleneck; cached Fragments load near-instantly.
  2. ALWAYS use StreamAllMeshes instead of LoadAllGeometry. Streaming processes geometry incrementally without peak memory spike.
  3. ALWAYS call ifcApi.CloseModel(modelID) when done to free WASM memory.
  4. Configure conservative memory limits:
const modelID = ifcApi.OpenModel(data, {
  MEMORY_LIMIT: 1073741824,  // 1GB — safe for most browsers
  TAPE_SIZE: 33554432,       // 32MB read buffer
});
  1. Use geometry instancing for repeated elements (identical windows, doors). web-ifc provides shared geometryExpressID values that indicate reusable geometry.
  2. Export Fragments for caching:
const fragsBuffer = await model.getBuffer(false);
// Store on server or in IndexedDB for instant reload

The Fragments Format

IFC files are NEVER rendered directly. The pipeline is:

IFC file → web-ifc WASM parsing → Fragment conversion → Three.js rendering
  • First load: slow (parsing + tessellation + conversion)
  • Subsequent loads from cached .frag files: near-instant
  • Fragments use Google Flatbuffers — 5-10x smaller than source IFC
  • ALWAYS cache Fragments server-side for production applications

Property Access Patterns

| Operation | @thatopen/components | Raw web-ifc | |-----------|---------------------|-------------| | Get by type | model.getItemsOfCategories(["IFCWALL"]) | ifcApi.GetLineIDsWithType(modelID, IFCWALL) | | Get entity | Via FragmentsManager | ifcApi.GetLine(modelID, expressID, true) | | Property sets | ifcApi.properties.getPropertySets(modelID, id, true) | Same | | Spatial structure | ifcApi.properties.getSpatialStructure(modelID) | Same | | Access name | entity.Name.value | entity.Name.value |

CRITICAL: web-ifc wraps string/numeric values in { value: ... } containers. ALWAYS access .value for the actual data. References are Handle objects unless flatten=true is passed to GetLine.


Critical Warnings

NEVER set the WASM path after calling ifcApi.Init() — the WASM module loads during Init() and path changes after that point have no effect.

NEVER use LoadAllGeometry for models with more than 10,000 elements — use StreamAllMeshes to avoid memory spikes.

NEVER mix Three.js versions — @thatopen/components pins a specific Three.js version. A second version in node_modules causes silent rendering failures.

NEVER manually rotate web-ifc output by -90 degrees — web-ifc ALREADY transforms IFC Z-up coordinates to Three.js Y-up internally.

ALWAYS call CloseModel(modelID) when disposing a model to free WASM memory.

ALWAYS verify the WASM path resolves correctly in your bundler (Webpack/Vite) — .wasm files MUST be copied to the output directory.


Reference Links

  • [references/methods.md](references/methods.md) — Complete API signatures for @thatopen/components and web-ifc viewer methods
  • [references/examples.md](references/examples.md) — Working code examples for common viewer features
  • [references/anti-patterns.md](references/anti-patterns.md) — What NOT to do when building BIM web viewers

Official Sources

  • https://github.com/ThatOpen/engine_web-ifc (web-ifc v0.0.77)
  • https://github.com/ThatOpen/engine_components (@thatopen/components v3.3.2)
  • https://docs.thatopen.com/Tutorials/Components/Core/IfcLoader
  • https://docs.thatopen.com/api/@thatopen/components-front/classes/Highlighter
  • https://threejs.org/docs/

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.