Files
sam-docs/features/construction-pmis/bim-viewer.md
김보곤 c822a2317d docs: [bim-viewer] Phase 3 SAM Show Effects 20종 문서 추가
- Show Effects 아키텍처 (클로저 기반 update 패턴)
- 헬퍼 메서드 (_mkItem, _mkFlash, _setOp, mkMix)
- 20종 효과 목록 및 설명
- UI 구성 (드롭다운 + 재생 버튼)
- 메모리 관리 (_cleanupShow)
- 향후 계획 Phase 번호 업데이트
2026-03-13 20:37:40 +09:00

22 KiB
Raw Blame History

BIM 뷰어 — Three.js + web-ifc 기반 웹 3D BIM 뷰어

작성일: 2026-03-12 상태: Phase 3 완료 (SAM Show Effects 20종) 소스: 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
SAM Show Effects SAM/샘 텍스트 3D 애니메이션 20종 선택/재생 Phase 3

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 StreamAllMeshesBufferGeometry
요소 선택 시 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 파일을 처음 업로드할 때만 로드한다:

// 동적 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를 래핑하여 파싱과 요소 정보 조회를 담당한다:

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)마다 장면을 다시 그린다:

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 조명 구성

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와 분리한다:

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 뷰어 전체 (~1300줄, Three.js + web-ifc + React + Show Effects)
PlanningController.php mng/app/Http/Controllers/Juil/ bimViewer() 메서드
web.php mng/routes/ GET /juil/construction-pmis/bim-viewer

9. SAM Show Effects (Phase 3)

9.1 개요

BIM 뷰어에 SAM 브랜드 3D 애니메이션 효과 20종을 추가했다. 툴바 드롭다운에서 효과를 선택하고 재생 버튼을 누르면 3D 뷰포트 위에 "SAM"(3D TextGeometry)과 "샘"(Canvas Sprite) 텍스트가 다양한 패턴으로 애니메이션된다.

9.2 아키텍처

사용자 → 드롭다운(sam1~sam20) 선택 → ▶ 재생 버튼
    ↓
playShow(id)
    ↓  FontLoader로 3D 폰트 로드 (최초 1회)
_initEffect(id)
    ↓  switch(id): 20개 케이스
    ↓  각 케이스: items[] 생성 + update(s, elapsed) 클로저 정의
    ↓
this._show = { type, items, extras, startTime, duration, update }
    ↓
animate() 루프에서 _updateShow() 호출
    ↓  s.update(s, elapsed) — 클로저가 애니메이션 계산
    ↓  elapsed >= duration → _cleanupShow()
    ↓
_cleanupShow()
    ↓  items[].geometry.dispose() + material.dispose()
    ↓  extras[].geometry.dispose() + material.dispose()
    ↓  scene.remove(item/extra)

핵심 설계 원칙: 각 효과는 클로저 기반 update 함수로 자기 완결적이다. _updateShow()는 단순 디스패처로, s.update(s, elapsed)만 호출하고 시간 초과 시 _cleanupShow()를 호출한다.

9.3 헬퍼 메서드

메서드 역할
_makeTextSprite(text, color, size) Canvas 2D로 한글 텍스트("샘") 스프라이트 생성
_mkItem(text, color, size, pos) "SAM" → 3D TextGeometry, "샘" → Canvas Sprite 통합 생성
_mkFlash(pos) 흰색 반투명 SphereGeometry (폭발/번쩍 효과용)
_setOp(item, value) 아이템 투명도 설정 (0~1 클램핑)
mkMix(n, sMin, sMax) SAM/샘 혼합 아이템 N개 일괄 생성 (효과 내부 헬퍼)

9.4 아이템 생성 패턴

// 3D TextGeometry (영문 "SAM")
const geo = new THREE.TextGeometry('SAM', { font, size, height:size*0.2 });
const mat = new THREE.MeshPhongMaterial({ color, transparent:true, opacity:1 });
const mesh = new THREE.Mesh(geo, mat);

// Canvas Sprite (한글 "샘")
const canvas = document.createElement('canvas');
ctx.fillText('샘', ...);  // 한글 렌더링
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ map:texture }));

각 아이템에 size, color, idx, targetPos, burstSpeed 등 애니메이션용 커스텀 속성을 부착한다.

9.5 효과 목록 (20종)

ID 이름 설명 패턴
1 Star Gathering 사방에서 별처럼 모여 폭발 gather → burst
2 Spiral Galaxy 은하 소용돌이 나선 회전 → 수축
3 Fireworks 불꽃놀이 상승 → 방사 폭발
4 Matrix Rain 매트릭스 문자비 상단에서 하강
5 DNA Helix DNA 이중나선 쌍 나선 회전
6 Wormhole 웜홀 통과 원형 터널 돌진
7 Constellation 별자리 연결 점 배치 → 선 연결
8 Heartbeat 심장 박동 하트 형태 펄스
9 Tornado 토네이도 원뿔형 회전 상승
10 Domino Wave 도미노 파동 순차 기울어짐
11 Gravity Well 중력 우물 공전 → 흡입
12 Northern Lights 오로라 커튼 수평 웨이브 컬러 변화
13 Clock Mechanism 시계 톱니바퀴 동심원 역방향 회전
14 Particle Storm 입자 폭풍 격렬한 랜덤 이동
15 Bloom Garden 꽃 피어남 중심에서 방사 성장
16 Ocean Waves 해양 파도 사인파 물결
17 Lightning Storm 번개 번쩍 + 분산
18 Pendulum 진자 시계 좌우 스윙
19 Flock of Birds 새 떼 비행 Boids 군집 이동
20 Grand Finale 그랜드 피날레 나선 → 모임 → 대폭발

9.6 애니메이션 시간 관리

// 정규화된 시간 t (0.0 ~ 1.0)
const t = elapsed / s.duration;

// 페이즈 분할 예시 (Star Gathering)
if (t < 0.6) {
    // GATHER 페이즈: 사방에서 중심으로 모임
    const p = t / 0.6;  // 0~1 정규화
} else if (t < 0.7) {
    // FLASH 페이즈: 흰색 번쩍
} else {
    // BURST 페이즈: 폭발 분산
    const bp = (t - 0.7) / 0.3;
}
  • performance.now() 기준 절대 시간 사용
  • 각 효과의 duration은 3~6초 범위
  • 프레임 독립적 (delta time 기반)

9.7 UI 구성

┌─────────────────────────────────────────────┐
│  BimToolbar                                 │
│  ┌──────────────────┐  ┌──┐                │
│  │ sam1 Star Gather ▼│  │▶│  ... 기존 버튼  │
│  └──────────────────┘  └──┘                │
│  (드롭다운 select)     (재생)               │
└─────────────────────────────────────────────┘
UI 요소 React 상태 설명
효과 드롭다운 effectId SHOW_EFFECTS 배열(20개) 중 선택
재생 버튼 sceneRef.current.playShow(effectId) 호출

SHOW_EFFECTS 상수:

const SHOW_EFFECTS = [
    { id:1, name:'sam1 Star Gathering' },
    { id:2, name:'sam2 Spiral Galaxy' },
    // ... 20개
];

9.8 메모리 관리

_cleanupShow()가 모든 효과의 리소스를 통일적으로 정리한다:

_cleanupShow() {
    if (!this._show) return;
    const { items, extras } = this._show;
    // items: TextGeometry 또는 Sprite
    [items, extras].forEach(arr => arr.forEach(it => {
        if (it.geometry) it.geometry.dispose();
        if (it.material) {
            if (it.material.map) it.material.map.dispose();
            it.material.dispose();
        }
        this.scene.remove(it);
    }));
    this._show = null;
}

dispose() (BimScene 전체 정리) 시에도 _cleanupShow()를 호출하여 누수를 방지한다.


10. 향후 계획 (Phase 4+)

Phase 기능 기술 상태
Phase 1 데모 건물 + 3D 뷰어 Three.js r128 완료
Phase 2 IFC 업로드/다운로드 + 실제 BIM 렌더링 web-ifc 0.0.66 완료
Phase 3 SAM Show Effects 20종 Three.js TextGeometry + Sprite 완료
Phase 4 요소 속성 패널 고도화, 2D 평면도 모드 Three.js Orthographic Camera
Phase 5 모델 파일 관리, 버전 이력, 협력업체 조회 Laravel API + DB

관련 문서


최종 업데이트: 2026-03-13