qsxft258@gmail.com f6a6034387 Fix: OptiTrack 플러그인 런타임 안전성 강화
- m_dataDescs null 체크 추가: SkipDataDescriptions=true 시 NatNet 스레드 크래시 방지
- m_dataDescs를 NatNet 콜백에서 로컬 변수로 캡처: UpdateDefinitions() 중 참조 교체 레이스 방지
- m_assetIdToNameCache 클리어를 락으로 보호: NatNet 스레드와의 동시 접근 방지
- _EnterFrameDataUpdateLock/_ExitFrameDataUpdateLock을 internal+Obsolete로 변경: 데드락 위험 차단
- OptitrackRawDataReceiver를 FillBoneSnapshot 패턴으로 변경: torn read 방지

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 19:11:14 +09:00

81 lines
2.7 KiB
C#

using UnityEngine;
using System.Collections.Generic;
public class OptitrackRawDataReceiver : MonoBehaviour
{
[Header("OptiTrack 설정")]
public OptitrackStreamingClient StreamingClient;
public string SkeletonAssetName = "Skeleton1";
private OptitrackSkeletonDefinition m_skeletonDef;
private Dictionary<int, OptitrackPose> m_lastBonePoses = new Dictionary<int, OptitrackPose>();
// FillBoneSnapshot 패턴: 락 안에서 데이터를 복사하여 torn read 방지
private Dictionary<int, Vector3> m_snapshotPositions = new Dictionary<int, Vector3>();
private Dictionary<int, Quaternion> m_snapshotOrientations = new Dictionary<int, Quaternion>();
void Start()
{
if (StreamingClient == null)
{
StreamingClient = FindFirstObjectByType<OptitrackStreamingClient>();
if (StreamingClient == null)
{
Debug.LogError("OptiTrack Streaming Client를 찾을 수 없습니다.");
return;
}
}
// 스켈레톤 등록
StreamingClient.RegisterSkeleton(this, SkeletonAssetName);
}
void Update()
{
if (StreamingClient == null) return;
// 스켈레톤 정의 가져오기
if (m_skeletonDef == null)
{
m_skeletonDef = StreamingClient.GetSkeletonDefinitionByName(SkeletonAssetName);
if (m_skeletonDef == null) return;
}
// FillBoneSnapshot으로 락 보호 하에 스냅샷 복사 (torn read 방지)
OptitrackHiResTimer.Timestamp ts;
if (!StreamingClient.FillBoneSnapshot(m_skeletonDef.Id, m_snapshotPositions, m_snapshotOrientations, out ts))
return;
// 스냅샷에서 OptitrackPose로 변환하여 저장
foreach (var bone in m_skeletonDef.Bones)
{
if (m_snapshotPositions.TryGetValue(bone.Id, out Vector3 pos) &&
m_snapshotOrientations.TryGetValue(bone.Id, out Quaternion ori))
{
if (!m_lastBonePoses.TryGetValue(bone.Id, out OptitrackPose existingPose))
{
existingPose = new OptitrackPose();
m_lastBonePoses[bone.Id] = existingPose;
}
existingPose.Position = pos;
existingPose.Orientation = ori;
}
}
}
// 외부에서 원본 데이터 접근을 위한 메서드
public Dictionary<int, OptitrackPose> GetRawBonePoses()
{
return m_lastBonePoses;
}
// 특정 본의 원본 데이터 가져오기
public OptitrackPose GetRawBonePose(int boneId)
{
if (m_lastBonePoses.TryGetValue(boneId, out OptitrackPose pose))
{
return pose;
}
return null;
}
}