/* 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; /// /// Implements live tracking of streamed OptiTrack rigid body data onto an object. /// [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; } } }