--- id: wiki-2026-0508-ifcjs title: IFCjs category: 10_Wiki/Topics status: verified canonical_id: self aliases: [That Open Engine, web-ifc, Three.js IFC, BIM viewer] duplicate_of: none source_trust_level: A confidence_score: 0.85 verification_status: applied tags: [bim, ifc, web, three-js, aec, webgl] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: web-ifc-three --- # IFCjs ## 매 한 줄 > **"매 browser 안의 IFC (Industry Foundation Classes) reader/viewer — 매 BIM 데이터를 매 Three.js scene 으로"**. IFCjs (현재 매 "That Open Engine" 으로 rebrand) 는 매 web-ifc (WASM IFC parser) + 매 Three.js based 매 viewer 의 묶음. 매 AEC (Architecture/Engineering/Construction) 매 web 진입의 standard. 매 2026 현재 매 OpenBIM 운동 의 매 핵심 component. ## 매 핵심 ### 매 stack 의 layer - **web-ifc (C++ → WASM)**: 매 IFC2x3 / IFC4 / IFC4x3 STEP file 의 매 streaming parser. - **web-ifc-three** (legacy): 매 Three.js mesh 로 변환, property set 추출. - **@thatopen/components**: 매 modern (2024+) — Three.js 위 UI/tool framework. - **@thatopen/ui**: 매 web-component 기반 panel/grid/property card. ### 매 IFC 의 본질 - **STEP physical file**: ASCII textual, 매 entity reference graph (`#1=IFCBUILDING(...)`). - **EXPRESS schema**: 매 IFC4 는 매 1700+ entity types. - **Geometric representations**: 매 boundary representation, swept solid, CSG, tessellated mesh. - **Property sets (Psets)**: 매 entity 별 매 metadata bag. ### 매 web vs desktop trade-off - 매 desktop (Revit, ArchiCAD): full editing, plugin ecosystem. - 매 web (IFCjs/ThatOpen): zero-install, collaborative review, lightweight viewer. - 매 hybrid: 매 export IFC from Revit → web viewer. ## 💻 패턴 ### Basic IFC viewer (ThatOpen Components 2024+) ```ts import * as OBC from '@thatopen/components'; import * as THREE from 'three'; const components = new OBC.Components(); const worlds = components.get(OBC.Worlds); const world = worlds.create< OBC.SimpleScene, OBC.SimpleCamera, OBC.SimpleRenderer >(); const container = document.getElementById('app')!; world.scene = new OBC.SimpleScene(components); world.renderer = new OBC.SimpleRenderer(components, container); world.camera = new OBC.SimpleCamera(components); world.scene.setup(); components.init(); const fragments = components.get(OBC.FragmentsManager); const ifcLoader = components.get(OBC.IfcLoader); await ifcLoader.setup(); const file = await fetch('/models/building.ifc'); const buffer = new Uint8Array(await file.arrayBuffer()); const model = await ifcLoader.load(buffer); world.scene.three.add(model); ``` ### Property extraction via web-ifc directly ```ts import { IfcAPI, IFCWALLSTANDARDCASE } from 'web-ifc'; const api = new IfcAPI(); api.SetWasmPath('/wasm/'); await api.Init(); const data = new Uint8Array(await fetch('/m.ifc').then(r => r.arrayBuffer())); const modelID = api.OpenModel(data); const wallIDs = api.GetLineIDsWithType(modelID, IFCWALLSTANDARDCASE); for (let i = 0; i < wallIDs.size(); i++) { const id = wallIDs.get(i); const wall = api.GetLine(modelID, id, true); // recursive expand refs console.log(wall.GlobalId.value, wall.Name?.value); } api.CloseModel(modelID); ``` ### Picking + property panel ```ts const highlighter = components.get(OBC.Highlighter); highlighter.setup({ world }); highlighter.events.select.onHighlight.add(async (fragmentMap) => { const indexer = components.get(OBC.IfcRelationsIndexer); for (const fragId in fragmentMap) { const expressIDs = [...fragmentMap[fragId]]; for (const id of expressIDs) { const psets = await indexer.getEntityRelations(model, id, 'IsDefinedBy'); console.log('expressID', id, 'psets', psets); } } }); ``` ### Convert IFC → fragments (compact binary) ```ts // fragments are ThatOpen's optimized binary mesh format const fragmentManager = components.get(OBC.FragmentsManager); const buffer = fragmentManager.export(model); await fetch('/upload', { method: 'POST', body: buffer }); ``` ### Server-side conversion (Node + web-ifc-node) ```ts import { IfcAPI } from 'web-ifc/web-ifc-api-node'; import { promises as fs } from 'fs'; const api = new IfcAPI(); api.SetWasmPath('node_modules/web-ifc/'); await api.Init(); const buf = await fs.readFile('input.ifc'); const id = api.OpenModel(buf); const flatMesh = api.LoadAllGeometry(id); // ... extract triangles, write to glTF ``` ### BIM clash detection (rough proxy) ```ts import { Box3, Vector3 } from 'three'; function findClashes(meshes: THREE.Mesh[]) { const boxes = meshes.map(m => { const b = new Box3().setFromObject(m); return { box: b, mesh: m }; }); const clashes: [THREE.Mesh, THREE.Mesh][] = []; for (let i = 0; i < boxes.length; i++) for (let j = i + 1; j < boxes.length; j++) if (boxes[i].box.intersectsBox(boxes[j].box)) clashes.push([boxes[i].mesh, boxes[j].mesh]); return clashes; } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Web-only IFC viewer | ThatOpen Components | | Server-side IFC parsing | web-ifc-node | | Property extraction only | web-ifc API directly | | Heavy editing | desktop (Revit) export | | Massive models (>1GB) | fragments format + tile streaming | | Clash detection on web | use AABB pre-filter + GPU mesh-mesh | **기본값**: 매 modern AEC web app — ThatOpen Components + fragments. ## 🔗 Graph - 응용: [[Digital Twin]] - Adjacent: [[Three.js]] ## 🤖 LLM 활용 **언제**: IFC entity 의 mapping 설명 (IFC → glTF), property set 매 자연어 query, 매 UI panel scaffold. **언제 X**: 매 large IFC parsing performance 최적화 — 매 measure 매 직접. ## ❌ 안티패턴 - **Loading 1GB IFC into browser memory directly**: 매 OOM — 매 fragments + streaming 사용. - **Recursive GetLine on every entity**: 매 N² — 매 IfcRelationsIndexer 사용. - **Treating IFC as glTF**: 매 IFC 는 매 graph + semantics, 매 mesh-only X. - **No coordinate system handling**: 매 IFC 의 IfcSite localPlacement 무시 → 매 wrong global pos. - **Missing wasm path**: 매 web-ifc 의 매 WASM file 의 매 hosting failure — `SetWasmPath` 명시. ## 🧪 검증 / 중복 - Verified (ThatOpen Engine docs 2026, web-ifc GitHub). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — IFC stack + ThatOpen Components patterns |