Streamingle_URP/RemoteMotionCapture/Documentation/TECHNICAL_SPECIFICATIONS.md

480 lines
13 KiB (Stored with Git LFS)
Markdown

# 기술 사양서 (Technical Specifications)
## 🏗️ 시스템 아키텍처
### 전체 시스템 구조
```
[모션캡쳐 스튜디오] → [Unity 서버] → [WebSocket] → [웹 클라이언트]
↓ ↓ ↓
모션 데이터 수집 실시간 스트리밍 3D 렌더링 + 제어
```
### 데이터 흐름
1. **모션캡쳐 → Unity**: Mocap 장비에서 Unity로 실시간 데이터 입력
2. **Unity → Web**: WebSocket을 통한 실시간 아바타 Transform 전송
3. **Web → Unity**: 녹화 명령 및 제어 신호 전송
4. **Web**: 로컬 모션 데이터 저장 및 파일 생성
## 📡 통신 프로토콜
### WebSocket 메시지 포맷
#### 1. 모션 데이터 (Unity → Web)
```json
{
"type": "motion_frame",
"timestamp": 1640995200000,
"frameRate": 30,
"avatar": {
"bones": [
{
"name": "Hips",
"position": {"x": 0.0, "y": 1.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0},
"scale": {"x": 1.0, "y": 1.0, "z": 1.0}
},
{
"name": "Spine",
"position": {"x": 0.0, "y": 1.2, "z": 0.0},
"rotation": {"x": 0.1, "y": 0.0, "z": 0.0, "w": 0.995}
}
]
}
}
```
#### 2. 녹화 제어 명령 (Web → Unity)
```json
{
"type": "recording_command",
"command": "start_recording",
"parameters": {
"filename": "motion_capture_001",
"format": "fbx",
"frameRate": 30,
"duration": 300
}
}
```
#### 3. 상태 응답 (Unity → Web)
```json
{
"type": "recording_status",
"status": "recording",
"filename": "motion_capture_001.fbx",
"duration": 45.2,
"frameCount": 1356,
"fileSizeKB": 2048
}
```
### 에러 처리
```json
{
"type": "error",
"code": "RECORDING_FAILED",
"message": "녹화 파일 생성에 실패했습니다.",
"details": {
"cause": "disk_space_insufficient",
"timestamp": 1640995200000
}
}
```
## 🎮 Unity 구현 사양
### 핵심 컴포넌트
#### 1. WebSocketServer.cs
```csharp
public class WebSocketServer : MonoBehaviour
{
[SerializeField] private int port = 8080;
[SerializeField] private float updateRate = 30f;
private WebSocketSharp.Server.WebSocketServer server;
private MotionDataStreamer streamer;
public void StartServer() { /* 서버 시작 */ }
public void StopServer() { /* 서버 종료 */ }
public void BroadcastMotionData(MotionFrame frame) { /* 데이터 전송 */ }
}
```
#### 2. MotionDataStreamer.cs
```csharp
public class MotionDataStreamer : MonoBehaviour
{
[SerializeField] private Transform avatarRoot;
[SerializeField] private string[] boneNames;
private Dictionary<string, Transform> boneTransforms;
public MotionFrame CaptureCurrentFrame()
{
// 현재 아바타 상태를 MotionFrame으로 변환
}
public void SendMotionData()
{
// 실시간으로 모션 데이터 전송
}
}
```
#### 3. RemoteMotionRecorder.cs
```csharp
public class RemoteMotionRecorder : MonoBehaviour
{
private EasyMotionRecorder motionRecorder;
private bool isRecording = false;
public void StartRecording(string filename) { /* 녹화 시작 */ }
public void StopRecording() { /* 녹화 종료 */ }
public RecordingStatus GetStatus() { /* 상태 조회 */ }
}
```
### 데이터 구조
```csharp
[System.Serializable]
public class MotionFrame
{
public long timestamp;
public float frameRate;
public BoneData[] bones;
}
[System.Serializable]
public class BoneData
{
public string name;
public Vector3 position;
public Quaternion rotation;
public Vector3 scale;
}
```
## 🌐 웹 클라이언트 사양
### 핵심 모듈
#### 1. WebSocketClient.js
```javascript
class WebSocketClient {
constructor(url) {
this.url = url;
this.socket = null;
this.onMotionData = null;
this.onStatusUpdate = null;
}
connect() {
this.socket = new WebSocket(this.url);
this.setupEventHandlers();
}
sendCommand(command) {
this.socket.send(JSON.stringify(command));
}
setupEventHandlers() {
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
}
}
```
#### 2. MotionViewer.js
```javascript
class MotionViewer {
constructor(containerId) {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera();
this.renderer = new THREE.WebGLRenderer();
this.avatar = null;
this.bones = new Map();
}
loadAvatar(modelPath) {
// 3D 아바타 모델 로드
}
updateMotion(motionFrame) {
// 실시간 모션 데이터 적용
motionFrame.bones.forEach(bone => {
const boneObject = this.bones.get(bone.name);
if (boneObject) {
boneObject.position.set(bone.position.x, bone.position.y, bone.position.z);
boneObject.quaternion.set(bone.rotation.x, bone.rotation.y, bone.rotation.z, bone.rotation.w);
}
});
}
render() {
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(() => this.render());
}
}
```
#### 3. MotionRecorder.js
```javascript
class MotionRecorder {
constructor() {
this.isRecording = false;
this.motionData = [];
this.startTime = null;
}
startRecording() {
this.isRecording = true;
this.motionData = [];
this.startTime = Date.now();
}
addFrame(motionFrame) {
if (this.isRecording) {
this.motionData.push({
...motionFrame,
relativeTime: Date.now() - this.startTime
});
}
}
stopRecording() {
this.isRecording = false;
return this.motionData;
}
}
```
#### 4. MotionExporter.js
```javascript
class MotionExporter {
exportToBVH(motionData) {
// BVH 포맷으로 변환
const bvhContent = this.generateBVHContent(motionData);
return this.createDownloadableFile(bvhContent, 'motion.bvh');
}
exportToFBX(motionData) {
// FBX 포맷으로 변환 (Three.js FBXExporter 활용)
const fbxContent = this.generateFBXContent(motionData);
return this.createDownloadableFile(fbxContent, 'motion.fbx');
}
exportToJSON(motionData) {
// JSON 포맷으로 내보내기
const jsonContent = JSON.stringify(motionData, null, 2);
return this.createDownloadableFile(jsonContent, 'motion.json');
}
createDownloadableFile(content, filename) {
const blob = new Blob([content], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
}
```
## 🎨 사용자 인터페이스 사양
### HTML 구조
```html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>원격 모션캡쳐 시스템</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="app">
<!-- 연결 상태 -->
<div id="connection-status">
<span id="status-indicator"></span>
<span id="status-text">연결 대기 중...</span>
</div>
<!-- 3D 뷰어 -->
<div id="viewer-container">
<canvas id="three-canvas"></canvas>
</div>
<!-- 제어 패널 -->
<div id="control-panel">
<div id="recording-controls">
<button id="record-btn">⏺️ 녹화 시작</button>
<button id="stop-btn" disabled>⏹️ 정지</button>
<button id="play-btn" disabled>▶️ 재생</button>
</div>
<div id="recording-info">
<span id="timer">00:00:00</span>
<span id="frame-count">프레임: 0</span>
<span id="file-size">크기: 0KB</span>
</div>
<div id="export-controls">
<button id="export-bvh">BVH 다운로드</button>
<button id="export-fbx">FBX 다운로드</button>
<button id="export-json">JSON 다운로드</button>
</div>
</div>
<!-- 설정 패널 -->
<div id="settings-panel">
<h3>녹화 설정</h3>
<label>파일명: <input type="text" id="filename" value="motion_001"></label>
<label>프레임레이트:
<select id="framerate">
<option value="24">24 FPS</option>
<option value="30" selected>30 FPS</option>
<option value="60">60 FPS</option>
</select>
</label>
</div>
</div>
<script src="js/websocketClient.js"></script>
<script src="js/motionViewer.js"></script>
<script src="js/motionRecorder.js"></script>
<script src="js/motionExporter.js"></script>
<script src="js/main.js"></script>
</body>
</html>
```
### CSS 스타일 가이드
```css
/* 주요 색상 팔레트 */
:root {
--primary-color: #2c3e50;
--secondary-color: #3498db;
--success-color: #27ae60;
--warning-color: #f39c12;
--danger-color: #e74c3c;
--background-color: #ecf0f1;
}
/* 레이아웃 */
#app {
display: grid;
grid-template-areas:
"status status"
"viewer controls"
"viewer settings";
grid-template-columns: 2fr 1fr;
height: 100vh;
gap: 10px;
padding: 10px;
}
#viewer-container {
grid-area: viewer;
background: #2c3e50;
border-radius: 8px;
position: relative;
}
/* 반응형 디자인 */
@media (max-width: 768px) {
#app {
grid-template-areas:
"status"
"viewer"
"controls"
"settings";
grid-template-columns: 1fr;
}
}
```
## ⚡ 성능 최적화
### 네트워크 최적화
- **데이터 압축**: gzip 압축 적용
- **전송 주기**: 30fps 기본, 네트워크 상태에 따라 조절
- **델타 압축**: 이전 프레임과의 차이만 전송
- **버퍼링**: 클라이언트 사이드 적응형 버퍼
### 렌더링 최적화
- **LOD 시스템**: 거리에 따른 모델 디테일 조절
- **프러스텀 컬링**: 카메라 시야 밖 객체 제외
- **인스턴싱**: 반복 객체 최적화
- **텍스처 압축**: 적절한 해상도 및 포맷 사용
### 메모리 최적화
- **오브젝트 풀링**: 반복 생성/삭제 객체 재사용
- **가비지 컬렉션**: 불필요한 객체 생성 최소화
- **IndexedDB**: 대용량 모션 데이터 효율적 저장
## 🔒 보안 고려사항
### 데이터 보안
- **HTTPS/WSS**: 암호화된 통신 프로토콜 사용
- **토큰 인증**: JWT 기반 사용자 인증
- **데이터 무결성**: 체크섬을 통한 데이터 검증
### 네트워크 보안
- **CORS 설정**: 허용된 도메인에서만 접근
- **레이트 리미팅**: 과도한 요청 방지
- **방화벽 설정**: 필요한 포트만 개방
## 📊 모니터링 및 로깅
### 성능 메트릭
- **지연시간**: WebSocket 왕복 시간 측정
- **프레임 드롭**: 누락된 프레임 수 추적
- **대역폭 사용량**: 실시간 네트워크 사용량
- **CPU/메모리 사용률**: 시스템 리소스 모니터링
### 로그 시스템
```javascript
class Logger {
static logMotionData(frameData) {
console.log(`[MOTION] Frame ${frameData.frameNumber} received`);
}
static logError(error, context) {
console.error(`[ERROR] ${context}:`, error);
}
static logPerformance(metric, value) {
console.log(`[PERF] ${metric}: ${value}ms`);
}
}
```
## 🧪 테스트 전략
### Unity 단위 테스트
- WebSocket 서버 연결/해제 테스트
- 모션 데이터 직렬화/역직렬화 테스트
- 녹화 기능 통합 테스트
### 웹 클라이언트 테스트
- WebSocket 통신 테스트
- 3D 렌더링 성능 테스트
- 파일 내보내기 기능 테스트
### 통합 테스트
- 전체 시스템 End-to-End 테스트
- 장시간 연결 안정성 테스트
- 다양한 네트워크 환경 테스트
---
*이 기술 사양서는 개발 과정에서 지속적으로 업데이트됩니다.*