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: