# Crosstech Impl Bim Web Viewer

> >

- **Type:** Skill
- **Install:** `agentstack add skill-impertio-studio-cross-tech-aec-claude-skill-package-crosstech-impl-bim-web-viewer`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Impertio-Studio](https://agentstack.voostack.com/s/impertio-studio)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** https://github.com/Impertio-Studio/Cross-Tech-AEC-Claude-Skill-Package/tree/main/skills/source/crosstech-impl/crosstech-impl-bim-web-viewer

## Install

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

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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

```bash
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

```typescript
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

```typescript
// 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

```typescript
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:

```typescript
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

```typescript
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)

```typescript
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**:

```typescript
const modelID = ifcApi.OpenModel(data, {
  MEMORY_LIMIT: 1073741824,  // 1GB — safe for most browsers
  TAPE_SIZE: 33554432,       // 32MB read buffer
});
```

5. **Use geometry instancing** for repeated elements (identical windows, doors). web-ifc provides shared `geometryExpressID` values that indicate reusable geometry.
6. **Export Fragments for caching**:

```typescript
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.

- **Author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** [Impertio-Studio/Cross-Tech-AEC-Claude-Skill-Package](https://github.com/Impertio-Studio/Cross-Tech-AEC-Claude-Skill-Package)
- **License:** MIT

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

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-impertio-studio-cross-tech-aec-claude-skill-package-crosstech-impl-bim-web-viewer
- Seller: https://agentstack.voostack.com/s/impertio-studio
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
