- Show Effects 아키텍처 (클로저 기반 update 패턴) - 헬퍼 메서드 (_mkItem, _mkFlash, _setOp, mkMix) - 20종 효과 목록 및 설명 - UI 구성 (드롭다운 + 재생 버튼) - 메모리 관리 (_cleanupShow) - 향후 계획 Phase 번호 업데이트
553 lines
22 KiB
Markdown
553 lines
22 KiB
Markdown
# 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 `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 뷰어 전체 (~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 아이템 생성 패턴
|
||
|
||
```javascript
|
||
// 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 애니메이션 시간 관리
|
||
|
||
```javascript
|
||
// 정규화된 시간 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` 상수:
|
||
|
||
```javascript
|
||
const SHOW_EFFECTS = [
|
||
{ id:1, name:'sam1 Star Gathering' },
|
||
{ id:2, name:'sam2 Spiral Galaxy' },
|
||
// ... 20개
|
||
];
|
||
```
|
||
|
||
### 9.8 메모리 관리
|
||
|
||
`_cleanupShow()`가 모든 효과의 리소스를 통일적으로 정리한다:
|
||
|
||
```javascript
|
||
_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 | |
|
||
|
||
---
|
||
|
||
## 관련 문서
|
||
|
||
- [건설PMIS 대시보드](../construction-pmis/) — PMIS 메인 대시보드
|
||
- [PmisWorker 모델](../../../../api/database/migrations/2026_03_12_120000_create_pmis_workers_table.php) — PMIS 작업자 프로필 테이블
|
||
|
||
---
|
||
|
||
**최종 업데이트**: 2026-03-13
|