Files
sam-docs/features/construction-pmis/bim-viewer.md
김보곤 fa85bd388a docs: [bim] BIM 뷰어 문서 Phase 2 반영
- Phase 1 전용 → Phase 2 완료 상태로 업데이트
- IFC 업로드/다운로드, web-ifc, 듀얼 모드 설명 추가
- IFC→Three.js 변환 파이프라인, IFC Export 구조 문서화
- BimScene 클래스 메서드 목록 최신화
2026-03-12 13:35:14 +09:00

395 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BIM 뷰어 — Three.js + web-ifc 기반 웹 3D BIM 뷰어
> **작성일**: 2026-03-12
> **상태**: Phase 2 완료 (IFC 업로드 + 다운로드)
> **소스**: `mng/resources/views/juil/bim-viewer.blade.php`
---
## 1. 개요
### 1.1 목적
건설PMIS(Construction Project Management Information System)의 BIM(Building Information Modeling) 뷰어 기능이다. 웹 브라우저에서 별도 플러그인 없이 3D 건물 모델을 렌더링하고, 요소를 선택하여 속성 정보를 조회할 수 있다.
### 1.2 주요 기능
| 기능 | 설명 | Phase |
|------|------|-------|
| 데모 건물 렌더링 | 물류센터 3D 모델 (BoxGeometry 조합) | Phase 1 |
| 요소 클릭 선택 | Raycaster로 3D 요소 선택 → 속성 조회 | Phase 1 |
| 시점 전환 | 투시도/정면/우측/상부/배면 프리셋 | Phase 1 |
| 요소 토글 & 와이어프레임 | 요소 유형별 표시/숨김, 와이어프레임 모드 | Phase 1 |
| **IFC 파일 업로드** | `.ifc` 파일 업로드 → 실제 BIM 모델 렌더링 | **Phase 2** |
| **IFC 파일 다운로드** | 현재 모델을 IFC 파일로 내보내기 | **Phase 2** |
| **드래그 앤 드롭** | IFC 파일을 뷰포트에 드래그하여 로드 | **Phase 2** |
### 1.3 핵심 기술 스택
| 기술 | 역할 | 버전/CDN |
|------|------|----------|
| **Three.js** | WebGL 기반 3D 렌더링 엔진 | r128 (CDN) |
| **OrbitControls** | 카메라 회전/줌/팬 조작 | r128 (CDN) |
| **web-ifc** | IFC 파일 파싱 (WASM 기반) | 0.0.66 (CDN, 지연 로드) |
| **React 18** | UI 컴포넌트 (사이드바, 툴바, 정보 패널) | CDN + Babel 브라우저 트랜스파일 |
| **Tailwind CSS** | UI 스타일링 | CDN |
### 1.4 아키텍처
```
┌─────────────────────────────────────────────────────────┐
│ Blade 템플릿 (@extends layouts.app) │
│ ┌─────────────────────────────────────────────────────┐│
│ │ React (CDN + Babel) ││
│ │ ┌──────┐ ┌──────────────────┐ ┌───────────────┐ ││
│ │ │사이드 │ │ Three.js │ │ 정보 패널 │ ││
│ │ │ 바 │ │ 3D Viewport │ │ (모델/선택/ │ ││
│ │ │(React)│ │ (WebGL) │ │ 통계) │ ││
│ │ └──────┘ └──────────────────┘ └───────────────┘ ││
│ │ ┌──────────────────┐ ││
│ │ │ 툴바 (React) │ ││
│ │ │ IFC업로드/다운로드│ ││
│ │ └──────────────────┘ ││
│ └─────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────┐ ┌──────────────────────────────┐ │
│ │ IFCHelper │ │ generateDemoIFC() │ │
│ │ (web-ifc 래퍼) │ │ (Three.js → IFC2X3 변환) │ │
│ └─────────────────┘ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
- **React**: UI 컴포넌트 관리 (사이드바, 툴바, 정보 패널)
- **Three.js**: 3D 렌더링 (건물, 조명, 그림자, 카메라)
- **web-ifc**: IFC 파일 파싱 → 지오메트리 추출 (WASM, 지연 로드)
- **연결 방식**: React `useRef`로 DOM 요소를 Three.js에 전달, `useEffect`로 생명주기 관리
---
## 2. 듀얼 모드 — 데모 / IFC
BIM 뷰어는 두 가지 모드로 동작한다:
| 항목 | 데모 모드 (`demo`) | IFC 모드 (`ifc`) |
|------|-------------------|------------------|
| 진입 방식 | 기본 (페이지 로드 시) | IFC 파일 업로드/드래그 시 |
| 모델 원본 | `createDemoBuilding()` (코드 내 하드코딩) | 사용자 업로드 `.ifc` 파일 |
| 지오메트리 | `BoxGeometry` 조합 (~200개) | web-ifc `StreamAllMeshes``BufferGeometry` |
| 요소 선택 시 | `userData` (type, name, material, floor 등) | `IFCHelper.getElementInfo()` (IFC 속성) |
| 요소 토글 | 유형별 Group 표시/숨김 | 미지원 (전체 모델 표시) |
| IFC 다운로드 | `generateDemoIFC()` → IFC2X3 STEP 생성 | 원본 버퍼 그대로 재다운로드 |
모드 전환 흐름:
```
[페이지 로드] → 데모 모드
├── IFC 업로드 ──→ IFC 모드 (데모 숨김)
│ │
│ ├── "데모 모델" 버튼 ──→ 데모 모드 복귀
│ └── 다른 IFC 업로드 ──→ 새 IFC 로드
└── IFC 다운로드 → generateDemoIFC() 실행 → .ifc 파일 다운로드
```
---
## 3. IFC 파일 처리 (Phase 2)
### 3.1 web-ifc 지연 로드
web-ifc WASM 번들은 약 7MB이다. 페이지 로드 시 다운로드하지 않고, IFC 파일을 처음 업로드할 때만 로드한다:
```javascript
// 동적 module script 주입 방식
function loadWebIFCLib() {
const s = document.createElement('script');
s.type = 'module';
s.textContent = `
import * as WebIFC from '${WEBIFC_CDN}web-ifc-api.js';
window._WebIFC = WebIFC;
window.dispatchEvent(new CustomEvent('_wifcOk'));
`;
document.head.appendChild(s);
}
```
> Babel `text/babel` 컨텍스트에서 동적 `import()`가 불안정하므로, 별도 `<script type="module">`을 주입하여 ES module import를 수행하고 `window._WebIFC`로 노출한다.
### 3.2 IFCHelper 클래스
web-ifc API를 래핑하여 파싱과 요소 정보 조회를 담당한다:
```javascript
class IFCHelper {
async init(onMsg) // web-ifc 초기화, WASM 로드, typeMap 구축
parse(buffer, onMsg) // IFC 파일 → StreamAllMeshes → 지오메트리 배열
getElementInfo(expressID) // expressID → IFC 속성 (타입, 이름, GlobalId 등)
close() // 모델 닫기, 캐시 정리
}
```
### 3.3 IFC → Three.js 변환 파이프라인
```
.ifc 파일 (ArrayBuffer)
↓ IFCHelper.parse()
web-ifc StreamAllMeshes
↓ 각 메시에서 vertices + indices + transform 추출
Float32Array (위치 3 + 노멀 3 = stride 6)
↓ Three.js BufferGeometry로 변환
BufferAttribute(position, 3) + BufferAttribute(normal, 3)
↓ Matrix4.fromArray(transform) 적용
MeshPhongMaterial (IFC 원본 색상 + 투명도)
THREE.Mesh → ifcGroup(THREE.Group)에 추가
fitToModel() → 바운딩 박스 기준 카메라 자동 조정
```
### 3.4 IFC 파일 다운로드 (IFC Export)
| 모드 | 동작 | 출력 파일명 |
|------|------|-----------|
| 데모 | `generateDemoIFC(meshes)` → IFC2X3 STEP 텍스트 생성 | `SAM_Demo_Building.ifc` |
| IFC | `this.ifcBuffer` (업로드 시 저장한 원본) 그대로 다운로드 | `model.ifc` |
데모 모델의 IFC 변환 구조:
```
Three.js 메시 (BoxGeometry)
↓ geometry.parameters → width, height, depth
↓ mesh.position → x, y, z
↓ mesh.userData → type, floor, name, material
IFC2X3 STEP 엔티티 생성:
IfcProject → IfcSite → IfcBuilding → IfcBuildingStorey (1F~RF)
각 메시 → IfcRectangleProfileDef + IfcExtrudedAreaSolid + IfcLocalPlacement
유형 매핑: column→IFCCOLUMN, beam→IFCBEAM, floor/roof→IFCSLAB, ...
좌표계 변환:
Three.js (x, y, z) → IFC (x, z, y) // Y-up → Z-up
요소 기준점: 바닥 중심 (Three.js center → IFC bottom)
```
---
## 4. Three.js 렌더링 파이프라인
### 4.1 기본 구조
```
브라우저 (Chrome/Edge)
└── WebGL API (GPU 가속 그래픽)
└── Three.js r128 (WebGL 추상화 라이브러리)
├── Scene — 3D 공간 (모든 객체의 컨테이너)
├── Camera — 시점 (PerspectiveCamera, FOV 45도)
├── Renderer — 화면 출력 (WebGLRenderer, 안티앨리어싱)
├── Lights — 조명 (Hemisphere + Directional + Ambient)
├── Mesh — 물체 = Geometry(형태) + Material(재질)
└── FogExp2 — 거리 안개 (밀도 0.004)
```
### 4.2 렌더링 루프
Three.js는 매 프레임(~60fps)마다 장면을 다시 그린다:
```javascript
animate() {
requestAnimationFrame(() => this.animate());
if (this.targetPos) {
this.camera.position.lerp(this.targetPos, 0.07);
this.controls.target.lerp(this.targetLook, 0.07);
if (this.camera.position.distanceTo(this.targetPos) < 0.2)
this.targetPos = null;
}
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
```
### 4.3 조명 구성
```javascript
HemisphereLight(0xb1e1ff, 0xb97a20, 0.55) // 반구광 (하늘+지면)
DirectionalLight(0xffffff, 0.85) // 태양광 (그림자)
└── Shadow Map: 2048×2048 (PCFSoftShadowMap)
AmbientLight(0xffffff, 0.25) // 환경광
```
---
## 5. 데모 건물 모델
### 5.1 건물 사양
| 항목 | 값 |
|------|------|
| 건물 용도 | 물류센터 (데모) |
| 크기 | 60m (가로) × 30m (세로) × 12m (높이) |
| 층수 | 지상 3층 (각 4m) |
| 기둥 격자 | 7 × 4 (간격 10m × 10m) |
| 좌표계 | X = 가로(길이), Y = 높이(상향), Z = 세로(깊이) |
### 5.2 요소 색상 체계
| 요소 | 색상 코드 | 개수 |
|------|----------|------|
| 바닥 슬래브 | `0x81C784` 연두 | 3 |
| 지붕 | `0x4CAF50` 초록 | 1 |
| 기둥 | `0xFF9800` 주황 | 84 |
| 보 | `0xFFC107` 황금 | 33 |
| 벽체 | `0x42A5F5` 파랑 | ~50 |
| 창호 | `0x00BCD4` 시안 (반투명) | 18 |
| 출입문 | `0x8B4513` 갈색 | 3 |
| 계단실 | `0xFFAB91` 살몬 | 15 |
| **합계** | | **~200+** |
### 5.3 건물 단면
```
지붕 슬래브 ────── y = 12.0m
3F 기둥/벽/창 ─── y = 8.0 ~ 12.0m
2F 바닥 슬래브 ── y = 8.0m
2F 기둥/벽/창 ─── y = 4.0 ~ 8.0m
1F 바닥 슬래브 ── y = 4.0m
1F 기둥/벽/창 ─── y = 0.0 ~ 4.0m (후면: 하역장 도어 3개)
기초 슬래브 ───── y = 0.0m
지면 (그라운드) ─ y = -0.02m
```
---
## 6. 사용자 인터랙션
### 6.1 카메라 조작 (OrbitControls)
| 조작 | 동작 |
|------|------|
| 마우스 드래그 | 카메라가 건물 중심을 공전 (Orbit) |
| 스크롤 (휠) | 줌 인/아웃 |
| 우클릭 드래그 | 시점 평행 이동 (Pan) |
### 6.2 요소 클릭 선택 (Raycaster)
```
마우스 클릭 좌표 (2D)
↓ NDC 좌표 변환 (-1 ~ +1)
카메라에서 광선(Ray) 발사
↓ raycaster.intersectObjects()
3D 메시와 충돌 검사
[데모 모드] userData (type, name, material...) 읽기
[IFC 모드] expressID → IFCHelper.getElementInfo() → IFC 속성 조회
React 상태 업데이트 → 우측 정보 패널 표시
```
### 6.3 시점 전환
| 프리셋 | 용도 |
|--------|------|
| 투시도 | 전체 조감 |
| 정면 | 남측 입면 |
| 우측 | 동측 입면 |
| 상부 | 평면도 |
| 배면 | 북측 (하역장) |
IFC 모드에서는 모델 바운딩 박스 기준으로 시점이 자동 계산된다.
### 6.4 IFC 업로드 / 다운로드
| 기능 | 방법 |
|------|------|
| IFC 업로드 | 툴바 "IFC 업로드" 버튼 또는 뷰포트에 드래그 앤 드롭 |
| IFC 다운로드 | 툴바 "IFC 다운로드" 버튼 (초록색) |
| 데모 복귀 | IFC 모드에서 "데모 모델" 버튼 |
---
## 7. React-Three.js 연동 패턴
### 7.1 BimScene 클래스
Three.js 로직을 `BimScene` 클래스로 캡슐화하여 React와 분리한다:
```javascript
class BimScene {
constructor(el) // DOM 요소 참조, 상태 초기화
init() // Scene, Camera, Renderer, Controls, 데모 건물 생성
createDemoBuilding() // 데모 물류센터 모델 생성
async loadIFC(buffer) // IFC 파일 파싱 → BufferGeometry → 씬 추가
clearIFCModel() // IFC 모델 정리 (geometry/material dispose)
switchToDemo() // 데모 모드 복귀
exportIFC() // IFC 파일 다운로드 (데모: 생성, IFC: 원본)
onClick(e) // Raycaster로 요소 선택
setView(preset) // 카메라 전환 (lerp 보간)
fitToModel(obj) // 바운딩 박스 기준 카메라 자동 조정
toggleGroup(name) // 요소 유형 표시/숨김 (데모 모드)
toggleWireframe(on) // 와이어프레임 토글
getCounts() // 요소 통계 반환
animate() // 렌더 루프
dispose() // 리소스 정리
}
```
### 7.2 React 컴포넌트 구성
| 컴포넌트 | 역할 |
|----------|------|
| `BimViewerApp` | 루트 컴포넌트 — 상태 관리, 이벤트 핸들러 |
| `BimSidebar` | 좌측 PMIS 메뉴 사이드바 |
| `BimToolbar` | 하단 툴바 (시점, 토글, 업로드/다운로드) |
| `BimInfoPanel` | 우측 패널 (모델 정보, 선택 요소, 통계) |
| `LoadingOverlay` | IFC 로딩 중 오버레이 |
### 7.3 통신 패턴
```
React → Three.js: sceneRef.current.setView('front')
sceneRef.current.exportIFC()
sceneRef.current.loadIFC(buffer)
Three.js → React: bim.onSelect = setSelected (요소 선택 콜백)
bim.onProgress = setLoading (로딩 상태 콜백)
```
---
## 8. 파일 구조
| 파일 | 위치 | 설명 |
|------|------|------|
| `bim-viewer.blade.php` | `mng/resources/views/juil/` | BIM 뷰어 전체 (~900줄, Three.js + web-ifc + React) |
| `PlanningController.php` | `mng/app/Http/Controllers/Juil/` | `bimViewer()` 메서드 |
| `web.php` | `mng/routes/` | `GET /juil/construction-pmis/bim-viewer` |
---
## 9. 향후 계획 (Phase 3+)
| Phase | 기능 | 기술 |
|-------|------|------|
| ~~Phase 1~~ | ~~데모 건물 + 3D 뷰어~~ | ~~Three.js r128~~ (완료) |
| ~~Phase 2~~ | ~~IFC 업로드/다운로드 + 실제 BIM 렌더링~~ | ~~web-ifc 0.0.66~~ (완료) |
| **Phase 3** | 요소 속성 패널 고도화, 2D 평면도 모드 | Three.js Orthographic Camera |
| **Phase 4** | 모델 파일 관리, 버전 이력, 협력업체 조회 | Laravel API + DB |
---
## 관련 문서
- [건설PMIS 대시보드](../construction-pmis/) — PMIS 메인 대시보드
- [PmisWorker 모델](../../../../api/database/migrations/2026_03_12_120000_create_pmis_workers_table.php) — PMIS 작업자 프로필 테이블
---
**최종 업데이트**: 2026-03-12