babylon-ifc-loader 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +619 -0
- package/LICENSE +190 -0
- package/dist-npm/ifcInit.d.ts +60 -0
- package/dist-npm/ifcInit.d.ts.map +1 -0
- package/dist-npm/ifcInit.js +301 -0
- package/dist-npm/ifcInit.js.map +1 -0
- package/dist-npm/ifcModel.d.ts +51 -0
- package/dist-npm/ifcModel.d.ts.map +1 -0
- package/dist-npm/ifcModel.js +347 -0
- package/dist-npm/ifcModel.js.map +1 -0
- package/dist-npm/index.d.ts +5 -0
- package/dist-npm/index.d.ts.map +1 -0
- package/dist-npm/index.js +8 -0
- package/dist-npm/index.js.map +1 -0
- package/dist-npm/web-ifc.wasm +0 -0
- package/package.json +70 -0
- package/readme.md +304 -0
package/readme.md
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# Babylon.js IFC Loader
|
|
2
|
+
|
|
3
|
+
IFC Loader built with Babylon.js and web-ifc. Features automatic loading of sample IFC files, drag-and-drop support, intelligent mesh merging, element picking with metadata display, and automatic camera framing.
|
|
4
|
+
|
|
5
|
+
While providing the minimal viewer experience, this repo is dedicated to developing and testing the IFC Babylon.js Loader. The viewer is provided for testing and demonstration purposes only. Full-featured Babylon.js IFC Babylon.js Viewer will be available in a separate repo later.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Install dependencies
|
|
11
|
+
npm install
|
|
12
|
+
|
|
13
|
+
# Start development server
|
|
14
|
+
npm run dev
|
|
15
|
+
|
|
16
|
+
# Build for production
|
|
17
|
+
npm run build
|
|
18
|
+
|
|
19
|
+
# Preview production build
|
|
20
|
+
npm run preview
|
|
21
|
+
|
|
22
|
+
# Run tests
|
|
23
|
+
npm test
|
|
24
|
+
|
|
25
|
+
# Run tests with coverage
|
|
26
|
+
npm run test:coverage
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Dev Server URL
|
|
30
|
+
|
|
31
|
+
After running `npm run dev`, open:
|
|
32
|
+
|
|
33
|
+
| URL | Entry Point | Description |
|
|
34
|
+
| ---------------------- | ----------- | ------------------------------------------------ |
|
|
35
|
+
| http://localhost:5173/ | `main.ts` | Uses low-level two-step API (ifcInit + ifcModel) |
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **Automatic Loading:** Sample IFC file loads on startup
|
|
40
|
+
- **Drag & Drop:** Drop `.ifc` files onto the canvas to load them
|
|
41
|
+
- **Element Picking:** Click on elements to view metadata and highlight them
|
|
42
|
+
- **Intelligent Merging:** Automatically merges meshes with same material while preserving metadata
|
|
43
|
+
- **Camera Framing:** Automatically positions camera to view the entire model
|
|
44
|
+
- **Inspector:** Built-in Babylon.js Inspector for debugging
|
|
45
|
+
- **Keyboard Shortcuts:** Ctrl+I (or Cmd+I on Mac) toggles the inspector, works across all keyboard layouts
|
|
46
|
+
- **Memory Management:** Proper cleanup when loading new files
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
The codebase follows a strict layered architecture with clear separation of concerns:
|
|
51
|
+
|
|
52
|
+
### IFC Data Layer (`src/ifcInit.ts`)
|
|
53
|
+
|
|
54
|
+
All web-ifc interaction. **Zero Babylon.js dependencies.**
|
|
55
|
+
|
|
56
|
+
- `initializeWebIFC(wasmPath?, logLevel?)` — initialize web-ifc API
|
|
57
|
+
- `loadIfcModel(ifcAPI, source, options?)` — load and extract raw geometry data
|
|
58
|
+
- `closeIfcModel(ifcAPI, modelID)` — free IFC model memory
|
|
59
|
+
- `getProjectInfo(ifcAPI, modelID)` — extract project metadata
|
|
60
|
+
|
|
61
|
+
### Rendering Layer (`src/ifcModel.ts`)
|
|
62
|
+
|
|
63
|
+
All Babylon.js scene construction. **Zero web-ifc dependencies.**
|
|
64
|
+
|
|
65
|
+
- `buildIfcModel(model, scene, options?)` — create meshes, materials, merge, center
|
|
66
|
+
- `disposeIfcModel(scene)` — dispose all IFC meshes and materials
|
|
67
|
+
- `getModelBounds(meshes)` — calculate bounding box
|
|
68
|
+
- `centerModelAtOrigin(meshes, rootNode?)` — center model at origin
|
|
69
|
+
|
|
70
|
+
### Application Layer
|
|
71
|
+
|
|
72
|
+
- `src/main.ts` — uses low-level two-step API (ifcInit + ifcModel)
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
### Two-Step Loading API
|
|
77
|
+
|
|
78
|
+
For more control, use the two-step API directly:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// Step 1: Initialize web-ifc
|
|
82
|
+
const ifcAPI = await initializeWebIFC("./");
|
|
83
|
+
|
|
84
|
+
// Step 2: Load raw IFC data (web-ifc only)
|
|
85
|
+
const model = await loadIfcModel(ifcAPI, "/test.ifc", {
|
|
86
|
+
coordinateToOrigin: true,
|
|
87
|
+
verbose: true,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Step 3: Extract metadata (optional)
|
|
91
|
+
const projectInfo = getProjectInfo(ifcAPI, model.modelID);
|
|
92
|
+
|
|
93
|
+
// Step 4: Build Babylon.js scene (Babylon only)
|
|
94
|
+
const { meshes, rootNode, stats } = buildIfcModel(model, scene, {
|
|
95
|
+
autoCenter: true,
|
|
96
|
+
mergeMeshes: true,
|
|
97
|
+
doubleSided: true,
|
|
98
|
+
verbose: true,
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Load from URL or File
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// From URL
|
|
106
|
+
const model = await loadIfcModel(ifcAPI, "/path/to/file.ifc");
|
|
107
|
+
|
|
108
|
+
// From File object (drag-and-drop)
|
|
109
|
+
const model = await loadIfcModel(ifcAPI, fileObject);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Cleanup before loading a new model
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// Dispose Babylon.js scene (meshes, materials, root node)
|
|
116
|
+
disposeIfcModel(scene);
|
|
117
|
+
|
|
118
|
+
// Close IFC model and free WASM memory
|
|
119
|
+
closeIfcModel(ifcAPI, modelID);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## API Reference
|
|
123
|
+
|
|
124
|
+
See [API.md](./API.md) for complete API documentation with all types, parameters, and examples.
|
|
125
|
+
|
|
126
|
+
## NPM Package
|
|
127
|
+
|
|
128
|
+
The package is published as `babylon-ifc-loader` and exports:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// Low-level IFC Data Layer (web-ifc only)
|
|
132
|
+
import { initializeWebIFC, loadIfcModel, closeIfcModel, getProjectInfo } from "babylon-ifc-loader";
|
|
133
|
+
|
|
134
|
+
// Rendering Layer (Babylon.js only)
|
|
135
|
+
import { buildIfcModel, disposeIfcModel, getModelBounds, centerModelAtOrigin } from "babylon-ifc-loader";
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Testing NPM Package Locally
|
|
139
|
+
|
|
140
|
+
To test the npm package locally before publishing:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# 1. Build the npm package
|
|
144
|
+
npm run build:npm
|
|
145
|
+
|
|
146
|
+
# 2. Run Vite dev server with the test-npm entry point
|
|
147
|
+
npx vite . --open test-npm/index.html
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The `test-npm/` folder contains a test page that imports from `babylon-ifc-loader`. Since the package's `main` entry points to `dist-npm/index.js`, Vite resolves the import from the built output.
|
|
151
|
+
|
|
152
|
+
**Testing from another project:**
|
|
153
|
+
|
|
154
|
+
To test the package in a different project:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# In ifc-babylon root
|
|
158
|
+
npm link
|
|
159
|
+
|
|
160
|
+
# In the other project
|
|
161
|
+
npm link babylon-ifc-loader
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Testing
|
|
165
|
+
|
|
166
|
+
The project uses [Vitest](https://vitest.dev/) for unit testing with the following setup:
|
|
167
|
+
|
|
168
|
+
- **Test Runner:** Vitest v4 with `jsdom` environment
|
|
169
|
+
- **Coverage:** `@vitest/coverage-v8` for code coverage reports
|
|
170
|
+
- **Location:** Test files are in `src/__tests__/`
|
|
171
|
+
|
|
172
|
+
### Test Files
|
|
173
|
+
|
|
174
|
+
| File | Description |
|
|
175
|
+
| -------------------------- | ------------------------------------- |
|
|
176
|
+
| `initializeWebIFC.test.ts` | Tests for web-ifc initialization |
|
|
177
|
+
| `loadIfcModel.test.ts` | Tests for IFC model loading |
|
|
178
|
+
| `closeIfcModel.test.ts` | Tests for model cleanup |
|
|
179
|
+
| `getProjectInfo.test.ts` | Tests for project metadata extraction |
|
|
180
|
+
| `buildIfcModel.test.ts` | Tests for Babylon.js scene building |
|
|
181
|
+
| `zOffset.test.ts` | Tests for z-offset material handling |
|
|
182
|
+
|
|
183
|
+
### Running Tests
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Run tests in watch mode
|
|
187
|
+
npm test
|
|
188
|
+
|
|
189
|
+
# Run tests once
|
|
190
|
+
npm test -- --run
|
|
191
|
+
|
|
192
|
+
# Run tests with coverage report
|
|
193
|
+
npm run test:coverage
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Writing Tests
|
|
197
|
+
|
|
198
|
+
Tests follow the standard Vitest pattern with mocked dependencies:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
202
|
+
|
|
203
|
+
describe("myFunction", () => {
|
|
204
|
+
beforeEach(() => {
|
|
205
|
+
vi.clearAllMocks();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("should do something", async () => {
|
|
209
|
+
const result = await myFunction();
|
|
210
|
+
expect(result).toBe(expected);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Project Structure
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
src/
|
|
219
|
+
├── main.ts — app entry (two-step API: ifcInit + ifcModel)
|
|
220
|
+
├── ifcInit.ts — IFC data layer (web-ifc only)
|
|
221
|
+
├── ifcModel.ts — rendering layer (Babylon.js only)
|
|
222
|
+
├── style.css — basic styling
|
|
223
|
+
└── __tests__/ — unit tests
|
|
224
|
+
|
|
225
|
+
public/
|
|
226
|
+
├── test.ifc — sample IFC file loaded at startup
|
|
227
|
+
├── example.ifc — additional sample
|
|
228
|
+
└── bplogo.svg — asset
|
|
229
|
+
|
|
230
|
+
Root
|
|
231
|
+
├── index.html — HTML entry
|
|
232
|
+
├── vite.config.ts — copies web-ifc.wasm to dist/, sets WASM handling
|
|
233
|
+
├── tsconfig.json — TypeScript config
|
|
234
|
+
└── package.json — scripts and deps
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Picking and Highlighting
|
|
238
|
+
|
|
239
|
+
- Left-click a mesh to log full element data via `ifcAPI.GetLine(modelID, expressID, true)` and type name via `GetNameFromTypeCode`
|
|
240
|
+
- Highlight uses `renderOverlay` with teal color and alpha=0.3
|
|
241
|
+
- Upper text banner shows type, name, and ExpressID; clicking empty space clears it
|
|
242
|
+
|
|
243
|
+
## Materials, Merging, and Performance
|
|
244
|
+
|
|
245
|
+
- Materials are `StandardMaterial` per unique RGBA color, configurable `backFaceCulling`, incremental `zOffset` to mitigate z-fighting
|
|
246
|
+
- Meshes are merged per (expressID + color) when safe; safety check prevents merging across different storeys using spatial relations
|
|
247
|
+
- Metadata (`expressID`, `modelID`) preserved on merged meshes
|
|
248
|
+
- Stats for counts, triangles, materials, and build time are computed
|
|
249
|
+
|
|
250
|
+
### Custom Merging Strategy
|
|
251
|
+
|
|
252
|
+
When `mergeMeshes = false`, each geometry part remains as a separate mesh with full metadata. This lets you implement your own merging strategy based on:
|
|
253
|
+
|
|
254
|
+
- Mesh metadata (`expressID`, `modelID`) for element identification
|
|
255
|
+
- IFC queries via `ifcAPI.GetLine()` for property-based grouping
|
|
256
|
+
- Spatial relationships for storey/zone-based organization
|
|
257
|
+
- Material or color-based batching
|
|
258
|
+
|
|
259
|
+
Example:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const { meshes } = buildIfcModel(model, scene, { mergeMeshes: false });
|
|
263
|
+
|
|
264
|
+
// Custom grouping by IFC type
|
|
265
|
+
for (const mesh of meshes) {
|
|
266
|
+
const element = ifcAPI.GetLine(modelID, mesh.metadata.expressID, true);
|
|
267
|
+
const typeName = ifcAPI.GetNameFromTypeCode(element.type);
|
|
268
|
+
// Group or merge meshes by typeName, storey, etc.
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Coordinate System and Geometry
|
|
273
|
+
|
|
274
|
+
- web-ifc streams interleaved vertex data `[x,y,z,nx,ny,nz]`
|
|
275
|
+
- Optional normal generation when required
|
|
276
|
+
- Per-part transforms baked from placed geometry matrices
|
|
277
|
+
- Z-axis flip applied via root node scaling for IFC-to-Babylon coordinate conversion
|
|
278
|
+
|
|
279
|
+
## Build and Deploy Notes
|
|
280
|
+
|
|
281
|
+
- The Vite config copies `node_modules/web-ifc/web-ifc.wasm` to `dist/`
|
|
282
|
+
- In production, `initializeWebIFC("./")` ensures the WASM is loaded from the dist root
|
|
283
|
+
- `optimizeDeps.exclude = ["web-ifc"]` prevents esbuild issues during dev
|
|
284
|
+
|
|
285
|
+
## Dependencies
|
|
286
|
+
|
|
287
|
+
| Package | Version | Description |
|
|
288
|
+
| ----------------------- | ------- | ----------------------------------- |
|
|
289
|
+
| @babylonjs/core | ^8.52.0 | Core Babylon.js engine |
|
|
290
|
+
| @babylonjs/inspector | ^8.52.0 | Built-in debugging inspector |
|
|
291
|
+
| web-ifc | ^0.0.75 | IFC parsing and geometry extraction |
|
|
292
|
+
| vite | ^7.3.1 | Build tool and dev server |
|
|
293
|
+
| vite-plugin-static-copy | ^3.2.0 | Copy WASM files to dist |
|
|
294
|
+
|
|
295
|
+
## Limitations and Future Improvements
|
|
296
|
+
|
|
297
|
+
- No spatial tree or filters yet
|
|
298
|
+
- No property panel UI
|
|
299
|
+
- No outline/edge rendering highlight option
|
|
300
|
+
- No UI controls for scene manipulation
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
Apache-2.0 - See [LICENSE](./LICENSE) for details.
|