Streamingle_URP/Assets/External/OptiTrack Unity Plugin
qsxft258@gmail.com 026eaf094b Add: OptiTrack MirrorMode 좌우 반전 기능 구현
- OptitrackStreamingClient.FillBoneSnapshot: L/R 쌍 본은 위치 유지 + 회전 YZ반사 후 스왑, 중심 본(Hip/척추 등)은 위치 X반전 + 회전 YZ반사
- OptitrackStreamingClient.GetLatestRigidBodyState: 리짓바디 위치·회전 YZ반사 (기존)
- GetOrBuildMirrorBoneIdMap: SkeletonName_BoneName 접두사 형식 지원
- 마커/TMarkerset 마커/TMarkerset BonePoses 미러 적용
- OptitrackSkeletonAnimator_Mingle: MirrorMode 토글 시 1€ 필터 상태 자동 리셋
- 월드 공간 미러 코드(ApplyWorldSpaceMirror 등) 제거, 데이터 수신 레벨에서 처리

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 18:05:04 +09:00
..

OptiTrack Unity Plugin

NaturalPoint OptiTrack 모션캡처 시스템과 Unity를 연결하는 플러그인. NatNet SDK(C# Managed Wrapper)를 통해 Motive 소프트웨어로부터 실시간 스켈레톤/리짓바디 데이터를 수신한다.


폴더 구조

OptiTrack Unity Plugin/
└── OptiTrack/
    ├── Scripts/                        # 핵심 런타임 스크립트
    │   ├── OptitrackStreamingClient.cs     ← 네트워크 클라이언트 (메인 허브)
    │   ├── OptitrackSkeletonAnimator_Mingle.cs ← 커스텀 스켈레톤 애니메이터 (Streamingle 전용)
    │   ├── OptitrackRigidBody.cs           ← 리짓바디 추적
    │   ├── OptitrackRawDataReceiver.cs     ← 원시 NatNet 데이터 수신
    │   ├── OptitrackVisualizer.cs          ← 씬뷰 디버그 시각화
    │   └── OptitrackTrainedMarkerset.cs    ← Trained Markerset 지원
    ├── Plugins/
    │   ├── Managed/NatNetLib/              ← C# NatNet 래퍼 (Client.cs, Native.cs)
    │   ├── x86/NatNetLib.dll               ← 32bit 네이티브 라이브러리
    │   └── x86_64/NatNetLib.dll            ← 64bit 네이티브 라이브러리
    ├── Editor/                             ← Inspector 커스텀 에디터
    ├── Prefabs/                            ← 씬 배치용 프리팹
    │   ├── Client - OptiTrack.prefab           ← StreamingClient 오브젝트
    │   ├── BaseAvatar - OptiTrack.prefab       ← 기본 스켈레톤 아바타
    │   ├── BaseAvatar - OptiTrack ver 2.prefab ← 개선된 스켈레톤 아바타
    │   └── Rigid Body.prefab                   ← 리짓바디 추적 오브젝트
    └── BaseAvatar/                         ← Y Bot 베이스 메시 에셋

핵심 클래스 설명

OptitrackStreamingClient

역할: Motive(서버) ↔ Unity(클라이언트) 간 NatNet 통신 허브

Inspector 설정:

항목 설명
ServerAddress Motive PC IP (로컬: 127.0.0.1)
LocalAddress Unity PC 네트워크 인터페이스 IP
ConnectionType Multicast / Unicast
SkeletonCoordinates Local (권장) / Global
BoneNamingConvention Motive / FBX / BVH
RecordOnPlay 재생 시 Motive 녹화 자동 시작
SkipDataDescriptions 리짓바디만 쓸 때 네트워크 절약

주요 공개 API:

// 스켈레톤 최신 상태 조회
OptitrackSkeletonState GetLatestSkeletonState(int skeletonId)

// 리짓바디 최신 상태 조회
OptitrackRigidBodyState GetLatestRigidBodyState(int rigidBodyId, bool useNetworkCompensation)

// 이름으로 스켈레톤 정의 조회
OptitrackSkeletonDefinition GetSkeletonDefinitionByName(string name)

// 이름으로 리짓바디 ID 조회
int GetRigidBodyIdByName(string name)

// 등록
void RegisterSkeleton(MonoBehaviour component, string skeletonAssetName)
void RegisterRigidBody(MonoBehaviour component, int rigidBodyId)

스레드 안전성:

  • OnNatNetFrameReceived는 백그라운드 스레드에서 호출됨
  • m_frameDataUpdateLock 으로 상태 딕셔너리 보호
  • Unity API 접근은 반드시 메인 스레드에서

OptitrackSkeletonAnimator_Mingle

역할: Streamingle 전용 커스텀 스켈레톤 애니메이터. 기존 원본 OptitrackSkeletonAnimator.cs를 대체함.

원본과의 차이점:

  • Humanoid Avatar 없이 FBX 노드 직접 매핑 방식
  • GetBoneTransform(HumanBodyBones) 제공 → KindRetargeting에서 Animator.GetBoneTransform() 대신 사용
  • 스파인/넥 체인 전용 헬퍼 메서드 (GetSpineChainTransforms, GetNeckChainTransforms)
  • torn read 방지 스냅샷 버퍼 적용
  • 0.1mm 이하 위치 노이즈, 0.00001 이하 쿼터니언 노이즈 스냅 처리

본 매핑 구조 (OptiTrackBoneMapping):

Motive 본 이름 (optiTrackBoneName) → FBX 노드 이름 (fbxNodeName) → Transform (cachedTransform)

Motive → FBX 기본 본 이름 매핑:

Motive (OptiTrack) FBX 노드 접미사 Unity HumanBodyBones
Hip Hips Hips
Ab Spine Spine
Spine2~4 Spine1~3 Chest 분배
Chest Spine4 UpperChest
Neck / Neck2 Neck / Neck1 Neck 분배
Head Head Head
LShoulder / LUArm / LFArm / LHand LeftShoulder~Hand 왼팔
RShoulder / RUArm / RFArm / RHand RightShoulder~Hand 오른팔
LThigh / LShin / LFoot / LToe LeftUpLeg~ToeBase 왼다리
RThigh / RShin / RFoot / RToe RightUpLeg~ToeBase 오른다리
LThumb1~3, LIndex1~3 ... LeftHandThumb1~3 ... 왼손 손가락
RThumb1~3, RIndex1~3 ... RightHandThumb1~3 ... 오른손 손가락

Inspector에서 매핑 설정 방법:

  1. Inspector → FBX 분석 → 자동 매핑 생성 버튼 클릭
  2. 매핑 실패 본은 logUnmappedBones = true로 확인
  3. 수동 수정 가능 (boneMappings 리스트)

주요 공개 API (KindRetargeting 연동):

// HumanBodyBones로 Transform 조회 (CustomRetargetingScript에서 사용)
Transform GetBoneTransform(HumanBodyBones bone)

// OptiTrack 본 이름으로 Transform 직접 조회
Transform GetMappedTransform(string optiTrackBoneName)

// 스파인/넥 체인 Transform 리스트 반환
List<Transform> GetSpineChainTransforms()   // Ab → Spine2 → Spine3 → Spine4 → Chest
List<Transform> GetNeckChainTransforms()    // Neck → Neck2

// 체인 누적 로컬 회전 계산
static Quaternion ComputeChainRotation(List<Transform> chain)

실행 순서: [DefaultExecutionOrder(-100)] — 대부분의 스크립트보다 먼저 실행


OptitrackRigidBody

역할: Motive에서 스트리밍된 리짓바디의 위치/회전을 실시간으로 GameObject에 적용

요구 컴포넌트: PropTypeController (KindRetargeting 의존성)

동작:

  • propName 설정 시 이름으로 ID 자동 해석 (ID 직접 입력 불필요)
  • useNetworkCompensation: 네트워크 지연 보상 활성화
  • OnBeforeRender 훅으로 렌더 직전 최신 포즈 적용 (레이턴시 최소화)

데이터 클래스 (상태/정의)

OptitrackSkeletonDefinition
  └── BoneDefinition[]  (Id, ParentId, Name, Offset)
  └── BoneIdToParentIdMap Dictionary<int,int>

OptitrackSkeletonState
  └── BonePoses Dictionary<int, OptitrackPose>       ← 전역 좌표
  └── LocalBonePoses Dictionary<int, OptitrackPose>  ← 로컬 좌표

OptitrackRigidBodyState
  └── Pose (Position, Orientation)
  └── IsTracked
  └── DeliveryTimestamp

OptitrackPose
  └── Position (Vector3)
  └── Orientation (Quaternion)

씬 설정 절차

기본 설정

  1. Client - OptiTrack 프리팹 씬에 배치
  2. ServerAddress = Motive PC IP 설정
  3. LocalAddress = 현재 Unity PC IP 설정
  4. ConnectionType = Unicast (단일 PC 권장) 또는 Multicast
  5. SkeletonCoordinates = Local

스켈레톤 추적 설정

  1. BaseAvatar - OptiTrack ver 2 프리팹 씬에 배치
  2. SkeletonAssetName = Motive에서 설정한 스켈레톤 에셋 이름 (예: "Skeleton1")
  3. Inspector → FBX 분석 → 자동 매핑 생성 실행
  4. Play Mode 진입 후 isSkeletonFound = true 확인

리짓바디 추적 설정

  1. Rigid Body 프리팹 배치
  2. propName = Motive 리짓바디 이름 설정 (또는 rigidBodyId 직접 입력)

KindRetargeting과의 연동

// CustomRetargetingScript.cs 에서 optitrackSource 참조
[SerializeField] public OptitrackSkeletonAnimator_Mingle optitrackSource;

// 본 Transform 접근 래퍼
private Transform GetSourceBoneTransform(HumanBodyBones bone)
{
    if (optitrackSource != null)
        return optitrackSource.GetBoneTransform(bone);
    return null;
}

의존 방향:

KindRetargeting.CustomRetargetingScript
    → OptitrackSkeletonAnimator_Mingle.GetBoneTransform(HumanBodyBones)
        → OptitrackStreamingClient.GetLatestSkeletonState()
            → NatNetLib (네이티브 DLL)

주의사항

  • NatNetLib.dll은 x86/x86_64 두 버전 존재 — 빌드 타겟에 맞게 설정 확인
  • OptitrackStreamingClient.csusing UnityEditor 포함 → 빌드 시 에러 발생 가능 (Editor 전용 코드는 #if UNITY_EDITOR 처리 필요)
  • Motive 버전에 따라 BoneNamingConvention이 다를 수 있음 — 매핑 실패 시 확인
  • SkeletonCoordinates = Local 사용 시 BonePoses가 아닌 LocalBonePoses 사용
  • 스켈레톤 본 이름 앞에 Motive가 접두사(SkeletonName_)를 붙임 → Mingle에서 _ 기준 split 처리

디버그

방법 설명
logUnmappedBones = true 매핑 실패한 OptiTrack 본을 콘솔에 출력
DrawMarkers = true 씬뷰에 마커 큐브 시각화
DrawCameras = true OptiTrack 카메라 위치 시각화 (Motive 3.0+)
isSkeletonFound 확인 Inspector에서 연결 성공 여부 확인
debugReceivedBoneNames Motive에서 수신된 실제 본 목록 Inspector 확인