threejs-impl-ifc-viewer について
Three.jsでIFC解析やBIMビューアを実装するためのリファレンススキルです。低レベルのweb-ifc APIと高レベルの@thatopen/componentsを比較し、形状・属性・メモリ管理の手法を解説します。
主な機能
- 01web-ifcと@thatopen/componentsの比較
- 02IFC形状・属性の抽出方法
- 03BIMビューアのコンポーネント利用法
- 04大規模IFCモデルのメモリ管理
- 05非推奨ライブラリとライセンスの注意点
活用シーン
- 01独自のThree.js処理でIFC形状を抽出
- 02要素選択や断面表示のあるBIMビューアを構築
- 03形状を描画せずにIFC要素の属性を取得
- 04大規模IFCファイルの分割読み込みを設計
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ impertio-studio/ three.js-claude-skill-package --skill threejs-impl-ifc-viewerインストール後、Claude Code で/threejs-impl-ifc-viewerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.gitkeepreferences/anti-patterns.mdexamples.mdmethods.md詳細
threejs-impl-ifc-viewer
クイックリファレンス
ライブラリの比較
| ライブラリ | ライセンス | レベル | ステータス | 用途 |
|---|---|---|---|---|
web-ifc | MIT | 低レベル WASM パーサー | アクティブ(v0.77+) | カスタムパイプライン、完全な制御 |
@thatopen/components | MIT(v3.x) | 高レベル BIM ツールキット | アクティブ(v3.3.2+) | 完全な BIM ビューアー、迅速なプロトタイピング |
@thatopen/components-front | MIT | ブラウザー固有の拡張機能 | アクティブ | UI、高度なビジュアライゼーション |
web-ifc-three | — | 非推奨のブリッジ | 終了 | 絶対に使用しないでください |
openbim-components (v1) | — | 非推奨のツールキット | 終了 | 絶対に使用しないでください |
パッケージのインストール
# Low-level approach (MIT, full control)
npm install web-ifc three
# High-level approach (MIT, batteries-included)
npm install @thatopen/components @thatopen/components-front web-ifc three
重要な警告
ライセンス履歴に関する警告:@thatopen パッケージ(v3.x)は MIT ライセンスです。ただし、以前の IFC.js エコシステムのパッケージ(openbim-components v1、IFC.js v0.x)には AGPL-3.0 が使用されていました。インストールするパッケージの正確なバージョンのライセンスを必ず確認してください。AGPL-3.0 を使用するパッケージを利用する場合、アプリケーション全体を AGPL-3.0 または互換性のあるライセンスで必ずオープンソース化する必要があります。
絶対に web-ifc-three を使用しないでください。非推奨で、メンテナンスされていません。代わりに web-ifc を直接使用するか、@thatopen/components を使用してください。
絶対に openbim-components (v1) を使用しないでください。非推奨です。代わりに @thatopen/components (v3.x) を使用してください。
50MB を超えるファイルの IFC ジオメトリを一度にすべて読み込まないでください。必ずストリーミングするか、必要に応じてジオメトリを読み込んでください。WASM メモリには制限があり、ページを再読み込みしない限り解放できません。
処理後に ifcApi.CloseModel(modelID) を呼び出し忘れないでください。WASM メモリリークは、ページを再読み込みするまで解消されません。
@thatopen ビューアーのマウント解除時には、必ず components.dispose() を呼び出してください。呼び出さないと GPU メモリ、Three.js オブジェクト、イベントリスナーがリークします。
必ず IfcAPI を非同期で初期化してください。モデル操作を行う前に await ifcApi.Init() を完了する必要があります。
アプローチ 1:web-ifc(低レベル WASM パーサー)
使用する場面
- ジオメトリの抽出とレンダリングを完全に制御する必要がある場合
- カスタムレンダリングパイプラインを構築する場合
- MIT ライセンスのコードのみを使用したい場合
- ジオメトリをレンダリングせずに IFC プロパティを抽出する必要がある場合
WASM のセットアップ
import * as WebIFC from 'web-ifc';
const ifcApi = new WebIFC.IfcAPI();
ifcApi.SetWasmPath('./'); // MUST point to directory containing web-ifc.wasm
await ifcApi.Init(); // ALWAYS await before any operations
WASM ファイルの要件:web-ifc.wasm ファイルは、ブラウザーからアクセスできる場所で提供する必要があります。node_modules/web-ifc/ からパブリック/静的ディレクトリにコピーしてください。Vite などのバンドラーでは、WASM ファイルを提供するための明示的な設定が必要です。
IfcAPI の主なメソッド
| メソッド | シグネチャ | 説明 |
|---|---|---|
Init() | () => Promise<void> | WASM モジュールを初期化します |
SetWasmPath(path) | (string) => void | .wasm ファイルを含むディレクトリを設定します |
OpenModel(data, settings?) | (Uint8Array, object?) => number | IFC データを読み込み、modelID を返します |
CloseModel(modelID) | (number) => void | モデルのメモリを解放します |
GetGeometry(modelID, expressID) | (number, number) => object | 要素のジオメトリを取得します |
GetFlatMesh(modelID, expressID) | (number, number) => FlatMesh | フラット化されたメッシュデータを取得します |
GetPlacedGeometry(modelID, pg) | (number, PlacedGeometry) => MeshData | 変換情報を含む配置済みジオメトリを取得します |
GetLine(modelID, expressID, flatten?) | (number, number, boolean?) => object | expressID で単一の IFC エンティティを読み取ります |
GetAllLines(modelID) | (number) => number[] | モデル内のすべての express ID を取得します |
GetLineIDsWithType(modelID, type) | (number, number) => number[] | IFC 型定数で ID を取得します |
GetAllTypesOfModel(modelID) | (number) => TypeInfo[] | モデル内のすべてのエンティティ型を一覧表示します |
ジオメトリ抽出のパターン
const modelID = ifcApi.OpenModel(ifcData); // ifcData: Uint8Array
const wallIDs = ifcApi.GetLineIDsWithType(modelID, WebIFC.IFCWALL);
for (const wallID of wallIDs) {
const flatMesh = ifcApi.GetFlatMesh(modelID, wallID);
for (const pg of flatMesh.geometries) {
const meshData = ifcApi.GetPlacedGeometry(modelID, pg);
// meshData.vertexData: Float32Array (interleaved position + normal, 6 floats per vertex)
// meshData.indexData: Uint32Array (triangle indices)
// pg.flatTransformation: Float64Array (4x4 column-major matrix)
// pg.color: { x: r, y: g, z: b, w: alpha } (0-1 range)
}
}
ifcApi.CloseModel(modelID); // ALWAYS release when done
web-ifc のジオメトリを Three.js に変換する
import * as THREE from 'three';
function ifcMeshToThree(meshData, placedGeometry) {
const { vertexData, indexData } = meshData;
// vertexData is interleaved: [px, py, pz, nx, ny, nz, px, py, pz, nx, ny, nz, ...]
const posFloats = new Float32Array(vertexData.length / 2);
const normFloats = new Float32Array(vertexData.length / 2);
for (let i = 0; i < vertexData.length; i += 6) {
const j = i / 2;
posFloats[j] = vertexData[i];
posFloats[j + 1] = vertexData[i + 1];
posFloats[j + 2] = vertexData[i + 2];
normFloats[j] = vertexData[i + 3];
normFloats[j + 1] = vertexData[i + 4];
normFloats[j + 2] = vertexData[i + 5];
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(posFloats, 3));
geometry.setAttribute('normal', new THREE.BufferAttribute(normFloats, 3));
geometry.setIndex(new THREE.BufferAttribute(indexData, 1));
const { x: r, y: g, z: b, w: a } = placedGeometry.color;
const material = new THREE.MeshPhongMaterial({
color: new THREE.Color(r, g, b),
opacity: a,
transparent: a < 1,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(geometry, material);
// Apply the 4x4 transform matrix (column-major)
const mat = new THREE.Matrix4();
mat.fromArray(placedGeometry.flatTransformation);
mesh.applyMatrix4(mat);
return mesh;
}
IFC プロパティの抽出
// Read a specific entity
const wall = ifcApi.GetLine(modelID, wallExpressID, true); // flatten=true resolves references
console.log(wall.Name?.value); // "Basic Wall:Generic - 200mm"
console.log(wall.GlobalId?.value); // IFC GUID
// Get all property sets for an element
const relDefines = ifcApi.GetLineIDsWithType(modelID, WebIFC.IFCRELDEFINESBYPROPERTIES);
for (const relID of relDefines) {
const rel = ifcApi.GetLine(modelID, relID, false);
// Check if this relation references our element
// rel.RelatedObjects contains expressIDs of related elements
// rel.RelatingPropertyDefinition points to the property set
}
IFC 型定数
| 定数 | IFC エンティティ |
|---|---|
WebIFC.IFCWALL | 壁 |
WebIFC.IFCWALLSTANDARDCASE | 標準的な壁 |
WebIFC.IFCSLAB | スラブ/床 |
WebIFC.IFCCOLUMN | 柱 |
WebIFC.IFCBEAM | 梁 |
WebIFC.IFCDOOR | ドア |
WebIFC.IFCWINDOW | 窓 |
WebIFC.IFCROOF | 屋根 |
WebIFC.IFCSTAIR | 階段 |
WebIFC.IFCSPACE | 空間/部屋 |
WebIFC.IFCSITE | 敷地 |
WebIFC.IFCBUILDING | 建物 |
WebIFC.IFCBUILDINGSTOREY | 階/階レベル |
WebIFC.IFCPROJECT | プロジェクトルート |
WebIFC.IFCRELDEFINESBYPROPERTIES | プロパティセットの関連付け |
WebIFC.IFCPROPERTYSET | プロパティセット |
WebIFC.IFCPROPERTYSINGLEVALUE | 個別のプロパティ値 |
アプローチ 2:@thatopen/components(高レベル BIM ツールキット)
使用する場面
- 高機能な BIM ビューアーをすばやく用意する必要がある
- 大規模モデル向けのフラグメント最適化機能が組み込まれているものを使いたい
- 要素のハイライト、断面平面、またはフロアプランが必要である
- MIT ライセンス(v3.x)がプロジェクトで許容される
アーキテクチャのセットアップ
import * as OBC from '@thatopen/components';
const components = new OBC.Components();
const worlds = components.get(OBC.Worlds);
const world = worlds.create();
// Scene
world.scene = new OBC.SimpleScene(components);
world.scene.setup(); // Adds default lights and grid
// Camera
world.camera = new OBC.SimpleCamera(components);
world.camera.controls.setLookAt(10, 10, 10, 0, 0, 0);
// Renderer (container is an HTMLDivElement)
world.renderer = new OBC.SimpleRenderer(components, container);
// ALWAYS call init after setup
components.init();
IFC ファイルの読み込み
const ifcLoader = components.get(OBC.IfcLoader);
await ifcLoader.setup(); // Downloads and initializes WASM
// Load from file input
const file = event.target.files[0];
const data = new Uint8Array(await file.arrayBuffer());
const model = await ifcLoader.load(data);
// model is added to the active world automatically
// model contains Three.js Group with fragment meshes
主なコンポーネント
| コンポーネント | アクセス方法 | 用途 |
|---|---|---|
OBC.Components | new OBC.Components() | すべてのサブシステムを統括するマネージャー |
OBC.Worlds | components.get(OBC.Worlds) | 複数のワールド環境の管理 |
OBC.SimpleScene | コンストラクター | デフォルト設定を備えた Three.js シーンラッパー |
OBC.SimpleCamera | コンストラクター | 軌道制御機能を内蔵したカメラ |
OBC.SimpleRenderer | コンストラクター | DOM 要素に紐付けられた WebGL レンダラー |
OBC.IfcLoader | components.get(OBC.IfcLoader) | IFC ファイルの読み込みとフラグメントへの変換 |
OBC.FragmentsManager | components.get(OBC.FragmentsManager) | 効率的なバッチジオメトリ管理 |
OBC.Highlighter | components.get(OBC.Highlighter) | 要素の選択とハイライト |
OBC.Clipper | components.get(OBC.Clipper) | 断面平面ツール |
OBC.Plans | components.get(OBC.Plans) | フロアプランの生成 |
フラグメントシステム
フラグメントシステムは IFC ジオメトリをバッチ化されたドローコールに変換します。大規模な BIM モデル(要素数 10,000 以上)のパフォーマンスにとって、これは非常に重要です。
- 各 IFC タイプは単一のフラグメントメッシュにバッチ化されます
- 可能な場合、フラグメントはマテリアルを共有し、状態変更を最小限に抑えます
- expressID を使って、個々の要素を引き続き選択、ハイライト、非表示にできます
- 要素数が 1,000 を超えるモデルでは、必ずフラグメントを使用してください
クリーンアップ
// ALWAYS dispose when unmounting the viewer
components.dispose();
// This releases: Three.js scene, renderer, geometries, materials, textures,
// WASM memory, event listeners, and all component state
IFC 空間ツリー
IFC ファイルは、次のような階層的な空間構造に従います。
IFCPROJECT
└── IFCSITE
└── IFCBUILDING
├── IFCBUILDINGSTOREY (Level 0)
│ ├── IFCWALL
│ ├── IFCSLAB
│ └── IFCSPACE
└── IFCBUILDINGSTOREY (Level 1)
├── IFCWALL
├── IFCCOLUMN
└── IFCDOOR
空間への包含は、IFCRELCONTAINEDINSPATIALSTRUCTURE と IFCRELAGGREGATES の関係によって定義されます。ナビゲーション可能なツリーを構築するには、必ずこれらの関係をたどってください。フラットな要素リストが建物の構造を表していると想定してはいけません。
大規模 IFC ファイルのメモリ管理
| ファイルサイズ | 方針 |
|---|---|
| <10MB | 全体を読み込み、すべてのジオメトリを一度に描画する |
| 10-50MB | 全体を読み込み、フラグメントのバッチ化を使用する |
| 50-200MB | 階またはタイプごとにジオメトリをストリーミングし、不要になったものを破棄する |
| >200MB | サーバー側でフラグメントに前処理し、必要に応じて読み込む |
ルール
- WASM ヒープの使用量を必ず監視してください。web-ifc は上限が固定された WASM 線形メモリに割り当てます
- 現在表示されていないジオメトリ(画面外の階、非表示のタイプ)は必ず破棄してください
- WASM ヒープと JavaScript ヒープの両方に重複したジオメトリを保持してはいけません
- 必ず
BufferGeometryを使用してください(レガシーのGeometryは使用しないでください) - シーンからメッシュを削除するときは、必ず
geometry.dispose()とmaterial.dispose()を呼び出してください
参考リンク
- references/methods.md -- web-ifc と @thatopen/components の API シグネチャー一覧
- references/examples.md -- IFC の読み込みと表示に関する実用的なコード例
- references/anti-patterns.md -- Three.js で IFC を扱う際にしてはいけないこと