diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Prefabs/Client - OptiTrack.prefab b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Prefabs/Client - OptiTrack.prefab
index 97689afd9..3933c493e 100644
--- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Prefabs/Client - OptiTrack.prefab
+++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Prefabs/Client - OptiTrack.prefab
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:43ef0abb5666904f8ce55aa84e14e8fbb95098937c4ff895a7fbb67a83254084
-size 5049
+oid sha256:f93d9e4ac45a52115d731e674821e0229dca8bf00730b6c4c91dc0c988fad065
+size 1988
diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/UXML/OptitrackStreamingClientEditor.uxml b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/UXML/OptitrackStreamingClientEditor.uxml
index 1e5c09f4c..c85e656a0 100644
--- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/UXML/OptitrackStreamingClientEditor.uxml
+++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/UXML/OptitrackStreamingClientEditor.uxml
@@ -35,14 +35,21 @@
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs
deleted file mode 100644
index 713d9c293..000000000
--- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs
+++ /dev/null
@@ -1,229 +0,0 @@
-//======================================================================================================
-// OptitrackFaceDevice
-//
-// Subscribes to one or more analog devices that the iFacialMocapPlugin (Motive plugin)
-// publishes via NatNet, and applies blendshape weights + (optionally) head/eye pose to
-// SkinnedMeshRenderers / Transforms.
-//
-// The plugin splits a face stream when total channels exceed NatNet's 32-per-device limit:
-// - (e.g. "iFacialMocap_40001") - single device, <= 32 channels
-// - _A + _B - two devices when > 32 channels
-//
-// Set DeviceBaseName to the base ("iFacialMocap_40001"); this component automatically
-// finds and merges _A and _B if they exist.
-//
-// Pose channel names produced by the plugin:
-// head_eulerX/Y/Z, head_posX/Y/Z, leftEye_eulerX/Y/Z, rightEye_eulerX/Y/Z
-//======================================================================================================
-
-using System;
-using System.Collections.Generic;
-using UnityEngine;
-
-[DefaultExecutionOrder(-50)]
-public class OptitrackFaceDevice : MonoBehaviour
-{
- [Tooltip("OptitrackStreamingClient instance to read device data from. Falls back to FindObjectOfType<>() at Start if null.")]
- public OptitrackStreamingClient streamingClient;
-
- [Tooltip("Motive device base name (e.g. \"iFacialMocap_40001\"). _A/_B suffixes are auto-detected if the device was split.")]
- public string deviceBaseName = "iFacialMocap_40001";
-
- [Header("Output: Blendshapes")]
- [Tooltip("SkinnedMeshRenderers whose blendshape weights will be driven by incoming channel values. Channel names are matched against blendshape names (case-sensitive, with _L/_R or Left/Right tolerance).")]
- public SkinnedMeshRenderer[] faceMeshRenderers;
-
- [Tooltip("Multiply incoming blendshape values (0-100 typically) by this scalar before applying.")]
- [Range(0f, 3f)]
- public float blendshapeScale = 1.0f;
-
- [Header("Output: Head Pose (optional)")]
- [Tooltip("If set, applies head_eulerXYZ to this Transform's localEulerAngles. Leave null to skip.")]
- public Transform headBone;
-
- [Tooltip("If set, applies leftEye_eulerXYZ to this Transform.")]
- public Transform leftEyeBone;
-
- [Tooltip("If set, applies rightEye_eulerXYZ to this Transform.")]
- public Transform rightEyeBone;
-
- [Header("Diagnostics (read-only)")]
- [SerializeField] private bool _connected;
- [SerializeField] private int _resolvedDeviceCount;
- [SerializeField] private int _totalChannelCount;
- [SerializeField] private int _appliedBlendshapeCount;
- [SerializeField] private float _lastJawOpen;
- [SerializeField] private float _lastEyeBlinkLeft;
-
- // Pre-resolved channel name -> (renderer, blendshape index) for O(1) apply.
- private struct BlendShapeMapping
- {
- public SkinnedMeshRenderer renderer;
- public int index;
- }
- private Dictionary> _blendshapeCache;
- private bool _cacheBuilt;
-
- // Resolved device names (1 or 2: base, base_A+_B)
- private List _resolvedDeviceNames = new List(2);
- private Dictionary _channelSnapshot = new Dictionary();
- private float _lastResolveAttempt = -10f;
- private const float k_ResolveRetrySeconds = 1.0f;
-
- void Start()
- {
- if (streamingClient == null)
- streamingClient = FindFirstObjectByType();
-
- if (streamingClient != null && !streamingClient.ReceiveDevices)
- {
- Debug.LogWarning($"[OptitrackFaceDevice] {streamingClient.name}.ReceiveDevices is OFF. " +
- "Enable it on the streaming client to get face data.", this);
- }
-
- BuildBlendshapeCache();
- }
-
- void BuildBlendshapeCache()
- {
- _blendshapeCache = new Dictionary>(StringComparer.OrdinalIgnoreCase);
- if (faceMeshRenderers == null) return;
-
- foreach (var smr in faceMeshRenderers)
- {
- if (smr == null || smr.sharedMesh == null) continue;
- for (int i = 0; i < smr.sharedMesh.blendShapeCount; ++i)
- {
- string name = smr.sharedMesh.GetBlendShapeName(i);
- AddToCache(name, smr, i);
-
- // Also accept _L/_R shorthand <-> Left/Right canonical
- if (name.EndsWith("_L"))
- AddToCache(name.Substring(0, name.Length - 2) + "Left", smr, i);
- else if (name.EndsWith("_R"))
- AddToCache(name.Substring(0, name.Length - 2) + "Right", smr, i);
- else if (name.EndsWith("Left"))
- AddToCache(name.Substring(0, name.Length - 4) + "_L", smr, i);
- else if (name.EndsWith("Right"))
- AddToCache(name.Substring(0, name.Length - 5) + "_R", smr, i);
- }
- }
- _cacheBuilt = true;
- }
-
- void AddToCache(string key, SkinnedMeshRenderer smr, int idx)
- {
- if (!_blendshapeCache.TryGetValue(key, out var list))
- {
- list = new List(1);
- _blendshapeCache[key] = list;
- }
- list.Add(new BlendShapeMapping { renderer = smr, index = idx });
- }
-
- void TryResolveDevices()
- {
- // Probe for base, base_A, base_B. Devices appear dynamically as the
- // plugin sees its first packet, so retry every k_ResolveRetrySeconds.
- if (Time.unscaledTime - _lastResolveAttempt < k_ResolveRetrySeconds) return;
- _lastResolveAttempt = Time.unscaledTime;
-
- _resolvedDeviceNames.Clear();
- _totalChannelCount = 0;
-
- var defs = streamingClient.GetDeviceDefinitions();
- foreach (var d in defs)
- {
- if (d.Name == deviceBaseName ||
- d.Name == deviceBaseName + "_A" ||
- d.Name == deviceBaseName + "_B")
- {
- _resolvedDeviceNames.Add(d.Name);
- _totalChannelCount += d.ChannelCount;
- }
- }
- _resolvedDeviceCount = _resolvedDeviceNames.Count;
- _connected = _resolvedDeviceCount > 0;
- }
-
- void Update()
- {
- if (streamingClient == null) { _connected = false; return; }
- if (!_cacheBuilt) BuildBlendshapeCache();
-
- if (_resolvedDeviceNames.Count == 0)
- {
- TryResolveDevices();
- if (_resolvedDeviceNames.Count == 0) return;
- }
-
- // Pose accumulators (last device wins if both _A and _B somehow have pose
- // — in practice pose lives entirely in one device).
- Vector3? headEuler = null, headPos = null, leftEyeEuler = null, rightEyeEuler = null;
-
- int appliedBs = 0;
- for (int i = 0; i < _resolvedDeviceNames.Count; ++i)
- {
- if (!streamingClient.FillDeviceChannelSnapshot(_resolvedDeviceNames[i], _channelSnapshot))
- continue;
-
- foreach (var kv in _channelSnapshot)
- {
- string name = kv.Key;
- float v = kv.Value;
-
- // Pose channels?
- switch (name)
- {
- case "head_eulerX": headEuler = With(headEuler, 0, v); continue;
- case "head_eulerY": headEuler = With(headEuler, 1, v); continue;
- case "head_eulerZ": headEuler = With(headEuler, 2, v); continue;
- case "head_posX": headPos = With(headPos, 0, v); continue;
- case "head_posY": headPos = With(headPos, 1, v); continue;
- case "head_posZ": headPos = With(headPos, 2, v); continue;
- case "leftEye_eulerX": leftEyeEuler = With(leftEyeEuler, 0, v); continue;
- case "leftEye_eulerY": leftEyeEuler = With(leftEyeEuler, 1, v); continue;
- case "leftEye_eulerZ": leftEyeEuler = With(leftEyeEuler, 2, v); continue;
- case "rightEye_eulerX": rightEyeEuler = With(rightEyeEuler, 0, v); continue;
- case "rightEye_eulerY": rightEyeEuler = With(rightEyeEuler, 1, v); continue;
- case "rightEye_eulerZ": rightEyeEuler = With(rightEyeEuler, 2, v); continue;
- }
-
- // Blendshape — apply scaled value to all matching renderers.
- if (_blendshapeCache != null && _blendshapeCache.TryGetValue(name, out var maps))
- {
- float weight = Mathf.Clamp(v * blendshapeScale, 0f, 100f);
- foreach (var m in maps)
- {
- if (m.renderer != null)
- m.renderer.SetBlendShapeWeight(m.index, weight);
- }
- appliedBs++;
- }
-
- // Diagnostics
- if (name == "jawOpen") _lastJawOpen = v;
- if (name == "eyeBlink_L" || name == "eyeBlinkLeft") _lastEyeBlinkLeft = v;
- }
- }
- _appliedBlendshapeCount = appliedBs;
-
- // Apply pose
- if (headBone != null && headEuler.HasValue)
- headBone.localEulerAngles = headEuler.Value;
- if (leftEyeBone != null && leftEyeEuler.HasValue)
- leftEyeBone.localEulerAngles = leftEyeEuler.Value;
- if (rightEyeBone != null && rightEyeEuler.HasValue)
- rightEyeBone.localEulerAngles = rightEyeEuler.Value;
- }
-
- // Helper: set component i of a Vector3?, initializing to zero if null.
- private static Vector3 With(Vector3? cur, int axis, float v)
- {
- Vector3 r = cur ?? Vector3.zero;
- if (axis == 0) r.x = v;
- else if (axis == 1) r.y = v;
- else r.z = v;
- return r;
- }
-}
diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs.meta b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs.meta
deleted file mode 100644
index 6b953355d..000000000
--- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 5ba2193cab25f3f48b3e660ae26e8c3b
\ No newline at end of file
diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackRigidBody.cs b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackRigidBody.cs
index cd403c6d1..ded6eda7a 100644
--- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackRigidBody.cs
+++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackRigidBody.cs
@@ -15,7 +15,6 @@ limitations under the License.
*/
using System;
-using System.Collections;
using KindRetargeting;
using UnityEngine;
@@ -38,6 +37,7 @@ public class OptitrackRigidBody : MonoBehaviour
private OptitrackStreamingClient m_streamingClient;
private bool m_isRigidBodyFound = false;
private int m_resolvedRigidBodyId = -1;
+ private float m_nextResolveAttemptTime = 0f;
private const float k_RetryInterval = 3.0f; // Motive 재조회 부하 완화 (기존 1초 → 3초)
@@ -61,54 +61,34 @@ public class OptitrackRigidBody : MonoBehaviour
}
TryResolveRigidBody();
-
- // 초기 해결 실패 시 주기적으로 재시도
- if (m_resolvedRigidBodyId == -1 && !string.IsNullOrEmpty(propName))
- {
- StartCoroutine(RetryResolveCoroutine());
- }
}
- private void TryResolveRigidBody()
+ private bool TryResolveRigidBody()
{
if (!string.IsNullOrEmpty(propName))
{
int resolved = m_streamingClient.GetRigidBodyIdByName(propName);
if (resolved != -1)
{
+ if (m_resolvedRigidBodyId != -1 && m_resolvedRigidBodyId != resolved)
+ m_streamingClient.UnregisterRigidBody(this, m_resolvedRigidBodyId);
+
m_resolvedRigidBodyId = resolved;
m_streamingClient.RegisterRigidBody(this, m_resolvedRigidBodyId);
Debug.Log($"OptitrackRigidBody: '{propName}' 해결 완료 (ID: {m_resolvedRigidBodyId})", this);
+ return true;
}
}
else
{
m_resolvedRigidBodyId = rigidBodyId;
m_streamingClient.RegisterRigidBody(this, m_resolvedRigidBodyId);
+ return true;
}
+
+ return false;
}
- 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()
{
@@ -131,9 +111,15 @@ public class OptitrackRigidBody : MonoBehaviour
void Update()
{
- if (m_streamingClient == null || m_resolvedRigidBodyId == -1)
+ if (m_streamingClient == null)
return;
+ if (m_resolvedRigidBodyId == -1)
+ {
+ RetryResolveIfNeeded();
+ return;
+ }
+
// NatNet 호출은 Update()에서 1회만 — 캐시하여 OnBeforeRender()에서 재사용
OptitrackRigidBodyState rbState = m_streamingClient.GetLatestRigidBodyState(m_resolvedRigidBodyId, useNetworkCompensation);
if (rbState != null)
@@ -153,6 +139,20 @@ public class OptitrackRigidBody : MonoBehaviour
m_isRigidBodyFound = false;
m_hasCachedPose = false;
}
+
+ if (!m_isRigidBodyFound && !string.IsNullOrEmpty(propName))
+ RetryResolveIfNeeded();
+ }
+
+
+ private void RetryResolveIfNeeded()
+ {
+ if (Time.unscaledTime < m_nextResolveAttemptTime)
+ return;
+
+ m_nextResolveAttemptTime = Time.unscaledTime + k_RetryInterval;
+ m_streamingClient.RequestDefinitionRefresh();
+ TryResolveRigidBody();
}
diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs
index 27509ca0c..d10f83912 100644
--- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs
+++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs
@@ -1,5 +1,5 @@
-/*
-Copyright 짤 2016 NaturalPoint Inc.
+/*
+Copyright (c) 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.
@@ -11,7 +11,7 @@ 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.
+limitations under the License.
*/
using System;
@@ -71,7 +71,7 @@ public class OptitrackSkeletonState
/// Maps from OptiTrack bone IDs to their corresponding bone poses.
public Dictionary BonePoses;
public Dictionary LocalBonePoses;
- /// NatNet ?꾨젅?꾩씠 ?섏떊???쒓컖 (怨좏빐?곷룄 ?섎뱶?⑥뼱 ??대㉧ 湲곕컲).
+ /// Timestamp for received NatNet frame data.
public OptitrackHiResTimer.Timestamp DeliveryTimestamp;
}
@@ -231,29 +231,6 @@ public class OptitrackCameraDefinition
public Quaternion Orientation;
}
-/// Definition of a generic analog device (e.g. iFacialMocap face stream).
-public class OptitrackDeviceDefinition
-{
- public Int32 Id;
- public string Name;
- public string SerialNumber;
- public Int32 DeviceType;
- public Int32 ChannelDataType;
- public Int32 ChannelCount;
- public List ChannelNames;
-}
-
-/// Latest sampled frame for a generic analog device.
-public class OptitrackDeviceState
-{
- public Int32 Id;
- public string Name;
- /// Channel values keyed by channel name (matches ChannelNames in definition).
- public Dictionary ChannelValues;
- public Int32 FrameNumber;
- public OptitrackHiResTimer.Timestamp DeliveryTimestamp;
-}
-
public static class OptitrackHiResTimer
{
public struct Timestamp
@@ -290,8 +267,6 @@ public class OptitrackStreamingClient : MonoBehaviour
{
private const int k_DefaultNatNetCommandPort = 1510;
private const int k_DefaultNatNetDataPort = 1511;
- private const int k_MaxNatNetDevices = 32;
-
public enum ClientConnectionType
{
Multicast,
@@ -364,9 +339,6 @@ public class OptitrackStreamingClient : MonoBehaviour
[Tooltip("Draws force plate visuals in the viewport for debugging and other uses.")]
public bool DrawForcePlates = false;
- [Tooltip("Receive analog device data (e.g. iFacialMocap face streams). Leave off if no devices are needed.")]
- public bool ReceiveDevices = false;
-
[Tooltip("Motive will record when the Unity project is played.")]
public bool RecordOnPlay = false;
@@ -396,8 +368,8 @@ public class OptitrackStreamingClient : MonoBehaviour
private bool m_hasDrawnCameras = false;
private bool m_hasDrawnForcePlates = false;
private bool m_subscribedToMarkers = false;
- private float m_markerSubscribeRetryCooldown = 0f; // 留덉빱 援щ룆 ?ㅽ뙣 ???ъ떆??荑⑤떎??
- private const float k_MarkerSubscribeRetryInterval = 10f; // 10珥?媛꾧꺽 ?ъ떆??
+ private float m_markerSubscribeRetryCooldown = 0f;
+ private const float k_MarkerSubscribeRetryInterval = 10f;
private OptitrackHiResTimer.Timestamp m_lastFrameDeliveryTimestamp;
private Coroutine m_connectionHealthCoroutine = null;
@@ -427,12 +399,6 @@ public class OptitrackStreamingClient : MonoBehaviour
private List m_markersDefinitions = new List();
private List m_cameraDefinitions = new List();
private List m_forcePlateDefinitions = new List();
- private List m_deviceDefinitions = new List();
-
- /// Maps from a streamed device's ID to its most recent channel values.
- private Dictionary m_latestDeviceStates = new Dictionary();
- /// Name -> Id lookup for devices (built from descriptions).
- private Dictionary m_deviceNameToId = new Dictionary();
/// Maps from a streamed rigid body's ID to its most recent available pose data.
private Dictionary m_latestRigidBodyStates = new Dictionary();
@@ -443,7 +409,7 @@ public class OptitrackStreamingClient : MonoBehaviour
/// Reusable staging buffers used to validate a complete skeleton frame before committing it.
private Dictionary m_skeletonFrameScratch = new Dictionary();
- /// MirrorMode?? ?ㅼ펷?덊넠 ID ??(boneId ??mirrorBoneId) 留ㅽ븨 罹먯떆.
+ /// Cache for MirrorMode skeleton bone ID mappings.
private Dictionary> m_mirrorBoneIdMaps = new Dictionary>();
/// Maps from a streamed trained markerset's ID to its most recent available pose data.
@@ -476,30 +442,31 @@ public class OptitrackStreamingClient : MonoBehaviour
///
private object m_frameDataUpdateLock = new object();
- /// assetID ??assetName 罹먯떆 (GetMarkerName ??3以??좏삎 ?먯깋 ?쒓굅??.
+ /// Cache from streamed asset ID to asset name.
private Dictionary m_assetIdToNameCache = new Dictionary();
- // 以묎컙?????ㅼ펷?덊넠???앹꽦??寃쎌슦 ?뺤쓽 ?ъ“???뚮옒洹?(NatNet ?ㅻ젅?쒖뿉?쒕룄 ? ??volatile)
private volatile bool m_pendingDefinitionRefresh = false;
private float m_definitionRefreshCooldown = 0f;
- // ?먮룞 ?ъ뿰寃?吏꾪뻾 以??щ? (以묐났 ?ъ뿰寃?諛⑹?)
private bool m_isReconnecting = false;
- // ?뱁솕 ?곹깭 異붿쟻 (ToggleRecording?먯꽌 遺덊븘?뷀븳 ?댁쨷 紐낅졊 諛⑹?)
private bool m_isRecording = false;
- // SetProperty ?먭꺽 紐낅졊??理쒖큹 ?곌껐?먯꽌留??꾩넚 (?ъ뿰寃???Motive ?ㅼ젙 諛섎났 蹂寃?諛⑹?)
private bool m_hasAppliedServerSettings = false;
- // ?뺤쓽 ?ъ“???잛닔 ?쒗븳 (Motive 遺??諛⑹?: 理쒕? ?잛닔 珥덇낵 ???섎룞 ?몃━嫄??꾩슂)
- private int m_definitionRefreshCount = 0;
- private const int k_MaxAutoDefinitionRefreshes = 10;
+ [Header("Definition Refresh")]
+ [Tooltip("Motive에서 Actor/RigidBody가 추가되거나 삭제될 때 Unity 재시작 없이 DataDescription을 주기적으로 다시 조회합니다.")]
+ public bool AutoRefreshDefinitions = true;
+
+ [Tooltip("DataDescription 자동 재조회 간격(초). 값이 낮을수록 변경 반영이 빠르지만 Motive 명령 요청이 늘어납니다.")]
+ [Min(1f)]
+ public float DefinitionRefreshInterval = 5f;
+
+ private int m_definitionRefreshFailureCount = 0;
+ private float m_nextAutoDefinitionRefreshTime = 0f;
#endregion Private fields
///
- /// ?몃??먯꽌 ?뺤쓽 ?ъ“?뚮? ?붿껌?⑸땲?? 荑⑤떎??5珥? ?댁뿉??臾댁떆?⑸땲??
- /// OptitrackRigidBody, OptitrackSkeletonAnimator_Mingle ?깆뿉???먯뀑??李얠? 紐삵븷 ???몄텧.
///
public void RequestDefinitionRefresh()
{
@@ -516,7 +483,7 @@ public class OptitrackStreamingClient : MonoBehaviour
if (m_directTimeoutLogPending)
{
m_directTimeoutLogPending = false;
- Debug.LogWarning(GetType().FullName + ": direct NatNet receiver is joined but has not received multicast packets yet. If Python receives frames on this PC, check Unity Editor firewall permission or disable the separate NatNetDeviceListener objects for this test.", this);
+ Debug.LogWarning(GetType().FullName + ": direct NatNet receiver is joined but has not received multicast packets yet. If Python receives frames on this PC, check Unity Editor firewall permission or multicast routing.", this);
}
if (m_directSocketErrorLogPending)
{
@@ -535,7 +502,6 @@ public class OptitrackStreamingClient : MonoBehaviour
if (DrawMarkers)
{
- // 留덉빱 援щ룆 ?ㅽ뙣 ??留??꾨젅???ъ떆??諛⑹? ??荑⑤떎???곸슜
if (m_client != null && ConnectionType == ClientConnectionType.Unicast && !m_subscribedToMarkers)
{
if (m_markerSubscribeRetryCooldown <= 0f)
@@ -550,7 +516,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
}
- // ??踰붿쐞 理쒖냼?? ?곗씠?곕쭔 蹂듭궗 ?????댁젣 ??GameObject ?앹꽦/?뚭눼
var markerSnapshot = new Dictionary();
lock (m_frameDataUpdateLock)
{
@@ -565,7 +530,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
}
- // ??諛뽰뿉??GameObject ?낅뜲?댄듃/?앹꽦
var activeIds = new HashSet(markerSnapshot.Count);
foreach (var kvp in markerSnapshot)
{
@@ -586,7 +550,6 @@ public class OptitrackStreamingClient : MonoBehaviour
m_latestMarkerSpheres[kvp.Key] = sphere;
}
}
- // ??諛뽰뿉??stale ?ㅻ툕?앺듃 ?쒓굅
var staleIds = new List();
foreach (var sphereEntry in m_latestMarkerSpheres)
{
@@ -609,12 +572,10 @@ public class OptitrackStreamingClient : MonoBehaviour
m_latestMarkerSpheres.Clear();
}
- // ??諛뽰뿉??移대찓??吏?ㅻ찓?몃━ ?앹꽦 (1?뚯꽦 ???곗씠?곕뒗 m_cameraDefinitions???대? 蹂듭궗??
if (DrawCameras && !m_hasDrawnCameras )
{
if (m_client.ServerAppVersion >= new Version(3, 0, 0))
{
- // m_cameraDefinitions??硫붿씤 ?ㅻ젅?쒖쓽 UpdateDefinitions()?먯꽌 梨꾩썙吏?????遺덊븘??
var cameraGroup = new GameObject("Cameras");
foreach (OptitrackCameraDefinition camera in m_cameraDefinitions)
{
@@ -635,7 +596,6 @@ public class OptitrackStreamingClient : MonoBehaviour
m_hasDrawnCameras = true;
}
- // ??諛뽰뿉???ъ뒪 ?뚮젅?댄듃 吏?ㅻ찓?몃━ ?앹꽦 (1?뚯꽦)
if (DrawForcePlates && !m_hasDrawnForcePlates)
{
var forcePlateGroup = new GameObject("Force Plates");
@@ -667,7 +627,6 @@ public class OptitrackStreamingClient : MonoBehaviour
// Trained Markerset Markers if requested to draw // trained markerset added
if (DrawTMarkersetMarkers)
{
- // ??踰붿쐞 理쒖냼?? ?곗씠?곕쭔 蹂듭궗 ?????댁젣 ??GameObject ?앹꽦/?뚭눼
var tmarkSnapshot = new Dictionary();
lock (m_frameDataUpdateLock)
{
@@ -682,7 +641,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
}
- // ??諛뽰뿉??GameObject ?낅뜲?댄듃/?앹꽦
var activeTMarkIds = new HashSet(tmarkSnapshot.Count);
foreach (var kvp in tmarkSnapshot)
{
@@ -703,7 +661,6 @@ public class OptitrackStreamingClient : MonoBehaviour
m_latestTMarkMarkerSpheres[kvp.Key] = cube;
}
}
- // ??諛뽰뿉??stale ?ㅻ툕?앺듃 ?쒓굅
var staleTMarkIds = new List();
foreach (var cubeEntry in m_latestTMarkMarkerSpheres)
{
@@ -726,28 +683,37 @@ public class OptitrackStreamingClient : MonoBehaviour
m_latestTMarkMarkerSpheres.Clear();
}
- // 誘몃벑濡??ㅼ펷?덊넠???꾨젅?꾩뿉 ?깆옣?덉쓣 ???뺤쓽 ?먮룞 ?ъ“??(Motive 以묎컙 ?앹꽦 ???
- // 荑⑤떎??15珥?+ 理쒕? ?잛닔 ?쒗븳?쇰줈 Motive DataDescription ?붿껌 ??뭾 諛⑹?
+ // Refresh streamed asset definitions when Motive actors or rigid bodies are added/removed.
if (m_pendingDefinitionRefresh && m_definitionRefreshCooldown <= 0f && m_client != null && !SkipDataDescriptions)
{
m_pendingDefinitionRefresh = false;
-
- if (m_definitionRefreshCount < k_MaxAutoDefinitionRefreshes)
+ try
{
- m_definitionRefreshCooldown = 15f;
- m_definitionRefreshCount++;
- try { UpdateDefinitions(); }
- catch (Exception ex) { Debug.LogException(ex, this); }
+ UpdateDefinitions();
+ ResubscribeRegisteredAssets();
+ m_definitionRefreshCooldown = Mathf.Max(DefinitionRefreshInterval, 1f);
+ m_nextAutoDefinitionRefreshTime = Time.unscaledTime + Mathf.Max(DefinitionRefreshInterval, 1f);
}
- else
+ catch (Exception ex)
{
- // 理쒕? ?잛닔 珥덇낵 ??寃쎄퀬 濡쒓렇 異쒕젰 ???먮룞 ?ъ“??以묐떒
- m_definitionRefreshCooldown = 60f; // 1遺????ㅼ떆 ?쒕룄 ?덉슜
- Debug.LogWarning(GetType().FullName + ": Automatic definition refresh limit reached (" + k_MaxAutoDefinitionRefreshes + "). Check the streamed assets in Motive.", this);
+ m_definitionRefreshFailureCount++;
+ m_definitionRefreshCooldown = Mathf.Min(60f, 2f * Mathf.Pow(2f, Mathf.Min(m_definitionRefreshFailureCount, 5)));
+ Debug.LogWarning(GetType().FullName + ": DataDescription refresh failed. Retrying in " + m_definitionRefreshCooldown + " seconds.", this);
+ Debug.LogException(ex, this);
}
}
if (m_definitionRefreshCooldown > 0f)
m_definitionRefreshCooldown -= Time.deltaTime;
+
+ if (AutoRefreshDefinitions &&
+ m_client != null &&
+ !SkipDataDescriptions &&
+ m_definitionRefreshCooldown <= 0f &&
+ Time.unscaledTime >= m_nextAutoDefinitionRefreshTime)
+ {
+ m_nextAutoDefinitionRefreshTime = Time.unscaledTime + Mathf.Max(DefinitionRefreshInterval, 1f);
+ RequestDefinitionRefresh();
+ }
}
///
@@ -822,7 +788,6 @@ public class OptitrackStreamingClient : MonoBehaviour
Debug.Log("OptiTrack: reconnect requested.");
- // 湲곗〈 ?곌껐 ?뺣━ (StopAllCoroutines ?ы븿)
OnDisable();
StartCoroutine(ReconnectCoroutine());
@@ -845,10 +810,8 @@ public class OptitrackStreamingClient : MonoBehaviour
Debug.Log(string.Format("{0}: reconnect attempt {1}/{2} in {3} seconds...", GetType().FullName, attempt, maxAttempts, delay), this);
yield return new WaitForSeconds(delay);
- // ConnectCoroutine ?ㅽ뻾: ?곌껐 ?섎┰ + CheckConnectionHealth ?쒖옉
yield return StartCoroutine(ConnectCoroutine());
- // 泥??꾨젅???섏떊源뚯? 理쒕? 3珥??湲?
float deadline = Time.realtimeSinceStartup + 3.0f;
while (!m_receivedFrameSinceConnect && Time.realtimeSinceStartup < deadline)
yield return null;
@@ -856,7 +819,6 @@ public class OptitrackStreamingClient : MonoBehaviour
if (m_receivedFrameSinceConnect)
{
Debug.Log(GetType().FullName + ": reconnect succeeded.", this);
- // ?ъ뿰寃??깃났 ?꾩뿉留??뱁솕 ?ъ떆??
if (RecordOnPlay)
StartRecording();
m_isReconnecting = false;
@@ -865,7 +827,6 @@ public class OptitrackStreamingClient : MonoBehaviour
Debug.LogWarning(string.Format("{0}: reconnect attempt {1}/{2} failed; no streaming frames received.", GetType().FullName, attempt, maxAttempts), this);
- // ?ㅼ쓬 ?쒕룄 ??遺遺??곌껐 ?뺣━ (ReconnectCoroutine ?먯떊? 以묒??섏? ?딅룄濡?StopCoroutine ?ъ슜)
if (m_connectionHealthCoroutine != null)
{
StopCoroutine(m_connectionHealthCoroutine);
@@ -961,7 +922,6 @@ public class OptitrackStreamingClient : MonoBehaviour
{
if (m_client != null)
{
- // ?곹깭 異붿쟻 湲곕컲 ?좉? ??湲곗〈: "?쇰떒 Start ???ㅽ뙣?섎㈃ Stop" ?댁쨷 紐낅졊 ??1??紐낅졊?쇰줈 媛쒖꽑
if (m_isRecording)
{
if (StopRecording())
@@ -1049,10 +1009,8 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// ?쎌쓣 ?좎???梨꾨줈 ?ㅼ펷?덊넠 ?ъ쫰瑜?pre-allocated ?뺤뀛?덈━??蹂듭궗.
- /// GetLatestSkeletonState() ?????놁씠 BonePoses瑜??쒗쉶?섎뒗 torn read瑜?諛⑹?.
///
- /// ?ㅼ펷?덊넠 ?곗씠?곌? 議댁옱?섎㈃ true, ?놁쑝硫?false.
+ /// True when the requested data exists; otherwise false.
public bool FillBoneSnapshot( Int32 skeletonId,
Dictionary posOut,
Dictionary oriOut,
@@ -1078,13 +1036,11 @@ public class OptitrackStreamingClient : MonoBehaviour
if ( targetId == kvp.Key )
{
- // ?移?蹂?(Hip, 泥숈텛, 紐? 癒몃━ ??: YZ ?됰㈃ 諛섏궗 ?곸슜
posOut[kvp.Key] = MirrorPosition( kvp.Value.Position );
oriOut[kvp.Key] = MirrorOrientation( kvp.Value.Orientation );
}
else
{
- // L/R ??蹂? ?꾩튂???먭린 ?먮━ ?좎?, ?뚯쟾? YZ 諛섏궗 ??誘몃윭 蹂몄쑝濡??ㅼ솑
posOut[kvp.Key] = kvp.Value.Position;
oriOut[targetId] = MirrorOrientation( kvp.Value.Orientation );
}
@@ -1103,8 +1059,8 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// 蹂??대쫫??L/R ?묐몢?щ? 諛섏쟾???대쫫??諛섑솚. ?移?蹂몄씠硫?null.
- /// ?? "LUArm" ??"RUArm", "RShin" ??"LShin", "Hip" ??null
+ /// Returns the left/right counterpart for a Motive bone name, or null for center bones.
+ /// For example, LUArm maps to RUArm, RShin maps to LShin, and Hip maps to null.
///
private static string GetMirrorBoneName( string name )
{
@@ -1115,8 +1071,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// ?ㅼ펷?덊넠 ?뺤쓽瑜?湲곕컲?쇰줈 boneId ??mirrorBoneId 留ㅽ븨??鍮뚮뱶?섍퀬 罹먯떆?????
- /// m_frameDataUpdateLock ?댁뿉???몄텧?댁빞 ??
///
private Dictionary GetOrBuildMirrorBoneIdMap( Int32 skeletonId )
{
@@ -1126,7 +1080,6 @@ public class OptitrackStreamingClient : MonoBehaviour
OptitrackSkeletonDefinition skelDef = GetSkeletonDefinitionById( skeletonId );
if ( skelDef == null ) return null;
- // ?대쫫 ??ID 猷⑹뾽 ?뚯씠釉?
var nameToId = new Dictionary( skelDef.Bones.Count );
foreach ( var bone in skelDef.Bones )
nameToId[bone.Name] = bone.Id;
@@ -1134,7 +1087,6 @@ public class OptitrackStreamingClient : MonoBehaviour
var map = new Dictionary( skelDef.Bones.Count );
foreach ( var bone in skelDef.Bones )
{
- // "SkeletonName_BoneName" ?뺤떇 吏?? "_" ?ㅼ쓽 吏㏃? ?대쫫?먯꽌 L/R ?묐몢??泥섎━
string fullName = bone.Name;
int sep = fullName.IndexOf( '_' );
string prefix = sep >= 0 ? fullName.Substring( 0, sep + 1 ) : ""; // "Skeleton1_"
@@ -1146,18 +1098,18 @@ public class OptitrackStreamingClient : MonoBehaviour
if ( mirrorFull != null && nameToId.TryGetValue( mirrorFull, out Int32 mirrorId ) )
map[bone.Id] = mirrorId;
else
- map[bone.Id] = bone.Id; // ?移?蹂몄? ?먭린 ?먯떊??留ㅽ븨
+ map[bone.Id] = bone.Id;
}
m_mirrorBoneIdMaps[skeletonId] = map;
return map;
}
- /// YZ ?됰㈃ 湲곗? ?뚯쟾 誘몃윭 (R' = M쨌R쨌M, M=diag(-1,1,1)): Y, Z ?깅텇 遺??諛섏쟾.
+ /// Mirror helper for YZ-plane coordinate conversion.
private static Quaternion MirrorOrientation( Quaternion q )
=> new Quaternion( q.x, -q.y, -q.z, q.w );
- /// YZ ?됰㈃ 湲곗? ?꾩튂 誘몃윭: X ?깅텇 遺??諛섏쟾.
+ /// Mirror helper for YZ-plane coordinate conversion.
private static Vector3 MirrorPosition( Vector3 pos )
=> new Vector3( -pos.x, pos.y, pos.z );
@@ -1389,28 +1341,15 @@ public class OptitrackStreamingClient : MonoBehaviour
{
descriptionTypeMask |= (1 << (int)NatNetDataDescriptionType.NatNetDataDescriptionType_ForcePlate);
}
- if( ReceiveDevices )
- {
- descriptionTypeMask |= (1 << (int)NatNetDataDescriptionType.NatNetDataDescriptionType_Device);
- }
m_dataDescs = m_client.GetDataDescriptions(descriptionTypeMask);
- // ?뺤쓽瑜??깃났?곸쑝濡?諛쏆븯?쇰?濡??먮룞 ?ъ“??移댁슫??由ъ뀑
- m_definitionRefreshCount = 0;
+ m_definitionRefreshFailureCount = 0;
m_rigidBodyDefinitions.Clear();
m_skeletonDefinitions.Clear();
m_tmarkersetDefinitions.Clear();
- m_mirrorBoneIdMaps.Clear(); // ?ㅼ펷?덊넠 ?뺤쓽 蹂寃???mirror map 罹먯떆 臾댄슚??
- // Device caches are accessed from NatNet thread; lock them.
- lock (m_frameDataUpdateLock)
- {
- m_deviceDefinitions.Clear();
- m_deviceNameToId.Clear();
- m_latestDeviceStates.Clear();
- }
+ m_mirrorBoneIdMaps.Clear();
- // NatNet ?ㅻ젅?쒓? ?묎렐?섎뒗 罹먯떆???쎌쑝濡?蹂댄샇?섏뿬 ?덉씠??諛⑹?
lock (m_frameDataUpdateLock)
{
m_assetIdToNameCache.Clear();
@@ -1637,57 +1576,85 @@ public class OptitrackStreamingClient : MonoBehaviour
m_forcePlateDefinitions.Add(forcePlateDef);
}
- // ----------------------------------
- // - Device Definitions (generic analog: iFacialMocap, NIDAQ, etc.)
- // ----------------------------------
+ PruneStatesMissingFromDefinitions();
+
+ }
+
+ private void PruneStatesMissingFromDefinitions()
+ {
+ var validRigidBodyIds = new HashSet();
+ foreach (var def in m_rigidBodyDefinitions)
+ validRigidBodyIds.Add(def.Id);
+
+ var validSkeletonIds = new HashSet();
+ foreach (var def in m_skeletonDefinitions)
+ validSkeletonIds.Add(def.Id);
+
+ var validTMarkersetIds = new HashSet();
+ foreach (var def in m_tmarkersetDefinitions)
+ validTMarkersetIds.Add(def.Id);
+
lock (m_frameDataUpdateLock)
{
- if (m_dataDescs.DeviceDescriptions != null)
- {
- for (int devIdx = 0; devIdx < m_dataDescs.DeviceDescriptions.Count; ++devIdx)
- {
- sDeviceDescription dev = m_dataDescs.DeviceDescriptions[devIdx];
+ RemoveMissingKeys(m_latestRigidBodyStates, validRigidBodyIds);
+ RemoveMissingKeys(m_latestSkeletonStates, validSkeletonIds);
+ RemoveMissingKeys(m_skeletonFrameScratch, validSkeletonIds);
+ RemoveMissingKeys(m_latestTMarkersetStates, validTMarkersetIds);
+ }
+ }
- // dev.ChannelNames is a fixed-size 32 array (NatNet C# wrapper limit).
- int marshalNameCount = (dev.ChannelNames != null)
- ? Math.Min(dev.ChannelCount, dev.ChannelNames.Length)
- : 0;
+ private static void RemoveMissingKeys(Dictionary dictionary, HashSet validKeys)
+ {
+ if (dictionary.Count == 0)
+ return;
- OptitrackDeviceDefinition deviceDef = new OptitrackDeviceDefinition
- {
- Id = dev.Id,
- Name = dev.Name,
- SerialNumber = dev.SerialNo,
- DeviceType = dev.DeviceType,
- ChannelDataType = dev.ChannelDataType,
- ChannelCount = dev.ChannelCount,
- ChannelNames = new List(marshalNameCount),
- };
-
- for (int i = 0; i < marshalNameCount; ++i)
- {
- deviceDef.ChannelNames.Add(dev.ChannelNames[i]);
- }
-
- m_deviceDefinitions.Add(deviceDef);
- m_deviceNameToId[deviceDef.Name] = deviceDef.Id;
- }
- }
+ var staleKeys = new List();
+ foreach (var kvp in dictionary)
+ {
+ if (!validKeys.Contains(kvp.Key))
+ staleKeys.Add(kvp.Key);
}
+ foreach (var key in staleKeys)
+ dictionary.Remove(key);
+ }
+
+ private void ResubscribeRegisteredAssets()
+ {
+ if (m_client == null || ConnectionType != ClientConnectionType.Unicast)
+ return;
+
+ m_doneSubscriptionNotice = false;
+ ResetStreamingSubscriptions();
+
+ foreach (KeyValuePair rb in m_rigidBodies)
+ SubscribeRigidBody(rb.Value, rb.Key);
+ foreach (KeyValuePair skel in m_skeletons)
+ SubscribeSkeleton(skel.Value, skel.Key);
+ foreach (KeyValuePair tmark in m_tmarkersets)
+ SubscribeTMarkerset(tmark.Value, tmark.Key);
+ if (DrawTMarkersetMarkers)
+ SubscribeTMarkMarkers();
+ if (DrawMarkers)
+ SubscribeMarkers();
}
public void RegisterRigidBody( MonoBehaviour component, Int32 rigidBodyId )
{
- if ( m_rigidBodies.ContainsKey( rigidBodyId ) )
+ if ( m_rigidBodies.TryGetValue( rigidBodyId, out MonoBehaviour existingComponent ) )
{
+ if (existingComponent == component)
+ {
+ SubscribeRigidBody(component, rigidBodyId);
+ return;
+ }
+
#if false
MonoBehaviour existingRb = m_rigidBodies[rigidBodyId];
Debug.LogError( GetType().FullName + ": " + rb.GetType().FullName + " has duplicate rigid body ID " + rigidBodyId, component );
Debug.LogError( GetType().FullName + ": (Existing " + existingRb.GetType().FullName + " was already registered with that ID)", existingRb );
rb.enabled = false;
#endif
- return;
}
m_rigidBodies[rigidBodyId] = component;
@@ -1695,15 +1662,29 @@ public class OptitrackStreamingClient : MonoBehaviour
SubscribeRigidBody(component, rigidBodyId);
}
+ public void UnregisterRigidBody( MonoBehaviour component, Int32 rigidBodyId )
+ {
+ if (m_rigidBodies.TryGetValue(rigidBodyId, out MonoBehaviour existingComponent) &&
+ existingComponent == component)
+ {
+ m_rigidBodies.Remove(rigidBodyId);
+ }
+ }
+
public void RegisterSkeleton(MonoBehaviour component, string name)
{
- if (m_skeletons.ContainsKey(name))
+ if (m_skeletons.TryGetValue(name, out MonoBehaviour existingComponent))
{
+ if (existingComponent == component)
+ {
+ SubscribeSkeleton(component, name);
+ return;
+ }
+
#if false
MonoBehaviour existingSkel = m_skeletons[rigidBodyId];
Debug.LogError( "Duplicate skeleton detected, " + GetType().FullName + ": (Existing " + existingRb.GetType().FullName + " was already registered with that ID)", existingRb );
#endif
- return;
}
m_skeletons[name] = component;
@@ -1713,9 +1694,13 @@ public class OptitrackStreamingClient : MonoBehaviour
public void RegisterTMarkerset(MonoBehaviour component, string name) // trained markerset added
{
- if (m_tmarkersets.ContainsKey(name))
+ if (m_tmarkersets.TryGetValue(name, out MonoBehaviour existingComponent))
{
- return;
+ if (existingComponent == component)
+ {
+ SubscribeTMarkerset(component, name);
+ return;
+ }
}
m_tmarkersets[name] = component;
@@ -1725,12 +1710,10 @@ public class OptitrackStreamingClient : MonoBehaviour
///
/// (Re)initializes and connects to the configured streaming server.
- /// ?곌껐 珥덇린?붾뒗 肄붾(?댁쑝濡??꾩엫?섏뿬 Thread.Sleep?쇰줈 硫붿씤 ?ㅻ젅?쒕? 釉붾씫?섏? ?딅뒗??
///
void OnEnable()
{
m_receivedFrameSinceConnect = false;
- // AutoReconnect媛 耳쒖졇?덉쑝硫?珥덇린 ?곌껐 ?ㅽ뙣 ?쒖뿉???먮룞 ?ъ떆??
if (AutoReconnect)
StartCoroutine( InitialConnectWithRetry() );
else
@@ -1741,21 +1724,14 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// 珥덇린 ?곌껐??諛깆삤?꾩? ?④퍡 ?먮룞 ?ъ떆?꾪빀?덈떎.
- /// 泥섎━?섎뒗 耳?댁뒪:
- /// A) NatNet_Client_Connect ?먯껜媛 throw (NatNetError_Network ?? ??Motive PC 誘몄쓳?? 諛⑺솕踰? ?섎せ??IP ??
- /// B) Connect???깃났?덉쑝??泥??꾨젅?꾩씠 ?곸쁺 ??????Motive Streaming 誘명솢?깊솕, Multicast ?쇱슦??李⑤떒 ??
- /// 耳?댁뒪 C(?곌껐 ???꾨젅???딄?)??湲곗〈 CheckConnectionHealth+ReconnectCoroutine?먯꽌 泥섎━??
///
private System.Collections.IEnumerator InitialConnectWithRetry()
{
const int maxAttempts = 10;
- // 諛깆삤?? 1, 2, 3, 5, 10, 10, ... 珥?(珥???60珥덇컙 ?쒕룄)
float[] retryDelays = { 1f, 2f, 3f, 5f, 10f };
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
- // ConnectCoroutine ?ㅽ뻾
yield return StartCoroutine(ConnectCoroutine());
bool transportConnected = m_client != null || m_directNatNetConnected;
@@ -1768,7 +1744,6 @@ public class OptitrackStreamingClient : MonoBehaviour
yield break;
}
- // 耳?댁뒪 B ?뺤씤: Connect???깃났 ??泥??꾨젅???꾩갑源뚯? 5珥??湲?
float deadline = Time.realtimeSinceStartup + 5.0f;
while (!m_receivedFrameSinceConnect && Time.realtimeSinceStartup < deadline)
yield return null;
@@ -1777,10 +1752,9 @@ public class OptitrackStreamingClient : MonoBehaviour
{
if (attempt > 1)
Debug.Log(string.Format("{0}: initial connection succeeded (attempt {1}/{2}).", GetType().FullName, attempt, maxAttempts), this);
- yield break; // ?깃났
+ yield break;
}
- // 耳?댁뒪 B: ?곌껐?먯?留??꾨젅??誘몄닔?????대씪?댁뼵???뺣━ ???ъ떆??
Debug.LogWarning(string.Format("{0}: initial connection attempt {1}/{2} connected to {3} but received no frames for 5 seconds. ServerNatNet={4}, ClientNatNet={5}, Local={6}, Connection={7}. Check Motive streaming mode, multicast interface, or NatNet SDK compatibility.", GetType().FullName, attempt, maxAttempts, ServerAddress, ServerNatNetVersion, ClientNatNetVersion, ResolvedLocalAddress, ConnectionType), this);
CleanupClient();
}
@@ -1798,8 +1772,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// m_client? 愿??肄붾(???대깽?몃? ?덉쟾?섍쾶 ?뺣━?⑸땲??
- /// InitialConnectWithRetry??耳?댁뒪 B ?ъ떆?????ъ슜.
///
private void CleanupClient()
{
@@ -2083,9 +2055,7 @@ public class OptitrackStreamingClient : MonoBehaviour
if (!ReadDirectSection(data, ref o, out skipCount, out skipStart, out skipEnd)) return; // labeled markers
if (!ReadDirectSection(data, ref o, out skipCount, out skipStart, out skipEnd)) return; // force plates
- int deviceCount, deviceStart, deviceEnd;
- if (!ReadDirectSection(data, ref o, out deviceCount, out deviceStart, out deviceEnd)) return; // devices
- ParseDirectDevices(data, deviceCount, deviceStart, deviceEnd, frameNumber, frameTimestamp);
+ if (!ReadDirectSection(data, ref o, out skipCount, out skipStart, out skipEnd)) return; // devices
}
finally
@@ -2094,57 +2064,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
}
- private void ParseDirectDevices(byte[] data, int count, int start, int end, int frameNumber, OptitrackHiResTimer.Timestamp frameTimestamp)
- {
- if (!ReceiveDevices)
- return;
-
- m_latestDeviceStates.Clear();
-
- int o = start;
- for (int i = 0; i < count && o + 8 <= end; i++)
- {
- int id = BitConverter.ToInt32(data, o); o += 4;
- int channelCount = BitConverter.ToInt32(data, o); o += 4;
- if (channelCount < 0 || channelCount > 4096)
- return;
-
- OptitrackDeviceDefinition definition = m_deviceDefinitions.Find(d => d.Id == id);
- Dictionary values = new Dictionary(channelCount);
-
- for (int ch = 0; ch < channelCount; ch++)
- {
- if (o + 4 > end) return;
- int sampleCount = BitConverter.ToInt32(data, o); o += 4;
- if (sampleCount < 0 || sampleCount > 1024 || o + sampleCount * 4 > end) return;
-
- float value = sampleCount > 0 ? BitConverter.ToSingle(data, o) : 0.0f;
- o += sampleCount * 4;
-
- string channelName = (definition != null && definition.ChannelNames != null && ch < definition.ChannelNames.Count)
- ? definition.ChannelNames[ch]
- : "Channel" + ch;
- values[channelName] = value;
- }
-
- int stateKey = DirectDeviceStateKey(id, i);
- string deviceName = definition != null ? definition.Name : ("Device" + stateKey);
- m_latestDeviceStates[stateKey] = new OptitrackDeviceState
- {
- Id = stateKey,
- Name = deviceName,
- ChannelValues = values,
- FrameNumber = frameNumber,
- DeliveryTimestamp = frameTimestamp,
- };
- }
- }
-
- private static int DirectDeviceStateKey(int deviceId, int wireIndex)
- {
- return deviceId != 0 ? deviceId : -1 - wireIndex;
- }
-
private static bool ReadDirectSection(byte[] data, ref int o, out int count, out int start, out int end)
{
count = 0;
@@ -2217,7 +2136,7 @@ public class OptitrackStreamingClient : MonoBehaviour
if (skelDef == null)
{
- if (!m_pendingDefinitionRefresh && m_definitionRefreshCount < k_MaxAutoDefinitionRefreshes)
+ if (!m_pendingDefinitionRefresh)
m_pendingDefinitionRefresh = true;
return;
}
@@ -2621,7 +2540,6 @@ public class OptitrackStreamingClient : MonoBehaviour
List rigidBodies = new List();
List skeletons = new List();
- List devices = new List();
int dataSetCount = BitConverter.ToInt32(modelDefPacket, o); o += 4;
for (int i = 0; i < dataSetCount && o + 8 <= end; i++)
@@ -2642,10 +2560,6 @@ public class OptitrackStreamingClient : MonoBehaviour
{
skeletons.Add(DirectReadSkeletonDefinition(modelDefPacket, ref o, dataSetEnd));
}
- else if (dataSetType == 4)
- {
- devices.Add(DirectReadDeviceDefinition(modelDefPacket, ref o, dataSetEnd));
- }
}
catch
{
@@ -2655,7 +2569,7 @@ public class OptitrackStreamingClient : MonoBehaviour
o = dataSetEnd;
}
- m_definitionRefreshCount = 0;
+ m_definitionRefreshFailureCount = 0;
m_rigidBodyDefinitions.Clear();
m_skeletonDefinitions.Clear();
m_tmarkersetDefinitions.Clear();
@@ -2665,19 +2579,11 @@ public class OptitrackStreamingClient : MonoBehaviour
lock (m_frameDataUpdateLock)
{
- m_deviceDefinitions.Clear();
- m_deviceNameToId.Clear();
- m_latestDeviceStates.Clear();
m_assetIdToNameCache.Clear();
-
- foreach (OptitrackDeviceDefinition deviceDef in devices)
- {
- m_deviceDefinitions.Add(deviceDef);
- if (!string.IsNullOrEmpty(deviceDef.Name))
- m_deviceNameToId[deviceDef.Name] = deviceDef.Id;
- }
}
+ PruneStatesMissingFromDefinitions();
+
return true;
}
@@ -2734,31 +2640,6 @@ public class OptitrackStreamingClient : MonoBehaviour
return skelDef;
}
- private static OptitrackDeviceDefinition DirectReadDeviceDefinition(byte[] data, ref int o, int end)
- {
- int id = DirectReadInt32(data, ref o, end);
- string name = DirectReadCString(data, ref o, end);
- string serial = DirectReadCString(data, ref o, end);
- int deviceType = DirectReadInt32(data, ref o, end);
- int channelDataType = DirectReadInt32(data, ref o, end);
- int channelCount = DirectReadInt32(data, ref o, end);
-
- OptitrackDeviceDefinition deviceDef = new OptitrackDeviceDefinition
- {
- Id = id,
- Name = name,
- SerialNumber = serial,
- DeviceType = deviceType,
- ChannelDataType = channelDataType,
- ChannelCount = channelCount,
- ChannelNames = new List(Math.Max(0, channelCount)),
- };
-
- for (int i = 0; i < channelCount; i++)
- deviceDef.ChannelNames.Add(DirectReadCString(data, ref o, end));
- return deviceDef;
- }
-
private static void DirectReadRigidBodyDesc(byte[] data, ref int o, int end, out string name, out int id, out int parentId, out Vector3 offset, out int markerCount)
{
name = DirectReadCString(data, ref o, end);
@@ -2824,7 +2705,6 @@ public class OptitrackStreamingClient : MonoBehaviour
{
m_receivedFrameSinceConnect = false;
- // --- 硫붿씤 ?ㅻ젅?? 二쇱냼/紐⑤뱶 ?뚯떛 (Unity API 誘몄궗?? 利됱떆 ?꾨즺) ---
IPAddress serverAddr;
IPAddress localAddr;
IPAddress multicastAddr = null;
@@ -2878,10 +2758,6 @@ public class OptitrackStreamingClient : MonoBehaviour
yield break;
#pragma warning disable 162
- // --- 諛깃렇?쇱슫???ㅻ젅?? 釉붾줈???ㅼ씠?곕툕 ?곌껐 + SetProperty ---
- // NatNet_Client_Connect ???쒕쾭媛 利됱떆 ?묐떟?섏? ?딆쑝硫??대? ??꾩븘?껉퉴吏 釉붾줈?밸맂??
- // 肄붾(?댁? 硫붿씤 ?ㅻ젅?쒖뿉???ㅽ뻾?섎?濡??ш린??吏곸젒 ?몄텧?섎㈃ 留??ъ젒?띾쭏???붾㈃??硫덉텣??
- // Task 濡?遺꾨━?섍퀬 硫붿씤 ?ㅻ젅?쒕뒗 留??꾨젅???묐낫(yield)?섎ʼn ?꾨즺瑜??대쭅?쒕떎.
bool applyServerSettings = !m_hasAppliedServerSettings;
NatNetClient connectedClient = null;
Exception connectError = null;
@@ -2903,7 +2779,6 @@ public class OptitrackStreamingClient : MonoBehaviour
c.Connect( connType, localAddr, serverAddr, commandPort, dataPort, multicastAddr );
}
- // SetProperty??理쒖큹 ?곌껐?먯꽌留??꾩넚 ???ъ뿰寃???Motive 湲濡쒕쾶 ?ㅼ젙 諛섎났 蹂寃?諛⑹?
if ( applyServerSettings )
{
// Remotely change the Skeleton Coordinate property to Global/Local
@@ -2930,7 +2805,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
} );
- // 硫붿씤 ?ㅻ젅?쒕뒗 留??꾨젅???묐낫?섎ʼn ?곌껐 ?꾨즺瑜??湲????뚮뜑留??낅젰??硫덉텛吏 ?딆쓬
while ( !connectTask.IsCompleted )
yield return null;
@@ -2948,34 +2822,24 @@ public class OptitrackStreamingClient : MonoBehaviour
else
Debug.Log(GetType().FullName + ": reconnect skipped SetProperty commands; they were already applied on the first connection.", this);
- // SetProperty 紐낅졊???쒕쾭???곸슜???뚭퉴吏 ?湲?(?ъ뿰寃???SetProperty ?ㅽ궢?덉쑝硫??湲?遺덊븘??
if (!m_isReconnecting)
yield return new UnityEngine.WaitForSeconds( 0.1f );
try
{
if (!SkipDataDescriptions)
+ {
UpdateDefinitions();
+ m_nextAutoDefinitionRefreshTime = Time.unscaledTime + Mathf.Max(DefinitionRefreshInterval, 1f);
+ }
}
catch (Exception ex)
{
Debug.LogException(ex, this);
}
- if (ConnectionType == ClientConnectionType.Unicast)
- {
- ResetStreamingSubscriptions();
- foreach (KeyValuePair rb in m_rigidBodies)
- SubscribeRigidBody(rb.Value, rb.Key);
- foreach (KeyValuePair skel in m_skeletons)
- SubscribeSkeleton(skel.Value, skel.Key);
- foreach (KeyValuePair tmark in m_tmarkersets) // trained markerset added
- SubscribeTMarkerset(tmark.Value, tmark.Key);
- if (DrawTMarkersetMarkers)
- SubscribeTMarkMarkers();
- }
+ ResubscribeRegisteredAssets();
- // ?ъ뿰寃?以묒뿉???뱁솕 ?쒖옉 ?ㅽ궢 ??Motive??Take ?뚯씪 諛섎났 ?닿린/?リ린 諛⑹?
if (RecordOnPlay && !m_isReconnecting)
StartRecording();
@@ -2994,7 +2858,6 @@ public class OptitrackStreamingClient : MonoBehaviour
///
void OnDisable()
{
- // ConnectCoroutine ?ы븿 紐⑤뱺 肄붾(???뺤?
StopAllCoroutines();
StopDirectFrameReceiver();
m_connectionHealthCoroutine = null;
@@ -3039,7 +2902,7 @@ public class OptitrackStreamingClient : MonoBehaviour
if ( warnedPendingFirstFrame == false )
{
if (m_directNatNetConnected)
- Debug.LogWarning( GetType().FullName + ": Direct NatNet receiver is running but no frames have arrived in Unity yet. Check Unity Editor firewall permission, multicast routing, or temporarily disable separate NatNetDeviceListener objects.", this );
+ Debug.LogWarning( GetType().FullName + ": Direct NatNet receiver is running but no frames have arrived in Unity yet. Check Unity Editor firewall permission or multicast routing.", this );
else
Debug.LogWarning( GetType().FullName + ": No frames received from the server yet. Verify your connection settings are correct and that the server is streaming.", this );
warnedPendingFirstFrame = true;
@@ -3074,7 +2937,6 @@ public class OptitrackStreamingClient : MonoBehaviour
{
Debug.Log( GetType().FullName + ": starting automatic reconnect.", this );
Reconnect();
- // OnDisable() ??StopAllCoroutines() 濡???肄붾(?댁? ?ㅼ쓬 yield ?먯꽌 以묒???
}
continue;
}
@@ -3103,9 +2965,6 @@ public class OptitrackStreamingClient : MonoBehaviour
return;
}
- // 寃쏀빀 ??理쒕? 1ms ?湲????쒕∼.
- // FillBoneSnapshot????蹂댁쑀 ?쒓컙??~0.1ms?대?濡??뺤긽 ?곹솴?먯꽌???쒕∼ ?놁쓬.
- // GC Pause ??1ms 珥덇낵 ?곹솴?먯꽌留??쒕∼ (?덉슜 媛??.
if ( ! Monitor.TryEnter( m_frameDataUpdateLock, 1 ) )
{
return;
@@ -3178,12 +3037,10 @@ public class OptitrackStreamingClient : MonoBehaviour
result = NaturalPoint.NatNetLib.NativeMethods.NatNet_Frame_Skeleton_GetRigidBodyCount( pFrame, skelIdx, out skelRbCount );
NatNetException.ThrowIfNotOK( result, "NatNet_Frame_Skeleton_GetRigidBodyCount failed." );
- // ?ㅼ펷?덊넠 ?뺤쓽 寃?됱쓣 蹂?猷⑦봽 諛뽰뿉??1?뚮쭔 ?섑뻾 (湲곗〈: 留?蹂몃쭏???좏삎 ?먯깋)
OptitrackSkeletonDefinition skelDef = GetSkeletonDefinitionById(skeletonId);
if (skelDef == null)
{
- // Motive?먯꽌 以묎컙???ㅼ펷?덊넠???앹꽦??寃쎌슦 ??硫붿씤 ?ㅻ젅?쒖뿉???뺤쓽 ?ъ“???덉빟 (以묐났 濡쒓렇 諛⑹?)
- if (!m_pendingDefinitionRefresh && m_definitionRefreshCount < k_MaxAutoDefinitionRefreshes)
+ if (!m_pendingDefinitionRefresh)
{
Debug.LogWarning(GetType().FullName + ": missing skeleton definition for streamed skeleton ID " + skeletonId + "; scheduling definition refresh.", this);
m_pendingDefinitionRefresh = true;
@@ -3198,8 +3055,6 @@ public class OptitrackStreamingClient : MonoBehaviour
NatNetException.ThrowIfNotOK( result, "NatNet_Frame_Skeleton_GetRigidBody failed." );
}
- // === 湲濡쒕쾶 ?몃옖?ㅽ뤌 而ㅻ컠 ===
- // ?꾪꽣 OFF ?먯꽌???먯긽(NaN쨌0荑쇳꽣?덉뼵)쨌誘몃ℓ??蹂몄? 媛쒕퀎濡?嫄대꼫?곗뼱 吏곸쟾 ?ъ쫰瑜?蹂댁〈?쒕떎.
for (int boneIdx = 0; boneIdx < skelRbCount; ++boneIdx)
{
sRigidBodyData boneData = stagedBones[boneIdx];
@@ -3252,8 +3107,6 @@ public class OptitrackStreamingClient : MonoBehaviour
// -----------------------------------------------------
// - Update trained markerset // trained markerset added
// ----------------------------------------------------
- // m_dataDescs瑜?濡쒖뺄 蹂?섎줈 罹≪쿂: UpdateDefinitions()(硫붿씤 ?ㅻ젅??媛 李몄“瑜?援먯껜?대룄 ?덉쟾
- // null 泥댄겕: SkipDataDescriptions=true ?먮뒗 UpdateDefinitions() 誘몄셿猷??ㅽ뙣 ???щ옒??諛⑹?
var dataDescsSnapshot = m_dataDescs;
m_latestTMarkMarkerStates.Clear();
if (dataDescsSnapshot != null && dataDescsSnapshot.AssetDescriptions != null)
@@ -3264,7 +3117,6 @@ public class OptitrackStreamingClient : MonoBehaviour
// Ensure we have a state corresponding to this tmarkerset ID.
OptitrackTMarkersetState tmarkState = GetOrCreateTMarkersetState(tmarkersetId);
- // TMarkerset ?뺤쓽 寃?됱쓣 蹂?猷⑦봽 諛뽰뿉??1?뚮쭔 ?섑뻾 (湲곗〈: 留?蹂몃쭏???좏삎 ?먯깋)
Int32 tmarkRbCount = dataDescsSnapshot.AssetDescriptions[tmarkIdx].RigidBodyCount;
OptitrackTMarkersetDefinition tmarkDef = GetTMarkersetDefinitionById(tmarkersetId);
if (tmarkDef == null)
@@ -3364,23 +3216,6 @@ public class OptitrackStreamingClient : MonoBehaviour
markerState.IsActive = (marker.Params & 0x20) != 0;
}
- // ----------------------------------
- // - Update analog devices (iFacialMocap face, NIDAQ, etc.)
- //
- // We CANNOT use eventArgs.MarshaledFrame here ??that triggers a full
- // sFrameOfMocapData marshal which throws on the fixed-size MarkerSets[]
- // array (uninitialized bytes in unused slots fail ANSI string decoding).
- //
- // Instead: unsafe pointer arithmetic to read DeviceCount + each sDeviceData
- // directly from native memory, skipping the problematic sections entirely.
- // sDeviceData / sAnalogChannelData have no string fields, so PtrToStructure
- // on individual entries is safe.
- // ----------------------------------
- if (m_deviceDefinitions.Count > 0)
- {
- ProcessDeviceData(eventArgs.NativeFramePointer, frameTimestamp);
- }
-
}
catch (Exception ex)
{
@@ -3393,179 +3228,11 @@ public class OptitrackStreamingClient : MonoBehaviour
}
}
- // Cached struct offsets/sizes for ProcessDeviceData. Computed once via reflection,
- // then reused ??Marshal.OffsetOf / SizeOf don't allocate on subsequent calls.
- private static int s_DeviceCountOffset = -1;
- private static int s_DevicesArrayOffset;
- private static int s_DeviceDataSize;
- private static int s_FrameNumberOffset;
-
- private static void EnsureDeviceOffsetsInitialized()
- {
- if (s_DeviceCountOffset >= 0) return;
- s_DeviceCountOffset = (int)Marshal.OffsetOf(typeof(sFrameOfMocapData), "DeviceCount");
- s_DevicesArrayOffset = (int)Marshal.OffsetOf(typeof(sFrameOfMocapData), "Devices");
- s_FrameNumberOffset = (int)Marshal.OffsetOf(typeof(sFrameOfMocapData), "FrameNumber");
- s_DeviceDataSize = Marshal.SizeOf(typeof(sDeviceData));
- }
-
- private void ProcessDeviceData(IntPtr pFrame, OptitrackHiResTimer.Timestamp frameTimestamp)
- {
- if (pFrame == IntPtr.Zero) return;
- EnsureDeviceOffsetsInitialized();
-
- int deviceCount = Marshal.ReadInt32(pFrame, s_DeviceCountOffset);
-
- if (deviceCount <= 0) return;
- if (deviceCount > k_MaxNatNetDevices) deviceCount = k_MaxNatNetDevices;
-
- int frameNumber = Marshal.ReadInt32(pFrame, s_FrameNumberOffset);
-
- for (int devIdx = 0; devIdx < deviceCount; ++devIdx)
- {
- IntPtr pDevice = IntPtr.Add(pFrame, s_DevicesArrayOffset + devIdx * s_DeviceDataSize);
- sDeviceData devData = (sDeviceData)Marshal.PtrToStructure(pDevice, typeof(sDeviceData));
-
- OptitrackDeviceDefinition devDef = GetDeviceDefinitionById(devData.Id);
- if (devDef == null) continue;
-
- OptitrackDeviceState devState = GetOrCreateDeviceState(devData.Id);
- devState.Name = devDef.Name;
- devState.FrameNumber = frameNumber;
- devState.DeliveryTimestamp = frameTimestamp;
-
- int chCount = Math.Min(devData.ChannelCount, devDef.ChannelCount);
- chCount = Math.Min(chCount, devData.ChannelData != null ? devData.ChannelData.Length : 0);
- chCount = Math.Min(chCount, devDef.ChannelNames != null ? devDef.ChannelNames.Count : 0);
- for (int ch = 0; ch < chCount; ++ch)
- {
- // Use subframe 0 ??face data is one sample per frame.
- sAnalogChannelData chData = devData.ChannelData[ch];
- if (chData.Values == null || chData.Values.Length == 0)
- continue;
- float value = chData.Values[0];
- devState.ChannelValues[devDef.ChannelNames[ch]] = value;
- }
- }
- }
-
- private OptitrackDeviceDefinition GetDeviceDefinitionById( Int32 id )
- {
- for (int i = 0; i < m_deviceDefinitions.Count; ++i)
- if (m_deviceDefinitions[i].Id == id) return m_deviceDefinitions[i];
- return null;
- }
-
- private OptitrackDeviceState GetOrCreateDeviceState( Int32 id )
- {
- if (m_latestDeviceStates.TryGetValue(id, out OptitrackDeviceState state))
- return state;
- state = new OptitrackDeviceState
- {
- Id = id,
- ChannelValues = new Dictionary(),
- };
- m_latestDeviceStates[id] = state;
- return state;
- }
-
- /// Get the latest channel values for a device by ID.
- public OptitrackDeviceState GetLatestDeviceState( Int32 deviceId )
- {
- Monitor.Enter( m_frameDataUpdateLock );
- try
- {
- return m_latestDeviceStates.TryGetValue(deviceId, out var state) ? CopyDeviceState(state) : null;
- }
- finally
- {
- Monitor.Exit( m_frameDataUpdateLock );
- }
- }
-
- /// Get the latest channel values for a device by Motive name (e.g. "iFacialMocap_40001").
- public OptitrackDeviceState GetLatestDeviceState( string deviceName )
- {
- Monitor.Enter( m_frameDataUpdateLock );
- try
- {
- if (m_deviceNameToId.TryGetValue(deviceName, out Int32 id) &&
- m_latestDeviceStates.TryGetValue(id, out var state))
- return CopyDeviceState(state);
- return null;
- }
- finally
- {
- Monitor.Exit( m_frameDataUpdateLock );
- }
- }
-
- private static OptitrackDeviceState CopyDeviceState(OptitrackDeviceState source)
- {
- if (source == null)
- return null;
-
- return new OptitrackDeviceState
- {
- Id = source.Id,
- Name = source.Name,
- FrameNumber = source.FrameNumber,
- DeliveryTimestamp = source.DeliveryTimestamp,
- ChannelValues = source.ChannelValues != null
- ? new Dictionary(source.ChannelValues)
- : new Dictionary(),
- };
- }
-
- /// Returns snapshots of all latest NatNet device samples, including direct UDP fallback devices without MODELDEF names.
- public List GetLatestDeviceStates()
- {
- lock (m_frameDataUpdateLock)
- {
- var snapshots = new List(m_latestDeviceStates.Count);
- foreach (var kvp in m_latestDeviceStates)
- snapshots.Add(CopyDeviceState(kvp.Value));
- return snapshots;
- }
- }
-
- /// Copies a device's latest channel values into a caller-owned dictionary.
- public bool FillDeviceChannelSnapshot(string deviceName, Dictionary channelValuesOut)
- {
- lock (m_frameDataUpdateLock)
- {
- channelValuesOut.Clear();
- if (!m_deviceNameToId.TryGetValue(deviceName, out Int32 id) ||
- !m_latestDeviceStates.TryGetValue(id, out var state) ||
- state == null)
- return false;
-
- foreach (var kvp in state.ChannelValues)
- channelValuesOut[kvp.Key] = kvp.Value;
- return true;
- }
- }
-
- /// Returns a snapshot of currently registered device definitions.
- public List GetDeviceDefinitions()
- {
- Monitor.Enter( m_frameDataUpdateLock );
- try
- {
- return new List(m_deviceDefinitions);
- }
- finally
- {
- Monitor.Exit( m_frameDataUpdateLock );
- }
- }
-
private string GetMarkerName( sMarker marker )
{
int assetID = marker.Id >> 16; // high word = Asset ID Number
int memberID = marker.Id & 0x00ffff; // low word = Member ID Number (constraint number)
- // assetID?믪씠由?罹먯떆 ?ъ슜 (湲곗〈: 留?留덉빱留덈떎 3媛?由ъ뒪???좏삎 ?먯깋)
string assetName;
if (!m_assetIdToNameCache.TryGetValue(assetID, out assetName))
{
@@ -3614,7 +3281,6 @@ public class OptitrackStreamingClient : MonoBehaviour
private void ResetStreamingSubscriptions()
{
- // 1媛?紐낅졊?쇰줈 ?듯빀: "SubscribeToData"??紐⑤뱺 ?꾪꽣瑜??대━?댄븯怨?湲곕낯 ?곹깭(援щ룆 ?놁쓬)濡?由ъ뀑
m_client.RequestCommand( "SubscribeToData" );
}
@@ -3846,8 +3512,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// 蹂??곗씠?곌? ?ъ쫰濡?而ㅻ컠?대룄 ?덉쟾?쒖? 寃?ы븳??醫뚰몴 ?좏븳 + 荑쇳꽣?덉뼵 ?뺤긽).
- /// ?몃옒??鍮꾪듃? 臾닿? ???꾪꽣 OFF?먯꽌???먯긽 蹂몄씠 吏곸쟾 ?ъ쫰瑜???뼱?곗? 紐삵븯寃?留됰뒗 ?덉쟾?μ튂.
///
private static bool IsBoneDataUsable(sRigidBodyData boneData)
{
@@ -3868,9 +3532,6 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// ?꾨젅??蹂??곗씠?곌? ???ㅼ펷?덊넠??而ㅻ컠 媛?ν븳吏 寃?ы븯怨?boneId瑜?異쒕젰?쒕떎.
- /// ?ㅼ펷?덊넠 ID ?쇱튂 + ?뺤쓽??留ㅽ븨??蹂?+ ?곗씠???뺤긽(NaN/0-荑쇳꽣?덉뼵 ?꾨떂)???뚮쭔 true.
- /// (?꾪꽣 ON/OFF 怨듯넻?쇰줈 而ㅻ컠 ?④퀎?먯꽌 ?ъ슜 ??OFF?먯꽌 ?먯긽쨌誘몃ℓ??蹂?媛쒕퀎 ?ㅽ궢, 2李??⑥뒪 KeyNotFound 諛⑹?)
///
private static bool TryGetCommittableBoneId(sRigidBodyData boneData, Int32 skeletonId, OptitrackSkeletonDefinition skelDef, out int boneId)
{
@@ -4031,19 +3692,16 @@ public class OptitrackStreamingClient : MonoBehaviour
}
///
- /// ?대? ?꾨젅???곗씠????吏꾩엯. 諛섎뱶??try-finally ?⑦꽩?쇰줈 _ExitFrameDataUpdateLock()怨??띿쑝濡??ъ슜?섏꽭??
- /// Exit ?놁씠 ?몄텧?섎㈃ NatNet ?ㅻ젅?쒓? ?곴뎄 ?곕뱶?쎈맗?덈떎.
///
- [System.Obsolete("吏곸젒 ??議곗옉 ???FillBoneSnapshot() ???ㅻ젅???덉쟾 API瑜??ъ슜?섏꽭??")]
+ [System.Obsolete("Use FillBoneSnapshot() instead of direct lock manipulation.")]
internal void _EnterFrameDataUpdateLock()
{
Monitor.Enter( m_frameDataUpdateLock );
}
///
- /// ?대? ?꾨젅???곗씠?????댁젣. 諛섎뱶??_EnterFrameDataUpdateLock()怨??띿쑝濡??ъ슜?섏꽭??
///
- [System.Obsolete("吏곸젒 ??議곗옉 ???FillBoneSnapshot() ???ㅻ젅???덉쟾 API瑜??ъ슜?섏꽭??")]
+ [System.Obsolete("Use FillBoneSnapshot() instead of direct lock manipulation.")]
internal void _ExitFrameDataUpdateLock()
{
Monitor.Exit( m_frameDataUpdateLock );
diff --git a/Assets/External/OptiTrack Unity Plugin/README.md b/Assets/External/OptiTrack Unity Plugin/README.md
index caa90dec8..c52cdd98f 100644
--- a/Assets/External/OptiTrack Unity Plugin/README.md
+++ b/Assets/External/OptiTrack Unity Plugin/README.md
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:818765858d1666693c5c2a0a7fc50842cc7480822907a9134fafd59645e78a49
-size 9800
+oid sha256:0a7e705816c49ebf0b2b6039613686bfeb023becc17cc6786efba61aec064639
+size 9703
diff --git a/Assets/External/StreamingleFacial/Editor/StreamingleFacialReceiverEditor.cs b/Assets/External/StreamingleFacial/Editor/StreamingleFacialReceiverEditor.cs
index dcac988a2..fdb3844cf 100644
--- a/Assets/External/StreamingleFacial/Editor/StreamingleFacialReceiverEditor.cs
+++ b/Assets/External/StreamingleFacial/Editor/StreamingleFacialReceiverEditor.cs
@@ -20,9 +20,6 @@ public class StreamingleFacialReceiverEditor : Editor
private VisualElement medianEuroFields;
private VisualElement sharedPortInfo;
private Label masterStatusValue;
- private VisualElement udpSourceFields;
- private VisualElement natnetFields;
- private Label natnetEffectiveNameValue;
public override VisualElement CreateInspectorGUI()
{
@@ -49,9 +46,6 @@ public class StreamingleFacialReceiverEditor : Editor
medianEuroFields = root.Q("medianEuroFields");
sharedPortInfo = root.Q("sharedPortInfo");
masterStatusValue = root.Q
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -52,23 +33,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assets/External/StreamingleFacial/NatnetDeviceListener.cs b/Assets/External/StreamingleFacial/NatnetDeviceListener.cs
deleted file mode 100644
index 3fd795f64..000000000
--- a/Assets/External/StreamingleFacial/NatnetDeviceListener.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using UnityEngine;
-
-///
-/// Legacy compatibility component.
-/// NatNet device channels are now parsed directly by OptitrackStreamingClient.
-/// This stub keeps old scenes/prefabs from producing missing-script references.
-///
-public class NatnetDeviceListener : MonoBehaviour
-{
- [Header("Legacy")]
- [Tooltip("Deprecated. OptitrackStreamingClient now parses NatNet device channels directly.")]
- public bool disabledByUnifiedOptitrackClient = true;
-
- [Header("Old Settings (unused)")]
- public string multicastGroup = "239.255.42.99";
- public int dataPort = 1511;
-}
diff --git a/Assets/External/StreamingleFacial/NatnetDeviceListener.cs.meta b/Assets/External/StreamingleFacial/NatnetDeviceListener.cs.meta
deleted file mode 100644
index dc83a0379..000000000
--- a/Assets/External/StreamingleFacial/NatnetDeviceListener.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: d700c07a89051f94ebc222b985e79115
\ No newline at end of file
diff --git a/Assets/External/StreamingleFacial/StreamingleFacialReceiver.cs b/Assets/External/StreamingleFacial/StreamingleFacialReceiver.cs
index be93866fb..1d2c18f92 100644
--- a/Assets/External/StreamingleFacial/StreamingleFacialReceiver.cs
+++ b/Assets/External/StreamingleFacial/StreamingleFacialReceiver.cs
@@ -22,65 +22,6 @@ public class StreamingleFacialReceiver : MonoBehaviour
private string messageString = "";
private string lastProcessedMessage = ""; // 이전 메시지 저장용
- // ── 데이터 소스 모드 ──
- public enum FacialSourceMode
- {
- DirectUDP, // 기존: iFacialMocap 앱에서 직접 UDP 수신
- NatNetDevice // 신규: Motive NatNet의 device 채널 데이터에서 수신
- }
-
- [Header("Source Mode")]
- [Tooltip("DirectUDP=기존 iFacialMocap UDP 수신 / NatNetDevice=Motive 거쳐서 NatNet으로 받기")]
- public FacialSourceMode sourceMode = FacialSourceMode.DirectUDP;
-
- [Header("NatNet Mode (sourceMode=NatNetDevice일 때)")]
- [Tooltip("OptitrackStreamingClient. 비워두면 Start에서 자동 검색. (디바이스 이름→ID, 채널 이름 메타데이터용)")]
- public OptitrackStreamingClient natnetStreamingClient;
- [Tooltip("Motive 디바이스 이름 prefix. 활성 포트가 붙어 최종 base name = prefix + activePort (예: \"iFacialMocap_\" + 40001 = \"iFacialMocap_40001\"). 32채널 초과로 분할되었으면 _A/_B 접미사 자동 감지.")]
- public string natnetDeviceNamePrefix = "iFacialMocap_";
-
- ///
- /// 현재 활성 포트로부터 합성된 디바이스 base name. NatNet 모드에서 사용.
- ///
- public string EffectiveNatnetDeviceBaseName => natnetDeviceNamePrefix + LOCAL_PORT;
-
- public void SetNatNetSource(OptitrackStreamingClient client)
- {
- if (natnetStreamingClient == client) return;
- natnetStreamingClient = client;
- ResetNatNetDeviceResolution();
- }
-
- private void ResetNatNetDeviceResolution()
- {
- natnetResolvedDevices.Clear();
- natnetLastResolveAttempt = -10f;
- natnetLastResolvedFor = "";
- Array.Clear(natnetLastSeenSeq, 0, natnetLastSeenSeq.Length);
- natnetDiagLogged = false;
- messageString = "";
- lastProcessedMessage = "";
- }
-
- // NatNet 모드: device name → (device id, channel names) 캐시.
- // OptitrackStreamingClient의 description으로부터 1회 lookup.
- private struct ResolvedDevice
- {
- public int Id;
- // NatNet 프레임 전체에서의 위치 (description 순서 = wire 순서 가정).
- // listener는 wire 글로벌 인덱스로 키잉되므로 로컬 리스트 인덱스를 넘기면 안 됨
- // — 다른 포트의 device 데이터를 잘못 가져오게 됨.
- public string DeviceName;
- public List ChannelNames;
- }
- private List natnetResolvedDevices = new List(2);
- private float natnetLastResolveAttempt = -10f;
- private string natnetLastResolvedFor = ""; // 포트 변경 감지용
- private ulong[] natnetLastSeenSeq = new ulong[2]; // resolved 디바이스별 frame seq 추적
- private bool natnetDiagLogged = false; // [NatNet Align Diag] 1회만 출력
- private const float k_NatnetResolveRetrySec = 1.0f;
- private System.Text.StringBuilder natnetMsgBuilder = new System.Text.StringBuilder(2048);
-
// ── 공유 포트 (마스터 모드) ──
[Header("Shared Port (Master Mode)")]
[Tooltip("활성화 시 UdpMaster를 통해 같은 포트의 데이터를 여러 Receiver가 공유 수신")]
@@ -252,24 +193,7 @@ public class StreamingleFacialReceiver : MonoBehaviour
// BlendShape 인덱스 캐싱 초기화
InitializeBlendShapeCache();
- if (sourceMode == FacialSourceMode.NatNetDevice)
- {
- // NatNet 모드: 자체 UDP 서버 안 띄움. 두 컴포넌트에 의존:
- // - OptitrackStreamingClient: device 이름→ID + 채널 이름 메타데이터
- // - NatnetDeviceListener: 실제 채널값 (wire 직접 파싱)
- if (natnetStreamingClient == null)
- natnetStreamingClient = FindObjectOfType();
- if (natnetStreamingClient == null)
- {
- Debug.LogError("[Streamingle] NatNet 모드인데 OptitrackStreamingClient를 못 찾음.", this);
- }
- else if (!natnetStreamingClient.ReceiveDevices)
- {
- Debug.LogWarning("[Streamingle] OptitrackStreamingClient.ReceiveDevices 가 꺼져있음 — 켜야 device description이 들어옴.", this);
- }
-
- }
- else if (useSharedPort)
+ if (useSharedPort)
{
// 마스터 모드: UdpMaster에 등록하여 공유 수신
StreamingleFacialUdpMaster.Instance.Register(LOCAL_PORT, this);
@@ -401,14 +325,6 @@ public class StreamingleFacialReceiver : MonoBehaviour
RebuildIntensityOverrideMap();
}
- // NatNet 모드: device 채널을 폴링해 iFacialMocap 와이어 포맷의 messageString을
- // 만든 뒤 기존 SetAnimation 파이프라인을 그대로 태운다. (이렇게 하면 필터/미러/
- // intensity 코드 한 줄도 안 고치고 NatNet 경로 추가됨)
- if (sourceMode == FacialSourceMode.NatNetDevice)
- {
- PollNatNetDevice();
- }
-
// 메시지가 변경되었을 때만 처리 (성능 최적화)
if (!string.IsNullOrEmpty(messageString) && messageString != lastProcessedMessage)
{
@@ -424,219 +340,6 @@ public class StreamingleFacialReceiver : MonoBehaviour
}
}
- // 2026-05-13 PROTOCOL-LEVEL FIX. Plugin 과 양쪽 모두 하드코딩된 canonical
- // blendshape 순서를 공유. Motive 가 description 을 reorder/strip 해도 wire 데이터
- // 만큼은 plugin 이 쓴 그대로 도달하므로 alignment 보장. 두 사이드는 이 리스트가
- // 일치해야 함 — plugin 의 FaceDevice.cpp `kCanonicalBlendshapeNames` 와 동일.
- private const int kCanonicalPrimaryCount = 31; // primary device 가 담는 first canonical names
- private static readonly string[] kCanonicalBlendshapeNames = new string[]
- {
- "browDown_L", "browDown_R", "browInnerUp", "browOuterUp_L", "browOuterUp_R",
- "cheekPuff", "cheekSquint_L", "cheekSquint_R",
- "eyeBlink_L", "eyeBlink_R",
- "eyeLookDown_L", "eyeLookDown_R", "eyeLookIn_L", "eyeLookIn_R",
- "eyeLookOut_L", "eyeLookOut_R", "eyeLookUp_L", "eyeLookUp_R",
- "eyeSquint_L", "eyeSquint_R", "eyeWide_L", "eyeWide_R",
- "hapihapi",
- "jawForward", "jawLeft", "jawOpen", "jawRight",
- "mouthClose", "mouthDimple_L", "mouthDimple_R",
- "mouthFrown_L", "mouthFrown_R", "mouthFunnel",
- "mouthLeft", "mouthLowerDown_L", "mouthLowerDown_R",
- "mouthPress_L", "mouthPress_R", "mouthPucker",
- "mouthRight", "mouthRollLower", "mouthRollUpper",
- "mouthShrugLower", "mouthShrugUpper",
- "mouthSmile_L", "mouthSmile_R",
- "mouthStretch_L", "mouthStretch_R",
- "mouthUpperUp_L", "mouthUpperUp_R",
- "noseSneer_L", "noseSneer_R",
- "tongueOut", "trackingStatus",
- };
-
- private static OptitrackDeviceState FindNatNetDeviceState(List states, int id, string name)
- {
- if (states == null) return null;
- for (int i = 0; i < states.Count; i++)
- {
- var state = states[i];
- if (state == null) continue;
- if (state.Id == id) return state;
- if (!string.IsNullOrEmpty(name) && state.Name == name) return state;
- }
- return null;
- }
-
- private static bool TryBuildNatNetDeviceValueArray(OptitrackDeviceState state, out float[] values)
- {
- values = null;
- if (state == null || state.ChannelValues == null || state.ChannelValues.Count == 0)
- return false;
-
- int maxChannelIndex = -1;
- foreach (var key in state.ChannelValues.Keys)
- {
- int index;
- if (TryParseNatNetChannelIndex(key, out index))
- maxChannelIndex = Mathf.Max(maxChannelIndex, index);
- }
-
- if (maxChannelIndex >= 0)
- {
- values = new float[maxChannelIndex + 1];
- foreach (var kvp in state.ChannelValues)
- {
- int index;
- if (TryParseNatNetChannelIndex(kvp.Key, out index) && index >= 0 && index < values.Length)
- values[index] = kvp.Value;
- }
- return true;
- }
-
- if (!state.ChannelValues.TryGetValue("sentinelPort", out float sentinel))
- return false;
-
- values = new float[1 + kCanonicalBlendshapeNames.Length];
- values[0] = sentinel;
- for (int i = 0; i < kCanonicalBlendshapeNames.Length; i++)
- {
- float value;
- if (state.ChannelValues.TryGetValue(kCanonicalBlendshapeNames[i], out value))
- values[i + 1] = value;
- }
- return true;
- }
-
- private static bool TryParseNatNetChannelIndex(string key, out int index)
- {
- index = -1;
- if (string.IsNullOrEmpty(key) || !key.StartsWith("Channel", StringComparison.Ordinal))
- return false;
- return int.TryParse(key.Substring("Channel".Length), NumberStyles.Integer, CultureInfo.InvariantCulture, out index);
- }
-
- void PollNatNetDevice()
- {
- if (natnetStreamingClient == null) return;
-
- string baseName = EffectiveNatnetDeviceBaseName;
- var deviceStates = natnetStreamingClient.GetLatestDeviceStates();
-
- // 포트 변경 또는 정의 미발견 시 재해석. OptitrackStreamingClient의 device descriptions
- // 에서 우리 base name과 매칭되는 device id + channel names를 캐시.
- bool portChanged = natnetLastResolvedFor != baseName;
- if (portChanged ||
- natnetResolvedDevices.Count == 0 ||
- Time.unscaledTime - natnetLastResolveAttempt > k_NatnetResolveRetrySec)
- {
- natnetLastResolveAttempt = Time.unscaledTime;
- if (portChanged) natnetDiagLogged = false;
- natnetLastResolvedFor = baseName;
- natnetResolvedDevices.Clear();
-
- // 2026-05-13 PROTOCOL-LEVEL FIX. Description 완전 무시. Plugin/Unity 가
- // 공유하는 kCanonicalBlendshapeNames 가 채널 이름 source of truth.
- // 1) wire 의 sentinel 값으로 우리 port 의 wire device 식별 (multi-iPhone 환경 OK)
- // 2) 채널 수 큰 게 primary, 작은 게 overflow
- // 3) ChannelNames 는 canonical 배열에서 슬라이스로 부여
- int port = LOCAL_PORT;
- var wireCandidates = new List<(OptitrackDeviceState state, float[] vals)>();
- for (int si = 0; si < deviceStates.Count; si++)
- {
- float[] vals;
- if (!TryBuildNatNetDeviceValueArray(deviceStates[si], out vals))
- continue;
- if (vals == null || vals.Length < 1) continue;
- int sentinel = Mathf.RoundToInt(vals[0]);
- if (sentinel != port) continue;
- wireCandidates.Add((deviceStates[si], vals));
- }
- wireCandidates.Sort((a, b) => b.vals.Length.CompareTo(a.vals.Length));
-
- // canonical 을 primary slice + overflow slice 로 분리
- var primarySlice = new List(1 + kCanonicalPrimaryCount);
- primarySlice.Add("sentinelPort");
- for (int i = 0; i < kCanonicalPrimaryCount && i < kCanonicalBlendshapeNames.Length; i++)
- primarySlice.Add(kCanonicalBlendshapeNames[i]);
-
- var overflowSlice = new List(1 + Math.Max(0, kCanonicalBlendshapeNames.Length - kCanonicalPrimaryCount));
- overflowSlice.Add("sentinelPort");
- for (int i = kCanonicalPrimaryCount; i < kCanonicalBlendshapeNames.Length; i++)
- overflowSlice.Add(kCanonicalBlendshapeNames[i]);
-
- // 0: primary (큰 채널 수), 1: overflow (작은 채널 수)
- if (wireCandidates.Count > 0)
- natnetResolvedDevices.Add(new ResolvedDevice { Id = wireCandidates[0].state.Id, DeviceName = wireCandidates[0].state.Name, ChannelNames = primarySlice });
- if (wireCandidates.Count > 1)
- natnetResolvedDevices.Add(new ResolvedDevice { Id = wireCandidates[1].state.Id, DeviceName = wireCandidates[1].state.Name, ChannelNames = overflowSlice });
- if (natnetLastSeenSeq.Length < natnetResolvedDevices.Count)
- natnetLastSeenSeq = new ulong[natnetResolvedDevices.Count];
- }
-
- if (natnetResolvedDevices.Count == 0) return;
-
- // GC OPT: skip rebuild if no listener has new wire frame since last poll.
- // (Unity Update tick can run faster than NatNet broadcast rate.)
- bool anyNew = false;
- for (int di = 0; di < natnetResolvedDevices.Count; di++)
- {
- OptitrackDeviceState state = FindNatNetDeviceState(deviceStates, natnetResolvedDevices[di].Id, natnetResolvedDevices[di].DeviceName);
- ulong seq = state != null ? (ulong)(uint)state.FrameNumber : 0;
- if (di < natnetLastSeenSeq.Length && seq != natnetLastSeenSeq[di])
- {
- natnetLastSeenSeq[di] = seq;
- anyNew = true;
- }
- }
- if (!anyNew) return;
-
- // iFacialMocap 와이어 포맷 합성: name-value|name-value|...=
- // 채널 이름은 canonical 리스트(plugin과 공유)에서, 값은 NatnetDeviceListener wire 파싱.
- natnetMsgBuilder.Length = 0;
- bool first = true;
- for (int di = 0; di < natnetResolvedDevices.Count; di++)
- {
- var rd = natnetResolvedDevices[di];
- OptitrackDeviceState state = FindNatNetDeviceState(deviceStates, rd.Id, rd.DeviceName);
- float[] vals;
- if (!TryBuildNatNetDeviceValueArray(state, out vals))
- continue;
- if (vals == null || rd.ChannelNames == null) continue;
-
- if (!natnetDiagLogged)
- {
- int dumpLen = Math.Min(5, vals.Length);
- var valDump = new string[dumpLen];
- for (int k = 0; k < dumpLen; k++) valDump[k] = vals[k].ToString("0.##", CultureInfo.InvariantCulture);
- int nameDump = Math.Min(5, rd.ChannelNames.Count);
- var nameArr = new string[nameDump];
- for (int k = 0; k < nameDump; k++) nameArr[k] = rd.ChannelNames[k];
- Debug.Log($"[NatNet Align Diag] dev[{di}] Device={rd.DeviceName} Id={rd.Id} canonicalLen={rd.ChannelNames.Count} wireLen={vals.Length} firstNames=[{string.Join(",", nameArr)}] firstVals=[{string.Join(",", valDump)}]");
- }
-
- // Canonical 매핑: rd.ChannelNames 는 [sentinelPort, canonical[a], canonical[a+1], ...]
- // 의 슬라이스. wire vals 는 plugin 이 같은 순서로 채움 (plugin 검증됨).
- // vals[0] = sentinel (40001), vals[c] = ChannelNames[c]'s blendshape value.
- int n = Math.Min(vals.Length, rd.ChannelNames.Count);
- for (int c = 0; c < n; c++)
- {
- string name = rd.ChannelNames[c];
- if (string.IsNullOrEmpty(name)) continue;
- if (name == "sentinelPort") continue;
-
- if (!first) natnetMsgBuilder.Append('|');
- natnetMsgBuilder.Append(name);
- natnetMsgBuilder.Append('-');
- natnetMsgBuilder.Append(vals[c].ToString("0.####", CultureInfo.InvariantCulture));
- first = false;
- }
- }
- if (!natnetDiagLogged && natnetResolvedDevices.Count > 0) natnetDiagLogged = true;
- // 파서가 split('=', RemoveEmptyEntries) 길이 >= 2를 요구함.
- // 빈 transforms 섹션은 RemoveEmptyEntries에 의해 제거되므로 dummy 채워넣음.
- natnetMsgBuilder.Append("=head#0,0,0,0,0,0|leftEye#0,0,0|rightEye#0,0,0");
-
- messageString = natnetMsgBuilder.ToString();
- }
-
//BlendShapeの設定
//set blendshapes (캐시 사용으로 최적화)
void SetBlendShapeWeightFromStrArray(string[] strArray2)
@@ -829,12 +532,6 @@ public class StreamingleFacialReceiver : MonoBehaviour
{
StartFlag = true;
- if (sourceMode == FacialSourceMode.NatNetDevice)
- {
- // NatNet 모드: UDP 자원 없음. OptitrackStreamingClient는 다른 곳이 관리.
- return;
- }
-
if (useSharedPort)
{
// 마스터에서 해제
@@ -878,8 +575,6 @@ public class StreamingleFacialReceiver : MonoBehaviour
///
/// 포트 핫스왑: 지정된 인덱스의 포트로 즉시 전환.
- /// 두 모드 공통: DirectUDP는 UDP listener 재바인드, NatNet은 다음 polling 사이클에서
- /// 새 포트의 디바이스로 자동 재해석됨.
///
public void SwitchToPort(int portIndex)
{
@@ -893,15 +588,6 @@ public class StreamingleFacialReceiver : MonoBehaviour
activePortIndex = portIndex;
Debug.Log($"[iFacialMocap] 포트 전환: {availablePorts[portIndex]}");
- if (sourceMode == FacialSourceMode.NatNetDevice)
- {
- // NatNet 모드: 다음 PollNatNetDevice 호출에서 새 base name으로 자동 재해석.
- // 즉시 강제하려면 캐시 무효화.
- natnetResolvedDevices.Clear();
- natnetLastResolveAttempt = -10f;
- return;
- }
-
if (useSharedPort)
{
// 마스터 모드: 포트 전환 요청
@@ -929,17 +615,6 @@ public class StreamingleFacialReceiver : MonoBehaviour
{
Debug.Log("[iFacialMocap] 재접속 시도 중...");
- if (sourceMode == FacialSourceMode.NatNetDevice)
- {
- // NatNet 모드: 디바이스 재해석 강제 (다음 PollNatNetDevice 호출에서 다시 검색).
- natnetResolvedDevices.Clear();
- natnetLastResolveAttempt = -10f;
- yield return null;
- reconnectCoroutine = null;
- Debug.Log("[iFacialMocap] NatNet device 재해석 강제 완료");
- yield break;
- }
-
if (useSharedPort)
{
// 마스터 모드: 해제 후 재등록
diff --git a/Assets/ResourcesData/Project/260327_모션촬영/260327_모션촬영.unity b/Assets/ResourcesData/Project/260327_모션촬영/260327_모션촬영.unity
index 058468fd9..3651b4332 100644
--- a/Assets/ResourcesData/Project/260327_모션촬영/260327_모션촬영.unity
+++ b/Assets/ResourcesData/Project/260327_모션촬영/260327_모션촬영.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:574d0c38f70f3dbc6fa0cec8eec753ac7d1f06180a4b62bff02f8f83d4464a19
-size 538570
+oid sha256:a4df40dc85a822d852b042421d0ea8fd6d41d8c3f0b517f2fe221ed5bcef2a79
+size 537190
diff --git a/Assets/ResourcesData/Project/260403_모션촬영/260403_모션촬영.unity b/Assets/ResourcesData/Project/260403_모션촬영/260403_모션촬영.unity
index 73307499d..e1b9e2f41 100644
--- a/Assets/ResourcesData/Project/260403_모션촬영/260403_모션촬영.unity
+++ b/Assets/ResourcesData/Project/260403_모션촬영/260403_모션촬영.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:76644ff3c870eed5fe1c934aa2ec579a7c64978868a956f5760019eadfbef741
-size 520223
+oid sha256:e34433de8a96ed6845955a722b42488e88b9f2e32bd67903ed575c32ac361ef0
+size 520094
diff --git a/Assets/ResourcesData/Project/260414_레드리허설/260416_레드리허설_레드스테이지.unity b/Assets/ResourcesData/Project/260414_레드리허설/260416_레드리허설_레드스테이지.unity
index d6e4aa79f..aedd24bca 100644
--- a/Assets/ResourcesData/Project/260414_레드리허설/260416_레드리허설_레드스테이지.unity
+++ b/Assets/ResourcesData/Project/260414_레드리허설/260416_레드리허설_레드스테이지.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bdbf46da7b702b035b42d7709dd42cdd8bb6bfbbae4a712cc34418a942115946
-size 9093062
+oid sha256:9383ba427df8db3d85257e494f4572403e0403a93ca592b0de6b3bc8b7d3397e
+size 8505976
diff --git a/Assets/ResourcesData/Project/260419_Rude모션촬영/260419_Rude모션촬영.unity b/Assets/ResourcesData/Project/260419_Rude모션촬영/260419_Rude모션촬영.unity
index 62084493d..92f5b296b 100644
--- a/Assets/ResourcesData/Project/260419_Rude모션촬영/260419_Rude모션촬영.unity
+++ b/Assets/ResourcesData/Project/260419_Rude모션촬영/260419_Rude모션촬영.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8c3ee21c091bd2a9be79ecaa9e4ee5d4011ed9222136a8dbc7b8cbe341a8a4b5
-size 67062
+oid sha256:7eec2d2a7de76fc68985c9d198c20ca561902fb6797e704163d3d58021bbf4f2
+size 66933
diff --git a/Assets/ResourcesData/Project/260429_모션촬영_뮤즈/260429_모션촬영_뮤즈.unity b/Assets/ResourcesData/Project/260429_모션촬영_뮤즈/260429_모션촬영_뮤즈.unity
index e22619738..12307f704 100644
--- a/Assets/ResourcesData/Project/260429_모션촬영_뮤즈/260429_모션촬영_뮤즈.unity
+++ b/Assets/ResourcesData/Project/260429_모션촬영_뮤즈/260429_모션촬영_뮤즈.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f57d4ce1e9dc5061615a9e66be77d359b366273f606883d23b4d49b4a6b0db04
-size 821431
+oid sha256:e1d2beb09760f5ff728859cfb310a863c33e3b923408fa7b2e915fa3b4100d0e
+size 821173
diff --git a/Assets/ResourcesData/Project/260501_모션촬영/260501_모션촬영.unity b/Assets/ResourcesData/Project/260501_모션촬영/260501_모션촬영.unity
index d7ee758aa..91b36c6eb 100644
--- a/Assets/ResourcesData/Project/260501_모션촬영/260501_모션촬영.unity
+++ b/Assets/ResourcesData/Project/260501_모션촬영/260501_모션촬영.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b2adce47fd66c908fb092a66fbf2739d74d74685e9b077f96021e5060d8a90f6
-size 511832
+oid sha256:9d97a716cafe309aa990d9e6539b70e059796075170de9a27e6e0aedfd98c400
+size 511703
diff --git a/Assets/ResourcesData/Project/260504_엠키스코어_은서모션촬영/260504_엠키스코어_은서모션촬영.unity b/Assets/ResourcesData/Project/260504_엠키스코어_은서모션촬영/260504_엠키스코어_은서모션촬영.unity
index cc8c8c30d..c85794d45 100644
--- a/Assets/ResourcesData/Project/260504_엠키스코어_은서모션촬영/260504_엠키스코어_은서모션촬영.unity
+++ b/Assets/ResourcesData/Project/260504_엠키스코어_은서모션촬영/260504_엠키스코어_은서모션촬영.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8a18331ae80b725b5367c73c8a93bd58aa4b37b4774687494a08a25aedf0e942
-size 564442
+oid sha256:1391bc441c1e99d52622feccc641ca93018d23ff4e0d06c3fa9f44d16564260f
+size 564184
diff --git a/Assets/ResourcesData/Project/260513_숙희님방송/260513_숙희님방송.unity b/Assets/ResourcesData/Project/260513_숙희님방송/260513_숙희님방송.unity
index 6f3978dd0..3b3a5ef54 100644
--- a/Assets/ResourcesData/Project/260513_숙희님방송/260513_숙희님방송.unity
+++ b/Assets/ResourcesData/Project/260513_숙희님방송/260513_숙희님방송.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ebfba509274e4cf33da39228ed2f0d430fd926fffd2aab6e0aa3c72b3b8f81ee
-size 825260
+oid sha256:ee71771ff99102e339a1576bdd25c82ffcf8a8fa1ff53427ed448c61be497af5
+size 824668
diff --git a/Assets/ResourcesData/Project/260515_모션촬영/260515_모션촬영.unity b/Assets/ResourcesData/Project/260515_모션촬영/260515_모션촬영.unity
index 8ee8fa581..72433528c 100644
--- a/Assets/ResourcesData/Project/260515_모션촬영/260515_모션촬영.unity
+++ b/Assets/ResourcesData/Project/260515_모션촬영/260515_모션촬영.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:495fd1b7ef06ef5a6c0372460e12ce3f9dd20eca7a6fd60081d5fe19fb1cc2cd
-size 585174
+oid sha256:56e5f9c9b0699a902ac370c0486a26b89f4f79fa24d2d190c13d74cec6044565
+size 584720
diff --git a/Assets/ResourcesData/Project/260526_모션촬영/260526_모션촬영.unity b/Assets/ResourcesData/Project/260526_모션촬영/260526_모션촬영.unity
index f07eb46c1..eb858636e 100644
--- a/Assets/ResourcesData/Project/260526_모션촬영/260526_모션촬영.unity
+++ b/Assets/ResourcesData/Project/260526_모션촬영/260526_모션촬영.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d05b9be995df089a87acb3b726c5d13b40c583e8139cc99455f8946af62e30f9
-size 427444
+oid sha256:43aebc36a973f6af4b3b184abc7d3febf34f005b57ccf0cd6f4804b5222894db
+size 426990
diff --git a/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송.unity b/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송.unity
index a05f48764..f6b8a6962 100644
--- a/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송.unity
+++ b/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:29a0a775ec8eef7c928a113e76175bbddacf10fae19a952bbc8db0bee636c24b
-size 412320
+oid sha256:d8e42d077569da698f94ad83236b71b0900847e6e25aa80c87ff40df6997e0e2
+size 412004
diff --git a/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송_콘서트장A.unity b/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송_콘서트장A.unity
index 279f938ec..6440a1ecb 100644
--- a/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송_콘서트장A.unity
+++ b/Assets/ResourcesData/Project/260530_텔라님_녹화방송/260530_텔라님_녹화방송_콘서트장A.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fad10ffe08ed761613118cb41ed983223110ec3db76fa06ee2f4dcef5b0458e0
-size 1248448
+oid sha256:67b2029bcfbc22af066bbbc223efc5e4f97eaf9e1af44837bcf5547aa2e53ea0
+size 1248319
diff --git a/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_무대A.unity b/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_무대A.unity
index cb4e2e06b..63c7f59f1 100644
--- a/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_무대A.unity
+++ b/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_무대A.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8285ad20a86c3251f9d64b1dd649da655172897cdef9fd479ce0e0a60958eccc
-size 1174912
+oid sha256:155c1af80ca89353c920c888a23473b8e5e34a5fb9c1feef707d7126a70ebbd2
+size 1174783
diff --git a/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_중세성당.unity b/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_중세성당.unity
index a0fa3f46c..a4f7bea4b 100644
--- a/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_중세성당.unity
+++ b/Assets/ResourcesData/Project/260531_이노리님_방송/260531_이노리님_방송_중세성당.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f7a2473faad56f8ae93e0af06c65662701054337f697bef8c80daf9bd78a132b
-size 251849
+oid sha256:ecb3fb665de44d4987a3c5f43413d8a61509c0c6def70df72ea8c2df6b9d5409
+size 251533
diff --git a/Assets/ResourcesData/Project/260605_치요님방송/260605_치요님방송_학교옥상.unity b/Assets/ResourcesData/Project/260605_치요님방송/260605_치요님방송_학교옥상.unity
index 56ca0740a..5fc18d65c 100644
--- a/Assets/ResourcesData/Project/260605_치요님방송/260605_치요님방송_학교옥상.unity
+++ b/Assets/ResourcesData/Project/260605_치요님방송/260605_치요님방송_학교옥상.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f0a004078c5c819e29add08329f3a6b3e691eee6cc62347af5a58916c04c88f5
-size 516028
+oid sha256:7af2cb361a632b87023f00dc9de373aae7e7ea044e4ca874af15d2afa1788ec3
+size 515420
diff --git a/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송.unity b/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송.unity
index c73ec139a..5f58c8ddf 100644
--- a/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송.unity
+++ b/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8142722c8966209b0b828bcf96d6c3e1b3a7ffea5e34bde76400690584ecbd17
-size 355086
+oid sha256:e295efa17623dd6d75be1fbdfdc794d2eac0a6e3cfd85c43002e9fbec667abed
+size 354948
diff --git a/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경.unity b/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경.unity
index f205c7b4f..4b8a1761b 100644
--- a/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경.unity
+++ b/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:29fe84aa5def37ff2ab1063bd55bef9e2df16fd9880f9e384be8fbeeba889210
-size 797507
+oid sha256:fbd9b96c16334c296fcbe7e744e7d24c449f816bce9bf19c11882dd6f663e3f8
+size 797405
diff --git a/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경2.unity b/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경2.unity
index 97237f28c..4697412f8 100644
--- a/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경2.unity
+++ b/Assets/ResourcesData/Project/260618_모코님방송/260618_모코님방송_웨딩풍선배경2.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6d292dfc5f17923816fb8ab523f40e3155e139d41ace6f492e1d9555c13bb451
-size 833357
+oid sha256:df66001cf591893e61b87d8473b96ab7d63a0416b4c0318a7902011a5e01f413
+size 833219
diff --git a/Assets/ResourcesData/Project/260620_숙희님방송/260620_숙희님방송.unity b/Assets/ResourcesData/Project/260620_숙희님방송/260620_숙희님방송.unity
index d5c0e83c4..5ce42b229 100644
--- a/Assets/ResourcesData/Project/260620_숙희님방송/260620_숙희님방송.unity
+++ b/Assets/ResourcesData/Project/260620_숙희님방송/260620_숙희님방송.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e58fe2e6daa950c305c1296cbe14ffe88c637eb4c54c49347cf3c5fe14332665
-size 830831
+oid sha256:8434bcd22080bd6cd684e029cbf597705e09be3b1ca3d146ee31ad571db5c93a
+size 830347