Skip to content

Latest commit

 

History

History
147 lines (103 loc) · 6.38 KB

File metadata and controls

147 lines (103 loc) · 6.38 KB

GLB 로딩 및 감정/모션 구성 정리

이 문서는 현재 apps/dawnlight 구현 기준으로, 플레이어 아바타의 GLB 로딩 방식과 감정/모션 처리 방식을 정리합니다.

1) 사용 에셋

  • 모델 GLB: https://static.dawn.lt/glb/leafy-explorer/leafy-explorer-v2.glb
  • 애니메이션 GLB: https://static.dawn.lt/glb/leafy-explorer/leafy-explorer-anim.glb
  • 스마일 텍스처: https://static.dawn.lt/glb/leafy-explorer/smile.webp

코드 위치:

  • apps/dawnlight/src/game/engine/player-avatar.ts
  • apps/dawnlight/src/components/dawnlight-ui/avatar-preview.tsx

2) 런타임 로딩 진입점

  1. 게임 엔진 초기화 시 PlayerAvatar 인스턴스를 생성합니다.
  2. 즉시 playerAvatar.load()를 비동기로 호출합니다.

코드 위치:

  • apps/dawnlight/src/game/engine/game-engine.ts

3) GLB 로딩 방식

PlayerAvatar.load() 내부 동작:

  1. GLTFLoader + MeshoptDecoder 설정
  • EXT_meshopt_compression 압축된 GLB를 읽기 위해 loader.setMeshoptDecoder(MeshoptDecoder)를 사용합니다.
  1. 모델/애니메이션 병렬 로딩
  • Promise.all([modelGLB, animationGLB])로 동시에 다운로드합니다.
  • 애니메이션 클립은 animationGLB.animations가 우선이며, 비어 있으면 모델의 내장 애니메이션을 fallback으로 사용합니다.
  1. 모델 준비(prepareAvatarModel)
  • 모든 Mesh를 순회하며 머티리얼 정규화(MeshBasicMaterial -> MeshStandardMaterial 변환 포함).
  • 투명 + 텍스처 조합은 alphaTest 기반으로 정리.
  • emissive를 제어 가능한 상태로 초기화.
  • 바운딩 박스 기준으로 아바타 높이를 PLAYER_HEIGHT * 1.02에 맞춰 스케일.
  • 바닥 기준으로 Y 오프셋 보정.
  • Hip/Pelvis 본 탐색(루트 모션 보정용).
  1. 애니메이션 액션 구성(setupAvatarAnimations)
  • AnimationMixer 생성.
  • 클립별 AnimationAction 등록(LoopRepeat, Infinity).
  • idle/crouch/sneak/walk/run/jump/swimForward/swimIdle 액션을 alias/패턴으로 탐색해 바인딩.
  1. 씬에 부착
  • 로딩/설정 완료된 modelRoot를 씬에 추가.

4) 모션(애니메이션) 상태 구성

프레임 업데이트는 AnimationLoop에서 updateLoopPlayerAvatar(...)를 통해 playerAvatar.update(...)를 매 틱 호출하는 구조입니다.

코드 위치:

  • apps/dawnlight/src/game/engine/animation-loop.ts
  • apps/dawnlight/src/game/engine/animation-loop-camera.ts

update(...)의 상태 결정 우선순위:

  1. 강제 디버그/감정 액션이 있으면 해당 액션 고정 재생
  2. 수영 상태면 swimForward/swimIdle
  3. 앉기(crouch) 상태면 crouch 액션
  4. 점프 조건 충족 시 jump 액션
  5. 이동 중이면 sneak/run/walk 중 하나
  6. 이동이 없으면 idle 사이클(여러 idle 액션 랜덤 순환)

추가 동작:

  • 이동 방향(intendedMoveYaw)에 따라 3인칭 아바타 Yaw를 보간 회전.
  • 클립 루트 모션이 캐릭터 위치를 밀어내지 않도록 수평 보정(hipBone 기준) 적용.
  • 야간 밝기 대응으로 emissive intensity를 태양 강도에 따라 보정.

5) 애니메이션 클립 매핑 방식

현재는 클립 이름과 실제 동작이 1:1이 아닌 리타게팅/오염 데이터가 있어, alias 매핑 테이블(CLIP_ACTUAL_MOTION_BY_LABEL)을 통해 실제 의미를 재해석합니다.

핵심 함수:

  • findClipByAlias(...)
  • findClip(...)
  • findClipNameByAlias(...)
  • findClipNameByPattern(...)

또한 crouch에 대해서는 createHorizontalRootLockedClip(...)으로 X/Z 이동 트랙을 고정한 클립을 만들어 사용합니다.

코드 위치:

  • apps/dawnlight/src/game/engine/player-avatar-setup.ts

6) 감정(Emotion) 구성

A. 사용자 트리거

  1. 모바일 UI 감정 버튼
  • EmotionButton -> onTriggerEmotion -> handleMobileEmotion
  • 동작: playGreetingForDuration(2000) + playSmileExpressionForDuration(2000)

코드 위치:

  • apps/dawnlight/src/components/virtual-joystick/emotion-button.tsx
  • apps/dawnlight/src/components/virtual-joystick/virtual-joystick.tsx
  • apps/dawnlight/src/components/dawnlight.tsx
  1. 채팅 명령
  • /emotion smile: 스마일 표정만 2초 요청
  • /hello: 웨이브 + 스마일 2초

코드 위치:

  • apps/dawnlight/src/components/dawnlight-chat.ts
  • apps/dawnlight/src/hooks/use-chat-commands.ts

B. 감정 실행 방식

  1. Greeting(웨이브)
  • playGreetingForDurationwave_for_help_1 alias 또는 (wave|greet|hello|hand) 패턴 클립을 찾아 강제 재생합니다.
  • 강제 상태는 일정 시간 후 자동 해제되며, 이동/점프/특정 상태 변화 시 중간 해제될 수 있습니다.
  1. Smile(표정)
  • playSmileExpressionForDuration은 smile 텍스처 프리셋을 적용해 얼굴 텍스처를 교체합니다.
  • 최초 호출 시 비동기 프리로드 후 적용, 이후는 캐시된 프리셋 재사용.
  • 만료 시간이 지나면 기본 텍스처 프리셋으로 자동 복원.
  • 현재 구현상 playSmileExpressionForDuration(...)는 preload 성공/실패와 무관하게 즉시 true를 반환합니다.
  • 따라서 채팅의 실패 문구 분기(/emotion smile 실패)는 사실상 잘 발생하지 않고, 실제 preload 실패는 콘솔 에러 로그로 확인하는 구조입니다.
  1. 랜덤 스마일
  • 별도 트리거 없이도 5~15초 간격으로 1초짜리 랜덤 스마일이 자동 발생합니다.

코드 위치:

  • apps/dawnlight/src/game/engine/player-avatar.ts
  • apps/dawnlight/src/game/engine/player-avatar-behavior.ts

7) 프리뷰(AvatarPreview)에서의 반영

UI 좌상단 아바타 프리뷰도 동일 GLB/애니메이션 GLB를 별도 로딩합니다.

  • 런타임 아바타의 현재 클립 이름(getCurrentClipName)을 읽어 동일 클립으로 동기화 재생.
  • 프리뷰에서도 hip 기반 수평 루트 모션 보정을 적용해 화면 내 위치 드리프트를 줄입니다.

코드 위치:

  • apps/dawnlight/src/components/dawnlight-ui/avatar-preview.tsx

8) 에셋 최적화 파이프라인(참고)

개발 스크립트 scripts/optimize-gltf-assets.mjsasset-origin의 GLB를 asset-optimized로 변환합니다.

  • glTF Transform CLI(@gltf-transform/cli)로 meshopt 압축 적용
  • 텍스처 포맷/크기 조정
  • 파일명에 animation 문자열이 포함된 경우에만 animation pack으로 분류하여 더 공격적인 텍스처 축소 정책 사용

코드 위치:

  • apps/dawnlight/scripts/optimize-gltf-assets.mjs