From c55103a3e5df1c570d418a57883dc7a4d7a47037 Mon Sep 17 00:00:00 2001 From: "qsxft258@gmail.com" Date: Sun, 21 Jun 2026 21:18:28 +0900 Subject: [PATCH] Fix OptiTrack refresh and UDP facial flow --- .../Prefabs/Client - OptiTrack.prefab | 4 +- .../UXML/OptitrackStreamingClientEditor.uxml | 11 +- .../OptiTrack/Scripts/OptitrackFaceDevice.cs | 229 ------- .../Scripts/OptitrackFaceDevice.cs.meta | 2 - .../OptiTrack/Scripts/OptitrackRigidBody.cs | 60 +- .../Scripts/OptitrackStreamingClient.cs | 634 ++++-------------- .../External/OptiTrack Unity Plugin/README.md | 4 +- .../Editor/StreamingleFacialReceiverEditor.cs | 35 - .../UXML/StreamingleFacialReceiverEditor.uxml | 50 +- .../StreamingleFacial/NatnetDeviceListener.cs | 17 - .../NatnetDeviceListener.cs.meta | 2 - .../StreamingleFacialReceiver.cs | 327 +-------- .../260327_모션촬영/260327_모션촬영.unity | 4 +- .../260403_모션촬영/260403_모션촬영.unity | 4 +- .../260416_레드리허설_레드스테이지.unity | 4 +- .../260419_Rude모션촬영.unity | 4 +- .../260429_모션촬영_뮤즈.unity | 4 +- .../260501_모션촬영/260501_모션촬영.unity | 4 +- .../260504_엠키스코어_은서모션촬영.unity | 4 +- .../260513_숙희님방송/260513_숙희님방송.unity | 4 +- .../260515_모션촬영/260515_모션촬영.unity | 4 +- .../260526_모션촬영/260526_모션촬영.unity | 4 +- .../260530_텔라님_녹화방송.unity | 4 +- .../260530_텔라님_녹화방송_콘서트장A.unity | 4 +- .../260531_이노리님_방송_무대A.unity | 4 +- .../260531_이노리님_방송_중세성당.unity | 4 +- .../260605_치요님방송_학교옥상.unity | 4 +- .../260618_모코님방송/260618_모코님방송.unity | 4 +- .../260618_모코님방송_웨딩풍선배경.unity | 4 +- .../260618_모코님방송_웨딩풍선배경2.unity | 4 +- .../260620_숙희님방송/260620_숙희님방송.unity | 4 +- 31 files changed, 241 insertions(+), 1210 deletions(-) delete mode 100644 Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs delete mode 100644 Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackFaceDevice.cs.meta delete mode 100644 Assets/External/StreamingleFacial/NatnetDeviceListener.cs delete mode 100644 Assets/External/StreamingleFacial/NatnetDeviceListener.cs.meta 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