qsxft258@gmail.com 5aa805e16a Optimize: OptiTrack 플러그인 Motive 부하 최적화
- SetProperty 원격 명령을 최초 연결에서만 전송, 재연결 시 스킵
- 정의 재조회(UpdateDefinitions) 쿨다운 5초→15초, 최대 10회 제한
- DrawMarkers/DrawTMarkersetMarkers/DrawCameras/DrawForcePlates 락 범위 축소
- RecordOnPlay 재연결 루프에서 녹화 시작/종료 스킵, 성공 후에만 재시작
- SubscribeMarkers 실패 시 10초 쿨다운 (매 프레임 재시도 방지)
- OnNatNetFrameReceived 내 GetSkeletonDefinitionById를 본 루프 밖으로 이동
- GetMarkerName assetID→이름 캐시 도입 (3중 선형 탐색 제거)
- OptitrackRigidBody Update+OnBeforeRender 이중 NatNet 호출 제거 (캐싱)
- OptitrackSkeletonAnimator_Mingle 스켈레톤 체크 주기 0.1초→1초+지수 백오프
- ToggleRecording 녹화 상태 추적으로 2중 명령 제거
- ResetStreamingSubscriptions 중복 명령 축소
- GetLatestTMarkMarkerStates 디버그 로그 잔재 제거

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

169 lines
4.9 KiB
C#

/*
Copyright © 2016 NaturalPoint Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System;
using System.Collections;
using KindRetargeting;
using UnityEngine;
/// <summary>
/// Implements live tracking of streamed OptiTrack rigid body data onto an object.
/// </summary>
[RequireComponent(typeof(PropTypeController))]
public class OptitrackRigidBody : MonoBehaviour
{
[Tooltip("The ID of the rigid body to track.")]
public int rigidBodyId;
[Tooltip("The name of the prop to track. If set, this will override the rigidBodyId.")]
public string propName;
[Tooltip("Whether to use network compensation for this rigid body.")]
public bool useNetworkCompensation = true;
private OptitrackStreamingClient m_streamingClient;
private bool m_isRigidBodyFound = false;
private int m_resolvedRigidBodyId = -1;
private const float k_RetryInterval = 3.0f; // Motive 재조회 부하 완화 (기존 1초 → 3초)
// Update()에서 조회한 포즈를 캐시 — OnBeforeRender()에서 재사용하여 이중 NatNet 호출 방지
private Vector3 m_cachedPosition;
private Quaternion m_cachedRotation;
private bool m_hasCachedPose = false;
public bool isRigidBodyFound
{
get { return m_isRigidBodyFound; }
}
void Start()
{
m_streamingClient = OptitrackStreamingClient.FindDefaultClient();
if (m_streamingClient == null)
{
Debug.LogError("OptitrackRigidBody: No OptitrackStreamingClient found in scene.", this);
return;
}
TryResolveRigidBody();
// 초기 해결 실패 시 주기적으로 재시도
if (m_resolvedRigidBodyId == -1 && !string.IsNullOrEmpty(propName))
{
StartCoroutine(RetryResolveCoroutine());
}
}
private void TryResolveRigidBody()
{
if (!string.IsNullOrEmpty(propName))
{
int resolved = m_streamingClient.GetRigidBodyIdByName(propName);
if (resolved != -1)
{
m_resolvedRigidBodyId = resolved;
m_streamingClient.RegisterRigidBody(this, m_resolvedRigidBodyId);
Debug.Log($"OptitrackRigidBody: '{propName}' 해결 완료 (ID: {m_resolvedRigidBodyId})", this);
}
}
else
{
m_resolvedRigidBodyId = rigidBodyId;
m_streamingClient.RegisterRigidBody(this, m_resolvedRigidBodyId);
}
}
private IEnumerator RetryResolveCoroutine()
{
var wait = new WaitForSeconds(k_RetryInterval);
while (m_resolvedRigidBodyId == -1)
{
yield return wait;
if (m_streamingClient == null) yield break;
// 서버에 정의 재조회 요청
m_streamingClient.RequestDefinitionRefresh();
// 다음 프레임까지 대기 (정의 갱신이 Update에서 처리되므로)
yield return null;
TryResolveRigidBody();
}
}
#if UNITY_2017_1_OR_NEWER
void OnEnable()
{
Application.onBeforeRender += OnBeforeRender;
}
void OnDisable()
{
Application.onBeforeRender -= OnBeforeRender;
}
void OnBeforeRender()
{
UpdatePose();
}
#endif
void Update()
{
if (m_streamingClient == null || m_resolvedRigidBodyId == -1)
return;
// NatNet 호출은 Update()에서 1회만 — 캐시하여 OnBeforeRender()에서 재사용
OptitrackRigidBodyState rbState = m_streamingClient.GetLatestRigidBodyState(m_resolvedRigidBodyId, useNetworkCompensation);
if (rbState != null)
{
m_isRigidBodyFound = rbState.IsTracked;
if (m_isRigidBodyFound)
{
m_cachedPosition = rbState.Pose.Position;
m_cachedRotation = rbState.Pose.Orientation;
m_hasCachedPose = true;
transform.localPosition = m_cachedPosition;
transform.localRotation = m_cachedRotation;
}
}
else
{
m_isRigidBodyFound = false;
m_hasCachedPose = false;
}
}
void UpdatePose()
{
// OnBeforeRender용: NatNet 재호출 없이 캐시된 포즈 적용 (렌더링 직전 최신 적용)
if (m_hasCachedPose && m_isRigidBodyFound)
{
transform.localPosition = m_cachedPosition;
transform.localRotation = m_cachedRotation;
}
}
}