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 4195b225..cad1c68c 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:0725d6622005cda966a2c32dda8b2199a6f4dc1cc0feb58bbff880ba7fe4b83e -size 2480 +oid sha256:027d573d9f781cd5c05525d407e75ff8e3ed6a248765c9fdd52838cab422483c +size 3406 diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor.meta b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor.meta new file mode 100644 index 00000000..b3ef2c7c --- /dev/null +++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a17d9f1818e8e79429f9741f498ec4cd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/OptitrackStreamingClientEditor.cs b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/OptitrackStreamingClientEditor.cs new file mode 100644 index 00000000..6a8b4e8c --- /dev/null +++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/OptitrackStreamingClientEditor.cs @@ -0,0 +1,81 @@ +using UnityEngine; +using UnityEditor; + +[CustomEditor(typeof(OptitrackStreamingClient))] +public class OptitrackStreamingClientEditor : Editor +{ + public override void OnInspectorGUI() + { + // 기본 Inspector 그리기 + DrawDefaultInspector(); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("OptiTrack 연결 제어", EditorStyles.boldLabel); + + OptitrackStreamingClient client = (OptitrackStreamingClient)target; + + // 연결 상태 표시 + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("연결 상태:", GUILayout.Width(80)); + + string connectionStatus = Application.isPlaying ? client.GetConnectionStatus() : "게임 실행 중이 아님"; + Color originalColor = GUI.color; + + if (Application.isPlaying) + { + if (client.IsConnected()) + { + GUI.color = Color.green; + } + else + { + GUI.color = Color.red; + } + } + else + { + GUI.color = Color.gray; + } + + EditorGUILayout.LabelField(connectionStatus, EditorStyles.boldLabel); + GUI.color = originalColor; + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + + // 재접속 버튼 + EditorGUI.BeginDisabledGroup(!Application.isPlaying); + + if (GUILayout.Button("OptiTrack 재접속", GUILayout.Height(30))) + { + client.Reconnect(); + } + + EditorGUI.EndDisabledGroup(); + + if (!Application.isPlaying) + { + EditorGUILayout.HelpBox("재접속 기능은 게임이 실행 중일 때만 사용할 수 있습니다.", MessageType.Info); + } + + EditorGUILayout.Space(); + + // 추가 정보 표시 + if (Application.isPlaying) + { + EditorGUILayout.LabelField("서버 정보", EditorStyles.boldLabel); + EditorGUILayout.LabelField("서버 주소:", client.ServerAddress); + EditorGUILayout.LabelField("로컬 주소:", client.LocalAddress); + EditorGUILayout.LabelField("연결 유형:", client.ConnectionType.ToString()); + EditorGUILayout.LabelField("서버 NatNet 버전:", client.ServerNatNetVersion); + EditorGUILayout.LabelField("클라이언트 NatNet 버전:", client.ClientNatNetVersion); + } + + // Inspector를 지속적으로 업데이트 (실시간 연결 상태 표시를 위해) + if (Application.isPlaying) + { + EditorUtility.SetDirty(target); + Repaint(); + } + } +} diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/OptitrackStreamingClientEditor.cs.meta b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/OptitrackStreamingClientEditor.cs.meta new file mode 100644 index 00000000..ae4ee2d6 --- /dev/null +++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/Editor/OptitrackStreamingClientEditor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9732ad88efa255744849892f43401dc6 \ No newline at end of file diff --git a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs index 1d4cad2c..4c8ca73f 100644 --- a/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs +++ b/Assets/External/OptiTrack Unity Plugin/OptiTrack/Scripts/OptitrackStreamingClient.cs @@ -650,6 +650,145 @@ public class OptitrackStreamingClient : MonoBehaviour return false; } + /// + /// Reconnects to the OptiTrack streaming server by disconnecting and reconnecting. + /// + public void Reconnect() + { + Debug.Log("OptiTrack: 재접속을 시도합니다..."); + + // 현재 연결이 있다면 먼저 해제 + if (m_client != null) + { + Debug.Log("OptiTrack: 기존 연결을 해제합니다."); + OnDisable(); + } + + // 잠시 대기 후 재연결 + StartCoroutine(ReconnectCoroutine()); + } + + /// + /// Coroutine for handling the reconnection process with a delay. + /// + private System.Collections.IEnumerator ReconnectCoroutine() + { + // 1초 대기 후 재연결 시도 + yield return new WaitForSeconds(1.0f); + + try + { + Debug.Log("OptiTrack: 서버에 재연결을 시도합니다."); + OnEnable(); + Debug.Log("OptiTrack: 재연결이 완료되었습니다."); + } + catch (System.Exception ex) + { + Debug.LogError("OptiTrack: 재연결 실패 - " + ex.Message); + } + } + + /// + /// Gets the current connection status of the client. + /// + /// True if connected and receiving data, false otherwise. + public bool IsConnected() + { + return m_client != null && m_receivedFrameSinceConnect; + } + + /// + /// Gets the connection status as a readable string. + /// + /// Connection status description. + public string GetConnectionStatus() + { + if (m_client == null) + { + return "연결 안됨"; + } + else if (!m_receivedFrameSinceConnect) + { + return "연결됨 (데이터 대기 중)"; + } + else + { + float lastFrameAge = m_lastFrameDeliveryTimestamp.AgeSeconds; + if (lastFrameAge < 5.0f) + { + return "연결됨 (데이터 수신 중)"; + } + else + { + return "연결됨 (데이터 중단됨)"; + } + } + } + + #region Recording Control Functions + /// + /// Starts recording with enhanced logging. + /// + public void StartRecordingWithLog() + { + bool result = StartRecording(); + if (result) + { + Debug.Log("OptiTrack: 레코딩을 시작했습니다."); + } + else + { + Debug.LogWarning("OptiTrack: 레코딩 시작에 실패했습니다."); + } + } + + /// + /// Stops recording with enhanced logging. + /// + public void StopRecordingWithLog() + { + bool result = StopRecording(); + if (result) + { + Debug.Log("OptiTrack: 레코딩을 중지했습니다."); + } + else + { + Debug.LogWarning("OptiTrack: 레코딩 중지에 실패했습니다."); + } + } + + /// + /// Toggles recording state on this client. + /// + public void ToggleRecording() + { + if (m_client != null) + { + // Note: There's no direct way to check if recording is active, + // so we'll try to start recording first, and if it fails, try to stop + bool startResult = StartRecording(); + if (!startResult) + { + // If start failed, try to stop (might already be recording) + bool stopResult = StopRecording(); + if (stopResult) + { + Debug.Log("OptiTrack: 레코딩을 중지했습니다."); + } + } + else + { + Debug.Log("OptiTrack: 레코딩을 시작했습니다."); + } + } + else + { + Debug.LogError("OptiTrack: 클라이언트가 연결되지 않았습니다."); + } + } + #endregion + /// Get the most recently received state for the specified rigid body. /// Corresponds to the "User ID" field in Motive. diff --git a/Assets/External/Rokoko/Scripts/Mono/StudioManager.cs b/Assets/External/Rokoko/Scripts/Mono/StudioManager.cs index 55a90ec6..f6e04d19 100644 --- a/Assets/External/Rokoko/Scripts/Mono/StudioManager.cs +++ b/Assets/External/Rokoko/Scripts/Mono/StudioManager.cs @@ -338,5 +338,152 @@ namespace Rokoko if (instance.propOverrides.Contains(prop)) return; instance.propOverrides.Add(prop); } + + #region Recording Control Functions + + /// + /// CommandAPI를 자동으로 찾아서 설정합니다. + /// + private void EnsureCommandAPI() + { + if (CommandAPI == null) + { + // 같은 GameObject에서 찾기 + CommandAPI = GetComponent(); + + // 씬 전체에서 찾기 + if (CommandAPI == null) + { + CommandAPI = FindObjectOfType(); + } + + if (CommandAPI != null) + { + Debug.Log("Rokoko: CommandAPI를 자동으로 찾아서 설정했습니다."); + } + } + } + + /// + /// Rokoko 녹화를 시작합니다. + /// + public void StartRokokoRecording() + { + EnsureCommandAPI(); + + if (CommandAPI != null) + { + CommandAPI.StartRecording(); + Debug.Log("Rokoko: 녹화를 시작했습니다."); + } + else + { + Debug.LogWarning("Rokoko: CommandAPI를 찾을 수 없습니다. StudioCommandAPI 컴포넌트가 씬에 있는지 확인해주세요."); + } + } + + /// + /// Rokoko 녹화를 중지합니다. + /// + public void StopRokokoRecording() + { + EnsureCommandAPI(); + + if (CommandAPI != null) + { + CommandAPI.StopRecording(); + Debug.Log("Rokoko: 녹화를 중지했습니다."); + } + else + { + Debug.LogWarning("Rokoko: CommandAPI를 찾을 수 없습니다. StudioCommandAPI 컴포넌트가 씬에 있는지 확인해주세요."); + } + } + + /// + /// Rokoko 녹화 상태를 토글합니다. + /// Note: Rokoko API에는 현재 녹화 상태를 확인하는 직접적인 방법이 없으므로, + /// 시작 명령을 먼저 시도하고 실패하면 중지를 시도합니다. + /// + public void ToggleRokokoRecording() + { + EnsureCommandAPI(); + + if (CommandAPI != null) + { + // 실제로는 사용자가 수동으로 시작/중지를 선택하는 것이 좋습니다. + // 여기서는 예시로 시작 명령만 실행합니다. + CommandAPI.StartRecording(); + Debug.Log("Rokoko: 녹화 시작을 시도했습니다."); + } + else + { + Debug.LogWarning("Rokoko: CommandAPI를 찾을 수 없습니다. StudioCommandAPI 컴포넌트가 씬에 있는지 확인해주세요."); + } + } + + /// + /// 현재 CommandAPI 상태를 확인합니다. + /// + public bool IsCommandAPIAvailable() + { + EnsureCommandAPI(); + return CommandAPI != null; + } + + /// + /// CommandAPI 상태와 설정 정보를 로그로 출력합니다. + /// + [ContextMenu("Check Rokoko CommandAPI Status")] + public void CheckCommandAPIStatus() + { + EnsureCommandAPI(); + + if (CommandAPI != null) + { + Debug.Log($"Rokoko CommandAPI 상태: 사용 가능\n" + + $"API 키: {(string.IsNullOrEmpty(CommandAPI.apiKey) ? "설정되지 않음" : "설정됨")}\n" + + $"포트: {CommandAPI.port}\n" + + $"IP 주소: {CommandAPI.ipAddress}"); + } + else + { + Debug.LogWarning("Rokoko CommandAPI를 찾을 수 없습니다. 다음을 확인해주세요:\n" + + "1. StudioCommandAPI 컴포넌트가 씬에 있는지 확인\n" + + "2. StudioManager의 CommandAPI 필드에 수동으로 할당\n" + + "3. Rokoko Studio가 실행 중이고 Command API가 활성화되어 있는지 확인"); + } + } + + /// + /// 정적 메서드로 Rokoko 녹화 시작 + /// + public static void StartGlobalRokokoRecording() + { + if (instance != null) + { + instance.StartRokokoRecording(); + } + else + { + Debug.LogError("Rokoko: StudioManager 인스턴스를 찾을 수 없습니다."); + } + } + + /// + /// 정적 메서드로 Rokoko 녹화 중지 + /// + public static void StopGlobalRokokoRecording() + { + if (instance != null) + { + instance.StopRokokoRecording(); + } + else + { + Debug.LogError("Rokoko: StudioManager 인스턴스를 찾을 수 없습니다."); + } + } + #endregion } } \ No newline at end of file diff --git a/Assets/External/UniGLTF/Runtime/UniHumanoid/Resources/UniHumanoid/I-Pose.pose.asset b/Assets/External/UniGLTF/Runtime/UniHumanoid/Resources/UniHumanoid/I-Pose.pose.asset new file mode 100644 index 00000000..e87dc2c7 --- /dev/null +++ b/Assets/External/UniGLTF/Runtime/UniHumanoid/Resources/UniHumanoid/I-Pose.pose.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f7d87fe14c4d6657f4e6bb187c8516091c66d24fe726314f40446ea29a23de7 +size 1944 diff --git a/Assets/External/UniGLTF/Runtime/UniHumanoid/Resources/UniHumanoid/I-Pose.pose.asset.meta b/Assets/External/UniGLTF/Runtime/UniHumanoid/Resources/UniHumanoid/I-Pose.pose.asset.meta new file mode 100644 index 00000000..32c01500 --- /dev/null +++ b/Assets/External/UniGLTF/Runtime/UniHumanoid/Resources/UniHumanoid/I-Pose.pose.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa247bedd682660459a8067079696177 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Motions.meta b/Assets/Motions.meta new file mode 100644 index 00000000..f3f278cd --- /dev/null +++ b/Assets/Motions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed68c69f4bf1c85459377a625ff33ad0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefeb.meta b/Assets/Prefeb.meta new file mode 100644 index 00000000..7945e2db --- /dev/null +++ b/Assets/Prefeb.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e058f167f2406824ba142fa059bb4090 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefeb/이벤트 컨트롤러 - 녹화 세팅.prefab b/Assets/Prefeb/이벤트 컨트롤러 - 녹화 세팅.prefab new file mode 100644 index 00000000..9eb2c4fe --- /dev/null +++ b/Assets/Prefeb/이벤트 컨트롤러 - 녹화 세팅.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cbc146a4d31d4a1e6d8059017290fa3e10ad3a6dc150edc8c33314e986707bf +size 7725 diff --git a/Assets/Prefeb/이벤트 컨트롤러 - 녹화 세팅.prefab.meta b/Assets/Prefeb/이벤트 컨트롤러 - 녹화 세팅.prefab.meta new file mode 100644 index 00000000..33e345cf --- /dev/null +++ b/Assets/Prefeb/이벤트 컨트롤러 - 녹화 세팅.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fe392ab1a0d3eb943816f404e085022c +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: