diff --git a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs index 1993683b..ccccb5c3 100644 --- a/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs +++ b/Assets/Scripts/KindRetargeting/CustomRetargetingScript.cs @@ -38,12 +38,16 @@ namespace KindRetargeting [HideInInspector] public float HipsWeightOffset = 1f; - // 로컬-월드 축 방향 정규화 계수 - private Vector3 axisNormalizer = Vector3.one; + // 축 매핑: 월드 방향(Right/Up/Forward)을 담당하는 로컬 축을 저장 + // 예: localAxisForWorldRight = (0, 0, 1) 이면 로컬 Z축이 월드 Right 방향을 담당 + // 부호는 방향을 나타냄: (0, 0, -1)이면 로컬 -Z가 월드 Right를 담당 + private Vector3 localAxisForWorldRight = Vector3.right; // 월드 좌우(Right)를 담당하는 로컬 축 + private Vector3 localAxisForWorldUp = Vector3.up; // 월드 상하(Up)를 담당하는 로컬 축 + private Vector3 localAxisForWorldForward = Vector3.forward; // 월드 앞뒤(Forward)를 담당하는 로컬 축 [Header("축 정규화 정보 (읽기 전용)")] - [SerializeField, HideInInspector] - private Vector3 debugAxisNormalizer = Vector3.one; + [SerializeField] + public Vector3 debugAxisNormalizer = Vector3.one; // HumanPoseHandler를 이용하여 원본 및 대상 아바타의 포즈를 관리 private HumanPoseHandler sourcePoseHandler; @@ -86,6 +90,12 @@ namespace KindRetargeting [SerializeField, Range(-1f, 1f)] private float kneeFrontBackWeight = 0.4f; // 무릎 앞/뒤 위치 조정 가중치 + [Header("발 IK 위치 조정")] + [SerializeField, Range(-1f, 1f)] + private float footFrontBackOffset = 0f; // 발 앞뒤 오프셋 (+: 앞, -: 뒤) + [SerializeField, Range(-1f, 1f)] + private float footInOutOffset = 0f; // 발 안쪽/바깥쪽 오프셋 (+: 벌리기, -: 모으기) + [Header("바닥 높이 조정")] [SerializeField, Range(-1f, 1f)] public float floorHeight = 0f; // 바닥 높이 조정값 @@ -135,6 +145,8 @@ namespace KindRetargeting public float hipsOffsetZ; public float kneeInOutWeight; public float kneeFrontBackWeight; + public float footFrontBackOffset; // 발 앞뒤 오프셋 + public float footInOutOffset; // 발 안쪽/바깥쪽 오프셋 public float floorHeight; public EnumsList.FingerCopyMode fingerCopyMode; public bool useMotionFilter; @@ -151,6 +163,40 @@ namespace KindRetargeting // 각 손가락 관절별로 필터 버퍼를 관리하는 Dictionary 추가 private Dictionary> fingerFilterBuffers = new Dictionary>(); + // CopyFingerPoseByMuscle에서 사용할 본 Transform 저장용 (메모리 재사용) + private Dictionary savedBoneTransforms = + new Dictionary(); + + // 손가락을 제외한 모든 휴먼본 목록 (캐싱) + private static readonly HumanBodyBones[] nonFingerBones = new HumanBodyBones[] + { + HumanBodyBones.Hips, + HumanBodyBones.Spine, + HumanBodyBones.Chest, + HumanBodyBones.UpperChest, + HumanBodyBones.Neck, + HumanBodyBones.Head, + HumanBodyBones.LeftShoulder, + HumanBodyBones.LeftUpperArm, + HumanBodyBones.LeftLowerArm, + HumanBodyBones.LeftHand, + HumanBodyBones.RightShoulder, + HumanBodyBones.RightUpperArm, + HumanBodyBones.RightLowerArm, + HumanBodyBones.RightHand, + HumanBodyBones.LeftUpperLeg, + HumanBodyBones.LeftLowerLeg, + HumanBodyBones.LeftFoot, + HumanBodyBones.LeftToes, + HumanBodyBones.RightUpperLeg, + HumanBodyBones.RightLowerLeg, + HumanBodyBones.RightFoot, + HumanBodyBones.RightToes, + HumanBodyBones.LeftEye, + HumanBodyBones.RightEye, + HumanBodyBones.Jaw + }; + // IK 조인트 싱을 위한 구조체 private struct IKJoints { @@ -220,8 +266,15 @@ namespace KindRetargeting } /// - /// 타겟 아바타의 로컬 축과 월드 축의 관계를 분석하여 정규화 계수를 계산합니다. - /// 이를 통해 사용자는 항상 직관적인 방향(앞/뒤/위/아래)으로 제어할 수 있습니다. + /// 타겟 아바타의 로컬 축과 월드 축의 관계를 분석하여 축 매핑을 계산합니다. + /// T-포즈 상태에서 힙의 각 로컬 축이 월드의 어느 방향을 가리키는지 분석합니다. + /// + /// 예시: + /// - 아바타 A: 로컬 Y가 월드 Up을 가리킴 → localAxisForWorldUp = (0, 1, 0) + /// - 아바타 B: 로컬 Z가 월드 Up을 가리킴 → localAxisForWorldUp = (0, 0, 1) + /// - 아바타 C: 로컬 -Z가 월드 Up을 가리킴 → localAxisForWorldUp = (0, 0, -1) + /// + /// 이를 통해 hipsOffsetY는 항상 "위/아래" 방향으로 작동합니다. /// private void CalculateAxisNormalizer() { @@ -230,46 +283,85 @@ namespace KindRetargeting Transform hips = targetAnimator.GetBoneTransform(HumanBodyBones.Hips); if (hips == null) return; - // 힙의 초기 로컬 축 방향을 월드 공간으로 변환 - Vector3 localRight = hips.TransformDirection(Vector3.right); - Vector3 localUp = hips.TransformDirection(Vector3.up); - Vector3 localForward = hips.TransformDirection(Vector3.forward); + // 힙의 각 로컬 축을 월드 공간으로 변환 + Vector3 localXInWorld = hips.TransformDirection(Vector3.right).normalized; + Vector3 localYInWorld = hips.TransformDirection(Vector3.up).normalized; + Vector3 localZInWorld = hips.TransformDirection(Vector3.forward).normalized; - // 각 로컬 축이 월드의 어느 축과 가장 가까운지 판단 - axisNormalizer = new Vector3( - GetAxisSign(localRight, Vector3.right), // X축 정규화 - GetAxisSign(localUp, Vector3.up), // Y축 정규화 - GetAxisSign(localForward, Vector3.forward) // Z축 정규화 + // 월드 Right(X)에 가장 가까운 로컬 축 찾기 + localAxisForWorldRight = FindBestLocalAxisForWorld(localXInWorld, localYInWorld, localZInWorld, Vector3.right, out string rightAxisName); + + // 월드 Up(Y)에 가장 가까운 로컬 축 찾기 + localAxisForWorldUp = FindBestLocalAxisForWorld(localXInWorld, localYInWorld, localZInWorld, Vector3.up, out string upAxisName); + + // 월드 Forward(Z)에 가장 가까운 로컬 축 찾기 + localAxisForWorldForward = FindBestLocalAxisForWorld(localXInWorld, localYInWorld, localZInWorld, Vector3.forward, out string forwardAxisName); + + // 디버그용: 각 오프셋이 어느 로컬 축에 매핑되는지 표시 + // X: 좌우 오프셋이 사용하는 로컬 축 (1=X, 2=Y, 3=Z, 부호는 방향) + // Y: 상하 오프셋이 사용하는 로컬 축 + // Z: 앞뒤 오프셋이 사용하는 로컬 축 + debugAxisNormalizer = new Vector3( + GetAxisIndex(localAxisForWorldRight), + GetAxisIndex(localAxisForWorldUp), + GetAxisIndex(localAxisForWorldForward) ); - debugAxisNormalizer = axisNormalizer; // Inspector 표시용 - - Debug.Log($"[{gameObject.name}] 축 정규화 계수:\n" + - $" X(좌우): {axisNormalizer.x} (로컬 Right {(axisNormalizer.x > 0 ? "→" : "←")} 월드 Right)\n" + - $" Y(상하): {axisNormalizer.y} (로컬 Up {(axisNormalizer.y > 0 ? "↑" : "↓")} 월드 Up)\n" + - $" Z(앞뒤): {axisNormalizer.z} (로컬 Forward {(axisNormalizer.z > 0 ? "→" : "←")} 월드 Forward)"); + Debug.Log($"[{gameObject.name}] 축 매핑 분석 완료:\n" + + $" 월드 Right(좌우) ← 로컬 {rightAxisName} → 매핑: {localAxisForWorldRight}\n" + + $" 월드 Up(상하) ← 로컬 {upAxisName} → 매핑: {localAxisForWorldUp}\n" + + $" 월드 Forward(앞뒤) ← 로컬 {forwardAxisName} → 매핑: {localAxisForWorldForward}"); } /// - /// 로컬 축이 월드 축과 같은 방향인지 반대 방향인지 판단합니다. + /// 축 벡터를 인덱스로 변환합니다 (디버그용). + /// X축=1, Y축=2, Z축=3, 부호는 방향을 나타냄 /// - /// 로컬 축 방향 (월드 공간) - /// 비교할 월드 축 - /// 같은 방향이면 1, 반대면 -1 - private float GetAxisSign(Vector3 localAxis, Vector3 worldAxis) + private float GetAxisIndex(Vector3 axisVector) { - // 내적으로 방향성 판단 - float dot = Vector3.Dot(localAxis.normalized, worldAxis); + if (Mathf.Abs(axisVector.x) > 0.5f) + return 1f * Mathf.Sign(axisVector.x); // X축 + else if (Mathf.Abs(axisVector.y) > 0.5f) + return 2f * Mathf.Sign(axisVector.y); // Y축 + else + return 3f * Mathf.Sign(axisVector.z); // Z축 + } - // 가장 지배적인 축 성분의 부호를 반환 - if (Mathf.Abs(dot) > 0.5f) // 45도 이내면 해당 축으로 간주 + /// + /// 세 로컬 축 중에서 목표 월드 방향과 가장 일치하는 축을 찾아 로컬 축 벡터를 반환합니다. + /// + /// 로컬 X축의 월드 방향 + /// 로컬 Y축의 월드 방향 + /// 로컬 Z축의 월드 방향 + /// 비교할 월드 방향 + /// 매칭된 축 이름 (출력용) + /// 해당 월드 방향을 담당하는 로컬 축 벡터 (부호 포함) + private Vector3 FindBestLocalAxisForWorld(Vector3 localXInWorld, Vector3 localYInWorld, Vector3 localZInWorld, Vector3 worldDirection, out string matchedAxisName) + { + float dotX = Vector3.Dot(localXInWorld, worldDirection); + float dotY = Vector3.Dot(localYInWorld, worldDirection); + float dotZ = Vector3.Dot(localZInWorld, worldDirection); + + float absDotX = Mathf.Abs(dotX); + float absDotY = Mathf.Abs(dotY); + float absDotZ = Mathf.Abs(dotZ); + + // 가장 큰 내적값을 가진 축이 해당 월드 방향과 가장 일치하는 축 + if (absDotX >= absDotY && absDotX >= absDotZ) { - return Mathf.Sign(dot); + matchedAxisName = dotX > 0 ? "+X (Right)" : "-X (Left)"; + return Vector3.right * Mathf.Sign(dotX); // 로컬 X축 (부호 포함) + } + else if (absDotY >= absDotX && absDotY >= absDotZ) + { + matchedAxisName = dotY > 0 ? "+Y (Up)" : "-Y (Down)"; + return Vector3.up * Mathf.Sign(dotY); // 로컬 Y축 (부호 포함) + } + else + { + matchedAxisName = dotZ > 0 ? "+Z (Forward)" : "-Z (Back)"; + return Vector3.forward * Mathf.Sign(dotZ); // 로컬 Z축 (부호 포함) } - - // 축이 너무 비스듬하면 경고 (정상적인 휴머노이드 리그라면 발생 안함) - Debug.LogWarning($"[AxisNormalizer] 축이 비정상적으로 기울어져 있습니다: {localAxis} vs {worldAxis}"); - return 1f; } /// @@ -415,6 +507,8 @@ namespace KindRetargeting hipsOffsetZ = hipsOffsetZ, kneeInOutWeight = kneeInOutWeight, kneeFrontBackWeight = kneeFrontBackWeight, + footFrontBackOffset = footFrontBackOffset, + footInOutOffset = footInOutOffset, floorHeight = floorHeight, fingerCopyMode = fingerCopyMode, useMotionFilter = useMotionFilter, @@ -473,6 +567,8 @@ namespace KindRetargeting hipsOffsetZ = settings.hipsOffsetZ; kneeInOutWeight = settings.kneeInOutWeight; kneeFrontBackWeight = settings.kneeFrontBackWeight; + footFrontBackOffset = settings.footFrontBackOffset; + footInOutOffset = settings.footInOutOffset; floorHeight = settings.floorHeight; fingerCopyMode = settings.fingerCopyMode; useMotionFilter = settings.useMotionFilter; @@ -572,10 +668,41 @@ namespace KindRetargeting try { + // 손가락 본들의 현재 로컬 회전을 저장 (I-포즈 적용 전) + // 손가락은 24~54번 본 (LeftThumbProximal ~ RightLittleDistal) + var savedFingerRotations = new Dictionary(); + for (int i = 24; i <= 54; i++) + { + HumanBodyBones bone = (HumanBodyBones)i; + Transform fingerBone = targetAnimator.GetBoneTransform(bone); + if (fingerBone != null) + { + savedFingerRotations[bone] = fingerBone.localRotation; + } + } + + // 타겟 아바타를 I-포즈로 설정 (몸체만 필요하지만 전체가 변경됨) SetIPose(targetAnimator); + + // 손가락 본들의 로컬 회전을 복원 (I-포즈 적용 후) + foreach (var kvp in savedFingerRotations) + { + Transform fingerBone = targetAnimator.GetBoneTransform(kvp.Key); + if (fingerBone != null) + { + fingerBone.localRotation = kvp.Value; + } + } + + // 몸체 본들의 오프셋만 계산 (0~23번) CalculateRotationOffsets(true); + + // 손가락 본들의 오프셋 별도 계산 (24~54번) + // 손가락은 복원된 상태에서 오프셋 계산 (Rotation 모드용) + CalculateFingerRotationOffsets(); + SaveSettings(); // 캘리브레이션 후 설정 저장 - Debug.Log("I-포즈 캘리브레이션이 완료되었습니다."); + Debug.Log("I-포즈 캘리브레이션이 완료되었습니다. (손가락 포즈 유지)"); } catch (System.Exception e) { @@ -583,6 +710,38 @@ namespace KindRetargeting } } + /// + /// 손가락 본들의 회전 오프셋을 계산합니다. + /// I-포즈 캘리브레이션 시 손가락 포즈가 복원된 상태에서 호출되어야 합니다. + /// + private void CalculateFingerRotationOffsets() + { + if (sourceAnimator == null || targetAnimator == null) + return; + + // 손가락 본들 (24~54)의 오프셋 계산 + for (int i = 24; i <= 54; i++) + { + HumanBodyBones bone = (HumanBodyBones)i; + Transform sourceBone = sourceAnimator.GetBoneTransform(bone); + Transform targetBone = targetAnimator.GetBoneTransform(bone); + + if (sourceBone != null && targetBone != null) + { + Quaternion offset = Quaternion.Inverse(sourceBone.rotation) * targetBone.rotation; + + if (rotationOffsets.ContainsKey(bone)) + { + rotationOffsets[bone] = offset; + } + else + { + rotationOffsets.Add(bone, offset); + } + } + } + } + private void InitializeIKJoints() { // IK 루트 찾기 @@ -639,15 +798,25 @@ namespace KindRetargeting } /// /// 머슬 데이터를 사용하여 손가락 포즈를 복제합니다. + /// SetHumanPose가 모든 본에 영향을 미치므로, 손가락을 제외한 모든 본의 Transform을 저장하고 복원합니다. /// private void CopyFingerPoseByMuscle() { - Vector3 originalPosition = targetAnimator.GetBoneTransform(HumanBodyBones.Hips).position; - Quaternion originalRotation = targetAnimator.GetBoneTransform(HumanBodyBones.Hips).rotation; - if (sourcePoseHandler == null || targetPoseHandler == null) return; + // 1. 손가락을 제외한 모든 본의 위치/회전 저장 + savedBoneTransforms.Clear(); + for (int i = 0; i < nonFingerBones.Length; i++) + { + Transform bone = targetAnimator.GetBoneTransform(nonFingerBones[i]); + if (bone != null) + { + savedBoneTransforms[nonFingerBones[i]] = (bone.position, bone.rotation); + } + } + + // 2. 머슬 데이터 업데이트 sourcePoseHandler.GetHumanPose(ref sourcePose); targetPoseHandler.GetHumanPose(ref targetPose); @@ -677,10 +846,18 @@ namespace KindRetargeting targetPose.muscles[muscleIndex] = targetValue; } + // 3. 머슬 포즈 적용 (손가락 포함 전체 본에 영향) targetPoseHandler.SetHumanPose(ref targetPose); - targetAnimator.GetBoneTransform(HumanBodyBones.Hips).position = originalPosition; - targetAnimator.GetBoneTransform(HumanBodyBones.Hips).rotation = originalRotation; + // 4. 손가락을 제외한 모든 본의 위치/회전 복원 + foreach (var kvp in savedBoneTransforms) + { + Transform bone = targetAnimator.GetBoneTransform(kvp.Key); + if (bone != null) + { + bone.SetPositionAndRotation(kvp.Value.position, kvp.Value.rotation); + } + } } private float ApplyFilter(float value, int fingerIndex) @@ -788,20 +965,33 @@ namespace KindRetargeting targetHips.rotation = finalHipsRotation; } - // 2. 로컬 오프셋 벡터 생성 (정규화 계수 적용) - Vector3 normalizedOffset = new Vector3( - hipsOffsetX * axisNormalizer.x * HipsWeightOffset, // 좌우 - hipsOffsetY * axisNormalizer.y * HipsWeightOffset, // 상하 - hipsOffsetZ * axisNormalizer.z * HipsWeightOffset // 앞뒤 - ); + // 2. 캐릭터 기준 로컬 오프셋 계산 (축 정규화 적용) + // + // 문제: 아바타마다 힙의 로컬 축 방향이 다름 + // - 아바타 A: 로컬 Y가 "위", 로컬 Z가 "앞" + // - 아바타 B: 로컬 Z가 "위", 로컬 X가 "앞" + // + // 해결: T-포즈에서 계산한 축 매핑을 사용 + // - localAxisForWorldRight: 실제로 "오른쪽"을 가리키는 로컬 축 + // - localAxisForWorldUp: 실제로 "위"를 가리키는 로컬 축 + // - localAxisForWorldForward: 실제로 "앞"을 가리키는 로컬 축 + // + // 이렇게 하면 모든 아바타에서 동일하게 작동합니다. - // 3. 로컬 좌표계를 월드 좌표계로 변환 - Vector3 worldOffset = targetHips.rotation * normalizedOffset; + // 힙의 현재 회전을 기준으로, 정규화된 방향 벡터 계산 + Vector3 characterRight = finalHipsRotation * localAxisForWorldRight; + Vector3 characterUp = finalHipsRotation * localAxisForWorldUp; + Vector3 characterForward = finalHipsRotation * localAxisForWorldForward; - // 4. 힙 위치 동기화 + 로컬 오프셋 적용 - Vector3 adjustedPosition = sourceHips.position + worldOffset; + Vector3 characterOffset = + characterRight * (hipsOffsetX * HipsWeightOffset) + // 캐릭터 기준 좌우 + characterUp * (hipsOffsetY * HipsWeightOffset) + // 캐릭터 기준 상하 + characterForward * (hipsOffsetZ * HipsWeightOffset); // 캐릭터 기준 앞뒤 - // 5. 바닥 높이는 월드 Y축에 직접 적용 (중력 방향이므로 월드 기준) + // 3. 힙 위치 동기화 + 캐릭터 기준 오프셋 적용 + Vector3 adjustedPosition = sourceHips.position + characterOffset; + + // 4. 바닥 높이 추가 (월드 Y축 - 바닥은 항상 월드 기준) adjustedPosition.y += floorHeight; targetHips.position = adjustedPosition; @@ -951,7 +1141,7 @@ namespace KindRetargeting } /// - /// 손과 발끝 타겟의 위치와 회전을 업업데이트합니다. + /// 손과 발끝 타겟의 위치와 회전을 업데이트합니다. /// /// 업데이트할 끝 타겟의 Transform /// 대상 본 @@ -968,10 +1158,35 @@ namespace KindRetargeting Vector3 targetPosition = sourceBone.position; Quaternion targetRotation = targetBone.rotation; - // 발/발가락 본인 경우에만 바닥 높이 적용 + // 발/발가락 본인 경우 오프셋 적용 if (endBone == HumanBodyBones.LeftToes || endBone == HumanBodyBones.RightToes) { + // 바닥 높이 적용 targetPosition.y += floorHeight; + + // 소스 아바타의 로컬 축 기준으로 발 오프셋 적용 + // +Z: 앞으로, -Z: 뒤로 + // 왼발: +X로 벌리기, -X로 모으기 + // 오른발: -X로 벌리기, +X로 모으기 (반대) + Vector3 footOffset = Vector3.zero; + + // 앞뒤 오프셋 (소스 아바타 기준 Z축) + footOffset += sourceAnimator.transform.forward * footFrontBackOffset; + + // 안쪽/바깥쪽 오프셋 (소스 아바타 기준 X축) + // footInOutOffset > 0: 벌리기, < 0: 모으기 + if (endBone == HumanBodyBones.LeftToes) + { + // 왼발: +일 때 왼쪽(+X)으로 벌림, -일 때 오른쪽(-X)으로 모음 + footOffset += sourceAnimator.transform.right * footInOutOffset; + } + else // RightToes + { + // 오른발: +일 때 오른쪽(-X)으로 벌림, -일 때 왼쪽(+X)으로 모음 + footOffset -= sourceAnimator.transform.right * footInOutOffset; + } + + targetPosition += footOffset; } // 최종 위치와 회전 적용 diff --git a/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs b/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs index 38d2cb92..29a24d27 100644 --- a/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs +++ b/Assets/Scripts/KindRetargeting/Editor/CustomRetargetingScriptEditor.cs @@ -15,6 +15,7 @@ namespace KindRetargeting private bool showMotionFilterSettings = false; private bool showRoughMotionSettings = false; private bool showKneeSettings = true; + private bool showFootSettings = true; private bool hasCachedData = false; private bool showFloorSettings = false; private bool showScaleSettings = true; @@ -35,6 +36,8 @@ namespace KindRetargeting private SerializedProperty fingerRoughnessProp; private SerializedProperty kneeInOutWeightProp; private SerializedProperty kneeFrontBackWeightProp; + private SerializedProperty footFrontBackOffsetProp; + private SerializedProperty footInOutOffsetProp; private SerializedProperty floorHeightProp; private SerializedProperty avatarScaleProp; @@ -57,6 +60,8 @@ namespace KindRetargeting fingerRoughnessProp = serializedObject.FindProperty("fingerRoughness"); kneeInOutWeightProp = serializedObject.FindProperty("kneeInOutWeight"); kneeFrontBackWeightProp = serializedObject.FindProperty("kneeFrontBackWeight"); + footFrontBackOffsetProp = serializedObject.FindProperty("footFrontBackOffset"); + footInOutOffsetProp = serializedObject.FindProperty("footInOutOffset"); floorHeightProp = serializedObject.FindProperty("floorHeight"); avatarScaleProp = serializedObject.FindProperty("avatarScale"); } @@ -97,24 +102,46 @@ namespace KindRetargeting { EditorGUI.indentLevel++; - // 축 정규화 정보 표시 + // 축 매핑 정보 표시 if (debugAxisNormalizerProp != null) { - Vector3 normalizer = debugAxisNormalizerProp.vector3Value; + Vector3 axisMapping = debugAxisNormalizerProp.vector3Value; EditorGUILayout.BeginVertical(EditorStyles.helpBox); - EditorGUILayout.LabelField("축 정규화 정보", EditorStyles.boldLabel); + EditorGUILayout.LabelField("축 매핑 정보 (T-포즈 기준)", EditorStyles.boldLabel); - EditorGUI.BeginDisabledGroup(true); - EditorGUILayout.Vector3Field("정규화 계수", normalizer); - EditorGUI.EndDisabledGroup(); + // 플레이 모드에서만 정확한 정보 표시 + if (Application.isPlaying && axisMapping != Vector3.one) + { + // axisMapping: 1=X, 2=Y, 3=Z, 부호는 방향 + string GetAxisName(float value) + { + int axis = Mathf.RoundToInt(Mathf.Abs(value)); + string sign = value > 0 ? "+" : "-"; + return axis switch + { + 1 => $"{sign}X", + 2 => $"{sign}Y", + 3 => $"{sign}Z", + _ => "?" + }; + } - EditorGUILayout.HelpBox( - $"X축: {(normalizer.x > 0 ? "정방향 ↔" : "역방향 ↔")}\n" + - $"Y축: {(normalizer.y > 0 ? "정방향 ↕" : "역방향 ↕")}\n" + - $"Z축: {(normalizer.z > 0 ? "정방향 ⇄" : "역방향 ⇄")}\n\n" + - "이 계수는 Start()에서 자동 계산되며, 항상 직관적인 방향으로 제어할 수 있게 합니다.", - MessageType.Info); + EditorGUILayout.HelpBox( + "T-포즈에서 분석된 축 매핑:\n" + + $" 좌우 오프셋 → 로컬 {GetAxisName(axisMapping.x)} 축\n" + + $" 상하 오프셋 → 로컬 {GetAxisName(axisMapping.y)} 축\n" + + $" 앞뒤 오프셋 → 로컬 {GetAxisName(axisMapping.z)} 축\n\n" + + "이 매핑 덕분에 모든 아바타에서 동일하게 작동합니다.", + MessageType.Info); + } + else + { + EditorGUILayout.HelpBox( + "플레이 모드에서 T-포즈 분석 후 축 매핑 정보가 표시됩니다.\n" + + "이 매핑은 각 아바타의 힙 로컬 축 방향에 맞춰 자동 계산됩니다.", + MessageType.Info); + } EditorGUILayout.EndVertical(); GUILayout.Space(5); @@ -142,7 +169,7 @@ namespace KindRetargeting { EditorGUI.indentLevel++; - // 몸릎 안/밖 정만 표시 + // 무릎 안/밖 조정 EditorGUILayout.Slider(kneeFrontBackWeightProp, -1f, 1f, new GUIContent("무릎 앞/뒤 가중치", "음수: 뒤로, 양수: 앞으로")); EditorGUILayout.Slider(kneeInOutWeightProp, -1f, 1f, @@ -153,6 +180,22 @@ namespace KindRetargeting GUILayout.Space(5); + // 발 IK 위치 조정 Foldout + showFootSettings = EditorGUILayout.Foldout(showFootSettings, "발 IK 위치 조정", true); + if (showFootSettings) + { + EditorGUI.indentLevel++; + + EditorGUILayout.Slider(footFrontBackOffsetProp, -1f, 1f, + new GUIContent("발 앞/뒤 오프셋", "+: 앞으로, -: 뒤로")); + EditorGUILayout.Slider(footInOutOffsetProp, -1f, 1f, + new GUIContent("발 벌리기/모으기", "+: 벌리기, -: 모으기")); + + EditorGUI.indentLevel--; + } + + GUILayout.Space(5); + // 손가락 복제 설정 Foldout showFingerCopySettings = EditorGUILayout.Foldout(showFingerCopySettings, "손가락 복제 설정"); if (showFingerCopySettings) diff --git a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs index 1bfbb98d..6e8eda2a 100644 --- a/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs +++ b/Assets/Scripts/KindRetargeting/Editor/RetargetingControlWindow.cs @@ -21,6 +21,7 @@ public class RetargetingControlWindow : EditorWindow private Dictionary motionSettingsFoldouts = new Dictionary(); private Dictionary floorSettingsFoldouts = new Dictionary(); private Dictionary scaleSettingsFoldouts = new Dictionary(); + private Dictionary footSettingsFoldouts = new Dictionary(); private Dictionary leftHandFoldouts = new Dictionary(); private Dictionary rightHandFoldouts = new Dictionary(); private Dictionary headFoldouts = new Dictionary(); @@ -226,6 +227,7 @@ public class RetargetingControlWindow : EditorWindow if (!motionSettingsFoldouts.ContainsKey(instanceID)) motionSettingsFoldouts[instanceID] = false; if (!floorSettingsFoldouts.ContainsKey(instanceID)) floorSettingsFoldouts[instanceID] = false; if (!scaleSettingsFoldouts.ContainsKey(instanceID)) scaleSettingsFoldouts[instanceID] = false; + if (!footSettingsFoldouts.ContainsKey(instanceID)) footSettingsFoldouts[instanceID] = true; if (!leftHandFoldouts.ContainsKey(instanceID)) leftHandFoldouts[instanceID] = false; if (!rightHandFoldouts.ContainsKey(instanceID)) rightHandFoldouts[instanceID] = false; if (!headFoldouts.ContainsKey(instanceID)) headFoldouts[instanceID] = false; @@ -264,18 +266,23 @@ public class RetargetingControlWindow : EditorWindow var hipsOffsetYProp = serializedObject.FindProperty("hipsOffsetY"); var hipsOffsetZProp = serializedObject.FindProperty("hipsOffsetZ"); - // 정규화 계수 표시 + // 축 매핑 정보 표시 var normalizerProp = serializedObject.FindProperty("debugAxisNormalizer"); - if (normalizerProp != null) + if (normalizerProp != null && Application.isPlaying) { - Vector3 norm = normalizerProp.vector3Value; - if (norm != Vector3.zero) // 초기화되었을 때만 표시 + Vector3 mapping = normalizerProp.vector3Value; + if (mapping != Vector3.one) // 초기화되었을 때만 표시 { - string xIcon = norm.x > 0 ? "↔" : "↔(반전)"; - string yIcon = norm.y > 0 ? "↕" : "↕(반전)"; - string zIcon = norm.z > 0 ? "⇄" : "⇄(반전)"; + // mapping: 1=X, 2=Y, 3=Z, 부호는 방향 + string GetAxis(float v) => Mathf.RoundToInt(Mathf.Abs(v)) switch + { + 1 => v > 0 ? "+X" : "-X", + 2 => v > 0 ? "+Y" : "-Y", + 3 => v > 0 ? "+Z" : "-Z", + _ => "?" + }; - EditorGUILayout.LabelField($"축 상태: X{xIcon} Y{yIcon} Z{zIcon}", + EditorGUILayout.LabelField($"축 매핑: 좌우→{GetAxis(mapping.x)} 상하→{GetAxis(mapping.y)} 앞뒤→{GetAxis(mapping.z)}", EditorStyles.miniLabel); } } @@ -303,6 +310,19 @@ public class RetargetingControlWindow : EditorWindow EditorGUI.indentLevel--; } + // 발 IK 위치 조정 + footSettingsFoldouts[instanceID] = EditorGUILayout.Foldout(footSettingsFoldouts[instanceID], "발 IK 위치 조정", true); + if (footSettingsFoldouts[instanceID]) + { + EditorGUI.indentLevel++; + var footFrontBackProp = serializedObject.FindProperty("footFrontBackOffset"); + var footInOutProp = serializedObject.FindProperty("footInOutOffset"); + + EditorGUILayout.Slider(footFrontBackProp, -1f, 1f, new GUIContent("발 앞/뒤 오프셋", "+: 앞으로, -: 뒤로")); + EditorGUILayout.Slider(footInOutProp, -1f, 1f, new GUIContent("발 벌리기/모으기", "+: 벌리기, -: 모으기")); + EditorGUI.indentLevel--; + } + // 손가락 제어 설정 fingerControlFoldouts[instanceID] = EditorGUILayout.Foldout(fingerControlFoldouts[instanceID], "손가락 제어 설정", true); if (fingerControlFoldouts[instanceID]) diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveAdapterSingleton.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveAdapterSingleton.cpp deleted file mode 100644 index d2f09d31..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveAdapterSingleton.cpp +++ /dev/null @@ -1,2279 +0,0 @@ -//====================================================================================================== -// Copyright 2023, NaturalPoint Inc. -// Modified 2025 for Rokoko Glove Integration -//====================================================================================================== - -#include -#include -#include // stringstream 사용을 위해 추가 -#include // sqrt 함수 사용을 위해 추가 -#include // setprecision 사용을 위해 추가 -#include // std::max, std::min 사용을 위해 추가 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX // Windows min/max 매크로 비활성화 -#include - -// Peripheral Import -#include "ExampleGloveAdapterSingleton.h" -#include "IDeviceManager.h" -#include "ExampleGloveDevice.h" -#include "LZ4Wrapper.h" -using namespace AnalogSystem; - -// Rokoko Integration - 기존 시뮬레이션 관련 정의를 주석 처리 -// #define SIM_DEVICE_COUNT 2 -// #define SIM_DEVICE_RATE 100 - - -OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ExampleGloveAdapterSingleton(AnalogSystem::IDeviceManager* pDeviceManager) -{ - s_Instance = this; - mDeviceManager = pDeviceManager; - - // 기존 시뮬레이션 코드를 주석 처리 (로코코 통신으로 대체) - // mGloveSimulator = new SimulatedPluginDevices::HardwareSimulator(); - - mGloveDataMutex = new std::recursive_mutex(); - mDetectedDevices.clear(); - mLatestGloveData.clear(); - - // start detection thread - mDetectionThread = std::thread(&ExampleGloveAdapterSingleton::DoDetectionThread, this); -} - - -OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::~ExampleGloveAdapterSingleton() -{ - mDetectedDevices.clear(); - mLatestGloveData.clear(); - delete mGloveDataMutex; - bIsConnected = false; - bIsDetecting = false; - s_Instance = nullptr; - - if (mDetectionThread.joinable()) - { - mDetectionThread.join(); - } - - // 기존 시뮬레이션 코드를 주석 처리 - // mGloveSimulator->Shutdown(); - // delete mGloveSimulator; - - // 로코코 연결 해제 - DisconnectFromRokoko(); -} - - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ClientShutdown() -{ - bIsConnected = false; - bIsDetecting = false; - - // 기존 시뮬레이션 코드를 주석 처리 - // if (mGloveSimulator != nullptr) - // { - // mGloveSimulator->Shutdown(); - // } - - // 로코코 연결 해제 - DisconnectFromRokoko(); - - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Rokoko Glove Host Detection Thread -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::DoDetectionThread() -{ - while (!bIsConnected && bIsDetecting) - { - // Try reconnecting - if (s_Instance->mConnectionAttemptCount < s_Instance->kMaxConnectionAttempt) { - bIsConnected = ConnectToHost(); - s_Instance->mConnectionAttemptCount++; - } - else { - bIsDetecting = false; - NotifyConnectionFail(); - } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // 🚀 실시간성 최우선 - 1ms로 최소화 - } -} - - -/////////////////////////////////////////////////////////////////////////////// -// -// Rokoko Integration - Connection Management -// -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConnectToHost() -{ - // Check if glove server address is defined in Motive's settings. - if ((mServerAddress.empty())) { - char* szServerAddress = new char[MAX_PATH]; - if (mDeviceManager->GetProperty("Glove Server Address", &szServerAddress)) - { - std::string str(szServerAddress); - mServerAddress = str; - } - delete[] szServerAddress; - } - - /* - * [Glove SDK Placeholder] - * SDK call to connect to the host at 'mServerAddress'. - */ - bIsConnected = true; - - // 로코코 UDP 통신 시작 - if (ConnectToRokoko()) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Successfully connected to Rokoko Studio", MessageType_StatusInfo); - } - return true; - } else { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Failed to connect to Rokoko Studio", MessageType_StatusInfo); - } - // 로코코 연결 실패 시 연결 실패로 처리 - bIsConnected = false; - return false; - } -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConnectToRokoko() -{ - try { - // 로코코 UDP 리시버 초기화 - mRokokoReceiver = std::make_unique(); - - // UDP 리시버 초기화 (포트 14043) - if (!mRokokoReceiver->Initialize(14043)) { - // 에러 메시지 출력 - std::string errorMsg = "Failed to initialize Rokoko UDP receiver: " + mRokokoReceiver->GetLastError(); - if (mDeviceManager) { - mDeviceManager->MessageToHost(errorMsg.c_str(), MessageType_StatusInfo); - } - return false; - } - - // 데이터 콜백 설정 - mRokokoReceiver->SetDataCallback([this](const std::vector& data, const std::string& senderIP) { - this->OnRokokoDataReceived(data, senderIP); - }); - - // UDP 수신 시작 - if (!mRokokoReceiver->StartListening()) { - std::string errorMsg = "Failed to start Rokoko UDP listening: " + mRokokoReceiver->GetLastError(); - if (mDeviceManager) { - mDeviceManager->MessageToHost(errorMsg.c_str(), MessageType_StatusInfo); - } - return false; - } - - bIsRokokoConnected = true; - - // 초기 장치 생성은 데이터 수신 후로 지연 - // DetectAndCreateRokokoDevices(); // 주석 처리 - - // 성공 메시지 출력 - if (mDeviceManager) { - mDeviceManager->MessageToHost("Successfully connected to Rokoko Studio via UDP on port 14043", MessageType_StatusInfo); - } - - return true; - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("Exception occurred while connecting to Rokoko", MessageType_StatusInfo); - } - return false; - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::DisconnectFromRokoko() -{ - if (mRokokoReceiver) { - mRokokoReceiver->StopListening(); - mRokokoReceiver.reset(); - } - bIsRokokoConnected = false; - - if (mDeviceManager) { - mDeviceManager->MessageToHost("Disconnected from Rokoko Studio", MessageType_StatusInfo); - } -} - - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::IsConnected() -{ - return bIsConnected; -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::SetLatestData(const sGloveDeviceData& gloveFingerData) -{ - s_Instance->mLatestGloveData[gloveFingerData.gloveId] = gloveFingerData; -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::SetDeviceManager(AnalogSystem::IDeviceManager* pDeviceManager) -{ - if (s_Instance == nullptr) return; - if (pDeviceManager != nullptr) - { - s_Instance->mDeviceManager = pDeviceManager; - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::SetLatestDeviceInfo(const sGloveDeviceBaseInfo& deviceInfo) -{ - s_Instance->mLatestDeviceInfo[deviceInfo.gloveId] = deviceInfo; -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::GetLatestData(const uint64_t mDeviceSerial, sGloveDeviceData& gloveFingerData) -{ - if (s_Instance == nullptr || !s_Instance -> bIsConnected) return false; - - // FIX: Always initialize result and use lock_guard for exception safety - bool res = false; - - // FIX: Use lock_guard instead of try_lock + manual unlock - std::lock_guard lock(*s_Instance->mGloveDataMutex); - - // Iterate through the glove data table and update - auto iter = s_Instance->mLatestGloveData.find(mDeviceSerial); - if (iter != s_Instance->mLatestGloveData.end()) - { - gloveFingerData = iter->second; - res = true; - } - - return res; -} - - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CreateNewGloveDevice(sGloveDeviceBaseInfo& deviceInfo) -{ - uint64_t gloveId = deviceInfo.gloveId; - - // Actor 이름을 포함한 장치 이름 생성 - std::string deviceName; - if (!deviceInfo.actorName.empty()) { - deviceName = deviceInfo.actorName + "_" + - (deviceInfo.handSide == eGloveHandSide::Left ? "Left" : "Right") + - "Hand"; - } else { - deviceName = "RokokoGlove_" + std::to_string(deviceInfo.gloveId); - } - - // Create device factory using the name/id/serial/client. - std::unique_ptr pDF = - std::make_unique(deviceName, deviceInfo); - pDF->mDeviceIndex = s_Instance->mCurrentDeviceIndex; - s_Instance->mDeviceManager->AddDevice(std::move(pDF)); - s_Instance->mCurrentDeviceIndex++; - - // 로코코 장치 생성 성공 메시지 - if (s_Instance->mDeviceManager) { - std::string successMsg = "[RokokoGlove] Created new device: " + deviceName; - s_Instance->mDeviceManager->MessageToHost(successMsg.c_str(), MessageType_StatusInfo); - } - - return; -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::NotifyConnectionFail() -{ - char* szServerAddress = new char[MAX_PATH]; - char szMessage[MAX_PATH]; - if (mDeviceManager->GetProperty("Glove Server Address", &szServerAddress)) { - if (!(strlen(szServerAddress) == 0)) - { - sprintf_s(szMessage, "[RokokoGlove] Failed to connect to Rokoko Studio on %s", szServerAddress); - mDeviceManager->MessageToHost(szMessage, MessageType_StatusInfo); - } - else - { - sprintf_s(szMessage, "[RokokoGlove] Failed to connect to Rokoko Studio on localhost"); - mDeviceManager->MessageToHost(szMessage, MessageType_StatusInfo); - } - } - delete[] szServerAddress; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Legacy Simulation Support (Commented Out) -// -/* -* Methods in this section were used for simulated hardware. -* Now replaced by Rokoko UDP communication. -*/ -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::StartSimulatedHardware(int deviceCount) -{ - // mGloveSimulator->StartData(); // 기존 시뮬레이션 코드를 주석 처리 -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::RegisterSDKCallbacks() -{ - // s_Instance->mGloveSimulator->RegisterFrameDataCallback(OnDataCallback); // 기존 시뮬레이션 코드를 주석 처리 - // s_Instance->mGloveSimulator->RegisterDeviceInfoCallback(OnDeviceInfoCallback); // 기존 시뮬레이션 코드를 주석 처리 - - // Add simulated glove devices - // s_Instance->mGloveSimulator->AddSimulatedGlove(1, 15, 1); // 기존 시뮬레이션 코드를 주석 처리 - // s_Instance->mGloveSimulator->AddSimulatedGlove(2, 15, 2); // 기존 시뮬레이션 코드를 주석 처리 -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::OnDataCallback(std::vector& gloveAllFingerData) -{ - // 기존 시뮬레이션 콜백을 주석 처리 (로코코 UDP 데이터 처리로 대체) - /* - if (s_Instance == nullptr) return; - - // [Glove SDK Placeholder] - // Data update callbacks to Keep the latest glove data map up-to-date. - s_Instance->mGloveDataMutex->lock(); - for (auto gloveFingerData : gloveAllFingerData) { - // Convert simulated data into glove data format. - sGloveDeviceData newGloveData = ConvertDataFormat(gloveFingerData); - s_Instance->SetLatestData(newGloveData); - } - s_Instance->mGloveDataMutex->unlock(); - */ - - // 로코코 UDP 데이터는 OnRokokoDataReceived에서 처리됨 -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::OnDeviceInfoCallback(std::vector& newGloveInfo) -{ - // 기존 시뮬레이션 콜백을 주석 처리 (로코코 장치 정보로 대체) - /* - if (s_Instance == nullptr) return; - - // Create new gloves - for (SimulatedPluginDevices::SimulatedDeviceInfo glove : newGloveInfo) - { - s_Instance->mDeviceManager->MessageToHost(" [ExampleGloveDevice] New device(s) detected.", MessageType_StatusInfo); - sGloveDeviceBaseInfo newGloveDevice = ConvertDeviceInfoFormat(glove); - s_Instance->CreateNewGloveDevice(newGloveDevice); - } - */ - - // 로코코 장치 정보는 ConnectToRokoko에서 처리됨 -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Legacy Data Conversion (Commented Out) -// -/* -* Methods in this section were used for converting simulated data. -* Now replaced by Rokoko data conversion. -*/ -sGloveDeviceBaseInfo OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertDeviceInfoFormat(SimulatedPluginDevices::SimulatedDeviceInfo& glove) -{ - // 기존 시뮬레이션 데이터 변환을 주석 처리 (로코코 데이터로 대체) - /* - sGloveDeviceBaseInfo ret; - - ret.battery = glove.mBattery; - ret.gloveId = glove.mDeviceSerial; - ret.signalStrength = glove.mSignalStrength; - ret.handSide = (glove.mHandSide == 1) ? eGloveHandSide::Left : - (glove.mHandSide == 2) ? eGloveHandSide::Right : - eGloveHandSide::Unknown; - - return ret; - */ - - // 로코코 장치 정보는 기본값으로 설정 - sGloveDeviceBaseInfo ret; - ret.battery = 100; // 기본 배터리 100% - ret.gloveId = 1; // 기본 장갑 ID - ret.signalStrength = 100; // 기본 신호 강도 100% - ret.handSide = eGloveHandSide::Left; // 기본 왼손 - - return ret; -} - -sGloveDeviceData OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertDataFormat(const SimulatedPluginDevices::SimulatedGloveFrameData& gloveFrameData) -{ - // 기존 시뮬레이션 데이터 변환을 주석 처리 (로코코 데이터로 대체) - /* - sGloveDeviceData ret; - ret.gloveId = gloveFrameData.mDeviceSerial; - ret.timestamp = 0; - - sFingerNode defaultNodes = { 0, 0, 0, 1 }; - ret.nodes = std::vector(15, defaultNodes); - - int node_iter = 0; - for (auto& fingerNode : ret.nodes) - { - fingerNode.node_id = node_iter; - fingerNode.quat_w = gloveFrameData.gloveFingerData[node_iter].quat_w; - fingerNode.quat_x = gloveFrameData.gloveFingerData[node_iter].quat_x; - fingerNode.quat_y = gloveFrameData.gloveFingerData[node_iter].quat_y; - fingerNode.quat_z = gloveFrameData.gloveFingerData[node_iter].quat_z; - node_iter++; - } - - return ret; - */ - - // 로코코 데이터는 ProcessRokokoData에서 처리됨 - // 기본 빈 데이터 반환 - sGloveDeviceData ret; - ret.gloveId = 1; - ret.timestamp = 0; - ret.nodes.clear(); - - return ret; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Rokoko Integration - UDP Data Processing -// -/* -* Methods in this section handle Rokoko UDP data reception and processing. -*/ - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::OnRokokoDataReceived(const std::vector& data, const std::string& senderIP) -{ - if (s_Instance == nullptr) return; - - // 🚀 실시간성 최우선: 예외 처리 및 로그 최소화 - s_Instance->ProcessRokokoData(data); -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ProcessRokokoData(const std::vector& data) -{ - try { - // 🚀 실시간성을 위해 정적 버퍼 사용 (메모리 할당 최소화) - static std::vector decompressedData; - decompressedData.clear(); - decompressedData.reserve(data.size() * 4); // 미리 메모리 예약 - - // 🚀 Unity 방식: LZ4 압축 해제 (모든 로그 제거) - decompressedData = RokokoIntegration::LZ4Wrapper::Decompress(data.data(), static_cast(data.size())); - - if (decompressedData.empty()) { - // 대안: LZ4 프레임 헤더 건너뛰고 압축 해제 시도 - if (data.size() > 8) { - // LZ4 프레임 헤더 건너뛰기 (일반적으로 7바이트) - std::vector dataWithoutHeader(data.begin() + 7, data.end()); - decompressedData = RokokoIntegration::LZ4Wrapper::Decompress(dataWithoutHeader.data(), static_cast(dataWithoutHeader.size())); - - if (decompressedData.empty()) { - // Unity에서는 실패 시 에러 로그만 출력하고 null 반환 - // 우리는 원본 데이터로 시도해봄 - decompressedData = data; - } - } else { - decompressedData = data; - } - } - - // 2단계: JSON 문자열로 변환 (유니티 방식 정확히 구현) - // Unity 방식: string text = System.Text.Encoding.UTF8.GetString(uncompressed); - std::string jsonString(decompressedData.begin(), decompressedData.end()); - - // 🚀 성능 최적화: 데이터 미리보기 로그 제거 - - // JSON 데이터 유효성 검사 - if (jsonString.empty() || jsonString.length() < 10) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Invalid JSON data (too short)", MessageType_StatusInfo); - } - return; - } - - // JSON 시작 문자 확인 - if (jsonString[0] != '{') { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Invalid JSON data (does not start with '{')", MessageType_StatusInfo); - } - - // 바이너리 데이터 분석 (첫 20바이트) - std::stringstream hexStream; - hexStream << "[RokokoGlove] Binary data detected: "; - for (size_t i = 0; i < 20 && i < decompressedData.size(); i++) { - char buf[8]; - snprintf(buf, sizeof(buf), "%02X ", static_cast(decompressedData[i])); - hexStream << buf; - } - mDeviceManager->MessageToHost(hexStream.str().c_str(), MessageType_StatusInfo); - return; - } - - // 3단계: JSON 파싱하여 Rokoko 데이터 구조로 변환 - RokokoData::LiveFrame_v4 rokokoFrame; - - // 🚀 실시간성 최우선 - 모든 로그 제거, 즉시 처리 - if (!RokokoIntegration::RokokoDataParser::ParseLiveFrame(jsonString, rokokoFrame)) { - return; // 실패 시 즉시 종료 - } - - // 🚀 Actor 기반 동적 장치 감지 (로그 제거) - DetectActorsFromRokokoData(rokokoFrame); - - // 🚀 첫 번째 데이터 수신 시 기본 장치도 생성 (백업) - if (mDetectedDevices.empty()) { - DetectAndCreateRokokoDevices(); - } - - // 🚀 다중 장치 데이터 처리 (왼손, 오른손) - 즉시 처리 - ProcessMultipleDeviceData(rokokoFrame); - - // 🚀 최신 프레임 업데이트 - 큐 없이 즉시 업데이트 - mLastRokokoFrame = rokokoFrame; - - } catch (...) { - // 🚀 실시간성 최우선 - 예외 발생 시 즉시 종료 (로그 제거) - return; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Device Information Management -// -/* -* Methods in this section handle device information updates and management. -*/ - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::UpdateDeviceInfo(uint64_t deviceId) -{ - try { - // 장치 정보 업데이트 (배터리, 신호 강도 등) - auto deviceInfoIter = mLatestDeviceInfo.find(deviceId); - if (deviceInfoIter != mLatestDeviceInfo.end()) { - // 기존 장치 정보 업데이트 - sGloveDeviceBaseInfo& deviceInfo = deviceInfoIter->second; - - // 배터리 상태 업데이트 (실제로는 Rokoko 데이터에서 가져와야 함) - deviceInfo.battery = 100; // 기본값 - - // 신호 강도 업데이트 (UDP 연결 상태 기반) - if (bIsRokokoConnected) { - deviceInfo.signalStrength = 100; // 연결됨 - } else { - deviceInfo.signalStrength = 0; // 연결 안됨 - } - - // 장치 정보 저장 - SetLatestDeviceInfo(deviceInfo); - } - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Error updating device info", MessageType_StatusInfo); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Dynamic Device Detection and Management -// -/* -* Methods in this section handle dynamic device detection and multi-device data processing. -*/ - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::DetectAndCreateRokokoDevices() -{ - try { - // 기존 장치 정보 초기화 - mDetectedDevices.clear(); - mLatestGloveData.clear(); - mLatestDeviceInfo.clear(); - - // 현재는 기본 2개 장치만 생성 (나중에 동적 감지로 변경) - // TODO: 실제 Rokoko 데이터에서 Actor 정보를 기반으로 동적 감지 - - // 왼손 장치 생성 - sGloveDeviceBaseInfo leftHandInfo; - leftHandInfo.gloveId = 1; - leftHandInfo.handSide = eGloveHandSide::Left; - leftHandInfo.battery = 100; - leftHandInfo.signalStrength = 100; - leftHandInfo.actorName = "DefaultActor"; // Actor 이름 추가 - CreateNewGloveDevice(leftHandInfo); - SetLatestDeviceInfo(leftHandInfo); - mDetectedDevices.push_back(1); - - // 오른손 장치 생성 - sGloveDeviceBaseInfo rightHandInfo; - rightHandInfo.gloveId = 2; - rightHandInfo.handSide = eGloveHandSide::Right; - rightHandInfo.battery = 100; - rightHandInfo.signalStrength = 100; - rightHandInfo.actorName = "DefaultActor"; // Actor 이름 추가 - CreateNewGloveDevice(rightHandInfo); - SetLatestDeviceInfo(rightHandInfo); - mDetectedDevices.push_back(2); - - // 장치 감지 완료 메시지 - if (mDeviceManager) { - std::string msg = "[RokokoGlove] Detected " + std::to_string(mDetectedDevices.size()) + " devices (Left & Right Hand)"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Error during device detection", MessageType_StatusInfo); - } - } -} - -// 새로운 함수: 동적 Actor 기반 장치 감지 -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::DetectActorsFromRokokoData(const RokokoData::LiveFrame_v4& rokokoFrame) -{ - try { - // 기존 장치 정보 백업 - std::vector oldDevices = mDetectedDevices; - mDetectedDevices.clear(); - - // Actor별로 장치 생성 - for (size_t actorIndex = 0; actorIndex < rokokoFrame.scene.actors.size(); actorIndex++) { - const auto& actor = rokokoFrame.scene.actors[actorIndex]; - std::string actorName = actor.name; - - // Actor 이름이 비어있으면 기본값 사용 - if (actorName.empty()) { - actorName = "Actor" + std::to_string(actorIndex + 1); - } - - // 왼손 장치 생성 (Actor별로 고유 ID 생성) - uint64_t leftHandId = (actorIndex * 2) + 1; - sGloveDeviceBaseInfo leftHandInfo; - leftHandInfo.gloveId = static_cast(leftHandId); - leftHandInfo.handSide = eGloveHandSide::Left; - leftHandInfo.battery = 100; - leftHandInfo.signalStrength = 100; - leftHandInfo.actorName = actorName; - - // 새 장치인지 확인하고 생성 - if (std::find(oldDevices.begin(), oldDevices.end(), leftHandId) == oldDevices.end()) { - CreateNewGloveDevice(leftHandInfo); - if (mDeviceManager) { - std::string msg = "[RokokoGlove] Created new left hand device for " + actorName + " (ID: " + std::to_string(leftHandId) + ")"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - } - SetLatestDeviceInfo(leftHandInfo); - mDetectedDevices.push_back(leftHandId); - - // 오른손 장치 생성 - uint64_t rightHandId = (actorIndex * 2) + 2; - sGloveDeviceBaseInfo rightHandInfo; - rightHandInfo.gloveId = static_cast(rightHandId); - rightHandInfo.handSide = eGloveHandSide::Right; - rightHandInfo.battery = 100; - rightHandInfo.signalStrength = 100; - rightHandInfo.actorName = actorName; - - // 새 장치인지 확인하고 생성 - if (std::find(oldDevices.begin(), oldDevices.end(), rightHandId) == oldDevices.end()) { - CreateNewGloveDevice(rightHandInfo); - if (mDeviceManager) { - std::string msg = "[RokokoGlove] Created new right hand device for " + actorName + " (ID: " + std::to_string(rightHandId) + ")"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - } - SetLatestDeviceInfo(rightHandInfo); - mDetectedDevices.push_back(rightHandId); - } - - // 🚀 성능 최적화: 장치 감지 완료 로그 제거 - - } catch (const std::exception& e) { - if (mDeviceManager) { - std::string errorMsg = "[RokokoGlove] Exception in actor detection: " + std::string(e.what()); - mDeviceManager->MessageToHost(errorMsg.c_str(), MessageType_StatusInfo); - } - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Unknown exception in actor detection", MessageType_StatusInfo); - } - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ProcessMultipleDeviceData(const RokokoData::LiveFrame_v4& rokokoFrame) -{ - try { - // 검증: actors가 존재하는지 확인 - if (rokokoFrame.scene.actors.empty()) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] No actors found in frame", MessageType_StatusInfo); - } - return; - } - - // 🚀 모든 Actor의 데이터를 처리 (다중 장갑 지원) - for (size_t actorIndex = 0; actorIndex < rokokoFrame.scene.actors.size(); actorIndex++) { - const auto& actor = rokokoFrame.scene.actors[actorIndex]; - - // Actor별 고유 ID 계산 - uint64_t leftHandId = (actorIndex * 2) + 1; - uint64_t rightHandId = (actorIndex * 2) + 2; - - // 왼손 데이터 처리 - if (ProcessHandData(actor.body, leftHandId, eGloveHandSide::Left)) { - UpdateDeviceInfo(leftHandId); - } - - // 오른손 데이터 처리 - if (ProcessHandData(actor.body, rightHandId, eGloveHandSide::Right)) { - UpdateDeviceInfo(rightHandId); - } - } - - } catch (const std::exception& e) { - if (mDeviceManager) { - std::string errorMsg = "[RokokoGlove] Exception in multi-device processing: " + std::string(e.what()); - mDeviceManager->MessageToHost(errorMsg.c_str(), MessageType_StatusInfo); - } - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Unknown exception in multi-device processing", MessageType_StatusInfo); - } - } -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ProcessHandData(const RokokoData::Body& body, uint64_t deviceId, eGloveHandSide handSide) -{ - try { - // 손가락 데이터를 OptiTrack 형식으로 변환 - sGloveDeviceData optiTrackData; - optiTrackData.gloveId = deviceId; - optiTrackData.timestamp = 0.0; - optiTrackData.nodes.resize(15); // 5개 손가락 × 3개 관절 - - // 손가락별 데이터 매핑 - if (handSide == eGloveHandSide::Left) { - // 왼손 데이터 매핑 - MapLeftHandJoints(body, optiTrackData.nodes); - } else { - // 오른손 데이터 매핑 - MapRightHandJoints(body, optiTrackData.nodes); - } - - // 노드 ID 설정 - for (int i = 0; i < 15; i++) { - optiTrackData.nodes[i].node_id = i; - } - - // 🚀 실시간성 최우선 - 모든 디버깅 제거 - - // 🎯 계층적 로컬 로테이션 계산 (Rokoko 월드 데이터 → 로컬 변환) - ConvertToTrueLocalRotations(optiTrackData.nodes, body, handSide); - - // 🚀 실시간성 최우선 - 데이터 유효성 검사 (로그 제거) - if (!RokokoIntegration::RokokoDataConverter::ValidateOptiTrackData(optiTrackData)) { - return false; - } - - // OptiTrack 데이터 저장 - mGloveDataMutex->lock(); - SetLatestData(optiTrackData); - mGloveDataMutex->unlock(); - - return true; - - } catch (...) { - // 🚀 실시간성 최우선 - 예외 발생 시 즉시 종료 (로그 제거) - return false; - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::MapLeftHandJoints(const RokokoData::Body& body, std::vector& nodes) -{ - // 엄지손가락 (Thumb) - if (body.leftThumbMedial) MapJoint(*body.leftThumbMedial, nodes[0]); // MP - if (body.leftThumbDistal) MapJoint(*body.leftThumbDistal, nodes[1]); // PIP - if (body.leftThumbTip) MapJoint(*body.leftThumbTip, nodes[2]); // DIP - - // 검지손가락 (Index) - if (body.leftIndexMedial) MapJoint(*body.leftIndexMedial, nodes[3]); // MP - if (body.leftIndexDistal) MapJoint(*body.leftIndexDistal, nodes[4]); // PIP - if (body.leftIndexTip) MapJoint(*body.leftIndexTip, nodes[5]); // DIP - - // 중지손가락 (Middle) - if (body.leftMiddleMedial) MapJoint(*body.leftMiddleMedial, nodes[6]); // MP - if (body.leftMiddleDistal) MapJoint(*body.leftMiddleDistal, nodes[7]); // PIP - if (body.leftMiddleTip) MapJoint(*body.leftMiddleTip, nodes[8]); // DIP - - // 약지손가락 (Ring) - if (body.leftRingMedial) MapJoint(*body.leftRingMedial, nodes[9]); // MP - if (body.leftRingDistal) MapJoint(*body.leftRingDistal, nodes[10]); // PIP - if (body.leftRingTip) MapJoint(*body.leftRingTip, nodes[11]); // DIP - - // 새끼손가락 (Little) - if (body.leftLittleMedial) MapJoint(*body.leftLittleMedial, nodes[12]); // MP - if (body.leftLittleDistal) MapJoint(*body.leftLittleDistal, nodes[13]); // PIP - if (body.leftLittleTip) MapJoint(*body.leftLittleTip, nodes[14]); // DIP -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::MapRightHandJoints(const RokokoData::Body& body, std::vector& nodes) -{ - // 엄지손가락 (Thumb) - if (body.rightThumbMedial) MapJoint(*body.rightThumbMedial, nodes[0]); // MP - if (body.rightThumbDistal) MapJoint(*body.rightThumbDistal, nodes[1]); // PIP - if (body.rightThumbTip) MapJoint(*body.rightThumbTip, nodes[2]); // DIP - - // 검지손가락 (Index) - if (body.rightIndexMedial) MapJoint(*body.rightIndexMedial, nodes[3]); // MP - if (body.rightIndexDistal) MapJoint(*body.rightIndexDistal, nodes[4]); // PIP - if (body.rightIndexTip) MapJoint(*body.rightIndexTip, nodes[5]); // DIP - - // 중지손가락 (Middle) - if (body.rightMiddleMedial) MapJoint(*body.rightMiddleMedial, nodes[6]); // MP - if (body.rightMiddleDistal) MapJoint(*body.rightMiddleDistal, nodes[7]); // PIP - if (body.rightMiddleTip) MapJoint(*body.rightMiddleTip, nodes[8]); // DIP - - // 약지손가락 (Ring) - if (body.rightRingMedial) MapJoint(*body.rightRingMedial, nodes[9]); // MP - if (body.rightRingDistal) MapJoint(*body.rightRingDistal, nodes[10]); // PIP - if (body.rightRingTip) MapJoint(*body.rightRingTip, nodes[11]); // DIP - - // 새끼손가락 (Little) - if (body.rightLittleMedial) MapJoint(*body.rightLittleMedial, nodes[12]); // MP - if (body.rightLittleDistal) MapJoint(*body.rightLittleDistal, nodes[13]); // PIP - if (body.rightLittleTip) MapJoint(*body.rightLittleTip, nodes[14]); // DIP -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::MapJoint(const RokokoData::ActorJointFrame& rokokoJoint, sFingerNode& optiTrackNode) -{ - // Rokoko 로우 데이터를 직접 OptiTrack에 적용 - optiTrackNode.quat_w = rokokoJoint.rotation.w; - optiTrackNode.quat_x = rokokoJoint.rotation.x; - optiTrackNode.quat_y = rokokoJoint.rotation.y; - optiTrackNode.quat_z = rokokoJoint.rotation.z; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// 로컬 로테이션 변환 함수들 -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertToLocalRotations(std::vector& nodes, eGloveHandSide handSide) -{ - // OptiTrack 손가락 배치: [Thumb(0-2), Index(3-5), Middle(6-8), Ring(9-11), Little(12-14)] - // 각 손가락의 계층 구조: Proximal/MP → Medial/PIP → Distal/DIP - - // 손가락별로 로컬 로테이션 계산 - for (int fingerIndex = 0; fingerIndex < 5; fingerIndex++) { - int baseIndex = fingerIndex * 3; - - // MP 관절 (루트) - 월드 로테이션 유지 - // 이미 설정된 값 사용 - - // PIP 관절 - MP 관절 기준 로컬 로테이션 - if (baseIndex + 1 < nodes.size()) { - ApplyLocalRotation(nodes[baseIndex + 1], nodes[baseIndex]); - } - - // DIP 관절 - PIP 관절 기준 로컬 로테이션 - if (baseIndex + 2 < nodes.size()) { - ApplyLocalRotation(nodes[baseIndex + 2], nodes[baseIndex + 1]); - } - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ApplyLocalRotation(sFingerNode& childNode, const sFingerNode& parentNode) -{ - // 로컬 로테이션 = parent^-1 * child - sFingerNode parentInverse; - InverseQuaternion(parentNode, parentInverse); - - sFingerNode localRotation; - MultiplyQuaternions(parentInverse, childNode, localRotation); - - // 결과를 자식 노드에 적용 - childNode.quat_w = localRotation.quat_w; - childNode.quat_x = localRotation.quat_x; - childNode.quat_y = localRotation.quat_y; - childNode.quat_z = localRotation.quat_z; - - // 정규화 - NormalizeQuaternion(childNode); -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::MultiplyQuaternions(const sFingerNode& q1, const sFingerNode& q2, sFingerNode& result) -{ - // 쿼터니언 곱셈: q1 * q2 - result.quat_w = q1.quat_w * q2.quat_w - q1.quat_x * q2.quat_x - q1.quat_y * q2.quat_y - q1.quat_z * q2.quat_z; - result.quat_x = q1.quat_w * q2.quat_x + q1.quat_x * q2.quat_w + q1.quat_y * q2.quat_z - q1.quat_z * q2.quat_y; - result.quat_y = q1.quat_w * q2.quat_y - q1.quat_x * q2.quat_z + q1.quat_y * q2.quat_w + q1.quat_z * q2.quat_x; - result.quat_z = q1.quat_w * q2.quat_z + q1.quat_x * q2.quat_y - q1.quat_y * q2.quat_x + q1.quat_z * q2.quat_w; -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::InverseQuaternion(const sFingerNode& q, sFingerNode& result) -{ - // 쿼터니언 역원: (w, -x, -y, -z) / |q|^2 - float norm = q.quat_w * q.quat_w + q.quat_x * q.quat_x + q.quat_y * q.quat_y + q.quat_z * q.quat_z; - - if (norm > 0.0001f) { // 0으로 나누기 방지 - result.quat_w = q.quat_w / norm; - result.quat_x = -q.quat_x / norm; - result.quat_y = -q.quat_y / norm; - result.quat_z = -q.quat_z / norm; - } else { - // 단위 쿼터니언으로 설정 - result.quat_w = 1.0f; - result.quat_x = 0.0f; - result.quat_y = 0.0f; - result.quat_z = 0.0f; - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::NormalizeQuaternion(sFingerNode& q) -{ - float norm = sqrt(q.quat_w * q.quat_w + q.quat_x * q.quat_x + q.quat_y * q.quat_y + q.quat_z * q.quat_z); - - if (norm > 0.0001f) { // 0으로 나누기 방지 - q.quat_w /= norm; - q.quat_x /= norm; - q.quat_y /= norm; - q.quat_z /= norm; - } else { - // 단위 쿼터니언으로 설정 - q.quat_w = 1.0f; - q.quat_x = 0.0f; - q.quat_y = 0.0f; - q.quat_z = 0.0f; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// T-포즈 캘리브레이션 시스템 -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CalibrateTPose(uint64_t deviceId, const std::vector& tPoseData) -{ - try { - // T-포즈 기준값 저장 - mTPoseReferences[deviceId] = tPoseData; - mTPoseCalibrated[deviceId] = true; - - if (mDeviceManager) { - std::string msg = "[RokokoGlove] T-pose calibration completed for device " + std::to_string(deviceId); - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - - } catch (...) { - if (mDeviceManager) { - std::string errorMsg = "[RokokoGlove] Error during T-pose calibration for device " + std::to_string(deviceId); - mDeviceManager->MessageToHost(errorMsg.c_str(), MessageType_StatusInfo); - } - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ApplyTPoseOffset(std::vector& nodes, uint64_t deviceId) -{ - if (!IsTPoseCalibrated(deviceId)) { - // T-포즈 캘리브레이션이 없으면 현재 데이터를 T-포즈로 사용 - CalibrateTPose(deviceId, nodes); - - // 첫 프레임은 중립 상태로 설정 - for (auto& node : nodes) { - node.quat_w = 1.0f; - node.quat_x = 0.0f; - node.quat_y = 0.0f; - node.quat_z = 0.0f; - } - return; - } - - try { - const auto& tPoseRef = mTPoseReferences[deviceId]; - - if (tPoseRef.size() != nodes.size()) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] T-pose reference size mismatch", MessageType_StatusInfo); - } - return; - } - - // 각 관절에 대해 T-포즈 기준 오프셋 계산 - for (size_t i = 0; i < nodes.size(); i++) { - // 오프셋 = T-pose^-1 * current - sFingerNode tPoseInverse; - InverseQuaternion(tPoseRef[i], tPoseInverse); - - sFingerNode offsetRotation; - MultiplyQuaternions(tPoseInverse, nodes[i], offsetRotation); - - // 결과를 노드에 적용 - nodes[i].quat_w = offsetRotation.quat_w; - nodes[i].quat_x = offsetRotation.quat_x; - nodes[i].quat_y = offsetRotation.quat_y; - nodes[i].quat_z = offsetRotation.quat_z; - - // 정규화 - NormalizeQuaternion(nodes[i]); - } - - } catch (...) { - if (mDeviceManager) { - std::string errorMsg = "[RokokoGlove] Error applying T-pose offset for device " + std::to_string(deviceId); - mDeviceManager->MessageToHost(errorMsg.c_str(), MessageType_StatusInfo); - } - } -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::IsTPoseCalibrated(uint64_t deviceId) const -{ - auto it = mTPoseCalibrated.find(deviceId); - return (it != mTPoseCalibrated.end() && it->second); -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ResetTPoseCalibration(uint64_t deviceId) -{ - mTPoseReferences.erase(deviceId); - mTPoseCalibrated[deviceId] = false; - - if (mDeviceManager) { - std::string msg = "[RokokoGlove] T-pose calibration reset for device " + std::to_string(deviceId); - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// 손목 기준 로컬 변환 시스템 -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertToWristLocalRotations(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide) -{ - try { - // 손목 로테이션 가져오기 - sFingerNode wristRotation; - if (!GetWristRotation(body, handSide, wristRotation)) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Failed to get wrist rotation", MessageType_StatusInfo); - } - return; - } - - // 손목 로테이션의 역원 계산 - sFingerNode wristInverse; - InverseQuaternion(wristRotation, wristInverse); - - // 모든 손가락에 대해 손목 기준 로컬 로테이션 계산 - for (auto& node : nodes) { - // 로컬 로테이션 = 손목^-1 * 손가락 - sFingerNode localRotation; - MultiplyQuaternions(wristInverse, node, localRotation); - - // 결과 적용 - node.quat_w = localRotation.quat_w; - node.quat_x = localRotation.quat_x; - node.quat_y = localRotation.quat_y; - node.quat_z = localRotation.quat_z; - - // 정규화 - NormalizeQuaternion(node); - } - - if (mDeviceManager) { - std::string handStr = (handSide == eGloveHandSide::Left) ? "Left" : "Right"; - std::string msg = "[RokokoGlove] Applied wrist-relative rotations for " + handStr + " hand"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Error in wrist-relative rotation conversion", MessageType_StatusInfo); - } - } -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::GetWristRotation(const RokokoData::Body& body, eGloveHandSide handSide, sFingerNode& wristRotation) -{ - try { - // 🎯 실제 손목 데이터 사용! (이제 body.leftHand, body.rightHand가 있음) - // ✅ OPTIMIZATION: Use optional reference instead of shared_ptr - const std::optional& wristJoint = - (handSide == eGloveHandSide::Left) ? body.leftHand : body.rightHand; - - if (wristJoint.has_value()) { - wristRotation.quat_w = wristJoint->rotation.w; - wristRotation.quat_x = wristJoint->rotation.x; - wristRotation.quat_y = wristJoint->rotation.y; - wristRotation.quat_z = wristJoint->rotation.z; - return true; - } - - // 데이터가 없으면 단위 쿼터니언 사용 - wristRotation.quat_w = 1.0f; - wristRotation.quat_x = 0.0f; - wristRotation.quat_y = 0.0f; - wristRotation.quat_z = 0.0f; - - if (mDeviceManager) { - std::string handStr = (handSide == eGloveHandSide::Left) ? "Left" : "Right"; - std::string msg = "[RokokoGlove] " + handStr + " reference joint not available, using identity rotation"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - - return true; - - } catch (...) { - // 에러 시 단위 쿼터니언 - wristRotation.quat_w = 1.0f; - wristRotation.quat_x = 0.0f; - wristRotation.quat_y = 0.0f; - wristRotation.quat_z = 0.0f; - return false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// 진짜 로컬 로테이션 변환 시스템 (계층적) -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertToTrueLocalRotations(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide) -{ - try { - // 모든 손가락의 관절 데이터 가져오기 - std::vector> allFingers; - if (!GetFingerJointRotations(body, handSide, allFingers)) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Failed to get finger joint rotations", MessageType_StatusInfo); - } - return; - } - - // 각 손가락별로 계층적 로컬 로테이션 계산 - int nodeIndex = 0; - for (const auto& fingerJoints : allFingers) { - if (fingerJoints.size() >= 3) { // MP, PIP, DIP 최소 3개 - CalculateFingerLocalRotations(fingerJoints, nodes, nodeIndex, body, handSide); - nodeIndex += 3; // OptiTrack은 손가락당 3개 노드 (MP, PIP, DIP) - } - } - - // 🚀 실시간성을 위해 로그 메시지 제거 - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Error in true local rotation conversion", MessageType_StatusInfo); - } - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CalculateFingerLocalRotations(const std::vector& fingerJoints, std::vector& outputNodes, int startIndex, const RokokoData::Body& body, eGloveHandSide handSide) -{ - try { - if (fingerJoints.size() < 3 || startIndex + 2 >= outputNodes.size()) { - return; - } - - // 🎯 핵심: 실제 손목 로테이션을 부모로 사용! - sFingerNode wristRotation; - GetWristRotation(body, handSide, wristRotation); // body 파라미터 사용 - sFingerNode parentRotation = wristRotation; // 첫 번째 손가락 관절의 부모는 손목 - - // 각 관절별로 부모 기준 로컬 로테이션 계산 - for (int i = 0; i < 3 && (startIndex + i) < outputNodes.size(); i++) { - if (i < fingerJoints.size() && fingerJoints[i]) { - // 현재 관절의 월드 로테이션 - sFingerNode currentWorld; - currentWorld.quat_w = fingerJoints[i]->rotation.w; - currentWorld.quat_x = fingerJoints[i]->rotation.x; - currentWorld.quat_y = fingerJoints[i]->rotation.y; - currentWorld.quat_z = fingerJoints[i]->rotation.z; - - // 부모 로테이션의 역원 - sFingerNode parentInverse; - InverseQuaternion(parentRotation, parentInverse); - - // 로컬 로테이션 = 부모^-1 × 현재 - sFingerNode localRotation; - MultiplyQuaternions(parentInverse, currentWorld, localRotation); - - // 🔧 좌표계 변환 적용 (Rokoko → OptiTrack) - 손별 대칭성 보정 - ConvertRokokoToOptiTrackCoordinates(localRotation, handSide); - - // 🎯 손가락별 축 순서 변경 적용 (엄지와 일반 손가락 분리) - sFingerNode finalRotation = localRotation; - - // 엄지손가락인지 확인 (startIndex == 0이면 엄지) - bool isThumb = (startIndex == 0); - - if (isThumb) { - // 🖐️ 엄지손가락 전용 축 변환 - if (handSide == eGloveHandSide::Left) { - // 🤚 왼손 엄지: 특별한 축 변환 - outputNodes[startIndex + i].quat_w = finalRotation.quat_w; // W는 그대로 - outputNodes[startIndex + i].quat_x = finalRotation.quat_y; // X ← Z - outputNodes[startIndex + i].quat_y = -finalRotation.quat_z; // Y ← Y (그대로) - outputNodes[startIndex + i].quat_z = -finalRotation.quat_x; // Z ← X (엄지 전용) - } else { - // 🖐️ 오른손 엄지: 특별한 축 변환 - outputNodes[startIndex + i].quat_w = finalRotation.quat_w; // W는 그대로 - outputNodes[startIndex + i].quat_x = -finalRotation.quat_y; // X ← Z (엄지 전용) - outputNodes[startIndex + i].quat_y = -finalRotation.quat_z; // Y ← Y (그대로) - outputNodes[startIndex + i].quat_z = finalRotation.quat_x; // Z ← -X (엄지 전용) - } - } else { - // 🤚 일반 손가락 축 변환 - if (handSide == eGloveHandSide::Left) { - // 🤚 왼손 일반 손가락: x y z → z y x (기존 방식) - outputNodes[startIndex + i].quat_w = finalRotation.quat_w; // W는 그대로 - outputNodes[startIndex + i].quat_x = finalRotation.quat_y; // X ← Z - outputNodes[startIndex + i].quat_y = -finalRotation.quat_z; // Y ← -Y - outputNodes[startIndex + i].quat_z = -finalRotation.quat_x; // Z ← -X - } else { - // 🖐️ 오른손 일반 손가락: 대칭적 축 매핑 (미러링 보정) - outputNodes[startIndex + i].quat_w = finalRotation.quat_w; // W는 그대로 - outputNodes[startIndex + i].quat_x = -finalRotation.quat_y; // X ← -Z (미러링) - outputNodes[startIndex + i].quat_y = -finalRotation.quat_z; // Y ← Y (그대로) - outputNodes[startIndex + i].quat_z = finalRotation.quat_x; // Z ← X (미러링) - } - } - - // 정규화 - NormalizeQuaternion(outputNodes[startIndex + i]); - - // 다음 계산을 위해 현재 관절을 부모로 설정 - parentRotation = currentWorld; - } - } - - } catch (...) { - // 에러 시 기본값 설정 - for (int i = 0; i < 3 && (startIndex + i) < outputNodes.size(); i++) { - outputNodes[startIndex + i].quat_w = 1.0f; - outputNodes[startIndex + i].quat_x = 0.0f; - outputNodes[startIndex + i].quat_y = 0.0f; - outputNodes[startIndex + i].quat_z = 0.0f; - } - } -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::GetFingerJointRotations(const RokokoData::Body& body, eGloveHandSide handSide, std::vector>& allFingers) -{ - try { - allFingers.clear(); - - // ✅ OPTIMIZATION: Use const_cast to get mutable pointer from const optional (no heap allocation) - if (handSide == eGloveHandSide::Left) { - // 왼손 손가락들 (5개 손가락) - allFingers.resize(5); - - // 엄지 - allFingers[0].push_back(const_cast(&*body.leftThumbProximal)); - allFingers[0].push_back(const_cast(&*body.leftThumbMedial)); - allFingers[0].push_back(const_cast(&*body.leftThumbDistal)); - - // 검지 - allFingers[1].push_back(const_cast(&*body.leftIndexProximal)); - allFingers[1].push_back(const_cast(&*body.leftIndexMedial)); - allFingers[1].push_back(const_cast(&*body.leftIndexDistal)); - - // 중지 - allFingers[2].push_back(const_cast(&*body.leftMiddleProximal)); - allFingers[2].push_back(const_cast(&*body.leftMiddleMedial)); - allFingers[2].push_back(const_cast(&*body.leftMiddleDistal)); - - // 약지 - allFingers[3].push_back(const_cast(&*body.leftRingProximal)); - allFingers[3].push_back(const_cast(&*body.leftRingMedial)); - allFingers[3].push_back(const_cast(&*body.leftRingDistal)); - - // 새끼 - allFingers[4].push_back(const_cast(&*body.leftLittleProximal)); - allFingers[4].push_back(const_cast(&*body.leftLittleMedial)); - allFingers[4].push_back(const_cast(&*body.leftLittleDistal)); - - } else { - // 오른손 손가락들 (5개 손가락) - allFingers.resize(5); - - // 엄지 - allFingers[0].push_back(const_cast(&*body.rightThumbProximal)); - allFingers[0].push_back(const_cast(&*body.rightThumbMedial)); - allFingers[0].push_back(const_cast(&*body.rightThumbDistal)); - - // 검지 - allFingers[1].push_back(const_cast(&*body.rightIndexProximal)); - allFingers[1].push_back(const_cast(&*body.rightIndexMedial)); - allFingers[1].push_back(const_cast(&*body.rightIndexDistal)); - - // 중지 - allFingers[2].push_back(const_cast(&*body.rightMiddleProximal)); - allFingers[2].push_back(const_cast(&*body.rightMiddleMedial)); - allFingers[2].push_back(const_cast(&*body.rightMiddleDistal)); - - // 약지 - allFingers[3].push_back(const_cast(&*body.rightRingProximal)); - allFingers[3].push_back(const_cast(&*body.rightRingMedial)); - allFingers[3].push_back(const_cast(&*body.rightRingDistal)); - - // 새끼 - allFingers[4].push_back(const_cast(&*body.rightLittleProximal)); - allFingers[4].push_back(const_cast(&*body.rightLittleMedial)); - allFingers[4].push_back(const_cast(&*body.rightLittleDistal)); - } - - return true; - - } catch (...) { - allFingers.clear(); - return false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// 좌표계 변환 및 디버깅 시스템 -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertRokokoToOptiTrackCoordinates(sFingerNode& node, eGloveHandSide handSide) -{ - // 🎯 손별 좌표계 변환 (왼손/오른손 대칭성 보정) - // - // 로코코: Unity 좌표계 (왼손 좌표계, Y-up, Z-forward) - // OptiTrack: 오른손 좌표계 (Y-up, Z-backward) - - float original_x = node.quat_x; - float original_z = node.quat_z; - - if (handSide == eGloveHandSide::Left) { - // 🤚 왼손: 미러링 변환 필요 (좌표계 차이로 인한 대칭성 보정) - node.quat_x = -original_x; // X축 반전 (좌우 미러링) - node.quat_z = -original_z; // Z축 반전 (앞뒤 미러링) - // Y, W는 그대로 유지 - - } else { - // 🖐️ 오른손: 직접 매핑 (OptiTrack과 좌표계 일치) - // X, Z 축 반전하지 않음 - node.quat_x = original_x; // X축 그대로 - node.quat_z = original_z; // Z축 그대로 - // Y, W는 그대로 유지 - } -} - -// 🚀 ApplyThumbOffset 함수 제거됨 - 인라인으로 처리 - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::LogRotationData(const std::string& label, const sFingerNode& rotation) -{ - if (mDeviceManager) { - std::stringstream ss; - ss << "[RokokoGlove] " << label << " - W:" << std::fixed << std::setprecision(3) << rotation.quat_w - << " X:" << rotation.quat_x << " Y:" << rotation.quat_y << " Z:" << rotation.quat_z; - mDeviceManager->MessageToHost(ss.str().c_str(), MessageType_StatusInfo); - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::DebugCoordinateSystem(const RokokoData::Body& body, eGloveHandSide handSide) -{ - // 🚀 실시간성 최우선 - 모든 디버깅 제거 (함수 비활성화) - return; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// 절대 로컬 로테이션 시스템 (손목에 완전히 독립적) -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertToAbsoluteLocalRotations(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide) -{ - try { - // 모든 손가락의 관절 데이터 가져오기 - std::vector> allFingers; - if (!GetFingerJointRotations(body, handSide, allFingers)) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Failed to get finger joint rotations for absolute local", MessageType_StatusInfo); - } - return; - } - - // 각 손가락별로 절대 로컬 로테이션 계산 - int nodeIndex = 0; - for (const auto& fingerJoints : allFingers) { - if (fingerJoints.size() >= 3) { // MP, PIP, DIP 최소 3개 - CalculateAbsoluteLocalForFinger(fingerJoints, nodes, nodeIndex, handSide); - nodeIndex += 3; // OptiTrack은 손가락당 3개 노드 (MP, PIP, DIP) - } - } - - if (mDeviceManager) { - std::string handStr = (handSide == eGloveHandSide::Left) ? "Left" : "Right"; - std::string msg = "[RokokoGlove] Applied absolute local rotations for " + handStr + " hand"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Error in absolute local rotation conversion", MessageType_StatusInfo); - } - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CalculateAbsoluteLocalForFinger(const std::vector& fingerJoints, std::vector& outputNodes, int startIndex, eGloveHandSide handSide) -{ - try { - if (fingerJoints.size() < 3 || startIndex + 2 >= outputNodes.size()) { - return; - } - - // 각 관절별로 절대 로컬 로테이션 계산 - for (int i = 0; i < 3 && (startIndex + i) < outputNodes.size(); i++) { - if (i < fingerJoints.size() && fingerJoints[i]) { - // 현재 관절의 월드 로테이션을 가져옴 - sFingerNode worldRotation; - worldRotation.quat_w = fingerJoints[i]->rotation.w; - worldRotation.quat_x = fingerJoints[i]->rotation.x; - worldRotation.quat_y = fingerJoints[i]->rotation.y; - worldRotation.quat_z = fingerJoints[i]->rotation.z; - - // 좌표계 변환 적용 - ConvertRokokoToOptiTrackCoordinates(worldRotation, handSide); - - // 절대적인 로컬 로테이션 계산 - sFingerNode localRotation; - if (i == 0) { - // 첫 번째 관절 (MP): 기본 T-포즈 기준으로 로컬 변환 - // T-포즈 = (0, 0, 0, 1) 즉, 단위 쿼터니언 - localRotation = worldRotation; // 첫 번째는 월드 그대로 사용 - } else { - // 나머지 관절들: 이전 관절 기준으로 상대 로테이션 계산 - sFingerNode parentWorld; - parentWorld.quat_w = fingerJoints[i-1]->rotation.w; - parentWorld.quat_x = fingerJoints[i-1]->rotation.x; - parentWorld.quat_y = fingerJoints[i-1]->rotation.y; - parentWorld.quat_z = fingerJoints[i-1]->rotation.z; - - // 부모도 좌표계 변환 - ConvertRokokoToOptiTrackCoordinates(parentWorld, handSide); - - // 상대 로테이션 = parent^-1 * child - localRotation = CalculateRelativeRotation(parentWorld, worldRotation); - } - - // 결과를 출력 노드에 저장 - outputNodes[startIndex + i].quat_w = localRotation.quat_w; - outputNodes[startIndex + i].quat_x = localRotation.quat_x; - outputNodes[startIndex + i].quat_y = localRotation.quat_y; - outputNodes[startIndex + i].quat_z = localRotation.quat_z; - - // 정규화 - NormalizeQuaternion(outputNodes[startIndex + i]); - } - } - - } catch (...) { - // 에러 시 기본값 설정 - for (int i = 0; i < 3 && (startIndex + i) < outputNodes.size(); i++) { - outputNodes[startIndex + i].quat_w = 1.0f; - outputNodes[startIndex + i].quat_x = 0.0f; - outputNodes[startIndex + i].quat_y = 0.0f; - outputNodes[startIndex + i].quat_z = 0.0f; - } - } -} - -sFingerNode OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CalculateRelativeRotation(const sFingerNode& parent, const sFingerNode& child) -{ - try { - // 상대 로테이션 = parent^-1 * child - sFingerNode parentInverse; - InverseQuaternion(parent, parentInverse); - - sFingerNode result; - MultiplyQuaternions(parentInverse, child, result); - - // 정규화 - NormalizeQuaternion(result); - - return result; - - } catch (...) { - // 에러 시 단위 쿼터니언 반환 - sFingerNode identity; - identity.quat_w = 1.0f; - identity.quat_x = 0.0f; - identity.quat_y = 0.0f; - identity.quat_z = 0.0f; - return identity; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// 가상 손 구조 시스템 (정확한 계층적 로컬 로테이션) -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertToUnityRokokoMethod(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide) -{ - try { - // Unity 방식: 원시 Rokoko 데이터에 T-포즈 오프셋 적용 - std::vector> allFingers; - if (!GetFingerJointRotations(body, handSide, allFingers)) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Failed to get finger joint rotations for Unity method", MessageType_StatusInfo); - } - return; - } - - // 각 손가락별로 Unity 방식 적용 - int nodeIndex = 0; - for (int fingerIndex = 0; fingerIndex < allFingers.size(); fingerIndex++) { - const auto& fingerJoints = allFingers[fingerIndex]; - if (fingerJoints.size() >= 3) { // MP, PIP, DIP 최소 3개 - for (int jointIndex = 0; jointIndex < 3 && (nodeIndex + jointIndex) < nodes.size(); jointIndex++) { - if (jointIndex < fingerJoints.size() && fingerJoints[jointIndex]) { - // Unity 방식 로테이션 적용 - ApplyUnityRokokoRotation(nodes[nodeIndex + jointIndex], fingerJoints[jointIndex], fingerIndex, jointIndex, handSide); - } else { - // 데이터가 없으면 단위 쿼터니언 - nodes[nodeIndex + jointIndex].quat_w = 1.0f; - nodes[nodeIndex + jointIndex].quat_x = 0.0f; - nodes[nodeIndex + jointIndex].quat_y = 0.0f; - nodes[nodeIndex + jointIndex].quat_z = 0.0f; - } - // 노드 ID 설정 - nodes[nodeIndex + jointIndex].node_id = nodeIndex + jointIndex; - } - nodeIndex += 3; // OptiTrack은 손가락당 3개 노드 - } - } - - if (mDeviceManager) { - std::string handStr = (handSide == eGloveHandSide::Left) ? "Left" : "Right"; - std::string msg = "[RokokoGlove] Applied Unity Rokoko method for " + handStr + " hand"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Error in Unity Rokoko method conversion", MessageType_StatusInfo); - } - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::BuildVirtualHandFromRokoko(VirtualHand& virtualHand, const RokokoData::Body& body, eGloveHandSide handSide) -{ - try { - // 손목 로테이션 설정 (기준점) - virtualHand.wristRotation.quat_w = 1.0f; - virtualHand.wristRotation.quat_x = 0.0f; - virtualHand.wristRotation.quat_y = 0.0f; - virtualHand.wristRotation.quat_z = 0.0f; - - // 각 손가락의 월드 로테이션 설정 - if (handSide == eGloveHandSide::Left) { - // 왼손 엄지 - if (body.leftThumbProximal) { - virtualHand.thumb.mp.worldRotation.quat_w = body.leftThumbProximal->rotation.w; - virtualHand.thumb.mp.worldRotation.quat_x = body.leftThumbProximal->rotation.x; - virtualHand.thumb.mp.worldRotation.quat_y = body.leftThumbProximal->rotation.y; - virtualHand.thumb.mp.worldRotation.quat_z = body.leftThumbProximal->rotation.z; - virtualHand.thumb.mp.parent = nullptr; // 손목이 부모 - } - if (body.leftThumbMedial) { - virtualHand.thumb.pip.worldRotation.quat_w = body.leftThumbMedial->rotation.w; - virtualHand.thumb.pip.worldRotation.quat_x = body.leftThumbMedial->rotation.x; - virtualHand.thumb.pip.worldRotation.quat_y = body.leftThumbMedial->rotation.y; - virtualHand.thumb.pip.worldRotation.quat_z = body.leftThumbMedial->rotation.z; - virtualHand.thumb.pip.parent = &virtualHand.thumb.mp; - } - if (body.leftThumbDistal) { - virtualHand.thumb.dip.worldRotation.quat_w = body.leftThumbDistal->rotation.w; - virtualHand.thumb.dip.worldRotation.quat_x = body.leftThumbDistal->rotation.x; - virtualHand.thumb.dip.worldRotation.quat_y = body.leftThumbDistal->rotation.y; - virtualHand.thumb.dip.worldRotation.quat_z = body.leftThumbDistal->rotation.z; - virtualHand.thumb.dip.parent = &virtualHand.thumb.pip; - } - - // 왼손 검지 - if (body.leftIndexProximal) { - virtualHand.index.mp.worldRotation.quat_w = body.leftIndexProximal->rotation.w; - virtualHand.index.mp.worldRotation.quat_x = body.leftIndexProximal->rotation.x; - virtualHand.index.mp.worldRotation.quat_y = body.leftIndexProximal->rotation.y; - virtualHand.index.mp.worldRotation.quat_z = body.leftIndexProximal->rotation.z; - virtualHand.index.mp.parent = nullptr; - } - if (body.leftIndexMedial) { - virtualHand.index.pip.worldRotation.quat_w = body.leftIndexMedial->rotation.w; - virtualHand.index.pip.worldRotation.quat_x = body.leftIndexMedial->rotation.x; - virtualHand.index.pip.worldRotation.quat_y = body.leftIndexMedial->rotation.y; - virtualHand.index.pip.worldRotation.quat_z = body.leftIndexMedial->rotation.z; - virtualHand.index.pip.parent = &virtualHand.index.mp; - } - if (body.leftIndexDistal) { - virtualHand.index.dip.worldRotation.quat_w = body.leftIndexDistal->rotation.w; - virtualHand.index.dip.worldRotation.quat_x = body.leftIndexDistal->rotation.x; - virtualHand.index.dip.worldRotation.quat_y = body.leftIndexDistal->rotation.y; - virtualHand.index.dip.worldRotation.quat_z = body.leftIndexDistal->rotation.z; - virtualHand.index.dip.parent = &virtualHand.index.pip; - } - - // 같은 방식으로 중지, 약지, 새끼 (여기서는 간략화) - // TODO: 나머지 손가락들도 동일하게 구현 - - } else { - // 오른손 - 같은 구조, 다른 데이터 - // TODO: 오른손 구현 - } - - } catch (...) { - // 에러 시 기본값 설정 - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CalculateHierarchicalLocalRotations(VirtualHand& virtualHand) -{ - try { - // 각 손가락의 계층적 로컬 로테이션 계산 - - // 엄지 손가락 - virtualHand.thumb.mp.localRotation = CalculateLocalRotationFromParent(virtualHand.thumb.mp, virtualHand.thumb.mp); // 첫 번째는 손목 기준 - virtualHand.thumb.pip.localRotation = CalculateLocalRotationFromParent(virtualHand.thumb.mp, virtualHand.thumb.pip); - virtualHand.thumb.dip.localRotation = CalculateLocalRotationFromParent(virtualHand.thumb.pip, virtualHand.thumb.dip); - - // 검지 손가락 - virtualHand.index.mp.localRotation = CalculateLocalRotationFromParent(virtualHand.index.mp, virtualHand.index.mp); - virtualHand.index.pip.localRotation = CalculateLocalRotationFromParent(virtualHand.index.mp, virtualHand.index.pip); - virtualHand.index.dip.localRotation = CalculateLocalRotationFromParent(virtualHand.index.pip, virtualHand.index.dip); - - // TODO: 나머지 손가락들 - - } catch (...) { - // 에러 시 기본값 - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ExtractLocalRotationsToNodes(const VirtualHand& virtualHand, std::vector& nodes) -{ - try { - if (nodes.size() < 15) return; - - // OptiTrack 노드 순서에 맞게 추출 - // 엄지: 0, 1, 2 - nodes[0] = virtualHand.thumb.mp.localRotation; - nodes[1] = virtualHand.thumb.pip.localRotation; - nodes[2] = virtualHand.thumb.dip.localRotation; - - // 검지: 3, 4, 5 - nodes[3] = virtualHand.index.mp.localRotation; - nodes[4] = virtualHand.index.pip.localRotation; - nodes[5] = virtualHand.index.dip.localRotation; - - // TODO: 나머지 손가락들 (6~14) - - // 노드 ID 설정 - for (int i = 0; i < 15; i++) { - nodes[i].node_id = i; - NormalizeQuaternion(nodes[i]); - } - - } catch (...) { - // 에러 시 기본값 - } -} - -sFingerNode OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CalculateLocalRotationFromParent(const VirtualJoint& parent, const VirtualJoint& child) -{ - try { - if (parent.parent == nullptr) { - // 첫 번째 관절은 월드 로테이션 그대로 (손목 기준) - return child.worldRotation; - } else { - // 부모 기준 로컬 로테이션 = parent^-1 * child - sFingerNode parentInverse; - InverseQuaternion(parent.worldRotation, parentInverse); - - sFingerNode localRotation; - MultiplyQuaternions(parentInverse, child.worldRotation, localRotation); - - return localRotation; - } - - } catch (...) { - // 에러 시 단위 쿼터니언 - sFingerNode identity; - identity.quat_w = 1.0f; - identity.quat_x = 0.0f; - identity.quat_y = 0.0f; - identity.quat_z = 0.0f; - return identity; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Unity 방식 Rokoko 데이터 처리 (원시 데이터 + T-포즈 오프셋) -// -sFingerNode OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::GetUnityTPoseOffset(int fingerIndex, int jointIndex, eGloveHandSide handSide) -{ - // Unity의 SmartsuitTPose 데이터 (정확히 동일) - sFingerNode offset; - - if (handSide == eGloveHandSide::Left) { - // 왼손 T-포즈 오프셋 (Unity Actor.cs에서 복사) - if (fingerIndex == 0) { // 엄지 - if (jointIndex == 0) { // Proximal - offset.quat_w = -0.092f; offset.quat_x = -0.561f; offset.quat_y = -0.701f; offset.quat_z = 0.430f; - } else { // Intermediate, Distal - offset.quat_w = -0.271f; offset.quat_x = -0.653f; offset.quat_y = -0.653f; offset.quat_z = 0.271f; - } - } else { // 검지, 중지, 약지, 새끼 - offset.quat_w = -0.500f; offset.quat_x = -0.500f; offset.quat_y = -0.500f; offset.quat_z = 0.500f; - } - } else { - // 오른손 T-포즈 오프셋 (Unity Actor.cs에서 복사) - if (fingerIndex == 0) { // 엄지 - if (jointIndex == 0) { // Proximal - offset.quat_w = 0.092f; offset.quat_x = 0.561f; offset.quat_y = -0.701f; offset.quat_z = 0.430f; - } else { // Intermediate, Distal - offset.quat_w = 0.271f; offset.quat_x = 0.653f; offset.quat_y = -0.653f; offset.quat_z = 0.271f; - } - } else { // 검지, 중지, 약지, 새끼 - offset.quat_w = 0.500f; offset.quat_x = 0.500f; offset.quat_y = -0.500f; offset.quat_z = 0.500f; - } - } - - return offset; -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ApplyUnityRokokoRotation(sFingerNode& node, const RokokoData::ActorJointFrame* jointFrame, int fingerIndex, int jointIndex, eGloveHandSide handSide) -{ - try { - // 1. Rokoko 원시 월드 로테이션 - sFingerNode worldRotation; - worldRotation.quat_w = jointFrame->rotation.w; - worldRotation.quat_x = jointFrame->rotation.x; - worldRotation.quat_y = jointFrame->rotation.y; - worldRotation.quat_z = jointFrame->rotation.z; - - // 2. Unity T-포즈 오프셋 - sFingerNode tposeOffset = GetUnityTPoseOffset(fingerIndex, jointIndex, handSide); - - // 3. Unity Actor.cs 공식 정확히 따라하기 - // Unity: boneTransform.rotation = parentRotation * worldRotation * offset - // OptiTrack: 각 손가락은 독립 센서이므로 worldRotation * offset만 적용 - - // 🎯 핵심: Unity는 worldRotation을 그대로 사용 (로컬 변환 안함!) - // 손가락이 손목에 따라 움직이는 것은 Unity Transform 계층에서 자동 처리됨 - sFingerNode finalRotation; - MultiplyQuaternions(worldRotation, tposeOffset, finalRotation); - - // 4. 결과 적용 - node.quat_w = finalRotation.quat_w; - node.quat_x = finalRotation.quat_x; - node.quat_y = finalRotation.quat_y; - node.quat_z = finalRotation.quat_z; - - // 5. 정규화 - NormalizeQuaternion(node); - - } catch (...) { - // 에러 시 단위 쿼터니언 - node.quat_w = 1.0f; - node.quat_x = 0.0f; - node.quat_y = 0.0f; - node.quat_z = 0.0f; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// -// 가상 Unity 아바타 시뮬레이션 시스템 -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ConvertToVirtualUnityAvatar(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide) -{ - try { - // 1. 가상 Unity 아바타 초기화 - VirtualUnityAvatar avatar; - InitializeVirtualUnityAvatar(avatar); - - // 2. Rokoko 데이터를 가상 아바타에 적용 (월드 로테이션) - ApplyRokokoDataToVirtualAvatar(avatar, body); - - // 3. Unity 방식으로 로컬 로테이션 계산 - CalculateLocalRotationsFromWorldRotations(avatar); - - // 4. 로컬 로테이션을 OptiTrack 노드로 추출 - ExtractLocalRotationsFromVirtualAvatar(avatar, nodes, handSide); - - if (mDeviceManager) { - std::string handStr = (handSide == eGloveHandSide::Left) ? "Left" : "Right"; - std::string msg = "[RokokoGlove] Applied Virtual Unity Avatar for " + handStr + " hand"; - mDeviceManager->MessageToHost(msg.c_str(), MessageType_StatusInfo); - } - - } catch (...) { - if (mDeviceManager) { - mDeviceManager->MessageToHost("[RokokoGlove] Error in Virtual Unity Avatar conversion", MessageType_StatusInfo); - } - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::InitializeVirtualUnityAvatar(VirtualUnityAvatar& avatar) -{ - try { - // Root Transform 초기화 - avatar.root.name = "Root"; - avatar.root.parent = nullptr; - avatar.root.localRotation.quat_w = 1.0f; // Identity - avatar.root.localRotation.quat_x = 0.0f; - avatar.root.localRotation.quat_y = 0.0f; - avatar.root.localRotation.quat_z = 0.0f; - avatar.root.worldRotation.quat_w = 1.0f; - avatar.root.worldRotation.quat_x = 0.0f; - avatar.root.worldRotation.quat_y = 0.0f; - avatar.root.worldRotation.quat_z = 0.0f; - - // === 전체 스켈레톤 계층 구조 생성 (Unity Humanoid와 동일) === - - // 1. 허리 (최상위 부모) - avatar.hips = new VirtualTransform(); - avatar.hips->name = "Hips"; - avatar.hips->parent = &avatar.root; - avatar.hips->localRotation.quat_w = 1.0f; - avatar.hips->localRotation.quat_x = 0.0f; - avatar.hips->localRotation.quat_y = 0.0f; - avatar.hips->localRotation.quat_z = 0.0f; - - // 2. 스파인 (허리의 자식) - avatar.spine = new VirtualTransform(); - avatar.spine->name = "Spine"; - avatar.spine->parent = avatar.hips; - avatar.spine->localRotation.quat_w = 1.0f; - avatar.spine->localRotation.quat_x = 0.0f; - avatar.spine->localRotation.quat_y = 0.0f; - avatar.spine->localRotation.quat_z = 0.0f; - - // 3. 가슴 (스파인의 자식) - avatar.chest = new VirtualTransform(); - avatar.chest->name = "Chest"; - avatar.chest->parent = avatar.spine; - avatar.chest->localRotation.quat_w = 1.0f; - avatar.chest->localRotation.quat_x = 0.0f; - avatar.chest->localRotation.quat_y = 0.0f; - avatar.chest->localRotation.quat_z = 0.0f; - - // 4. 어깨들 (가슴의 자식) - avatar.leftShoulder = new VirtualTransform(); - avatar.leftShoulder->name = "LeftShoulder"; - avatar.leftShoulder->parent = avatar.chest; - avatar.leftShoulder->localRotation.quat_w = 1.0f; - avatar.leftShoulder->localRotation.quat_x = 0.0f; - avatar.leftShoulder->localRotation.quat_y = 0.0f; - avatar.leftShoulder->localRotation.quat_z = 0.0f; - - avatar.rightShoulder = new VirtualTransform(); - avatar.rightShoulder->name = "RightShoulder"; - avatar.rightShoulder->parent = avatar.chest; - avatar.rightShoulder->localRotation.quat_w = 1.0f; - avatar.rightShoulder->localRotation.quat_x = 0.0f; - avatar.rightShoulder->localRotation.quat_y = 0.0f; - avatar.rightShoulder->localRotation.quat_z = 0.0f; - - // 5. 팔 계층 (어깨 → 상완 → 하완 → 손) - avatar.leftUpperArm = new VirtualTransform(); - avatar.leftUpperArm->name = "LeftUpperArm"; - avatar.leftUpperArm->parent = avatar.leftShoulder; - avatar.leftUpperArm->localRotation.quat_w = 1.0f; - avatar.leftUpperArm->localRotation.quat_x = 0.0f; - avatar.leftUpperArm->localRotation.quat_y = 0.0f; - avatar.leftUpperArm->localRotation.quat_z = 0.0f; - - avatar.leftLowerArm = new VirtualTransform(); - avatar.leftLowerArm->name = "LeftLowerArm"; - avatar.leftLowerArm->parent = avatar.leftUpperArm; - avatar.leftLowerArm->localRotation.quat_w = 1.0f; - avatar.leftLowerArm->localRotation.quat_x = 0.0f; - avatar.leftLowerArm->localRotation.quat_y = 0.0f; - avatar.leftLowerArm->localRotation.quat_z = 0.0f; - - avatar.leftHand = new VirtualTransform(); - avatar.leftHand->name = "LeftHand"; - avatar.leftHand->parent = avatar.leftLowerArm; - avatar.leftHand->localRotation.quat_w = 1.0f; - avatar.leftHand->localRotation.quat_x = 0.0f; - avatar.leftHand->localRotation.quat_y = 0.0f; - avatar.leftHand->localRotation.quat_z = 0.0f; - - // 오른쪽 팔도 동일 - avatar.rightUpperArm = new VirtualTransform(); - avatar.rightUpperArm->name = "RightUpperArm"; - avatar.rightUpperArm->parent = avatar.rightShoulder; - avatar.rightUpperArm->localRotation.quat_w = 1.0f; - avatar.rightUpperArm->localRotation.quat_x = 0.0f; - avatar.rightUpperArm->localRotation.quat_y = 0.0f; - avatar.rightUpperArm->localRotation.quat_z = 0.0f; - - avatar.rightLowerArm = new VirtualTransform(); - avatar.rightLowerArm->name = "RightLowerArm"; - avatar.rightLowerArm->parent = avatar.rightUpperArm; - avatar.rightLowerArm->localRotation.quat_w = 1.0f; - avatar.rightLowerArm->localRotation.quat_x = 0.0f; - avatar.rightLowerArm->localRotation.quat_y = 0.0f; - avatar.rightLowerArm->localRotation.quat_z = 0.0f; - - avatar.rightHand = new VirtualTransform(); - avatar.rightHand->name = "RightHand"; - avatar.rightHand->parent = avatar.rightLowerArm; - avatar.rightHand->localRotation.quat_w = 1.0f; - avatar.rightHand->localRotation.quat_x = 0.0f; - avatar.rightHand->localRotation.quat_y = 0.0f; - avatar.rightHand->localRotation.quat_z = 0.0f; - - // 왼손 손가락 계층 구조 생성 - for (int i = 0; i < 3; i++) { - // 엄지 - avatar.leftThumb[i] = new VirtualTransform(); - avatar.leftThumb[i]->name = "LeftThumb" + std::to_string(i); - avatar.leftThumb[i]->parent = (i == 0) ? avatar.leftHand : avatar.leftThumb[i-1]; - avatar.leftThumb[i]->localRotation.quat_w = 1.0f; - avatar.leftThumb[i]->localRotation.quat_x = 0.0f; - avatar.leftThumb[i]->localRotation.quat_y = 0.0f; - avatar.leftThumb[i]->localRotation.quat_z = 0.0f; - - // 검지 - avatar.leftIndex[i] = new VirtualTransform(); - avatar.leftIndex[i]->name = "LeftIndex" + std::to_string(i); - avatar.leftIndex[i]->parent = (i == 0) ? avatar.leftHand : avatar.leftIndex[i-1]; - avatar.leftIndex[i]->localRotation.quat_w = 1.0f; - avatar.leftIndex[i]->localRotation.quat_x = 0.0f; - avatar.leftIndex[i]->localRotation.quat_y = 0.0f; - avatar.leftIndex[i]->localRotation.quat_z = 0.0f; - - // 중지 - avatar.leftMiddle[i] = new VirtualTransform(); - avatar.leftMiddle[i]->name = "LeftMiddle" + std::to_string(i); - avatar.leftMiddle[i]->parent = (i == 0) ? avatar.leftHand : avatar.leftMiddle[i-1]; - avatar.leftMiddle[i]->localRotation.quat_w = 1.0f; - avatar.leftMiddle[i]->localRotation.quat_x = 0.0f; - avatar.leftMiddle[i]->localRotation.quat_y = 0.0f; - avatar.leftMiddle[i]->localRotation.quat_z = 0.0f; - - // 약지 - avatar.leftRing[i] = new VirtualTransform(); - avatar.leftRing[i]->name = "LeftRing" + std::to_string(i); - avatar.leftRing[i]->parent = (i == 0) ? avatar.leftHand : avatar.leftRing[i-1]; - avatar.leftRing[i]->localRotation.quat_w = 1.0f; - avatar.leftRing[i]->localRotation.quat_x = 0.0f; - avatar.leftRing[i]->localRotation.quat_y = 0.0f; - avatar.leftRing[i]->localRotation.quat_z = 0.0f; - - // 새끼 - avatar.leftLittle[i] = new VirtualTransform(); - avatar.leftLittle[i]->name = "LeftLittle" + std::to_string(i); - avatar.leftLittle[i]->parent = (i == 0) ? avatar.leftHand : avatar.leftLittle[i-1]; - avatar.leftLittle[i]->localRotation.quat_w = 1.0f; - avatar.leftLittle[i]->localRotation.quat_x = 0.0f; - avatar.leftLittle[i]->localRotation.quat_y = 0.0f; - avatar.leftLittle[i]->localRotation.quat_z = 0.0f; - } - - // 오른손도 동일하게 (간략화) - for (int i = 0; i < 3; i++) { - avatar.rightThumb[i] = new VirtualTransform(); - avatar.rightThumb[i]->name = "RightThumb" + std::to_string(i); - avatar.rightThumb[i]->parent = (i == 0) ? avatar.rightHand : avatar.rightThumb[i-1]; - avatar.rightThumb[i]->localRotation.quat_w = 1.0f; - avatar.rightThumb[i]->localRotation.quat_x = 0.0f; - avatar.rightThumb[i]->localRotation.quat_y = 0.0f; - avatar.rightThumb[i]->localRotation.quat_z = 0.0f; - - avatar.rightIndex[i] = new VirtualTransform(); - avatar.rightIndex[i]->name = "RightIndex" + std::to_string(i); - avatar.rightIndex[i]->parent = (i == 0) ? avatar.rightHand : avatar.rightIndex[i-1]; - avatar.rightIndex[i]->localRotation.quat_w = 1.0f; - avatar.rightIndex[i]->localRotation.quat_x = 0.0f; - avatar.rightIndex[i]->localRotation.quat_y = 0.0f; - avatar.rightIndex[i]->localRotation.quat_z = 0.0f; - - // 나머지 손가락들도 필요시 추가... - } - - } catch (...) { - // 에러 시 기본값 설정 - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ApplyRokokoDataToVirtualAvatar(VirtualUnityAvatar& avatar, const RokokoData::Body& body) -{ - try { - // === Rokoko 전신 + 손가락 데이터를 가상 아바타에 적용 === - // Unity BodyFrame과 동일한 구조로 전신 스켈레톤 데이터 적용 - - // 1. 전신 스켈레톤 데이터 적용 - if (body.hip) { - avatar.hips->worldRotation.quat_w = body.hip->rotation.w; - avatar.hips->worldRotation.quat_x = body.hip->rotation.x; - avatar.hips->worldRotation.quat_y = body.hip->rotation.y; - avatar.hips->worldRotation.quat_z = body.hip->rotation.z; - } - - if (body.spine) { - avatar.spine->worldRotation.quat_w = body.spine->rotation.w; - avatar.spine->worldRotation.quat_x = body.spine->rotation.x; - avatar.spine->worldRotation.quat_y = body.spine->rotation.y; - avatar.spine->worldRotation.quat_z = body.spine->rotation.z; - } - - if (body.chest) { - avatar.chest->worldRotation.quat_w = body.chest->rotation.w; - avatar.chest->worldRotation.quat_x = body.chest->rotation.x; - avatar.chest->worldRotation.quat_y = body.chest->rotation.y; - avatar.chest->worldRotation.quat_z = body.chest->rotation.z; - } - - // 2. 어깨 데이터 적용 - if (body.leftShoulder) { - avatar.leftShoulder->worldRotation.quat_w = body.leftShoulder->rotation.w; - avatar.leftShoulder->worldRotation.quat_x = body.leftShoulder->rotation.x; - avatar.leftShoulder->worldRotation.quat_y = body.leftShoulder->rotation.y; - avatar.leftShoulder->worldRotation.quat_z = body.leftShoulder->rotation.z; - } - - if (body.rightShoulder) { - avatar.rightShoulder->worldRotation.quat_w = body.rightShoulder->rotation.w; - avatar.rightShoulder->worldRotation.quat_x = body.rightShoulder->rotation.x; - avatar.rightShoulder->worldRotation.quat_y = body.rightShoulder->rotation.y; - avatar.rightShoulder->worldRotation.quat_z = body.rightShoulder->rotation.z; - } - - // 3. 팔 데이터 적용 - if (body.leftUpperArm) { - avatar.leftUpperArm->worldRotation.quat_w = body.leftUpperArm->rotation.w; - avatar.leftUpperArm->worldRotation.quat_x = body.leftUpperArm->rotation.x; - avatar.leftUpperArm->worldRotation.quat_y = body.leftUpperArm->rotation.y; - avatar.leftUpperArm->worldRotation.quat_z = body.leftUpperArm->rotation.z; - } - - if (body.leftLowerArm) { - avatar.leftLowerArm->worldRotation.quat_w = body.leftLowerArm->rotation.w; - avatar.leftLowerArm->worldRotation.quat_x = body.leftLowerArm->rotation.x; - avatar.leftLowerArm->worldRotation.quat_y = body.leftLowerArm->rotation.y; - avatar.leftLowerArm->worldRotation.quat_z = body.leftLowerArm->rotation.z; - } - - if (body.leftHand) { - avatar.leftHand->worldRotation.quat_w = body.leftHand->rotation.w; - avatar.leftHand->worldRotation.quat_x = body.leftHand->rotation.x; - avatar.leftHand->worldRotation.quat_y = body.leftHand->rotation.y; - avatar.leftHand->worldRotation.quat_z = body.leftHand->rotation.z; - } - - // 오른쪽 팔도 동일 - if (body.rightUpperArm) { - avatar.rightUpperArm->worldRotation.quat_w = body.rightUpperArm->rotation.w; - avatar.rightUpperArm->worldRotation.quat_x = body.rightUpperArm->rotation.x; - avatar.rightUpperArm->worldRotation.quat_y = body.rightUpperArm->rotation.y; - avatar.rightUpperArm->worldRotation.quat_z = body.rightUpperArm->rotation.z; - } - - if (body.rightLowerArm) { - avatar.rightLowerArm->worldRotation.quat_w = body.rightLowerArm->rotation.w; - avatar.rightLowerArm->worldRotation.quat_x = body.rightLowerArm->rotation.x; - avatar.rightLowerArm->worldRotation.quat_y = body.rightLowerArm->rotation.y; - avatar.rightLowerArm->worldRotation.quat_z = body.rightLowerArm->rotation.z; - } - - if (body.rightHand) { - avatar.rightHand->worldRotation.quat_w = body.rightHand->rotation.w; - avatar.rightHand->worldRotation.quat_x = body.rightHand->rotation.x; - avatar.rightHand->worldRotation.quat_y = body.rightHand->rotation.y; - avatar.rightHand->worldRotation.quat_z = body.rightHand->rotation.z; - } - - // 4. 손가락 데이터 적용 - - // 왼손 엄지 - if (body.leftThumbProximal) { - avatar.leftThumb[0]->worldRotation.quat_w = body.leftThumbProximal->rotation.w; - avatar.leftThumb[0]->worldRotation.quat_x = body.leftThumbProximal->rotation.x; - avatar.leftThumb[0]->worldRotation.quat_y = body.leftThumbProximal->rotation.y; - avatar.leftThumb[0]->worldRotation.quat_z = body.leftThumbProximal->rotation.z; - } - if (body.leftThumbMedial) { - avatar.leftThumb[1]->worldRotation.quat_w = body.leftThumbMedial->rotation.w; - avatar.leftThumb[1]->worldRotation.quat_x = body.leftThumbMedial->rotation.x; - avatar.leftThumb[1]->worldRotation.quat_y = body.leftThumbMedial->rotation.y; - avatar.leftThumb[1]->worldRotation.quat_z = body.leftThumbMedial->rotation.z; - } - if (body.leftThumbDistal) { - avatar.leftThumb[2]->worldRotation.quat_w = body.leftThumbDistal->rotation.w; - avatar.leftThumb[2]->worldRotation.quat_x = body.leftThumbDistal->rotation.x; - avatar.leftThumb[2]->worldRotation.quat_y = body.leftThumbDistal->rotation.y; - avatar.leftThumb[2]->worldRotation.quat_z = body.leftThumbDistal->rotation.z; - } - - // 왼손 검지 - if (body.leftIndexProximal) { - avatar.leftIndex[0]->worldRotation.quat_w = body.leftIndexProximal->rotation.w; - avatar.leftIndex[0]->worldRotation.quat_x = body.leftIndexProximal->rotation.x; - avatar.leftIndex[0]->worldRotation.quat_y = body.leftIndexProximal->rotation.y; - avatar.leftIndex[0]->worldRotation.quat_z = body.leftIndexProximal->rotation.z; - } - if (body.leftIndexMedial) { - avatar.leftIndex[1]->worldRotation.quat_w = body.leftIndexMedial->rotation.w; - avatar.leftIndex[1]->worldRotation.quat_x = body.leftIndexMedial->rotation.x; - avatar.leftIndex[1]->worldRotation.quat_y = body.leftIndexMedial->rotation.y; - avatar.leftIndex[1]->worldRotation.quat_z = body.leftIndexMedial->rotation.z; - } - if (body.leftIndexDistal) { - avatar.leftIndex[2]->worldRotation.quat_w = body.leftIndexDistal->rotation.w; - avatar.leftIndex[2]->worldRotation.quat_x = body.leftIndexDistal->rotation.x; - avatar.leftIndex[2]->worldRotation.quat_y = body.leftIndexDistal->rotation.y; - avatar.leftIndex[2]->worldRotation.quat_z = body.leftIndexDistal->rotation.z; - } - - // TODO: 나머지 손가락들과 오른손도 추가 - - } catch (...) { - // 에러 시 기본값 유지 - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::CalculateLocalRotationsFromWorldRotations(VirtualUnityAvatar& avatar) -{ - try { - // Unity 공식: localRotation = Quaternion.Inverse(parent.rotation) * child.rotation - - // === 전체 스켈레톤 계층적 로컬 로테이션 계산 === - - // 1. 몸통 계층 계산 - if (avatar.hips && avatar.hips->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.hips->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.hips->worldRotation, avatar.hips->localRotation); - } - - if (avatar.spine && avatar.spine->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.spine->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.spine->worldRotation, avatar.spine->localRotation); - } - - if (avatar.chest && avatar.chest->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.chest->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.chest->worldRotation, avatar.chest->localRotation); - } - - // 2. 어깨 계층 계산 - if (avatar.leftShoulder && avatar.leftShoulder->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.leftShoulder->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.leftShoulder->worldRotation, avatar.leftShoulder->localRotation); - } - - if (avatar.rightShoulder && avatar.rightShoulder->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.rightShoulder->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.rightShoulder->worldRotation, avatar.rightShoulder->localRotation); - } - - // 3. 팔 계층 계산 (상완 → 하완 → 손) - if (avatar.leftUpperArm && avatar.leftUpperArm->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.leftUpperArm->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.leftUpperArm->worldRotation, avatar.leftUpperArm->localRotation); - } - - if (avatar.leftLowerArm && avatar.leftLowerArm->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.leftLowerArm->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.leftLowerArm->worldRotation, avatar.leftLowerArm->localRotation); - } - - if (avatar.leftHand && avatar.leftHand->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.leftHand->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.leftHand->worldRotation, avatar.leftHand->localRotation); - } - - // 오른쪽 팔도 동일 - if (avatar.rightUpperArm && avatar.rightUpperArm->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.rightUpperArm->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.rightUpperArm->worldRotation, avatar.rightUpperArm->localRotation); - } - - if (avatar.rightLowerArm && avatar.rightLowerArm->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.rightLowerArm->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.rightLowerArm->worldRotation, avatar.rightLowerArm->localRotation); - } - - if (avatar.rightHand && avatar.rightHand->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.rightHand->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.rightHand->worldRotation, avatar.rightHand->localRotation); - } - - // 4. 손가락 계층 계산 (이제 손을 올바른 부모로 사용) - for (int i = 0; i < 3; i++) { - if (avatar.leftThumb[i] && avatar.leftThumb[i]->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.leftThumb[i]->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.leftThumb[i]->worldRotation, avatar.leftThumb[i]->localRotation); - } - - if (avatar.leftIndex[i] && avatar.leftIndex[i]->parent) { - sFingerNode parentInverse; - InverseQuaternion(avatar.leftIndex[i]->parent->worldRotation, parentInverse); - MultiplyQuaternions(parentInverse, avatar.leftIndex[i]->worldRotation, avatar.leftIndex[i]->localRotation); - } - - // TODO: 나머지 손가락들과 오른손도 추가 - } - - } catch (...) { - // 에러 시 기본값 유지 - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::ExtractLocalRotationsFromVirtualAvatar(const VirtualUnityAvatar& avatar, std::vector& nodes, eGloveHandSide handSide) -{ - try { - if (nodes.size() < 15) return; - - if (handSide == eGloveHandSide::Left) { - // 왼손 엄지: 0, 1, 2 - for (int i = 0; i < 3; i++) { - if (avatar.leftThumb[i]) { - nodes[i] = avatar.leftThumb[i]->localRotation; - nodes[i].node_id = i; - NormalizeQuaternion(nodes[i]); - } - } - - // 왼손 검지: 3, 4, 5 - for (int i = 0; i < 3; i++) { - if (avatar.leftIndex[i]) { - nodes[3 + i] = avatar.leftIndex[i]->localRotation; - nodes[3 + i].node_id = 3 + i; - NormalizeQuaternion(nodes[3 + i]); - } - } - - // TODO: 나머지 손가락들 (6~14) - } else { - // 오른손 처리 - // TODO: 오른손 구현 - } - - } catch (...) { - // 에러 시 기본값 - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveAdapterSingleton::UpdateWorldRotationFromParent(VirtualTransform* transform) -{ - // Unity 공식: rotation = parent.rotation * localRotation - try { - if (transform && transform->parent) { - MultiplyQuaternions(transform->parent->worldRotation, transform->localRotation, transform->worldRotation); - } - } catch (...) { - // 에러 시 기본값 유지 - } -} \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveAdapterSingleton.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveAdapterSingleton.h deleted file mode 100644 index 67029d97..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveAdapterSingleton.h +++ /dev/null @@ -1,348 +0,0 @@ -////====================================================================================================== -//// Copyright 2023, NaturalPoint Inc. -////====================================================================================================== -/** - * ExampleGloveAdapterSingleton class is an adapter class provided to demonstrate how communication between plugin device and - * the glove SDK can be managed. A singleton instance of this class manages interaction between plugin device and the glove - * device SDK. The adapter instance also stores a keeps the latest data and device info map that stores the currently detected device serials - * and the latest frame data so that corresponding glove device can poll from it. Please note that this is provided only as an example, and - * there could be other ways to set this up. - */ - - -#pragma once -#include -#include -#include -#include -#include -#include - -// OptiTrack Peripheral Device API -#include "AnalogChannelDescriptor.h" -#include "GloveDataFormat.h" -#include "HardwareSimulator.h" - -// Rokoko Integration -#include "RokokoData.h" -#include "RokokoUDPReceiver.h" -#include "RokokoDataParser.h" -#include "RokokoDataConverter.h" - -namespace OptiTrackPluginDevices -{ - namespace ExampleDevice - { - class ExampleGloveAdapterSingleton; - - static ExampleGloveAdapterSingleton* s_Instance = nullptr; - static std::unique_ptr gGloveAdapter; - - - /** - * This is an example glove adapter (singleton) class provided to demonstrate how glove data can be populated. - * This adapter class is reponsible for all interaction with the device SDK, and populating the glove data. - * Glove data for multiple glove devices should get aggregated in here. - */ - class ExampleGloveAdapterSingleton - { - friend class ExampleGloveDevice; - - public: - ExampleGloveAdapterSingleton(AnalogSystem::IDeviceManager* pDeviceManager); - ~ExampleGloveAdapterSingleton(); - - // Singleton design pattern - ExampleGloveAdapterSingleton(const ExampleGloveAdapterSingleton&) = delete; // Should not be cloneable - ExampleGloveAdapterSingleton& operator=(const ExampleGloveAdapterSingleton& other) = delete; // Shounot be assignable - - - /** - * Should call the shutdown from the plugin SDK. In this case, shutsdown the simulator - */ - bool ClientShutdown(); - - /** - * Detection thread for connecting to the glove server. - */ - void DoDetectionThread(); - std::thread mDetectionThread; - unsigned int mConnectionAttemptCount = 0; - const unsigned int kMaxConnectionAttempt = 10; - - - /** - * Connect to the glove host. - */ - bool ConnectToHost(); - - /** - * Connect to Rokoko Studio via UDP. - */ - bool ConnectToRokoko(); - - /** - * Disconnect from Rokoko Studio. - */ - void DisconnectFromRokoko(); - - private: - /** - * Returns whether detector is connected to Glove Host. - */ - bool IsConnected(); - - /** - * Saves the latest data to the map - */ - void SetLatestData(const sGloveDeviceData& gloveFingerData); - - /** - * Pointer to device manager for additional operations - */ - void SetDeviceManager(AnalogSystem::IDeviceManager* pDeviceManager); - - /** - * Saves the latest glove data to the map - */ - void SetLatestDeviceInfo(const sGloveDeviceBaseInfo& deviceInfo); - - - /** - * Gets the latest data for device with given unique serial - */ - bool GetLatestData(const std::uint64_t mDeviceSerial, sGloveDeviceData& gloveFingerData); - - /** - * Creates new device by instantiating the factory and transferring it to Motive - */ - void CreateNewGloveDevice(sGloveDeviceBaseInfo& deviceInfo); - - /** - * Prints error into Motive. - */ - void NotifyConnectionFail(); - - bool bIsConnected = false; - bool bIsDetecting = true; - int mCurrentDeviceIndex = 0; - std::string mServerAddress = ""; - - - /** - * [Glove SDK Placeholder] - * Example data map for storing aggregated device data. - */ - uint16_t mDeviceCount = 0; - std::vector mDetectedDevices; - std::unordered_map mLatestGloveData; - std::unordered_map mLatestDeviceInfo; - - // T-포즈 캘리브레이션 데이터 (로컬 로테이션 계산을 위해 필요) - std::unordered_map> mTPoseReferences; // 장치별 T-포즈 기준값 - std::unordered_map mTPoseCalibrated; // 장치별 캘리브레이션 상태 - - /** - * Rokoko integration members - */ - std::unique_ptr mRokokoReceiver; - bool bIsRokokoConnected = false; - RokokoData::LiveFrame_v4 mLastRokokoFrame; - - /** - * [Legacy Glove SDK Placeholder - Commented Out] - * The following methods were sample callback functions for simulated hardware. - * Now replaced by Rokoko UDP communication. - */ - // bool bIsSimulating; - void StartSimulatedHardware(int deviceCount); // Legacy support - static void RegisterSDKCallbacks(); // Legacy support - static void OnDataCallback(std::vector& gloveFingerData); // Legacy support - static void OnDeviceInfoCallback(std::vector& newGloveInfo); // Legacy support - static sGloveDeviceBaseInfo ConvertDeviceInfoFormat(SimulatedPluginDevices::SimulatedDeviceInfo& glove); // Legacy support - static sGloveDeviceData ConvertDataFormat(const SimulatedPluginDevices::SimulatedGloveFrameData& glove); // Legacy support - - /** - * Rokoko UDP data callback function. - */ - void OnRokokoDataReceived(const std::vector& data, const std::string& senderIP); - - /** - * Process received Rokoko data and convert to OptiTrack format. - */ - void ProcessRokokoData(const std::vector& data); - - /** - * Update device information (battery, signal strength, etc.) - */ - void UpdateDeviceInfo(uint64_t deviceId); - - // Dynamic Device Detection and Management - void DetectAndCreateRokokoDevices(); - void DetectActorsFromRokokoData(const RokokoData::LiveFrame_v4& rokokoFrame); - void ProcessMultipleDeviceData(const RokokoData::LiveFrame_v4& rokokoFrame); - - /** - * Process hand data for a specific device - */ - bool ProcessHandData(const RokokoData::Body& body, uint64_t deviceId, eGloveHandSide handSide); - - /** - * Map left hand joints from Rokoko data - */ - void MapLeftHandJoints(const RokokoData::Body& body, std::vector& nodes); - - /** - * Map right hand joints from Rokoko data - */ - void MapRightHandJoints(const RokokoData::Body& body, std::vector& nodes); - - /** - * Map individual joint data - */ - void MapJoint(const RokokoData::ActorJointFrame& rokokoJoint, sFingerNode& optiTrackNode); - - // 로컬 로테이션 변환 함수들 - void ConvertToLocalRotations(std::vector& nodes, eGloveHandSide handSide); - void ApplyLocalRotation(sFingerNode& childNode, const sFingerNode& parentNode); - - // 쿼터니언 연산 헬퍼 함수들 - void MultiplyQuaternions(const sFingerNode& q1, const sFingerNode& q2, sFingerNode& result); - void InverseQuaternion(const sFingerNode& q, sFingerNode& result); - void NormalizeQuaternion(sFingerNode& q); - - // T-포즈 캘리브레이션 시스템 - void CalibrateTPose(uint64_t deviceId, const std::vector& tPoseData); - void ApplyTPoseOffset(std::vector& nodes, uint64_t deviceId); - bool IsTPoseCalibrated(uint64_t deviceId) const; - void ResetTPoseCalibration(uint64_t deviceId); - - // 손목 기준 로컬 변환 시스템 (기존) - void ConvertToWristLocalRotations(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide); - bool GetWristRotation(const RokokoData::Body& body, eGloveHandSide handSide, sFingerNode& wristRotation); - - // 진짜 로컬 로테이션 변환 시스템 (계층적) - void ConvertToTrueLocalRotations(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide); - void CalculateFingerLocalRotations(const std::vector& fingerJoints, std::vector& outputNodes, int startIndex, const RokokoData::Body& body, eGloveHandSide handSide); - bool GetFingerJointRotations(const RokokoData::Body& body, eGloveHandSide handSide, std::vector>& allFingers); - - // 좌표계 변환 및 디버깅 - void ConvertRokokoToOptiTrackCoordinates(sFingerNode& node, eGloveHandSide handSide); - void LogRotationData(const std::string& label, const sFingerNode& rotation); - void DebugCoordinateSystem(const RokokoData::Body& body, eGloveHandSide handSide); - - // 절대 로컬 로테이션 시스템 (손목에 완전히 독립적) - void ConvertToAbsoluteLocalRotations(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide); - void CalculateAbsoluteLocalForFinger(const std::vector& fingerJoints, std::vector& outputNodes, int startIndex, eGloveHandSide handSide); - sFingerNode CalculateRelativeRotation(const sFingerNode& parent, const sFingerNode& child); - - // Unity 방식 Rokoko 데이터 처리 (원시 데이터 + T-포즈 오프셋) - void ConvertToUnityRokokoMethod(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide); - sFingerNode GetUnityTPoseOffset(int fingerIndex, int jointIndex, eGloveHandSide handSide); - void ApplyUnityRokokoRotation(sFingerNode& node, const RokokoData::ActorJointFrame* jointFrame, int fingerIndex, int jointIndex, eGloveHandSide handSide); - - // 가상 Unity 아바타 시뮬레이션 시스템 - void ConvertToVirtualUnityAvatar(std::vector& nodes, const RokokoData::Body& body, eGloveHandSide handSide); - - // Unity Transform 시뮬레이션 구조 - struct VirtualTransform { - sFingerNode localRotation; // Unity localRotation - sFingerNode worldRotation; // Unity rotation (world) - VirtualTransform* parent; // Unity parent transform - std::vector children; // Unity children - std::string name; // 디버깅용 - }; - - struct VirtualUnityAvatar { - // 전체 스켈레톤 구조 (Unity Humanoid와 동일한 계층) - VirtualTransform root; - - // 몸통 구조 - VirtualTransform* hips; // 허리 (Root) - VirtualTransform* spine; // 스파인 - VirtualTransform* chest; // 가슴 - - // 어깨 구조 - VirtualTransform* leftShoulder; // 왼쪽 어깨 - VirtualTransform* rightShoulder; // 오른쪽 어깨 - - // 팔 구조 - VirtualTransform* leftUpperArm; // 왼쪽 상완 - VirtualTransform* leftLowerArm; // 왼쪽 하완 - VirtualTransform* leftHand; // 왼쪽 손 - - VirtualTransform* rightUpperArm; // 오른쪽 상완 - VirtualTransform* rightLowerArm; // 오른쪽 하완 - VirtualTransform* rightHand; // 오른쪽 손 - - // 손가락 구조 (Unity 계층과 동일) - VirtualTransform* leftThumb[3]; // Proximal, Intermediate, Distal - VirtualTransform* leftIndex[3]; - VirtualTransform* leftMiddle[3]; - VirtualTransform* leftRing[3]; - VirtualTransform* leftLittle[3]; - - VirtualTransform* rightThumb[3]; - VirtualTransform* rightIndex[3]; - VirtualTransform* rightMiddle[3]; - VirtualTransform* rightRing[3]; - VirtualTransform* rightLittle[3]; - }; - - // 가상 Unity 아바타 헬퍼 함수들 - void InitializeVirtualUnityAvatar(VirtualUnityAvatar& avatar); - void ApplyRokokoDataToVirtualAvatar(VirtualUnityAvatar& avatar, const RokokoData::Body& body); - void CalculateLocalRotationsFromWorldRotations(VirtualUnityAvatar& avatar); - void ExtractLocalRotationsFromVirtualAvatar(const VirtualUnityAvatar& avatar, std::vector& nodes, eGloveHandSide handSide); - void UpdateWorldRotationFromParent(VirtualTransform* transform); - - // 가상 손 노드 구조 - struct VirtualJoint { - sFingerNode worldRotation; - sFingerNode localRotation; - VirtualJoint* parent; - }; - - struct VirtualFinger { - VirtualJoint mp; // Metacarpophalangeal - VirtualJoint pip; // Proximal Interphalangeal - VirtualJoint dip; // Distal Interphalangeal - }; - - struct VirtualHand { - sFingerNode wristRotation; - VirtualFinger thumb; - VirtualFinger index; - VirtualFinger middle; - VirtualFinger ring; - VirtualFinger little; - }; - - // 가상 손 구조 헬퍼 함수들 - void BuildVirtualHandFromRokoko(VirtualHand& virtualHand, const RokokoData::Body& body, eGloveHandSide handSide); - void CalculateHierarchicalLocalRotations(VirtualHand& virtualHand); - void ExtractLocalRotationsToNodes(const VirtualHand& virtualHand, std::vector& nodes); - sFingerNode CalculateLocalRotationFromParent(const VirtualJoint& parent, const VirtualJoint& child); - - protected: - - /** - * [Legacy Glove SDK Simulator - Commented Out] - * Instance of simulator (now replaced by Rokoko integration) - */ - // SimulatedPluginDevices::HardwareSimulator* mGloveSimulator; - - /** - * [Glove SDK Placeholder] - * Example glove data mutex - */ - std::recursive_mutex* mGloveDataMutex; - - /** - * Pointer to device manager in Motive for reporting error messages. - */ - AnalogSystem::IDeviceManager* mDeviceManager; - }; - } - -} \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveData.csv b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveData.csv deleted file mode 100644 index 21e484c7..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveData.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:816498d40aca6a7060d48313bd1946b20553894715095670b14192d4639bdce5 -size 549783 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveDevice.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveDevice.cpp deleted file mode 100644 index dbb24ec1..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveDevice.cpp +++ /dev/null @@ -1,311 +0,0 @@ -//====================================================================================================== -// Copyright 2022, NaturalPoint Inc. -//====================================================================================================== - -#pragma once -#include "ExampleGloveDevice.h" -#include "ExampleGloveAdapterSingleton.h" - -// OptiTrack Peripheral Device API -#include "AnalogChannelDescriptor.h" -#include "IDeviceManager.h" -using namespace AnalogSystem; -using namespace OptiTrackPluginDevices; -using namespace GloveDeviceProperties; -using namespace ExampleDevice; - - -/////////////////////////////////////////////////////////////////////////////// -// -// Device Helper: Initialization and Shutdown -// -void OptiTrackPluginDevices::ExampleDevice::ExampleGlove_EnumerateDeviceFactories(IDeviceManager* pDeviceManager, std::list>& dfs) -{ - // Start server detection - if (gGloveAdapter == nullptr) { - gGloveAdapter = std::make_unique(pDeviceManager); - } -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGlove_Shutdown() -{ - if (gGloveAdapter != nullptr) - { - gGloveAdapter->ClientShutdown(); - gGloveAdapter.reset(); - } -} - - -/////////////////////////////////////////////////////////////////////////////// -// -// Example Glove Device Factory -// -const char* OptiTrackPluginDevices::ExampleDevice::ExampleGloveDeviceFactory::Name() const -{ - return "ExampleGloveDevice"; -} - -std::unique_ptr OptiTrackPluginDevices::ExampleDevice::ExampleGloveDeviceFactory::Create() const -{ - ExampleGloveDevice* pDevice = new ExampleGloveDevice(mDeviceSerial, mDeviceInfo); - SetCommonGloveDeviceProperties(pDevice); - SetQuaternionDataChannels(pDevice); - - // Transfer ownership to host - std::unique_ptr ptrDevice(pDevice); - return ptrDevice; -} - - -/////////////////////////////////////////////////////////////////////////////// -// -// Example Glove Device -// -OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::ExampleGloveDevice(uint32_t serial, sGloveDeviceBaseInfo deviceInfo) -{ - mDeviceInfo = deviceInfo; - mDeviceSerial = deviceInfo.gloveId; - bIsEnabled = true; -} - - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::Configure() -{ - bool success = Deconfigure(); - if (!success) - return false; - - // update device's buffer allocation based on current channel and data type configuration - success = cPluginDevice::Configure(); - if (!success) - return false; - - bIsConfigured = success; - DeviceManager()->MessageToHost(this, "", MessageType_RequestRestart); - return success; -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::Deconfigure() -{ - bool success = cPluginDevice::Deconfigure(); - - return success; -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::StartCapture() -{ - bool success = cPluginDevice::StartCapture(); - - bIsCollecting = true; - mCollectionThread = CreateThread(nullptr, 0, CollectionThread, this, 0, nullptr); - if (mCollectionThread == nullptr) - { - return false; - bIsCollecting = false; - } - - return success; -} - -bool OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::StopCapture() -{ - bool success = cPluginDevice::StopCapture(); - - // REQUIRED: Stop collecting data. Terminate hardware device polling thread - bIsCollecting = false; - DWORD waitResult = WaitForSingleObject(mCollectionThread, 1000); - if (waitResult == WAIT_OBJECT_0) - { - CloseHandle(mCollectionThread); - mCollectionThread = NULL; - success = true; - } - else if (waitResult == WAIT_TIMEOUT) - { - BOOL result = TerminateThread(mCollectionThread, 0); - success = (result == TRUE); - } - else - { - success = false; - } - return success; -} - -void OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::OnPropertyChanged(const char* propertyName) -{ - cPluginDevice::OnPropertyChanged(propertyName); - - if (strcmp(propertyName, kEnabledPropName) == 0) - { - // Update device enabled state - GetProperty(kEnabledPropName, bIsEnabled); - } - else if (strcmp(propertyName, kRatePropName) == 0) - { - // Update device capture rate - GetProperty(kRatePropName, mDeviceRateFPS); - mRequestedRateMS = (1.0f / mDeviceRateFPS) * 1000.0f; - } - else if (strcmp(propertyName, kDataStatePropName) == 0) - { - int deviceState = 0; - GetProperty(kDataStatePropName, deviceState); - if (deviceState != DeviceDataState::DeviceState_ReceivingData) - { - // if not receiving data, disable battery state and signal strength - mBatteryLevel = GloveDeviceProp_BatteryUninitialized; - SetProperty(GloveDeviceProp_Battery, mBatteryLevel); - mSignalStrength = GloveDeviceProp_SignalStrengthUnitialized; - SetProperty(GloveDeviceProp_SignalStrength, mSignalStrength); - } - } - else if (strcmp(propertyName, GloveDeviceProp_Solver) == 0) - { - // Route solver type to device user data for interpreting in pipeline - int solver = 0; - GetProperty(GloveDeviceProp_Solver, solver); - SetProperty(cPluginDeviceBase::kUserDataPropName, solver); - } -} - -unsigned long __stdcall OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::CollectionThread(LPVOID Context) -{ - ExampleGloveDevice* pThis = static_cast(Context); - return pThis->DoCollectionThread(); -} - -unsigned long OptiTrackPluginDevices::ExampleDevice::ExampleGloveDevice::DoCollectionThread() -{ - /* Collection Thread - - Motive's 15 channel glove data channel format for finger node rotations: - 1 = Thumb MP 1 (w,x,y,z) - 2 = Thumb PIP 2 - 3 = Thumb DIP 3 - 4 = Index MP 1 - 5 = Index PIP 2 - 6 = Index DIP 3 - 7 = Middle MP 1 - 8 = Middle PIP 2 - 9 = Middle DIP 3 - 10 = Ring MP 1 - 11 = Ring PIP 2 - 12 = Ring DIP 3 - 13 = Pinky MP 1 - 14 = Pinky PIP 2 - 15 = Pinky DIP 3 - - Hand joint orientation respects right-handed coordinate system. - For left hand, +X is pointed towards the finger tip. - For right hand, +X is pointer towards the wrist or the body. - */ - - // timers used for frame and ui updates - std::chrono::high_resolution_clock::time_point frameTimerStart, frameTimerEnd; - std::chrono::high_resolution_clock::time_point uiTimerStart, uiTimerEnd; - std::chrono::milliseconds actualFrameDurationMS; - double adjustment = 0.0; - double durationDeltaMS = 0.0; - - - // Glove device parameters (rate / battery / signal) - GetProperty(kEnabledPropName, bIsEnabled); - GetProperty(GloveDeviceProp_Battery, mBatteryLevel); - GetProperty(GloveDeviceProp_SignalStrength, mSignalStrength); - GetProperty(kRatePropName, mDeviceRateFPS); - mRequestedRateMS = (1.0f / mDeviceRateFPS) * 1000.0f; - - // Initialize glove handedness - InitializeGloveProperty(); - - //glove channel data arrray to be copied - float gloveChannelData[kGloveAnalogChannelCount]; - - // Collection thread - uiTimerStart = std::chrono::high_resolution_clock::now(); - while (bIsCollecting) - { - frameTimerStart = std::chrono::high_resolution_clock::now(); - int deviceFrameID = this->FrameCounter(); - - // Skip disabled devices - bool isDeviceEnabled = false; - GetProperty(kEnabledPropName, isDeviceEnabled); - if (!isDeviceEnabled) continue; - - // Poll latest glove data from the adapter - sGloveDeviceData t_data; - bool isGloveDataAvailable = gGloveAdapter->GetLatestData(mDeviceInfo.gloveId, t_data); - - if (isGloveDataAvailable) - { - if (!bIsInitialized) { - InitializeGloveProperty(); - } - - // Update ui every 5 secs (too frequent can cause unnecessary slowdowns) - uiTimerEnd = std::chrono::high_resolution_clock::now(); - auto elapsedTime = std::chrono::duration_cast(uiTimerEnd - uiTimerStart); - if (elapsedTime.count() > 5) - { - UpdateGloveProperty(mDeviceInfo); - uiTimerStart = std::chrono::high_resolution_clock::now(); - } - - // Frame Begin: get frame from device buffer - AnalogFrame* pFrame = this->BeginFrameUpdate(deviceFrameID); - if (pFrame) - { - // fill in frame header - int flags = 0; - - // Iterate through each bone and populate channel data. - // Skip the first bone, hand base, as it's driven from rigid body transform instead. - for (int i = 0; i < 15; i++) - { - int nodeChannelIndex = i * 4; - gloveChannelData[nodeChannelIndex] = t_data.nodes.at(i).quat_w; //w - gloveChannelData[nodeChannelIndex + 1] = t_data.nodes.at(i).quat_x; //x - gloveChannelData[nodeChannelIndex + 2] = t_data.nodes.at(i).quat_y; //y - gloveChannelData[nodeChannelIndex + 3] = t_data.nodes.at(i).quat_z; //z - } - - pFrame->SetID(deviceFrameID); - pFrame->SetFlag(flags); - //pFrame->SetTimestamp((double)mLastGloveDataTimebstamp.time); - ::memcpy(pFrame->ChannelData(), &gloveChannelData[0], kGloveAnalogChannelCount * sizeof(float)); - EndFrameUpdate(); - this->SetFrameCounter(deviceFrameID + 1); - } - - - // End frame update. Sleep the thread for the remaining frame period. - std::this_thread::sleep_for(std::chrono::milliseconds((int)(mRequestedRateMS - adjustment))); - frameTimerEnd = std::chrono::high_resolution_clock::now(); - actualFrameDurationMS = std::chrono::duration_cast(frameTimerEnd - frameTimerStart); - durationDeltaMS = actualFrameDurationMS.count() - mRequestedRateMS; - - // estimating adjustment to prevent oscillation on sleep duration. - if (durationDeltaMS > 1.0) - adjustment = -1.0; - else if (durationDeltaMS > 2.0) - adjustment = -2.0; - else if (durationDeltaMS > 3.0) - adjustment = -3.0; - else if (durationDeltaMS < -3.0) - adjustment = -3.0; - else if (durationDeltaMS < -2.0) - adjustment = 2.0; - else if (durationDeltaMS < -1.0) - adjustment = 1.0; - else - adjustment = 0.0; - if (fabs(adjustment) > 1.0) - { - this->LogError(MessageType_StatusInfo, "[Example Device] Device timing resolution off by %3.1f ms (requested:%3.1f, actual:%3.1f)", durationDeltaMS, mRequestedRateMS, (double) actualFrameDurationMS.count()); - } - } - } - return 0; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveDevice.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveDevice.h deleted file mode 100644 index a7a6d3a7..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/ExampleGloveDevice.h +++ /dev/null @@ -1,86 +0,0 @@ -//====================================================================================================== -// Copyright 2023, NaturalPoint Inc. -//====================================================================================================== -/** - * This is an example glove device provided to demonstrate how a plugin device can be set up as a glove device - * in Motive. This class derives from GloveDeviceBase class which contains basic setups required by gloves. - */ - -#pragma once -#include -#include - -// OptiTrack Peripheral Device API -#include "dllcommon.h" -#include "GloveDeviceBase.h" -#include "GloveDataFormat.h" -#include "ExampleGloveAdapterSingleton.h" - -namespace OptiTrackPluginDevices -{ - namespace ExampleDevice{ - - class ExampleGloveDevice; - class ExampleGloveDeviceFactory; - - void ExampleGlove_EnumerateDeviceFactories(AnalogSystem::IDeviceManager* pDeviceManager, std::list>& dfs); - void ExampleGlove_Shutdown(); - - - /** - * For creating a device in Motive, a factory class must be created first. This example class inherits from the - * parent glove device factory where common glove functionalities and configurations are set. - */ - class ExampleGloveDeviceFactory : public OptiTrackPluginDevices::GloveDeviceFactoryBase - { - public: - ExampleGloveDeviceFactory(std::string deviceName, sGloveDeviceBaseInfo deviceInfo) : - GloveDeviceFactoryBase(deviceName.c_str(), deviceInfo.gloveId), mDeviceInfo(deviceInfo) - { - } - - /** - * Return device factory name - */ - virtual const char* Name() const override; - - /** - * The following method gets called by Motive. It creates and returns a new instance of device and transfers ownership to Motive. - */ - std::unique_ptr Create() const override; - - private: - sGloveDeviceBaseInfo mDeviceInfo; - }; - - - /** - * ExampleGloveDevice inherits from GloveDeviceBase and demonstrates how a glove plugin device can be created. - * Each device gets their own collection thread where they populate the channel data. This class is inherited from the parent - * GloveDeviceBase class where the basic glove setup is shown. - */ - class ExampleGloveDevice : public OptiTrackPluginDevices::GloveDeviceBase - { - public: - ExampleGloveDevice(uint32_t serial, sGloveDeviceBaseInfo deviceInfo); - virtual ~ExampleGloveDevice() = default; - - // IDevice implementation - virtual bool Configure(); - virtual bool Deconfigure(); - virtual bool StartCapture(); - virtual bool StopCapture(); - virtual void OnPropertyChanged(const char* propertyName); - - private: - - - /** - * Collection thread for populating glove analog data channels - */ - static unsigned long __stdcall CollectionThread(LPVOID Context); - unsigned long DoCollectionThread() override; - void* mCollectionThread = nullptr; - }; - } -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/FIXES_APPLIED.md b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/FIXES_APPLIED.md deleted file mode 100644 index bf170c0a..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/FIXES_APPLIED.md +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8503243908fbe3016fa8d6fc07ef2ad791733e4a24473df6e44ed209a850e042 -size 6364 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDataFormat.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDataFormat.h deleted file mode 100644 index 193eb2a6..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDataFormat.h +++ /dev/null @@ -1,84 +0,0 @@ -//====================================================================================================== -// Copyright 2023, NaturalPoint Inc. -//====================================================================================================== -/** - * This includes common glove data formats referenced in Motive. - */ - - -#pragma once -#include -#include -#include -#include - -enum class eGloveHandSide : int -{ - Unknown = 0, - Left = 1, - Right = 2 -}; - - -struct sGloveDataTimestamp -{ - uint8_t hour = 0; - uint8_t minute = 0; - uint32_t seconds = 0; - uint32_t nanoseconds = 0; - double t = 0; - sGloveDataTimestamp(uint32_t s, uint32_t n) : seconds(s), nanoseconds(n) { t = double(seconds) + double(nanoseconds) * 0.000000001; } - sGloveDataTimestamp(double time) { t = time; } - sGloveDataTimestamp() : t(0.0) {} - - bool operator >(const sGloveDataTimestamp& x) - { - return this->t > x.t; - } - bool operator <(const sGloveDataTimestamp& x) - { - return this->t < x.t; - } - bool operator ==(const sGloveDataTimestamp& x) - { - return this->t == x.t; - } - - sGloveDataTimestamp operator -(const sGloveDataTimestamp& x) - { - return sGloveDataTimestamp(this->t - x.t); - } - - operator const double() { - return t; - } -}; - -struct sFingerNode { - int node_id; - float quat_x; - float quat_y; - float quat_z; - float quat_w; -}; - -struct sGloveDeviceData { - uint64_t gloveId = 0; - std::vector nodes; - sGloveDataTimestamp timestamp; -}; - -struct sGloveDeviceBaseInfo -{ - uint32_t gloveId = 0; // device serial id - int battery = 0 ; - int signalStrength= 0; - eGloveHandSide handSide = eGloveHandSide::Unknown; - std::string actorName = ""; // Actor 이름 (다중 장비 구분용) -}; - - - - - - diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDeviceBase.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDeviceBase.cpp deleted file mode 100644 index 9700f47f..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDeviceBase.cpp +++ /dev/null @@ -1,258 +0,0 @@ -//====================================================================================================== -// Copyright 2022, NaturalPoint Inc. -//====================================================================================================== -#include "dllcommon.h" -#include "GloveDeviceBase.h" - -// Optitrack Peripheral Device API -#include "AnalogChannelDescriptor.h" -#include "IDeviceManager.h" -#include "GloveDataFormat.h" -using namespace AnalogSystem; -using namespace OptiTrackPluginDevices; -using namespace GloveDeviceProperties; - -OptiTrackPluginDevices::GloveDeviceBase::GloveDeviceBase() -{ - this->SetCommonDeviceProperties(); - this->SetCommonGloveDeviceProperties(); -} - -void OptiTrackPluginDevices::GloveDeviceBase::SetCommonDeviceProperties() -{ - // Set appropriate default property values (and set advanced state) - ModifyProperty(cPluginDeviceBase::kModelPropName, true, true, false); - ModifyProperty(cPluginDeviceBase::kOrderPropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kDisplayNamePropName, false, false, false); - - // Reveal glove related prop name - ModifyProperty(cPluginDeviceBase::kAssetPropName, false, false, false); // name of the paired skeleton asset - - // hide default properties that aren't relevant to glove device - ModifyProperty(cPluginDeviceBase::kSyncModePropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kSyncStatusPropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kUseExternalClockPropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kMocapRateMultiplePropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kScalePropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kCalOffsetPropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kCalSquareRotationPropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kUserDataPropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kZeroPropName, true, true, true); - ModifyProperty(cPluginDeviceBase::kGroupNamePropName, true, true, true); - - // hide advanced properties - ModifyProperty(cPluginDeviceBase::kConnectedPropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kNamePropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kChannelCountPropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kAppRunModePropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kMocapSyncFramePropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kSyncFramePropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kNeedDeviceSyncFramePropName, false, false, true); - ModifyProperty(cPluginDeviceBase::kNeedMocapSyncFramePropName, false, false, true); - ModifyProperty(cPluginDeviceBase::kUseDriftCorrectionPropName, false, false, true); - ModifyProperty(cPluginDeviceBase::kMasterSerialPropName, true, false, true); - ModifyProperty(cPluginDeviceBase::kDriftCorrectionPropName, true, false , true); - SetProperty(cPluginDeviceBase::kEnabledPropName, true); - SetProperty(cPluginDeviceBase::kConnectedPropName, true); - SetProperty(cPluginDeviceBase::kMocapRateMultiplePropName, 1); -} - -void OptiTrackPluginDevices::GloveDeviceBase::SetCommonGloveDeviceProperties() -{ - // Add glove related properties - AddProperty(GloveDeviceProperties::GloveDeviceProp_HandSide, GloveHandSide, kHandSideCount, 0, "Settings", false); - AddProperty(GloveDeviceProperties::GloveDeviceProp_Battery, GloveDeviceProp_BatteryUninitialized, "Settings", false); - AddProperty(GloveDeviceProperties::GloveDeviceProp_SignalStrength, GloveDeviceProp_SignalStrengthUnitialized, "Settings", false); - //AddProperty(GloveDeviceProperties::GloveDeviceProp_Reconnect, false, "Settings", false); - - // Modify property visibility - ModifyProperty(GloveDeviceProperties::GloveDeviceProp_SignalStrength, true, false, false, false); - ModifyProperty(GloveDeviceProperties::GloveDeviceProp_HandSide, true, false, false); - ModifyProperty(GloveDeviceProperties::GloveDeviceProp_Battery, true, false, false, false); -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Setter: Glove Device -// -void OptiTrackPluginDevices::GloveDeviceBase::SetDeviceRate(int rate) -{ - mDeviceRateFPS = rate; - mRequestedRateMS = (1.0f / mDeviceRateFPS) * 1000.0f; - return; -} - -void OptiTrackPluginDevices::GloveDeviceBase::SetHandSide(eGloveHandSide side) -{ - mHandSide = side; - return; -} - -void OptiTrackPluginDevices::GloveDeviceBase::SetEnabled(bool enabled) -{ - bIsEnabled = enabled; -} - -void OptiTrackPluginDevices::GloveDeviceBase::SetCollecting(bool collecting) -{ - bIsCollecting = collecting; -} - -bool OptiTrackPluginDevices::GloveDeviceBase::SetDeviceSerial(std::string serial) -{ - mDeviceSerial = serial; - // Set device serial property - return false; -} - -bool OptiTrackPluginDevices::GloveDeviceBase::SetDeviceData(sGloveDeviceData data) -{ - mLastGloveData = data; - return true; -} - -bool OptiTrackPluginDevices::GloveDeviceBase::SetBatteryLevel(int level) -{ - if (SetProperty(GloveDeviceProperties::GloveDeviceProp_Battery, (int)level)) - { - mBatteryLevel = level; - return true; - } - return false; -} - -bool OptiTrackPluginDevices::GloveDeviceBase::SetSignalStrength(int signal) -{ - if (SetProperty(GloveDeviceProperties::GloveDeviceProp_SignalStrength, (int)signal)) - { - mSignalStrength = signal; - return true; - } - return false; -} - -void OptiTrackPluginDevices::GloveDeviceBase::InitializeGloveProperty() -{ - if (mDeviceInfo.handSide == eGloveHandSide::Left) - { - SetProperty(GloveDeviceProperties::GloveDeviceProp_HandSide, 1); - SetProperty(cPluginDeviceBase::kOrderPropName, 1); - bIsInitialized = true; - } - else if (mDeviceInfo.handSide == eGloveHandSide::Right) - { - SetProperty(GloveDeviceProperties::GloveDeviceProp_HandSide, 2); - SetProperty(cPluginDeviceBase::kOrderPropName, 2); - bIsInitialized = true; - } - return; -} - - -void OptiTrackPluginDevices::GloveDeviceBase::UpdateGloveProperty(const sGloveDeviceBaseInfo& deviceInfo) -{ - // update glove property when the device is receiving data. - int deviceState = 0; - GetProperty(kDataStatePropName, deviceState); - if (deviceState == DeviceDataState::DeviceState_ReceivingData) - { - if (mSignalStrength != 1) - { - mSignalStrength = deviceInfo.signalStrength; - double signal = fabs(mSignalStrength); - SetProperty(GloveDeviceProp_SignalStrength, (int)signal); - } - SetProperty(GloveDeviceProp_SignalStrength, -100); - - if (mBatteryLevel != deviceInfo.battery) - { - mBatteryLevel = (int)(deviceInfo.battery); //Battery level percentage. - SetProperty(GloveDeviceProp_Battery, (int)mBatteryLevel); - } - } -} - - -/////////////////////////////////////////////////////////////////////////////// -// -// Glove Device Factory -// -void OptiTrackPluginDevices::GloveDeviceFactoryBase::SetCommonGloveDeviceProperties(GloveDeviceBase* pDevice) const -{ - // REQUIRED: Set device name/model/serial - pDevice->SetProperty(cPluginDeviceBase::kNamePropName, (char*)DeviceName()); - pDevice->SetProperty(cPluginDeviceBase::kDisplayNamePropName, (char*)DeviceName()); - pDevice->SetProperty(cPluginDeviceBase::kModelPropName, "Glove Model"); // model - char mDeviceSerial[MAX_PATH]; - sprintf_s(mDeviceSerial, "%s-serial", DeviceName()); - pDevice->SetProperty(cPluginDeviceBase::kSerialPropName, mDeviceSerial); // device serial (must be unique!) - pDevice->SetProperty(cPluginDeviceBase::kDeviceTypePropName, (long)DeviceType_Glove); // set device type as glove - pDevice->SetProperty(cPluginDeviceBase::kRatePropName, 120.0); // glove sampling rate - pDevice->SetProperty(cPluginDeviceBase::kUseDriftCorrectionPropName, true); // drift correction to fetch most recent data. - pDevice->SetProperty(cPluginDeviceBase::kOrderPropName, (int) eGloveHandSide::Unknown); // device order: (0 = uninitialized, 1=left, 2=right) -} - - -void OptiTrackPluginDevices::GloveDeviceFactoryBase::SetDeviceIndex(int t_index) -{ - mDeviceIndex = t_index; - return; -} - -void OptiTrackPluginDevices::GloveDeviceFactoryBase::SetQuaternionDataChannels(GloveDeviceBase* pDevice) const -{ - // Set glove data channels: - // 5 fingers * 3 joints per finger * 4 floats per quat (x/y/z/w) = 60 channels - int channelIndex; - int gloveAnalogChannelCount = 60; - char szChannelNames[MAX_PATH]; - - // add channels - for (int i = 0; i < gloveAnalogChannelCount; i++) - { - int finger = i / 12; // 12 channels per finger - switch (finger) - { - case 0: sprintf_s(szChannelNames, "T"); break; - case 1: sprintf_s(szChannelNames, "I"); break; - case 2: sprintf_s(szChannelNames, "M"); break; - case 3: sprintf_s(szChannelNames, "R"); break; - case 4: sprintf_s(szChannelNames, "P"); break; - } - - int joint = (i / 4) % 3; // 3 joints per finger - switch (joint) - { - case 0: sprintf_s(szChannelNames, "%s-MCP", szChannelNames); break; - case 1: sprintf_s(szChannelNames, "%s-PIP", szChannelNames); break; - case 2: sprintf_s(szChannelNames, "%s-DIP", szChannelNames); break; - } - - int axis = i % 4; // 4 floats per joint - switch (axis) - { - case 0: sprintf_s(szChannelNames, "%s W", szChannelNames); break; - case 1: sprintf_s(szChannelNames, "%s X", szChannelNames); break; - case 2: sprintf_s(szChannelNames, "%s Y", szChannelNames); break; - case 3: sprintf_s(szChannelNames, "%s Z", szChannelNames); break; - } - channelIndex = pDevice->AddChannelDescriptor(szChannelNames, ChannelType_Float); - - } - - // enable all channels by default - for (int i = 0; i <= channelIndex; i++) - { - // data channel enabled by default - pDevice->ChannelDescriptor(i)->SetProperty(ChannelProp_Enabled, true); - - // hide unused channel properties - pDevice->ChannelDescriptor(i)->ModifyProperty(ChannelProp_Units, true, true); - pDevice->ChannelDescriptor(i)->ModifyProperty(ChannelProp_Name, true, true); - pDevice->ChannelDescriptor(i)->ModifyProperty(ChannelProp_MinVoltage, true, true); - pDevice->ChannelDescriptor(i)->ModifyProperty(ChannelProp_MaxVoltage, true, true); - pDevice->ChannelDescriptor(i)->ModifyProperty(ChannelProp_TerminalName, true, true); - pDevice->ChannelDescriptor(i)->ModifyProperty(ChannelProp_TerminalType, true, true); - pDevice->ChannelDescriptor(i)->ModifyProperty(ChannelProp_MaxVoltage, true, true); - } -} \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDeviceBase.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDeviceBase.h deleted file mode 100644 index cac502cc..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/GloveDeviceBase.h +++ /dev/null @@ -1,180 +0,0 @@ -//====================================================================================================== -// Copyright 2023, NaturalPoint Inc. -//====================================================================================================== -/** - * GloveDeviceBase/GloveDeviceFactory class extends the cPluginDeviceBase and configures data channels and device properties required - * by a glove device in Motive. The purpose of this class is to abstract out setups needed for creating glove device as demonstrated - * in the ExampleGloveDevice. When developing a glove plugin to animate fingers in Motive, the following class can be inherited if needed. - */ - -#include -#include -#include -#include - -// OptiTrack Peripheral Device API -#include "PluginDevice.h" -#include "PluginDeviceFactory.h" -#include "GloveDataFormat.h" - -namespace OptiTrackPluginDevices -{ - class GloveDeviceBase; - class GloveDeviceFactoryBase; - - /** - * Common glove device properties used in Motive. - */ - namespace GloveDeviceProperties - { - static const int kHandSideCount = 3; - static const char* GloveHandSide[kHandSideCount] = - { - "Uninitialized", - "Left", - "Right" - }; - // Glove type needs to be set at the device level (0 = uninitialized, 1 = left, 2 = right) - static const char* GloveDeviceProp_HandSide = "Hand Side"; - static const char* GloveDeviceProp_Battery = "Battery"; - static const char* GloveDeviceProp_SignalStrength = "Signal Strength"; - static const char* GloveDeviceProp_ServerAddress = "Server Address"; - static const char* GloveDeviceProp_Reconnect = "Reconnect"; - static const char* GloveDeviceProp_Solver = "Glove Solver"; - static const int GloveDeviceProp_BatteryUninitialized = -1; - static const int GloveDeviceProp_SignalStrengthUnitialized = -1; - static const int kGloveAnalogChannelCount = 60; - } - - /** - * GloveDeviceFactory class demonstrates how plugin device factory can be set up for creating a glove device in Motive. - * To create a device in Motive, an instance of PLuginDeviceFactory must be created per each device, and then, its ownership must be - * transferred to Motive by using Create method. The GloveDeviceFactory inherits from PLuginDeviceFactory and demonstrates common - * glove properties and data channels can be configured; which is demonstrated in this base class. - */ - class GloveDeviceFactoryBase : public AnalogSystem::PluginDeviceFactory - { - public: - GloveDeviceFactoryBase(std::string deviceName, uint32_t serial) : - AnalogSystem::PluginDeviceFactory(deviceName.c_str()), mDeviceSerial(serial) {} - - uint32_t mDeviceSerial = -1; - int mDeviceIndex = 0; - - protected: - void SetDeviceIndex(int t_index); - - /** - * Sets up quaternion data channels for delivering local rotation of the finger nodes. - * Motive skeleton's hand consists of total 15 finger nodes, 3 per each finger. - * These data channels will get populated by the collection thread running on each device. - * Quaternion values are expected, resulting in total 60 float channels (15 finger nodes * 4 quat floats / node). - * Local rotation data is expected, and both hand data expects right-handed coordinate system with +x axis - * pointing towards the finger tip for left hand and towards the wrist/body for right hand. - */ - void SetQuaternionDataChannels(GloveDeviceBase* pDevice) const; - - /** - * Set Common Glove device properties - */ - void SetCommonGloveDeviceProperties(GloveDeviceBase* pDevice) const; - }; - - /** - * cGloveDeviceBase class is an example class which glove devices could inherit from. - * This class includes basic setups and configurations for glove devices in Motive. - */ - class GloveDeviceBase : public AnalogSystem::cPluginDevice - { - friend class GloveDeviceFactoryBase; - - public: - GloveDeviceBase(); - ~GloveDeviceBase() = default; - - void SetCommonDeviceProperties(); - void SetCommonGloveDeviceProperties(); - - protected: - // Device status - bool bIsEnabled = false; - bool bIsCollecting = false; - bool bIsInitialized = false; - bool bIsConfigured = false; - - // Device info - sGloveDeviceBaseInfo mGloveInfo; - std::string mDeviceSerial = ""; - eGloveHandSide mHandSide = eGloveHandSide::Unknown; - int mBatteryLevel = 0; - int mSignalStrength = 0; - double mDeviceRateFPS = 0; - double mRequestedRateMS = 0.0; - - // Glove data - sGloveDeviceData mLastGloveData; - - // Collection thread must be created on the device class. Defined in ExampleGloveDevice class. - virtual unsigned long DoCollectionThread() = 0; - - /** - * Configure device rate - */ - void SetDeviceRate(int rate); - - /** - * Configure hand side - */ - void SetHandSide(eGloveHandSide side); - - /** - * Enable or disable device - */ - void SetEnabled(bool enabled); - - /** - * Set collection status - */ - void SetCollecting(bool collecting); - - /** - * Set device serial string each device must have unique serial - */ - bool SetDeviceSerial(std::string serial); - - /** - * Set device data - */ - bool SetDeviceData(sGloveDeviceData data); - - /** - * Set device battery level (1-100) - */ - bool SetBatteryLevel(int level); - - /** - * Sets the signal stregth (1-128) - */ - bool SetSignalStrength(int signal); - - - bool IsEnabled() { return bIsEnabled; }; - bool IsCollecting() { return bIsCollecting; }; - int GetSignalStrength() { return mSignalStrength; } - int GetBatteryLevel() { return mBatteryLevel; } - double GetDeviceRate() { return mDeviceRateFPS; } - sGloveDeviceData GetLastestData() { return mLastGloveData; }; - - /** - * Initialize the properties for the glove device - */ - void InitializeGloveProperty(); - - /** - * Updates the device properties. Gets called periodically within collection thread. - * Mainly updates the battery level and signal strength. - */ - void UpdateGloveProperty(const sGloveDeviceBaseInfo& deviceInfo); - sGloveDeviceBaseInfo mDeviceInfo; - }; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/HardwareSimulator.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/HardwareSimulator.cpp deleted file mode 100644 index d6add29c..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/HardwareSimulator.cpp +++ /dev/null @@ -1,185 +0,0 @@ -//====================================================================================================== -// Copyright 2023, NaturalPoint Inc. -//====================================================================================================== -#include "HardwareSimulator.h" -#include -#include -#include -#include - -using namespace std; - -SimulatedPluginDevices::HardwareSimulator::HardwareSimulator() -{} - -SimulatedPluginDevices::HardwareSimulator::~HardwareSimulator() -{ - bIsRunning = false; - if (mUpdateThread.joinable()) { - mUpdateThread.join(); - } -} - -void SimulatedPluginDevices::HardwareSimulator::StartData() -{ - bIsRunning = true; - mUpdateThread = std::thread(&HardwareSimulator::ReadDataFromCSV, this); -} - -void SimulatedPluginDevices::HardwareSimulator::Shutdown() -{ - bIsRunning = false; - if (mUpdateThread.joinable()) - { - mUpdateThread.join(); - } -} - -void SimulatedPluginDevices::HardwareSimulator::RegisterDeviceInfoCallback(std::function&)> device_info_callback) -{ - mOnDeviceInfoUpdate = device_info_callback; -} - -void SimulatedPluginDevices::HardwareSimulator::NotifyDataCallback() -{ - if (mOnFrameDataUpdate) - { - mOnFrameDataUpdate(mSimulatedFrameDataSet); - } -} - -void SimulatedPluginDevices::HardwareSimulator::NotifyInfoCallback() -{ - if (mOnDeviceInfoUpdate) - { - mOnDeviceInfoUpdate(mNewDeviceInfo); - mNewDeviceInfo.clear(); - } -} - -void SimulatedPluginDevices::HardwareSimulator::AddSimulatedGlove(int deviceId, int nodeCount, int handedness) -{ - SimulatedDeviceInfo device(deviceId, nodeCount, handedness); - mSimulatedDeviceInfoSet.push_back(device); - mNewDeviceInfo.push_back(device); - mSimulatedFrameDataSet.push_back(SimulatedGloveFrameData(device.mDeviceSerial, device.mNodeCount)); - NotifyInfoCallback(); -} - -void SimulatedPluginDevices::HardwareSimulator::RegisterFrameDataCallback(std::function&)> data_callback) -{ - mOnFrameDataUpdate = data_callback; -} - - -/** - * [Example] Simply read each frame data from the provided csv file and update the data callback. - */ -bool SimulatedPluginDevices::HardwareSimulator::ReadDataFromCSV() -{ - double mRequestedRateMS = (1.0f / mDataSampleRate) * 1000.0f; - std::string filename = GetExePath() + "\\devices\\ExampleGloveData.csv"; - - // Read the provided example CSV file (60 channels) - std::ifstream fin(filename); - if (!fin.is_open()) - { - // Failed to open the device - return false; - } - - bool isFirstLine = true; - std::vector field_names; - - // loop through lines of data - std::string lineRead; - while (bIsRunning) - { - std::vector frame_data; - std::vector jointData; - - if (fin.eof()) - { - //loop back to begining - fin.clear(); - fin.seekg(0, std::ios::beg); - isFirstLine = true; - } - - std::getline(fin, lineRead); - std::stringstream lineStream(lineRead); - - // Read comma-separated values - std::string val; - std::getline(lineStream, val, ','); // skip first column - while (getline(lineStream, val, ',')) - { - if (isFirstLine) - { - field_names.push_back(val); - } - else - { - try - { - float val_f = std::stof(val); - jointData.push_back(val_f); - - } - catch (const std::exception* e) - { - // error converting the value. Terminate - return false; - } - - if (jointData.size() == 4) - { - SimulatedFingerData finger; - // next finger joint - finger.quat_w = jointData.at(0); - finger.quat_x = jointData.at(1); - finger.quat_y = jointData.at(2); - finger.quat_z = jointData.at(3); - frame_data.push_back(finger); - jointData.clear(); - } - } - } - - if (isFirstLine) - isFirstLine = false; - else { - if (frame_data.size() != 0) - { - UpdateAllDevicesWithData(frame_data); - } - } - - // End of a line sleep - std::this_thread::sleep_for(std::chrono::milliseconds((int)mRequestedRateMS)); - } - return true; -} - -void SimulatedPluginDevices::HardwareSimulator::UpdateAllDevicesWithData(std::vector& data) -{ - mDataLock.lock(); - // Loop through list of data instances in the frame data vector and update all instances - for (auto& gloveDevice : mSimulatedFrameDataSet) - { - // Set all finger data - gloveDevice.gloveFingerData = data; - } - NotifyDataCallback(); - mDataLock.unlock(); -} - -std::string SimulatedPluginDevices::HardwareSimulator::GetExePath() -{ - std::string path; - char buffer[MAX_PATH]; - GetModuleFileNameA(NULL, buffer, MAX_PATH); - string::size_type pos = string(buffer).find_last_of("\\/"); - path = string(buffer).substr(0, pos)/*+"\\system.exe"*/; - return path; -} \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/HardwareSimulator.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/HardwareSimulator.h deleted file mode 100644 index 6a1deada..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/HardwareSimulator.h +++ /dev/null @@ -1,111 +0,0 @@ -////====================================================================================================== -//// Copyright 2023, NaturalPoint Inc. -////====================================================================================================== -/* -* SimulatedHardware class is used for demonstrating third-party glove SDK DLL to simulate a third-party hardware. -* For the purpose of the example glove device, the finger tracking data is read from the csv file. -*/ -#pragma once -#define DATA_SAMPLERATE 120 - -#include -#include -#include -#include -#include -#include - -namespace SimulatedPluginDevices { - - class HardwareSimulator; - - struct SimulatedFingerData - { - float quat_x = 0; - float quat_y = 0; - float quat_z = 0; - float quat_w = 0; - float pos_x = 0; - float pos_y = 0; - float pos_z = 0; - - SimulatedFingerData() {}; - SimulatedFingerData(float x, float y, float z, float w) : - quat_x(x), quat_y(y), quat_z(z), quat_w(w) {} - }; - - struct SimulatedGloveFrameData { - int mDeviceSerial = 0; - int mChannelCount = 0; - int kChannelPerNode = 4; - int mNodeCount = 0; - - std::vector gloveFingerData; // for glove data - - SimulatedGloveFrameData() {} - SimulatedGloveFrameData(int deviceSerial, int nodeCount) : - mDeviceSerial(deviceSerial), - mNodeCount(nodeCount) - { - mChannelCount = mNodeCount * kChannelPerNode; - gloveFingerData.resize(mNodeCount); - } - }; - - struct SimulatedDeviceInfo { - int mDeviceSerial = 0; // device serial id - int mBattery = 100; - int mSignalStrength = 100; - int mHandSide = 0; - int mNodeCount = 0; - int mChannelCount = 0; - int kChannelPerNode = 4; - - SimulatedDeviceInfo() {} - SimulatedDeviceInfo(int deviceSerial, int channelCount): - mDeviceSerial(deviceSerial), mChannelCount(channelCount) - {} - SimulatedDeviceInfo(int deviceSerial, int nodeCount, int handSide) : - mDeviceSerial(deviceSerial), mHandSide(handSide), mNodeCount(nodeCount) - { - mChannelCount = nodeCount * kChannelPerNode; - } - }; - - /// - /// Simple simulator for outputting sine wave channel data. - /// - class HardwareSimulator { - public: - HardwareSimulator(); - ~HardwareSimulator(); - - void AddSimulatedGlove(int deviceId, int nodeCount, int handedness); - void RegisterFrameDataCallback(std::function&)> data_callback); - void RegisterDeviceInfoCallback(std::function&)> device_info_callback); - void StartData(); - void Shutdown(); - - private: - bool bIsRunning = false; - std::thread mUpdateThread; - - void NotifyDataCallback(); - void NotifyInfoCallback(); - bool ReadDataFromCSV(); - static std::string GetExePath(); - - void UpdateAllDevicesWithData(std::vector& data); - - std::vector mSimulatedFrameDataSet; - std::vector mSimulatedDeviceInfoSet; - std::vector mNewDeviceInfo; - std::function&)> mOnFrameDataUpdate; - std::function&)> mOnDeviceInfoUpdate; - - const double mDataSampleRate = DATA_SAMPLERATE; - - protected: - std::recursive_mutex mDataLock; - }; -} \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/LZ4Wrapper.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/LZ4Wrapper.cpp deleted file mode 100644 index 302fe07c..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/LZ4Wrapper.cpp +++ /dev/null @@ -1,161 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== - -#include "LZ4Wrapper.h" -#include -#include -#include - -// Unity LZ4 DLL 동적 로딩 -static HMODULE lz4Module = nullptr; -static decltype(&Unity_LZ4_uncompressSize) pUnity_LZ4_uncompressSize = nullptr; -static decltype(&Unity_LZ4_decompress) pUnity_LZ4_decompress = nullptr; - -static bool LoadUnityLZ4() -{ - if (lz4Module == nullptr) { - // 다양한 경로에서 Unity LZ4 DLL 로드 시도 - const wchar_t* dllPaths[] = { - L"lz4.dll", // 현재 디렉토리 - L".\\lz4.dll", // 명시적 현재 디렉토리 - L"x64\\Debug\\lz4.dll", // 디버그 폴더 - L"C:\\Users\\user\\Documents\\Streamingle_URP\\Assets\\External\\Rokoko\\Scripts\\Plugins\\LZ4\\x86_64\\lz4.dll" // Unity 절대 경로 - }; - - for (const auto& path : dllPaths) { - lz4Module = LoadLibrary(path); - if (lz4Module != nullptr) { - // DLL 로드 성공 - 함수 포인터 획득 - pUnity_LZ4_uncompressSize = (decltype(pUnity_LZ4_uncompressSize))GetProcAddress(lz4Module, "Unity_LZ4_uncompressSize"); - pUnity_LZ4_decompress = (decltype(pUnity_LZ4_decompress))GetProcAddress(lz4Module, "Unity_LZ4_decompress"); - - if (pUnity_LZ4_uncompressSize != nullptr && pUnity_LZ4_decompress != nullptr) { - // 성공 - break; - } else { - // 함수를 찾지 못함 - DLL 언로드하고 다음 시도 - FreeLibrary(lz4Module); - lz4Module = nullptr; - pUnity_LZ4_uncompressSize = nullptr; - pUnity_LZ4_decompress = nullptr; - } - } - } - } - return (lz4Module != nullptr && pUnity_LZ4_uncompressSize != nullptr && pUnity_LZ4_decompress != nullptr); -} - -namespace RokokoIntegration -{ - std::vector LZ4Wrapper::Decompress(const uint8_t* compressedData, int compressedSize) - { - try { - // 입력 데이터 검증 - if (!compressedData || compressedSize <= 0) { - return {}; - } - - // Unity LZ4 DLL 로드 - if (!LoadUnityLZ4()) { - return {}; - } - - // Unity 방식 정확히 구현 - // 1. Unity_LZ4_uncompressSize로 압축 해제된 크기 구하기 - int uncompressedSize = pUnity_LZ4_uncompressSize( - reinterpret_cast(compressedData), - compressedSize - ); - - if (uncompressedSize <= 0) { - return {}; - } - - // 크기 유효성 검사 - if (uncompressedSize > MAX_DECOMPRESSED_SIZE) { - return {}; - } - - // 2. Unity_LZ4_decompress로 압축 해제 - std::vector decompressed(uncompressedSize); - int result = pUnity_LZ4_decompress( - reinterpret_cast(compressedData), - compressedSize, - reinterpret_cast(decompressed.data()), - uncompressedSize - ); - - // Unity에서는 result != 0이면 실패 - if (result != 0) { - return {}; - } - - return decompressed; - - } catch (...) { - // 모든 예외 상황에서 안전하게 빈 벡터 반환 - return {}; - } - } - - bool LZ4Wrapper::IsValidLZ4Data(const uint8_t* data, int size) - { - if (!data || size < 4) { - return false; - } - - // Rokoko Studio의 LZ4 데이터는 일반적으로 JSON이 아니므로 - // 첫 번째 바이트가 '{'가 아닌 경우 LZ4로 간주 - if (size > 0 && data[0] == '{') { - return false; // JSON 데이터는 LZ4가 아님 - } - - // 기본적인 LZ4 매직 넘버 확인 - // LZ4 프레임 헤더의 일부 패턴 확인 - if (size >= 4) { - // LZ4 프레임 헤더의 매직 넘버 (0x184D2204) - if (data[0] == 0x04 && data[1] == 0x22 && data[2] == 0x4D && data[3] == 0x18) { - return true; - } - } - - // 매직 넘버가 없어도 압축된 데이터로 간주 (Rokoko Studio 특성상) - return true; - } - - int LZ4Wrapper::GetDecompressedSize(const uint8_t* compressedData, int compressedSize) - { - try { - // Unity LZ4 DLL 로드 - if (!LoadUnityLZ4()) { - return -1; - } - - // Unity 방식으로 압축 해제된 크기 구하기 - int decompressedSize = pUnity_LZ4_uncompressSize( - reinterpret_cast(compressedData), - compressedSize - ); - return decompressedSize; - - } catch (...) { - return -1; - } - } - - bool LZ4Wrapper::ValidateDecompressedSize(int compressedSize, int decompressedSize) - { - // 압축 해제된 크기가 합리적인 범위인지 확인 - if (decompressedSize <= 0 || decompressedSize > MAX_DECOMPRESSED_SIZE) { - return false; - } - - // 압축률이 합리적인지 확인 (일반적으로 1:1 ~ 1:10) - if (decompressedSize < compressedSize || decompressedSize > compressedSize * 10) { - return false; - } - - return true; - } -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/LZ4Wrapper.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/LZ4Wrapper.h deleted file mode 100644 index 120230f3..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/LZ4Wrapper.h +++ /dev/null @@ -1,62 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== -/** - * LZ4Wrapper class provides LZ4 decompression functionality for Rokoko glove data. - * This wrapper handles safe decompression and validation of compressed data. - */ - -#pragma once -#include -#include - -// Unity LZ4 함수들 선언 -extern "C" { - int Unity_LZ4_uncompressSize(const char* srcBuffer, int srcSize); - int Unity_LZ4_decompress(const char* src, int srcSize, char* dst, int dstCapacity); -} - -namespace RokokoIntegration -{ - class LZ4Wrapper - { - public: - /** - * Decompresses LZ4 compressed data - * @param compressedData Pointer to compressed data - * @param compressedSize Size of compressed data - * @return Decompressed data as vector, empty if decompression fails - */ - static std::vector Decompress(const uint8_t* compressedData, int compressedSize); - - /** - * Validates if the data appears to be valid LZ4 compressed data - * @param data Pointer to data to validate - * @param size Size of data - * @return true if data appears to be valid LZ4 data - */ - static bool IsValidLZ4Data(const uint8_t* data, int size); - - /** - * Gets the decompressed size from LZ4 header - * @param compressedData Pointer to compressed data - * @param compressedSize Size of compressed data - * @return Decompressed size, -1 if invalid - */ - static int GetDecompressedSize(const uint8_t* compressedData, int compressedSize); - - private: - /** - * Validates decompressed size is reasonable - * @param compressedSize Size of compressed data - * @param decompressedSize Size of decompressed data - * @return true if sizes are reasonable - */ - static bool ValidateDecompressedSize(int compressedSize, int decompressedSize); - - /** - * Maximum reasonable decompressed size (10MB) - */ - static const int MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024; - }; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoData.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoData.h deleted file mode 100644 index 1d904f46..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoData.h +++ /dev/null @@ -1,148 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== -/** - * RokokoData.h defines the data structures for Rokoko Studio LiveFrame_v4 format. - * These structures match the JSON format used by Rokoko Unity scripts. - */ - -#pragma once -#include -#include -#include -#include - -namespace RokokoData -{ - // Vector3 structure for position data - struct Vector3Frame - { - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - }; - - // Vector4 structure for quaternion rotation data - struct Vector4Frame - { - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - float w = 1.0f; - }; - - // Actor joint frame containing position and rotation - struct ActorJointFrame - { - Vector3Frame position; - Vector4Frame rotation; - }; - - // Body structure containing full skeleton (Unity BodyFrame와 동일) - // ✅ OPTIMIZATION: Using std::optional instead of shared_ptr for stack allocation (zero heap allocations) - struct Body - { - // 전신 스켈레톤 데이터 (Unity BodyFrame과 동일) - std::optional hip; - std::optional spine; - std::optional chest; - std::optional neck; - std::optional head; - - std::optional leftShoulder; - std::optional leftUpperArm; - std::optional leftLowerArm; - std::optional leftHand; - - std::optional rightShoulder; - std::optional rightUpperArm; - std::optional rightLowerArm; - std::optional rightHand; - - // 다리 데이터 (필요시) - std::optional leftUpLeg; - std::optional leftLeg; - std::optional leftFoot; - std::optional leftToe; - std::optional leftToeEnd; - - std::optional rightUpLeg; - std::optional rightLeg; - std::optional rightFoot; - std::optional rightToe; - std::optional rightToeEnd; - - // Left hand finger joints - std::optional leftThumbProximal; - std::optional leftThumbMedial; - std::optional leftThumbDistal; - std::optional leftThumbTip; - - std::optional leftIndexProximal; - std::optional leftIndexMedial; - std::optional leftIndexDistal; - std::optional leftIndexTip; - - std::optional leftMiddleProximal; - std::optional leftMiddleMedial; - std::optional leftMiddleDistal; - std::optional leftMiddleTip; - - std::optional leftRingProximal; - std::optional leftRingMedial; - std::optional leftRingDistal; - std::optional leftRingTip; - - std::optional leftLittleProximal; - std::optional leftLittleMedial; - std::optional leftLittleDistal; - std::optional leftLittleTip; - - // Right hand finger joints (similar structure) - std::optional rightThumbProximal; - std::optional rightThumbMedial; - std::optional rightThumbDistal; - std::optional rightThumbTip; - - std::optional rightIndexProximal; - std::optional rightIndexMedial; - std::optional rightIndexDistal; - std::optional rightIndexTip; - - std::optional rightMiddleProximal; - std::optional rightMiddleMedial; - std::optional rightMiddleDistal; - std::optional rightMiddleTip; - - std::optional rightRingProximal; - std::optional rightRingMedial; - std::optional rightRingDistal; - std::optional rightRingTip; - - std::optional rightLittleProximal; - std::optional rightLittleMedial; - std::optional rightLittleDistal; - std::optional rightLittleTip; - }; - - // Actor data structure - struct ActorData - { - std::string id; - std::string name; - Body body; - }; - - // Scene structure containing actors - struct SceneFrame - { - std::vector actors; - }; - - // Main LiveFrame_v4 structure - struct LiveFrame_v4 - { - double timestamp = 0.0; - SceneFrame scene; - }; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataConverter.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataConverter.cpp deleted file mode 100644 index 42a29055..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataConverter.cpp +++ /dev/null @@ -1,214 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== - -#include "RokokoDataConverter.h" -#include "RokokoData.h" -#include -#include -#include - -namespace RokokoIntegration -{ - // 정적 상수는 헤더에서 inline으로 정의됨 - - sGloveDeviceData RokokoDataConverter::ConvertRokokoToOptiTrack(const RokokoData::LiveFrame_v4& rokokoFrame) - { - sGloveDeviceData optiTrackData; - - try { - // 기본 데이터 설정 - optiTrackData.gloveId = 1; // 기본 장갑 ID - optiTrackData.timestamp = 0.0; // 타임스탬프는 나중에 설정 - - // 15개 OptiTrack 관절 노드 초기화 - optiTrackData.nodes.resize(TOTAL_OPTITRACK_JOINTS); - - // 검증: actors가 존재하는지 확인 - if (rokokoFrame.scene.actors.empty()) { - optiTrackData.nodes.clear(); - return optiTrackData; - } - - const auto& actor = rokokoFrame.scene.actors[0]; - - // ✅ OPTIMIZATION: Use has_value() for optional instead of implicit bool conversion - // 손가락별 데이터 매핑 (왼손) - // 엄지손가락 (Thumb) - if (actor.body.leftThumbMedial.has_value()) ConvertJoint(*actor.body.leftThumbMedial, optiTrackData.nodes[0]); // MP - if (actor.body.leftThumbDistal.has_value()) ConvertJoint(*actor.body.leftThumbDistal, optiTrackData.nodes[1]); // PIP - if (actor.body.leftThumbTip.has_value()) ConvertJoint(*actor.body.leftThumbTip, optiTrackData.nodes[2]); // DIP - - // 검지손가락 (Index) - if (actor.body.leftIndexMedial.has_value()) ConvertJoint(*actor.body.leftIndexMedial, optiTrackData.nodes[3]); // MP - if (actor.body.leftIndexDistal.has_value()) ConvertJoint(*actor.body.leftIndexDistal, optiTrackData.nodes[4]); // PIP - if (actor.body.leftIndexTip.has_value()) ConvertJoint(*actor.body.leftIndexTip, optiTrackData.nodes[5]); // DIP - - // 중지손가락 (Middle) - if (actor.body.leftMiddleMedial.has_value()) ConvertJoint(*actor.body.leftMiddleMedial, optiTrackData.nodes[6]); // MP - if (actor.body.leftMiddleDistal.has_value()) ConvertJoint(*actor.body.leftMiddleDistal, optiTrackData.nodes[7]); // PIP - if (actor.body.leftMiddleTip.has_value()) ConvertJoint(*actor.body.leftMiddleTip, optiTrackData.nodes[8]); // DIP - - // 약지손가락 (Ring) - if (actor.body.leftRingMedial.has_value()) ConvertJoint(*actor.body.leftRingMedial, optiTrackData.nodes[9]); // MP - if (actor.body.leftRingDistal.has_value()) ConvertJoint(*actor.body.leftRingDistal, optiTrackData.nodes[10]); // PIP - if (actor.body.leftRingTip.has_value()) ConvertJoint(*actor.body.leftRingTip, optiTrackData.nodes[11]); // DIP - - // 새끼손가락 (Little) - if (actor.body.leftLittleMedial.has_value()) ConvertJoint(*actor.body.leftLittleMedial, optiTrackData.nodes[12]); // MP - if (actor.body.leftLittleDistal.has_value()) ConvertJoint(*actor.body.leftLittleDistal, optiTrackData.nodes[13]); // PIP - if (actor.body.leftLittleTip.has_value()) ConvertJoint(*actor.body.leftLittleTip, optiTrackData.nodes[14]); // DIP - - // 노드 ID 설정 - for (int i = 0; i < TOTAL_OPTITRACK_JOINTS; i++) { - optiTrackData.nodes[i].node_id = i; - } - - } catch (...) { - // 에러 발생 시 빈 데이터 반환 - optiTrackData.nodes.clear(); - } - - return optiTrackData; - } - - bool RokokoDataConverter::ConvertJoint(const RokokoData::ActorJointFrame& rokokoJoint, sFingerNode& optiTrackNode) - { - try { - // 쿼터니언 변환 - ConvertQuaternion(rokokoJoint.rotation, - optiTrackNode.quat_w, - optiTrackNode.quat_x, - optiTrackNode.quat_y, - optiTrackNode.quat_z); - - // 쿼터니언 정규화 - NormalizeQuaternion(optiTrackNode.quat_w, - optiTrackNode.quat_x, - optiTrackNode.quat_y, - optiTrackNode.quat_z); - - // 쿼터니언 유효성 검사 - if (!ValidateQuaternion(optiTrackNode.quat_w, - optiTrackNode.quat_x, - optiTrackNode.quat_y, - optiTrackNode.quat_z)) { - return false; - } - - return true; - - } catch (...) { - return false; - } - } - - bool RokokoDataConverter::ValidateOptiTrackData(const sGloveDeviceData& gloveData) - { - // 기본 검증 - if (gloveData.nodes.size() != TOTAL_OPTITRACK_JOINTS) { - return false; - } - - // 각 노드 검증 - for (const auto& node : gloveData.nodes) { - if (!ValidateQuaternion(node.quat_w, node.quat_x, node.quat_y, node.quat_z)) { - return false; - } - } - - return true; - } - - std::string RokokoDataConverter::GetMappingInfo() - { - std::ostringstream oss; - oss << "Rokoko to OptiTrack Finger Joint Mapping:\n"; - oss << "Total Rokoko joints: " << TOTAL_ROKOKO_JOINTS << " (4 per finger)\n"; - oss << "Total OptiTrack joints: " << TOTAL_OPTITRACK_JOINTS << " (3 per finger)\n"; - oss << "Mapping removes proximal joints and keeps:\n"; - oss << " - Medial (MP): Metacarpophalangeal\n"; - oss << " - Distal (PIP): Proximal Interphalangeal\n"; - oss << " - Tip (DIP): Distal Interphalangeal\n"; - - return oss.str(); - } - - void RokokoDataConverter::MapFingerJoints(const std::vector& rokokoFingers, - std::vector& optiTrackNodes) - { - if (rokokoFingers.size() != TOTAL_ROKOKO_JOINTS || optiTrackNodes.size() != TOTAL_OPTITRACK_JOINTS) { - return; - } - - // 매핑 테이블을 사용하여 관절 변환 - for (int i = 0; i < TOTAL_OPTITRACK_JOINTS; i++) { - int rokokoIndex = JOINT_MAPPING[i]; - if (rokokoIndex >= 0 && rokokoIndex < TOTAL_ROKOKO_JOINTS) { - ConvertJoint(rokokoFingers[rokokoIndex], optiTrackNodes[i]); - } - } - } - - void RokokoDataConverter::ConvertQuaternion(const RokokoData::Vector4Frame& rokokoQuat, - float& quat_w, float& quat_x, float& quat_y, float& quat_z) - { - // Rokoko 쿼터니언을 OptiTrack 형식으로 복사 - quat_w = rokokoQuat.w; - quat_x = rokokoQuat.x; - quat_y = rokokoQuat.y; - quat_z = rokokoQuat.z; - } - - void RokokoDataConverter::NormalizeQuaternion(float& w, float& x, float& y, float& z) - { - float length = std::sqrt(w * w + x * x + y * y + z * z); - - if (length > 0.0f) { - float invLength = 1.0f / length; - w *= invLength; - x *= invLength; - y *= invLength; - z *= invLength; - } else { - // 유효하지 않은 쿼터니언인 경우 기본값 설정 - w = 1.0f; - x = 0.0f; - y = 0.0f; - z = 0.0f; - } - } - - bool RokokoDataConverter::ValidateQuaternion(float w, float x, float y, float z) - { - // NaN 체크 - if (std::isnan(w) || std::isnan(x) || std::isnan(y) || std::isnan(z)) { - return false; - } - - // 무한대 체크 - if (std::isinf(w) || std::isinf(x) || std::isinf(y) || std::isinf(z)) { - return false; - } - - // 쿼터니언 길이 체크 (정규화된 경우 1.0에 가까워야 함) - float length = std::sqrt(w * w + x * x + y * y + z * z); - if (std::abs(length - 1.0f) > 0.1f) { - return false; - } - - return true; - } - - void RokokoDataConverter::ApplyHandCoordinateSystem(bool isLeftHand, float& x, float& y, float& z) - { - // 좌우손 좌표계 변환 - if (isLeftHand) { - // 왼손: +X축이 손가락 끝 방향 - // 변환 없음 (기본값) - } else { - // 오른손: +X축이 손목/몸 방향 - x = -x; // X축 반전 - } - } -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataConverter.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataConverter.h deleted file mode 100644 index 96072590..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataConverter.h +++ /dev/null @@ -1,116 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== -/** - * RokokoDataConverter class provides data conversion functionality from Rokoko format to OptiTrack format. - * This converter handles the mapping of 20 Rokoko finger joints to 15 OptiTrack finger joints. - */ - -#pragma once -#include -#include -#include "GloveDataFormat.h" - -// Forward declarations -namespace RokokoData -{ - struct Vector3Frame; - struct Vector4Frame; - struct ActorJointFrame; - struct ActorData; - struct LiveFrame_v4; -} - -namespace RokokoIntegration -{ - class RokokoDataConverter - { - public: - /** - * Converts Rokoko LiveFrame_v4 data to OptiTrack glove data format - * @param rokokoFrame Input Rokoko frame data - * @return Converted OptiTrack glove data - */ - static sGloveDeviceData ConvertRokokoToOptiTrack(const RokokoData::LiveFrame_v4& rokokoFrame); - - /** - * Converts Rokoko finger joint data to OptiTrack finger node format - * @param rokokoJoint Input Rokoko joint data - * @param optiTrackNode Output OptiTrack node data - * @return true if conversion successful - */ - static bool ConvertJoint(const RokokoData::ActorJointFrame& rokokoJoint, sFingerNode& optiTrackNode); - - /** - * Validates converted OptiTrack data - * @param gloveData Data to validate - * @return true if data is valid - */ - static bool ValidateOptiTrackData(const sGloveDeviceData& gloveData); - - /** - * Gets the mapping information for debugging - * @return String containing mapping details - */ - static std::string GetMappingInfo(); - - private: - /** - * Maps Rokoko finger joints to OptiTrack format - * Maps 20 Rokoko joints (4 per finger) to 15 OptiTrack joints (3 per finger) - * Removes proximal joints and keeps medial, distal, tip - */ - static void MapFingerJoints(const std::vector& rokokoFingers, - std::vector& optiTrackNodes); - - /** - * Converts quaternion from Rokoko format to OptiTrack format - * @param rokokoQuat Input Rokoko quaternion - * @param optiTrackQuat Output OptiTrack quaternion - */ - static void ConvertQuaternion(const RokokoData::Vector4Frame& rokokoQuat, - float& quat_w, float& quat_x, float& quat_y, float& quat_z); - - /** - * Normalizes quaternion values - * @param w, x, y, z Quaternion components (modified in place) - */ - static void NormalizeQuaternion(float& w, float& x, float& y, float& z); - - /** - * Validates quaternion values - * @param w, x, y, z Quaternion components - * @return true if quaternion is valid - */ - static bool ValidateQuaternion(float w, float x, float y, float z); - - /** - * Applies coordinate system conversion for left/right hands - * @param isLeftHand true if left hand, false if right hand - * @param x, y, z Position coordinates (modified in place) - */ - static void ApplyHandCoordinateSystem(bool isLeftHand, float& x, float& y, float& z); - - // Finger mapping constants - static inline const int ROKOKO_JOINTS_PER_FINGER = 4; // Proximal, Medial, Distal, Tip - static inline const int OPTITRACK_JOINTS_PER_FINGER = 3; // MP, PIP, DIP - static inline const int TOTAL_FINGERS = 5; // Thumb, Index, Middle, Ring, Little - static inline const int TOTAL_ROKOKO_JOINTS = TOTAL_FINGERS * ROKOKO_JOINTS_PER_FINGER; // 20 - static inline const int TOTAL_OPTITRACK_JOINTS = TOTAL_FINGERS * OPTITRACK_JOINTS_PER_FINGER; // 15 - - // Joint mapping table: Rokoko index -> OptiTrack index - // Removes proximal joints (index 0, 4, 8, 12, 16) - static inline const int JOINT_MAPPING[15] = { - // Thumb: Medial(1), Distal(2), Tip(3) -> MP(0), PIP(1), DIP(2) - 1, 2, 3, - // Index: Medial(5), Distal(6), Tip(7) -> MP(3), PIP(4), DIP(5) - 5, 6, 7, - // Middle: Medial(9), Distal(10), Tip(11) -> MP(6), PIP(7), DIP(8) - 9, 10, 11, - // Ring: Medial(13), Distal(14), Tip(15) -> MP(9), PIP(10), DIP(11) - 13, 14, 15, - // Little: Medial(17), Distal(18), Tip(19) -> MP(12), PIP(13), DIP(14) - 17, 18, 19 - }; - }; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataParser.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataParser.cpp deleted file mode 100644 index 0a738260..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataParser.cpp +++ /dev/null @@ -1,339 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== - -#include "RokokoDataParser.h" -#include "RokokoData.h" -#include -#include -#include -#include - -namespace RokokoIntegration -{ - bool RokokoDataParser::ParseLiveFrame(const std::string& jsonString, RokokoData::LiveFrame_v4& frame) - { - try { - // nlohmann/json을 사용하여 JSON 파싱 - nlohmann::json j = nlohmann::json::parse(jsonString); - - // Unity JsonLiveSerializerV3.cs와 동일한 구조로 파싱 - - // Scene 데이터 파싱 - if (j.contains("scene")) { - auto& scene = j["scene"]; - - // 타임스탬프 - if (scene.contains("timestamp")) { - frame.timestamp = scene["timestamp"]; - } - - // Actors 파싱 - if (scene.contains("actors") && scene["actors"].is_array()) { - auto& actors = scene["actors"]; - for (const auto& actorJson : actors) { - RokokoData::ActorData actor; - - // Actor 기본 정보 - if (actorJson.contains("name")) { - actor.name = actorJson["name"]; - } - - // Body 데이터 파싱 - if (actorJson.contains("body")) { - ParseBodyFrame(actorJson["body"], actor.body); - } - - frame.scene.actors.push_back(actor); - } - } - } - - return true; - - } catch (const nlohmann::json::exception& e) { - // JSON 파싱 에러 처리 - std::cerr << "JSON parsing error: " << e.what() << std::endl; - return false; - } catch (...) { - // 기타 예외 처리 - std::cerr << "Unknown error during JSON parsing" << std::endl; - return false; - } - } - - void RokokoDataParser::ParseBodyFrame(const nlohmann::json& bodyJson, RokokoData::Body& body) - { - // Unity JsonLiveSerializerV3.cs의 BodyFrame 구조와 동일하게 파싱 - - // === 전신 스켈레톤 데이터 파싱 (Unity BodyFrame과 동일) === - - // 몸통 파싱 - ParseFingerJoint(bodyJson, "hip", body.hip); - ParseFingerJoint(bodyJson, "spine", body.spine); - ParseFingerJoint(bodyJson, "chest", body.chest); - ParseFingerJoint(bodyJson, "neck", body.neck); - ParseFingerJoint(bodyJson, "head", body.head); - - // 왼쪽 팔 파싱 - ParseFingerJoint(bodyJson, "leftShoulder", body.leftShoulder); - ParseFingerJoint(bodyJson, "leftUpperArm", body.leftUpperArm); - ParseFingerJoint(bodyJson, "leftLowerArm", body.leftLowerArm); - ParseFingerJoint(bodyJson, "leftHand", body.leftHand); - - // 오른쪽 팔 파싱 - ParseFingerJoint(bodyJson, "rightShoulder", body.rightShoulder); - ParseFingerJoint(bodyJson, "rightUpperArm", body.rightUpperArm); - ParseFingerJoint(bodyJson, "rightLowerArm", body.rightLowerArm); - ParseFingerJoint(bodyJson, "rightHand", body.rightHand); - - // 다리 파싱 (필요시) - ParseFingerJoint(bodyJson, "leftUpLeg", body.leftUpLeg); - ParseFingerJoint(bodyJson, "leftLeg", body.leftLeg); - ParseFingerJoint(bodyJson, "leftFoot", body.leftFoot); - ParseFingerJoint(bodyJson, "leftToe", body.leftToe); - ParseFingerJoint(bodyJson, "leftToeEnd", body.leftToeEnd); - - ParseFingerJoint(bodyJson, "rightUpLeg", body.rightUpLeg); - ParseFingerJoint(bodyJson, "rightLeg", body.rightLeg); - ParseFingerJoint(bodyJson, "rightFoot", body.rightFoot); - ParseFingerJoint(bodyJson, "rightToe", body.rightToe); - ParseFingerJoint(bodyJson, "rightToeEnd", body.rightToeEnd); - - // === 손가락 데이터 파싱 === - - // 왼손 손가락 데이터 파싱 - ParseFingerJoint(bodyJson, "leftThumbProximal", body.leftThumbProximal); - ParseFingerJoint(bodyJson, "leftThumbMedial", body.leftThumbMedial); - ParseFingerJoint(bodyJson, "leftThumbDistal", body.leftThumbDistal); - ParseFingerJoint(bodyJson, "leftThumbTip", body.leftThumbTip); - - ParseFingerJoint(bodyJson, "leftIndexProximal", body.leftIndexProximal); - ParseFingerJoint(bodyJson, "leftIndexMedial", body.leftIndexMedial); - ParseFingerJoint(bodyJson, "leftIndexDistal", body.leftIndexDistal); - ParseFingerJoint(bodyJson, "leftIndexTip", body.leftIndexTip); - - ParseFingerJoint(bodyJson, "leftMiddleProximal", body.leftMiddleProximal); - ParseFingerJoint(bodyJson, "leftMiddleMedial", body.leftMiddleMedial); - ParseFingerJoint(bodyJson, "leftMiddleDistal", body.leftMiddleDistal); - ParseFingerJoint(bodyJson, "leftMiddleTip", body.leftMiddleTip); - - ParseFingerJoint(bodyJson, "leftRingProximal", body.leftRingProximal); - ParseFingerJoint(bodyJson, "leftRingMedial", body.leftRingMedial); - ParseFingerJoint(bodyJson, "leftRingDistal", body.leftRingDistal); - ParseFingerJoint(bodyJson, "leftRingTip", body.leftRingTip); - - ParseFingerJoint(bodyJson, "leftLittleProximal", body.leftLittleProximal); - ParseFingerJoint(bodyJson, "leftLittleMedial", body.leftLittleMedial); - ParseFingerJoint(bodyJson, "leftLittleDistal", body.leftLittleDistal); - ParseFingerJoint(bodyJson, "leftLittleTip", body.leftLittleTip); - - // 오른손 손가락 데이터 파싱 - ParseFingerJoint(bodyJson, "rightThumbProximal", body.rightThumbProximal); - ParseFingerJoint(bodyJson, "rightThumbMedial", body.rightThumbMedial); - ParseFingerJoint(bodyJson, "rightThumbDistal", body.rightThumbDistal); - ParseFingerJoint(bodyJson, "rightThumbTip", body.rightThumbTip); - - ParseFingerJoint(bodyJson, "rightIndexProximal", body.rightIndexProximal); - ParseFingerJoint(bodyJson, "rightIndexMedial", body.rightIndexMedial); - ParseFingerJoint(bodyJson, "rightIndexDistal", body.rightIndexDistal); - ParseFingerJoint(bodyJson, "rightIndexTip", body.rightIndexTip); - - ParseFingerJoint(bodyJson, "rightMiddleProximal", body.rightMiddleProximal); - ParseFingerJoint(bodyJson, "rightMiddleMedial", body.rightMiddleMedial); - ParseFingerJoint(bodyJson, "rightMiddleDistal", body.rightMiddleDistal); - ParseFingerJoint(bodyJson, "rightMiddleTip", body.rightMiddleTip); - - ParseFingerJoint(bodyJson, "rightRingProximal", body.rightRingProximal); - ParseFingerJoint(bodyJson, "rightRingMedial", body.rightRingMedial); - ParseFingerJoint(bodyJson, "rightRingDistal", body.rightRingDistal); - ParseFingerJoint(bodyJson, "rightRingTip", body.rightRingTip); - - ParseFingerJoint(bodyJson, "rightLittleProximal", body.rightLittleProximal); - ParseFingerJoint(bodyJson, "rightLittleMedial", body.rightLittleMedial); - ParseFingerJoint(bodyJson, "rightLittleDistal", body.rightLittleDistal); - ParseFingerJoint(bodyJson, "rightLittleTip", body.rightLittleTip); - } - - void RokokoDataParser::ParseFingerJoint(const nlohmann::json& bodyJson, const std::string& jointName, std::optional& joint) - { - if (bodyJson.contains(jointName)) { - auto& jointJson = bodyJson[jointName]; - - // ✅ OPTIMIZATION: Direct assignment to optional (stack allocation, no heap) - RokokoData::ActorJointFrame tempJoint; - - // Position 파싱 - if (jointJson.contains("position")) { - auto& pos = jointJson["position"]; - tempJoint.position.x = pos.value("x", 0.0f); - tempJoint.position.y = pos.value("y", 0.0f); - tempJoint.position.z = pos.value("z", 0.0f); - } - - // Rotation 파싱 - if (jointJson.contains("rotation")) { - auto& rot = jointJson["rotation"]; - tempJoint.rotation.x = rot.value("x", 0.0f); - tempJoint.rotation.y = rot.value("y", 0.0f); - tempJoint.rotation.z = rot.value("z", 0.0f); - tempJoint.rotation.w = rot.value("w", 1.0f); - - // 쿼터니언 정규화 - NormalizeQuaternion(tempJoint.rotation.x, tempJoint.rotation.y, tempJoint.rotation.z, tempJoint.rotation.w); - } - - // Assign to optional - joint = tempJoint; - } - } - - bool RokokoDataParser::ValidateFrameData(const RokokoData::LiveFrame_v4& frame) - { - try { - // 기본 검증 - if (frame.scene.actors.empty()) { - return false; - } - - const auto& actor = frame.scene.actors[0]; - - // 손가락 데이터 검증 (왼손 엄지손가락만 체크) - // ✅ OPTIMIZATION: Use has_value() for optional instead of nullptr check - if (!actor.body.leftThumbMedial.has_value() || !actor.body.leftThumbDistal.has_value() || !actor.body.leftThumbTip.has_value()) { - return false; - } - - return true; - - } catch (...) { - return false; - } - } - - bool RokokoDataParser::ExtractFingerData(const RokokoData::LiveFrame_v4& frame, - std::vector& leftHandFingers, - std::vector& rightHandFingers) - { - try { - leftHandFingers.clear(); - rightHandFingers.clear(); - - if (frame.scene.actors.empty()) { - return false; - } - - const auto& actor = frame.scene.actors[0]; - - // ✅ OPTIMIZATION: Use has_value() for optional instead of implicit bool conversion - // 왼손 데이터 추출 (20개 관절) - if (actor.body.leftThumbProximal.has_value()) leftHandFingers.push_back(*actor.body.leftThumbProximal); - if (actor.body.leftThumbMedial.has_value()) leftHandFingers.push_back(*actor.body.leftThumbMedial); - if (actor.body.leftThumbDistal.has_value()) leftHandFingers.push_back(*actor.body.leftThumbDistal); - if (actor.body.leftThumbTip.has_value()) leftHandFingers.push_back(*actor.body.leftThumbTip); - - if (actor.body.leftIndexProximal.has_value()) leftHandFingers.push_back(*actor.body.leftIndexProximal); - if (actor.body.leftIndexMedial.has_value()) leftHandFingers.push_back(*actor.body.leftIndexMedial); - if (actor.body.leftIndexDistal.has_value()) leftHandFingers.push_back(*actor.body.leftIndexDistal); - if (actor.body.leftIndexTip.has_value()) leftHandFingers.push_back(*actor.body.leftIndexTip); - - if (actor.body.leftMiddleProximal.has_value()) leftHandFingers.push_back(*actor.body.leftMiddleProximal); - if (actor.body.leftMiddleMedial.has_value()) leftHandFingers.push_back(*actor.body.leftMiddleMedial); - if (actor.body.leftMiddleDistal.has_value()) leftHandFingers.push_back(*actor.body.leftMiddleDistal); - if (actor.body.leftMiddleTip.has_value()) leftHandFingers.push_back(*actor.body.leftMiddleTip); - - if (actor.body.leftRingProximal.has_value()) leftHandFingers.push_back(*actor.body.leftRingProximal); - if (actor.body.leftRingMedial.has_value()) leftHandFingers.push_back(*actor.body.leftRingMedial); - if (actor.body.leftRingDistal.has_value()) leftHandFingers.push_back(*actor.body.leftRingDistal); - if (actor.body.leftRingTip.has_value()) leftHandFingers.push_back(*actor.body.leftRingTip); - - if (actor.body.leftLittleProximal.has_value()) leftHandFingers.push_back(*actor.body.leftLittleProximal); - if (actor.body.leftLittleMedial.has_value()) leftHandFingers.push_back(*actor.body.leftLittleMedial); - if (actor.body.leftLittleDistal.has_value()) leftHandFingers.push_back(*actor.body.leftLittleDistal); - if (actor.body.leftLittleTip.has_value()) leftHandFingers.push_back(*actor.body.leftLittleTip); - - // 오른손 데이터 추출 (20개 관절) - if (actor.body.rightThumbProximal.has_value()) rightHandFingers.push_back(*actor.body.rightThumbProximal); - if (actor.body.rightThumbMedial.has_value()) rightHandFingers.push_back(*actor.body.rightThumbMedial); - if (actor.body.rightThumbDistal.has_value()) rightHandFingers.push_back(*actor.body.rightThumbDistal); - if (actor.body.rightThumbTip.has_value()) rightHandFingers.push_back(*actor.body.rightThumbTip); - - if (actor.body.rightIndexProximal.has_value()) rightHandFingers.push_back(*actor.body.rightIndexProximal); - if (actor.body.rightIndexMedial.has_value()) rightHandFingers.push_back(*actor.body.rightIndexMedial); - if (actor.body.rightIndexDistal.has_value()) rightHandFingers.push_back(*actor.body.rightIndexDistal); - if (actor.body.rightIndexTip.has_value()) rightHandFingers.push_back(*actor.body.rightIndexTip); - - if (actor.body.rightMiddleProximal.has_value()) rightHandFingers.push_back(*actor.body.rightMiddleProximal); - if (actor.body.rightMiddleMedial.has_value()) rightHandFingers.push_back(*actor.body.rightMiddleMedial); - if (actor.body.rightMiddleDistal.has_value()) rightHandFingers.push_back(*actor.body.rightMiddleDistal); - if (actor.body.rightMiddleTip.has_value()) rightHandFingers.push_back(*actor.body.rightMiddleTip); - - if (actor.body.rightRingProximal.has_value()) rightHandFingers.push_back(*actor.body.rightRingProximal); - if (actor.body.rightRingMedial.has_value()) rightHandFingers.push_back(*actor.body.rightRingMedial); - if (actor.body.rightRingDistal.has_value()) rightHandFingers.push_back(*actor.body.rightRingDistal); - if (actor.body.rightRingTip.has_value()) rightHandFingers.push_back(*actor.body.rightRingTip); - - if (actor.body.rightLittleProximal.has_value()) rightHandFingers.push_back(*actor.body.rightLittleProximal); - if (actor.body.rightLittleMedial.has_value()) rightHandFingers.push_back(*actor.body.rightLittleMedial); - if (actor.body.rightLittleDistal.has_value()) rightHandFingers.push_back(*actor.body.rightLittleDistal); - if (actor.body.rightLittleTip.has_value()) rightHandFingers.push_back(*actor.body.rightLittleTip); - - return true; - - } catch (...) { - return false; - } - } - - bool RokokoDataParser::ParseActorData(const void* actorJson, RokokoData::ActorData& actor) - { - // 현재는 기본 구현만 제공 - // 실제 JSON 파싱 라이브러리 사용 시 구현 - return true; - } - - bool RokokoDataParser::ParseFingerData(const void* fingerJson, RokokoData::ActorJointFrame& finger) - { - // 현재는 기본 구현만 제공 - // 실제 JSON 파싱 라이브러리 사용 시 구현 - return true; - } - - bool RokokoDataParser::ValidateQuaternion(float x, float y, float z, float w) - { - // NaN 체크 - if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w)) { - return false; - } - - // 무한대 체크 - if (std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w)) { - return false; - } - - // 쿼터니언 길이 체크 (정규화된 경우 1.0에 가까워야 함) - float length = std::sqrt(x * x + y * y + z * z + w * w); - if (length < 0.1f || length > 10.0f) { - return false; - } - - return true; - } - - void RokokoDataParser::NormalizeQuaternion(float& x, float& y, float& z, float& w) - { - float length = std::sqrt(x * x + y * y + z * z + w * w); - if (length > 0.0001f) { - x /= length; - y /= length; - z /= length; - w /= length; - } else { - // 기본값 설정 - x = 0.0f; - y = 0.0f; - z = 0.0f; - w = 1.0f; - } - } -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataParser.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataParser.h deleted file mode 100644 index 423d9c11..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoDataParser.h +++ /dev/null @@ -1,104 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== -/** - * RokokoDataParser class provides JSON parsing functionality for Rokoko glove data. - * This parser handles the LiveFrame_v4 JSON format from Rokoko Studio. - */ - -#pragma once -#include -#include -#include -#include -#include - -// Forward declarations for JSON data structures -namespace RokokoData -{ - struct Vector3Frame; - struct Vector4Frame; - struct ActorJointFrame; - struct ActorData; - struct LiveFrame_v4; - struct Body; -} - -namespace RokokoIntegration -{ - class RokokoDataParser - { - public: - /** - * Parses LiveFrame_v4 JSON data - * @param jsonString JSON string to parse - * @param frame Output frame data structure - * @return true if parsing successful - */ - static bool ParseLiveFrame(const std::string& jsonString, RokokoData::LiveFrame_v4& frame); - - /** - * Validates parsed frame data - * @param frame Frame data to validate - * @return true if frame data is valid - */ - static bool ValidateFrameData(const RokokoData::LiveFrame_v4& frame); - - /** - * Extracts finger joint data from frame - * @param frame Input frame data - * @param leftHandFingers Output left hand finger data - * @param rightHandFingers Output right hand finger data - * @return true if extraction successful - */ - static bool ExtractFingerData(const RokokoData::LiveFrame_v4& frame, - std::vector& leftHandFingers, - std::vector& rightHandFingers); - - private: - /** - * Parses body frame data from JSON - * @param bodyJson JSON object containing body data - * @param body Output body data structure - */ - static void ParseBodyFrame(const nlohmann::json& bodyJson, RokokoData::Body& body); - - /** - * Parses individual finger joint data from JSON - * @param bodyJson JSON object containing body data - * @param jointName Name of the joint to parse - * @param joint Output joint data structure (std::optional for stack allocation) - */ - static void ParseFingerJoint(const nlohmann::json& bodyJson, const std::string& jointName, - std::optional& joint); - - /** - * Parses actor data from JSON - * @param actorJson JSON object containing actor data - * @param actor Output actor data structure - * @return true if parsing successful - */ - static bool ParseActorData(const void* actorJson, RokokoData::ActorData& actor); - - /** - * Parses finger joint data from JSON - * @param fingerJson JSON object containing finger data - * @param finger Output finger data structure - * @return true if parsing successful - */ - static bool ParseFingerData(const void* fingerJson, RokokoData::ActorJointFrame& finger); - - /** - * Validates quaternion values - * @param x, y, z, w Quaternion components - * @return true if quaternion is valid - */ - static bool ValidateQuaternion(float x, float y, float z, float w); - - /** - * Normalizes quaternion values - * @param x, y, z, w Quaternion components (modified in place) - */ - static void NormalizeQuaternion(float& x, float& y, float& z, float& w); - }; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoGloveDevice_Fixed.vcxproj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoGloveDevice_Fixed.vcxproj deleted file mode 100644 index 2838c65e..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoGloveDevice_Fixed.vcxproj +++ /dev/null @@ -1,151 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE} - Win32Proj - RokokoGloveDevice_Fixed - 8.1 - RokokoGloveDevice_Fixed - - - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - - - - - - - - - - - - - true - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - C:\Program Files\OptiTrack\Motive\Motive.exe - WindowsLocalDebugger - - - false - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - C:\Program Files\OptiTrack\Motive\Motive.exe - WindowsLocalDebugger - - - - - - Level3 - Disabled - stdcpp17 - WIN32;_DEBUG;_WINDOWS;_USRDLL;ANALOGSYSTEM_IMPORTS;OPTITRACKPERIPHERALEXAMPLE_EXPORTS;%(PreprocessorDefinitions) - false - ..\include;..\external;..\external\nlohmann;%(AdditionalIncludeDirectories) - /utf-8 %(AdditionalOptions) - - - Windows - true - NotSet - PeripheralImport.lib;ws2_32.lib;%(AdditionalDependencies) - ..\lib;%(AdditionalLibraryDirectories) - - - false - - - - - Level3 - MaxSpeed - true - true - stdcpp17 - WIN32;NDEBUG;_WINDOWS;_USRDLL;OPTITRACKPERIPHERALEXAMPLE_EXPORTS;ANALOGSYSTEM_IMPORTS;%(PreprocessorDefinitions) - ..\include;..\external;..\external\nlohmann;%(AdditionalIncludeDirectories) - /utf-8 %(AdditionalOptions) - - - Windows - true - true - true - PeripheralImport.lib;ws2_32.lib;%(AdditionalDependencies) - ..\lib;%(AdditionalLibraryDirectories) - - - - - - - - - - - - - - - - - - false - - - false - - - - - - - - - - - - - - - - - - Document - - - - - - \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoGloveDevice_Fixed.vcxproj.filters b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoGloveDevice_Fixed.vcxproj.filters deleted file mode 100644 index 974cb17f..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoGloveDevice_Fixed.vcxproj.filters +++ /dev/null @@ -1,90 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - - - - - \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoUDPReceiver.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoUDPReceiver.cpp deleted file mode 100644 index ca607350..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoUDPReceiver.cpp +++ /dev/null @@ -1,307 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration - FIXED VERSION -//====================================================================================================== -/** - * FIXES APPLIED: - * 1. ✅ WSACleanup duplicate calls - Added reference counting - * 2. ✅ CPU 100% usage - Added select() with timeout instead of busy-wait - * 3. ✅ Thread safety - Using lock_guard consistently - */ - -#include "RokokoUDPReceiver.h" -#include -#include -#include - -namespace RokokoIntegration -{ - // Static members for WSA reference counting - std::atomic RokokoUDPReceiver::s_wsaRefCount{0}; - std::mutex RokokoUDPReceiver::s_wsaMutex; - - RokokoUDPReceiver::RokokoUDPReceiver() - : mSocket(INVALID_SOCKET) - , mIsRunning(false) - , mIsListening(false) - , mPacketsReceived(0) - , mBytesReceived(0) - , mLastPacketTime(0.0) - , mPort(14043) - , mBufferSize(65000) - , mWsaInitialized(false) - { - } - - RokokoUDPReceiver::~RokokoUDPReceiver() - { - StopListening(); - CloseSocket(); - } - - // FIX 1: WSA Reference Counting - bool RokokoUDPReceiver::InitializeWSA() - { - std::lock_guard lock(s_wsaMutex); - - if (s_wsaRefCount == 0) { - WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - return false; - } - } - - s_wsaRefCount++; - mWsaInitialized = true; - return true; - } - - void RokokoUDPReceiver::CleanupWSA() - { - if (!mWsaInitialized) { - return; - } - - std::lock_guard lock(s_wsaMutex); - - s_wsaRefCount--; - if (s_wsaRefCount == 0) { - WSACleanup(); - } - - mWsaInitialized = false; - } - - bool RokokoUDPReceiver::Initialize(int port) - { - try { - mPort = port; - - // FIX: Use reference-counted WSA initialization - if (!InitializeWSA()) { - SetError("WSAStartup failed"); - return false; - } - - // UDP 소켓 생성 - mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (mSocket == INVALID_SOCKET) { - SetError("Failed to create UDP socket"); - CleanupWSA(); - return false; - } - - // 소켓 옵션 설정 - int sendBufferSize = mBufferSize; - if (setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, - (char*)&sendBufferSize, sizeof(sendBufferSize)) == SOCKET_ERROR) { - SetError("Failed to set send buffer size"); - CloseSocket(); - return false; - } - - // 주소 구조체 설정 - struct sockaddr_in serverAddr; - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(mPort); - serverAddr.sin_addr.s_addr = INADDR_ANY; - - // 소켓 바인딩 - if (bind(mSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { - std::ostringstream oss; - oss << "Failed to bind to port " << mPort << ". Port may be in use."; - SetError(oss.str()); - CloseSocket(); - return false; - } - - // 논블로킹 모드는 select()와 함께 사용 - u_long mode = 1; - if (ioctlsocket(mSocket, FIONBIO, &mode) == SOCKET_ERROR) { - SetError("Failed to set non-blocking mode"); - CloseSocket(); - return false; - } - - return true; - - } catch (...) { - SetError("Exception during UDP initialization"); - return false; - } - } - - bool RokokoUDPReceiver::StartListening() - { - if (mSocket == INVALID_SOCKET) { - SetError("UDP socket not initialized"); - return false; - } - - if (mIsListening) { - SetError("Already listening"); - return false; - } - - try { - mIsRunning = true; - mIsListening = true; - - // 수신 스레드 시작 - mReceiveThread = std::thread(&RokokoUDPReceiver::ReceiveThread, this); - - return true; - - } catch (...) { - SetError("Exception starting receive thread"); - mIsRunning = false; - mIsListening = false; - return false; - } - } - - void RokokoUDPReceiver::StopListening() - { - mIsRunning = false; - mIsListening = false; - - if (mReceiveThread.joinable()) { - mReceiveThread.join(); - } - } - - void RokokoUDPReceiver::SetDataCallback(std::function&, const std::string&)> callback) - { - mDataCallback = callback; - } - - bool RokokoUDPReceiver::IsListening() const - { - return mIsListening; - } - - bool RokokoUDPReceiver::IsConnected() const - { - return mIsListening && mPacketsReceived > 0; - } - - std::string RokokoUDPReceiver::GetLastError() const - { - std::lock_guard lock(mErrorMutex); - return mLastError; - } - - void RokokoUDPReceiver::GetStatistics(uint64_t& packetsReceived, uint64_t& bytesReceived, double& lastPacketTime) const - { - std::lock_guard lock(mStatsMutex); - packetsReceived = mPacketsReceived; - bytesReceived = mBytesReceived; - lastPacketTime = mLastPacketTime; - } - - // FIX 2: Use select() to prevent CPU 100% usage - void RokokoUDPReceiver::ReceiveThread() - { - std::vector buffer(mBufferSize); - - while (mIsRunning) { - try { - // ✅ FIX: Use select() to wait for data efficiently - fd_set readSet; - FD_ZERO(&readSet); - FD_SET(mSocket, &readSet); - - // Timeout: 100ms (10 checks per second) - timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 100000; // 100ms - - int selectResult = select(0, &readSet, nullptr, nullptr, &timeout); - - if (selectResult > 0 && FD_ISSET(mSocket, &readSet)) { - // Data available - receive it - struct sockaddr_in senderAddr; - int senderAddrSize = sizeof(senderAddr); - - int bytesReceived = recvfrom(mSocket, - reinterpret_cast(buffer.data()), - mBufferSize, - 0, - (struct sockaddr*)&senderAddr, - &senderAddrSize); - - if (bytesReceived > 0) { - // 발신자 IP 주소 추출 - char senderIP[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &senderAddr.sin_addr, senderIP, INET_ADDRSTRLEN); - std::string senderIPStr(senderIP); - - // 데이터 처리 - ProcessIncomingData(buffer.data(), bytesReceived, senderIPStr); - - // 통계 업데이트 - UpdateStatistics(bytesReceived); - } - } else if (selectResult == SOCKET_ERROR) { - int error = WSAGetLastError(); - std::ostringstream oss; - oss << "select() error: " << error; - SetError(oss.str()); - break; - } - // selectResult == 0 means timeout - just continue loop (no busy-wait!) - - } catch (...) { - SetError("Exception in receive thread"); - break; - } - } - - mIsListening = false; - } - - void RokokoUDPReceiver::ProcessIncomingData(const uint8_t* data, int size, const std::string& senderIP) - { - if (mDataCallback && data && size > 0) { - try { - // 데이터를 벡터로 복사 - std::vector dataCopy(data, data + size); - - // 콜백 호출 - mDataCallback(dataCopy, senderIP); - - } catch (...) { - SetError("Exception in data callback"); - } - } - } - - void RokokoUDPReceiver::SetError(const std::string& error) - { - std::lock_guard lock(mErrorMutex); - mLastError = error; - - // 에러 로깅 (OptiTrack에서 확인 가능) - std::cerr << "[RokokoUDPReceiver] Error: " << error << std::endl; - } - - void RokokoUDPReceiver::UpdateStatistics(int packetSize) - { - std::lock_guard lock(mStatsMutex); - mPacketsReceived++; - mBytesReceived += packetSize; - mLastPacketTime = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch() - ).count() / 1000.0; - } - - void RokokoUDPReceiver::CloseSocket() - { - if (mSocket != INVALID_SOCKET) { - closesocket(mSocket); - mSocket = INVALID_SOCKET; - } - - // FIX: Use reference-counted cleanup - CleanupWSA(); - } -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoUDPReceiver.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoUDPReceiver.h deleted file mode 100644 index 331b152d..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/RokokoUDPReceiver.h +++ /dev/null @@ -1,154 +0,0 @@ -//====================================================================================================== -// Copyright 2025, Rokoko Glove OptiTrack Integration -//====================================================================================================== -/** - * RokokoUDPReceiver class provides UDP communication functionality for receiving Rokoko glove data. - * This receiver handles UDP socket communication and data reception from Rokoko Studio. - */ - -#pragma once -#include -#include -#include -#include -#include -#include -#include - -namespace RokokoIntegration -{ - class RokokoUDPReceiver - { - public: - /** - * Constructor - */ - RokokoUDPReceiver(); - - /** - * Destructor - */ - ~RokokoUDPReceiver(); - - /** - * Initializes UDP receiver - * @param port UDP port to listen on (default: 14043) - * @return true if initialization successful - */ - bool Initialize(int port = 14043); - - /** - * Starts listening for UDP data - * @return true if started successfully - */ - bool StartListening(); - - /** - * Stops listening for UDP data - */ - void StopListening(); - - /** - * Sets callback function for received data - * @param callback Function to call when data is received - */ - void SetDataCallback(std::function&, const std::string&)> callback); - - /** - * Checks if receiver is currently listening - * @return true if listening - */ - bool IsListening() const; - - /** - * Gets the current connection status - * @return true if connected and receiving data - */ - bool IsConnected() const; - - /** - * Gets the last error message - * @return Error message string - */ - std::string GetLastError() const; - - /** - * Gets connection statistics - * @param packetsReceived Number of packets received - * @param bytesReceived Total bytes received - * @param lastPacketTime Timestamp of last packet - */ - void GetStatistics(uint64_t& packetsReceived, uint64_t& bytesReceived, double& lastPacketTime) const; - - private: - // UDP socket - SOCKET mSocket; - - // Thread management - std::thread mReceiveThread; - std::atomic mIsRunning; - std::atomic mIsListening; - - // Data callback - std::function&, const std::string&)> mDataCallback; - - // Statistics - mutable std::mutex mStatsMutex; - uint64_t mPacketsReceived; - uint64_t mBytesReceived; - double mLastPacketTime; - - // Error handling - mutable std::mutex mErrorMutex; - std::string mLastError; - - // Configuration - int mPort; - int mBufferSize; - - /** - * Main receive thread function - */ - void ReceiveThread(); - - /** - * Processes incoming UDP data - * @param data Received data - * @param size Size of received data - * @param senderIP IP address of sender - */ - void ProcessIncomingData(const uint8_t* data, int size, const std::string& senderIP); - - /** - * Sets error message - * @param error Error message - */ - void SetError(const std::string& error); - - /** - * Updates statistics - * @param packetSize Size of received packet - */ - void UpdateStatistics(int packetSize); - - /** - * Closes UDP socket - */ - void CloseSocket(); - - // WSA reference counting (FIX: Prevent duplicate WSACleanup calls) - static std::atomic s_wsaRefCount; - static std::mutex s_wsaMutex; - bool mWsaInitialized; - - /** - * Initialize WSA with reference counting - */ - bool InitializeWSA(); - - /** - * Cleanup WSA with reference counting - */ - void CleanupWSA(); - }; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/dllcommon.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/dllcommon.h deleted file mode 100644 index 71f9b68b..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/dllcommon.h +++ /dev/null @@ -1,12 +0,0 @@ -//====================================================================================================== -// Copyright 2016, NaturalPoint Inc. -//====================================================================================================== - -// windows -#include -#define _CRTDBG_MAP_ALLOC -#include -#include -#define WIN32_LEAN_AND_MEAN -#include - diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/dllmain.cpp b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/dllmain.cpp deleted file mode 100644 index 31ce0fe6..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/dllmain.cpp +++ /dev/null @@ -1,103 +0,0 @@ -//====================================================================================================== -// Copyright 2016, NaturalPoint Inc. -//====================================================================================================== -// -// dllmain.cpp : Defines the entry point for the DLL application. -// - -#include "dllcommon.h" - -// stl -#include -#include -using namespace std; - -// OptiTrack Peripheral Device API -#include "IDeviceManager.h" -using namespace AnalogSystem; - -// local devices -#include "ExampleGloveDevice.h" -using namespace OptiTrackPluginDevices; - -int hostVersionMajor, hostVersionMinor, hostVersionRevision; - -#ifdef OPTITRACKPERIPHERALEXAMPLE_EXPORTS -#define OPTITRACKPERIPHERALEXAMPLE_API __declspec(dllexport) -#else -#define OPTITRACKPERIPHERALEXAMPLE_API __declspec(dllimport) -#endif - - -#ifdef __cplusplus -extern "C" { -#endif - -using namespace OptiTrackPluginDevices; -using namespace ExampleDevice; - - -/** - * Defines the version information of the plugin DLL within Motive. - */ -OPTITRACKPERIPHERALEXAMPLE_API void DLLVersion(int hostMajor, int hostMinor, int hostRevision, int& dllMajor, int& dllMinor, int& dllRevision) -{ - hostVersionMajor = hostMajor; - hostVersionMajor = hostMinor; - hostVersionMajor = hostRevision; - - // report this peripheral device's version to host - dllMajor = 1; - dllMinor = 0; - dllRevision = 0; -} - -/** - * Motive calls the following method on application start up. This register device factories with Motive - * so that the device can be instantiated when it's ready (1 factory per device) - */ -OPTITRACKPERIPHERALEXAMPLE_API int DLLEnumerateDeviceFactories(IDeviceManager* pDeviceManager) -{ - - list> availDFs; - - ExampleDevice::ExampleGlove_EnumerateDeviceFactories(pDeviceManager, availDFs); - for (list>::iterator iter = availDFs.begin(); iter != availDFs.end(); iter++) { - // transfers ownership of device factory to host - pDeviceManager->AddDevice(std::move(*iter)); - } - - return (int)availDFs.size(); -} - -/** - * The following method gets called on application shutdown. Proper shutdown should happen here; -* including termination of the process of the DLL and memory unload. - */ -OPTITRACKPERIPHERALEXAMPLE_API int PluginDLLUnload(IDeviceManager* pDeviceManager) -{ - // OPTIONAL: perform device DLL shutdown here - ExampleGlove_Shutdown(); - return 0; -} - -#ifdef __cplusplus -} -#endif - - -BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - break; - case DLL_THREAD_ATTACH: - break; - case DLL_THREAD_DETACH: - break; - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.c b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.c deleted file mode 100644 index 654bfdf3..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.c +++ /dev/null @@ -1,2722 +0,0 @@ -/* - LZ4 - Fast LZ compression algorithm - Copyright (C) 2011-2020, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ - -/*-************************************ -* Tuning parameters -**************************************/ -/* - * LZ4_HEAPMODE : - * Select how default compression functions will allocate memory for their hash table, - * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). - */ -#ifndef LZ4_HEAPMODE -# define LZ4_HEAPMODE 0 -#endif - -/* - * LZ4_ACCELERATION_DEFAULT : - * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 - */ -#define LZ4_ACCELERATION_DEFAULT 1 -/* - * LZ4_ACCELERATION_MAX : - * Any "acceleration" value higher than this threshold - * get treated as LZ4_ACCELERATION_MAX instead (fix #876) - */ -#define LZ4_ACCELERATION_MAX 65537 - - -/*-************************************ -* CPU Feature Detection -**************************************/ -/* LZ4_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets which assembly generation depends on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ -# if defined(__GNUC__) && \ - ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) -# define LZ4_FORCE_MEMORY_ACCESS 2 -# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) -# define LZ4_FORCE_MEMORY_ACCESS 1 -# endif -#endif - -/* - * LZ4_FORCE_SW_BITCOUNT - * Define this parameter if your target system or compiler does not support hardware bit count - */ -#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ -# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ -# define LZ4_FORCE_SW_BITCOUNT -#endif - - - -/*-************************************ -* Dependency -**************************************/ -/* - * LZ4_SRC_INCLUDED: - * Amalgamation flag, whether lz4.c is included - */ -#ifndef LZ4_SRC_INCLUDED -# define LZ4_SRC_INCLUDED 1 -#endif - -#ifndef LZ4_STATIC_LINKING_ONLY -#define LZ4_STATIC_LINKING_ONLY -#endif - -#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS -#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ -#endif - -#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ -#include "lz4.h" -/* see also "memory routines" below */ - - -/*-************************************ -* Compiler Options -**************************************/ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ -# include /* only present in VS2005+ */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ -#endif /* _MSC_VER */ - -#ifndef LZ4_FORCE_INLINE -# ifdef _MSC_VER /* Visual Studio */ -# define LZ4_FORCE_INLINE static __forceinline -# else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define LZ4_FORCE_INLINE static inline -# endif -# else -# define LZ4_FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -# endif /* _MSC_VER */ -#endif /* LZ4_FORCE_INLINE */ - -/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE - * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, - * together with a simple 8-byte copy loop as a fall-back path. - * However, this optimization hurts the decompression speed by >30%, - * because the execution does not go to the optimized loop - * for typical compressible data, and all of the preamble checks - * before going to the fall-back path become useless overhead. - * This optimization happens only with the -O3 flag, and -O2 generates - * a simple 8-byte copy loop. - * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 - * functions are annotated with __attribute__((optimize("O2"))), - * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute - * of LZ4_wildCopy8 does not affect the compression speed. - */ -#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) -# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) -# undef LZ4_FORCE_INLINE -# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) -#else -# define LZ4_FORCE_O2 -#endif - -#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) -# define expect(expr,value) (__builtin_expect ((expr),(value)) ) -#else -# define expect(expr,value) (expr) -#endif - -#ifndef likely -#define likely(expr) expect((expr) != 0, 1) -#endif -#ifndef unlikely -#define unlikely(expr) expect((expr) != 0, 0) -#endif - -/* Should the alignment test prove unreliable, for some reason, - * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ -#ifndef LZ4_ALIGN_TEST /* can be externally provided */ -# define LZ4_ALIGN_TEST 1 -#endif - - -/*-************************************ -* Memory routines -**************************************/ - -/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION : - * Disable relatively high-level LZ4/HC functions that use dynamic memory - * allocation functions (malloc(), calloc(), free()). - * - * Note that this is a compile-time switch. And since it disables - * public/stable LZ4 v1 API functions, we don't recommend using this - * symbol to generate a library for distribution. - * - * The following public functions are removed when this symbol is defined. - * - lz4 : LZ4_createStream, LZ4_freeStream, - * LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated) - * - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC, - * LZ4_createHC (deprecated), LZ4_freeHC (deprecated) - * - lz4frame, lz4file : All LZ4F_* functions - */ -#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) -# define ALLOC(s) lz4_error_memory_allocation_is_disabled -# define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled -# define FREEMEM(p) lz4_error_memory_allocation_is_disabled -#elif defined(LZ4_USER_MEMORY_FUNCTIONS) -/* memory management functions can be customized by user project. - * Below functions must exist somewhere in the Project - * and be available at link time */ -void* LZ4_malloc(size_t s); -void* LZ4_calloc(size_t n, size_t s); -void LZ4_free(void* p); -# define ALLOC(s) LZ4_malloc(s) -# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) -# define FREEMEM(p) LZ4_free(p) -#else -# include /* malloc, calloc, free */ -# define ALLOC(s) malloc(s) -# define ALLOC_AND_ZERO(s) calloc(1,s) -# define FREEMEM(p) free(p) -#endif - -#if ! LZ4_FREESTANDING -# include /* memset, memcpy */ -#endif -#if !defined(LZ4_memset) -# define LZ4_memset(p,v,s) memset((p),(v),(s)) -#endif -#define MEM_INIT(p,v,s) LZ4_memset((p),(v),(s)) - - -/*-************************************ -* Common Constants -**************************************/ -#define MINMATCH 4 - -#define WILDCOPYLENGTH 8 -#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ -#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ -#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ -#define FASTLOOP_SAFE_DISTANCE 64 -static const int LZ4_minLength = (MFLIMIT+1); - -#define KB *(1 <<10) -#define MB *(1 <<20) -#define GB *(1U<<30) - -#define LZ4_DISTANCE_ABSOLUTE_MAX 65535 -#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ -# error "LZ4_DISTANCE_MAX is too big : must be <= 65535" -#endif - -#define ML_BITS 4 -#define ML_MASK ((1U<=1) -# include -#else -# ifndef assert -# define assert(condition) ((void)0) -# endif -#endif - -#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ - -#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) -# include - static int g_debuglog_enable = 1; -# define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ - fprintf(stderr, __FILE__ ": "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " \n"); \ - } } -#else -# define DEBUGLOG(l, ...) {} /* disabled */ -#endif - -static int LZ4_isAligned(const void* ptr, size_t alignment) -{ - return ((size_t)ptr & (alignment -1)) == 0; -} - - -/*-************************************ -* Types -**************************************/ -#include -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# include - typedef uint8_t BYTE; - typedef uint16_t U16; - typedef uint32_t U32; - typedef int32_t S32; - typedef uint64_t U64; - typedef uintptr_t uptrval; -#else -# if UINT_MAX != 4294967295UL -# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" -# endif - typedef unsigned char BYTE; - typedef unsigned short U16; - typedef unsigned int U32; - typedef signed int S32; - typedef unsigned long long U64; - typedef size_t uptrval; /* generally true, except OpenVMS-64 */ -#endif - -#if defined(__x86_64__) - typedef U64 reg_t; /* 64-bits in x32 mode */ -#else - typedef size_t reg_t; /* 32-bits in x32 mode */ -#endif - -typedef enum { - notLimited = 0, - limitedOutput = 1, - fillOutput = 2 -} limitedOutput_directive; - - -/*-************************************ -* Reading and writing into memory -**************************************/ - -/** - * LZ4 relies on memcpy with a constant size being inlined. In freestanding - * environments, the compiler can't assume the implementation of memcpy() is - * standard compliant, so it can't apply its specialized memcpy() inlining - * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze - * memcpy() as if it were standard compliant, so it can inline it in freestanding - * environments. This is needed when decompressing the Linux Kernel, for example. - */ -#if !defined(LZ4_memcpy) -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) -# else -# define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) -# endif -#endif - -#if !defined(LZ4_memmove) -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4_memmove __builtin_memmove -# else -# define LZ4_memmove memmove -# endif -#endif - -static unsigned LZ4_isLittleEndian(void) -{ - const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ - return one.c[0]; -} - - -#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) -/* lie to the compiler about data alignment; use with caution */ - -static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } -static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } -static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } - -static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } -static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } - -#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; - -static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; } -static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; } -static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } - -static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; } -static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; } - -#else /* safe and portable access using memcpy() */ - -static U16 LZ4_read16(const void* memPtr) -{ - U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static U32 LZ4_read32(const void* memPtr) -{ - U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static reg_t LZ4_read_ARCH(const void* memPtr) -{ - reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static void LZ4_write16(void* memPtr, U16 value) -{ - LZ4_memcpy(memPtr, &value, sizeof(value)); -} - -static void LZ4_write32(void* memPtr, U32 value) -{ - LZ4_memcpy(memPtr, &value, sizeof(value)); -} - -#endif /* LZ4_FORCE_MEMORY_ACCESS */ - - -static U16 LZ4_readLE16(const void* memPtr) -{ - if (LZ4_isLittleEndian()) { - return LZ4_read16(memPtr); - } else { - const BYTE* p = (const BYTE*)memPtr; - return (U16)((U16)p[0] + (p[1]<<8)); - } -} - -static void LZ4_writeLE16(void* memPtr, U16 value) -{ - if (LZ4_isLittleEndian()) { - LZ4_write16(memPtr, value); - } else { - BYTE* p = (BYTE*)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ -LZ4_FORCE_INLINE -void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) -{ - BYTE* d = (BYTE*)dstPtr; - const BYTE* s = (const BYTE*)srcPtr; - BYTE* const e = (BYTE*)dstEnd; - - do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ -LZ4_FORCE_INLINE void -LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) -{ - BYTE* d = (BYTE*)dstPtr; - const BYTE* s = (const BYTE*)srcPtr; - BYTE* const e = (BYTE*)dstEnd; - - do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH - * - there is at least 8 bytes available to write after dstEnd */ -LZ4_FORCE_INLINE void -LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) -{ - BYTE v[8]; - - assert(dstEnd >= dstPtr + MINMATCH); - - switch(offset) { - case 1: - MEM_INIT(v, *srcPtr, 8); - break; - case 2: - LZ4_memcpy(v, srcPtr, 2); - LZ4_memcpy(&v[2], srcPtr, 2); -#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ -# pragma warning(push) -# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */ -#endif - LZ4_memcpy(&v[4], v, 4); -#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ -# pragma warning(pop) -#endif - break; - case 4: - LZ4_memcpy(v, srcPtr, 4); - LZ4_memcpy(&v[4], srcPtr, 4); - break; - default: - LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); - return; - } - - LZ4_memcpy(dstPtr, v, 8); - dstPtr += 8; - while (dstPtr < dstEnd) { - LZ4_memcpy(dstPtr, v, 8); - dstPtr += 8; - } -} -#endif - - -/*-************************************ -* Common functions -**************************************/ -static unsigned LZ4_NbCommonBytes (reg_t val) -{ - assert(val != 0); - if (LZ4_isLittleEndian()) { - if (sizeof(val) == 8) { -# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT) -/*-************************************************************************************************* -* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. -* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics -* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC. -****************************************************************************************************/ -# if defined(__clang__) && (__clang_major__ < 10) - /* Avoid undefined clang-cl intrinsics issue. - * See https://github.com/lz4/lz4/pull/1017 for details. */ - return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3; -# else - /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ - return (unsigned)_tzcnt_u64(val) >> 3; -# endif -# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanForward64(&r, (U64)val); - return (unsigned)r >> 3; -# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_ctzll((U64)val) >> 3; -# else - const U64 m = 0x0101010101010101ULL; - val ^= val - 1; - return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); -# endif - } else /* 32 bits */ { -# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r; - _BitScanForward(&r, (U32)val); - return (unsigned)r >> 3; -# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_ctz((U32)val) >> 3; -# else - const U32 m = 0x01010101; - return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; -# endif - } - } else /* Big Endian CPU */ { - if (sizeof(val)==8) { -# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_clzll((U64)val) >> 3; -# else -#if 1 - /* this method is probably faster, - * but adds a 128 bytes lookup table */ - static const unsigned char ctz7_tab[128] = { - 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - }; - U64 const mask = 0x0101010101010101ULL; - U64 const t = (((val >> 8) - mask) | val) & mask; - return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; -#else - /* this method doesn't consume memory space like the previous one, - * but it contains several branches, - * that may end up slowing execution */ - static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. - Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. - Note that this code path is never triggered in 32-bits mode. */ - unsigned r; - if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -#endif -# endif - } else /* 32 bits */ { -# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_clz((U32)val) >> 3; -# else - val >>= 8; - val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | - (val + 0x00FF0000)) >> 24; - return (unsigned)val ^ 3; -# endif - } - } -} - - -#define STEPSIZE sizeof(reg_t) -LZ4_FORCE_INLINE -unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) -{ - const BYTE* const pStart = pIn; - - if (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { - pIn+=STEPSIZE; pMatch+=STEPSIZE; - } else { - return LZ4_NbCommonBytes(diff); - } } - - while (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } - pIn += LZ4_NbCommonBytes(diff); - return (unsigned)(pIn - pStart); - } - - if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } - if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } - if ((pIn compression run slower on incompressible data */ - - -/*-************************************ -* Local Structures and types -**************************************/ -typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; - -/** - * This enum distinguishes several different modes of accessing previous - * content in the stream. - * - * - noDict : There is no preceding content. - * - withPrefix64k : Table entries up to ctx->dictSize before the current blob - * blob being compressed are valid and refer to the preceding - * content (of length ctx->dictSize), which is available - * contiguously preceding in memory the content currently - * being compressed. - * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere - * else in memory, starting at ctx->dictionary with length - * ctx->dictSize. - * - usingDictCtx : Everything concerning the preceding content is - * in a separate context, pointed to by ctx->dictCtx. - * ctx->dictionary, ctx->dictSize, and table entries - * in the current context that refer to positions - * preceding the beginning of the current compression are - * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx - * ->dictSize describe the location and size of the preceding - * content, and matches are found by looking in the ctx - * ->dictCtx->hashTable. - */ -typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; -typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; - - -/*-************************************ -* Local Utils -**************************************/ -int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } -const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } -int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } -int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); } - - -/*-**************************************** -* Internal Definitions, used only in Tests -*******************************************/ -#if defined (__cplusplus) -extern "C" { -#endif - -int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); - -int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, - int compressedSize, int maxOutputSize, - const void* dictStart, size_t dictSize); -int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, - int compressedSize, int targetOutputSize, int dstCapacity, - const void* dictStart, size_t dictSize); -#if defined (__cplusplus) -} -#endif - -/*-****************************** -* Compression functions -********************************/ -LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) -{ - if (tableType == byU16) - return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); - else - return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); -} - -LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) -{ - const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; - if (LZ4_isLittleEndian()) { - const U64 prime5bytes = 889523592379ULL; - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - } else { - const U64 prime8bytes = 11400714785074694791ULL; - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); - } -} - -LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) -{ - if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); - return LZ4_hash4(LZ4_read32(p), tableType); -} - -LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) -{ - switch (tableType) - { - default: /* fallthrough */ - case clearedTable: { /* illegal! */ assert(0); return; } - case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } - case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) -{ - switch (tableType) - { - default: /* fallthrough */ - case clearedTable: /* fallthrough */ - case byPtr: { /* illegal! */ assert(0); return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } - case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, - void* tableBase, tableType_t const tableType, - const BYTE* srcBase) -{ - switch (tableType) - { - case clearedTable: { /* illegal! */ assert(0); return; } - case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } - case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); -} - -/* LZ4_getIndexOnHash() : - * Index of match position registered in hash table. - * hash position must be calculated by using base+index, or dictBase+index. - * Assumption 1 : only valid if tableType == byU32 or byU16. - * Assumption 2 : h is presumed valid (within limits of hash table) - */ -LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) -{ - LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); - if (tableType == byU32) { - const U32* const hashTable = (const U32*) tableBase; - assert(h < (1U << (LZ4_MEMORY_USAGE-2))); - return hashTable[h]; - } - if (tableType == byU16) { - const U16* const hashTable = (const U16*) tableBase; - assert(h < (1U << (LZ4_MEMORY_USAGE-1))); - return hashTable[h]; - } - assert(0); return 0; /* forbidden case */ -} - -static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } - if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } - { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ -} - -LZ4_FORCE_INLINE const BYTE* -LZ4_getPosition(const BYTE* p, - const void* tableBase, tableType_t tableType, - const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); -} - -LZ4_FORCE_INLINE void -LZ4_prepareTable(LZ4_stream_t_internal* const cctx, - const int inputSize, - const tableType_t tableType) { - /* If the table hasn't been used, it's guaranteed to be zeroed out, and is - * therefore safe to use no matter what mode we're in. Otherwise, we figure - * out if it's safe to leave as is or whether it needs to be reset. - */ - if ((tableType_t)cctx->tableType != clearedTable) { - assert(inputSize >= 0); - if ((tableType_t)cctx->tableType != tableType - || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) - || ((tableType == byU32) && cctx->currentOffset > 1 GB) - || tableType == byPtr - || inputSize >= 4 KB) - { - DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); - MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); - cctx->currentOffset = 0; - cctx->tableType = (U32)clearedTable; - } else { - DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); - } - } - - /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, - * is faster than compressing without a gap. - * However, compressing with currentOffset == 0 is faster still, - * so we preserve that case. - */ - if (cctx->currentOffset != 0 && tableType == byU32) { - DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); - cctx->currentOffset += 64 KB; - } - - /* Finally, clear history */ - cctx->dictCtx = NULL; - cctx->dictionary = NULL; - cctx->dictSize = 0; -} - -/** LZ4_compress_generic() : - * inlined, to ensure branches are decided at compilation time. - * Presumed already validated at this stage: - * - source != NULL - * - inputSize > 0 - */ -LZ4_FORCE_INLINE int LZ4_compress_generic_validated( - LZ4_stream_t_internal* const cctx, - const char* const source, - char* const dest, - const int inputSize, - int* inputConsumed, /* only written when outputDirective == fillOutput */ - const int maxOutputSize, - const limitedOutput_directive outputDirective, - const tableType_t tableType, - const dict_directive dictDirective, - const dictIssue_directive dictIssue, - const int acceleration) -{ - int result; - const BYTE* ip = (const BYTE*) source; - - U32 const startIndex = cctx->currentOffset; - const BYTE* base = (const BYTE*) source - startIndex; - const BYTE* lowLimit; - - const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; - const BYTE* const dictionary = - dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; - const U32 dictSize = - dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; - const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ - - int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); - U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ - const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; - const BYTE* anchor = (const BYTE*) source; - const BYTE* const iend = ip + inputSize; - const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; - const BYTE* const matchlimit = iend - LASTLITERALS; - - /* the dictCtx currentOffset is indexed on the start of the dictionary, - * while a dictionary in the current context precedes the currentOffset */ - const BYTE* dictBase = (dictionary == NULL) ? NULL : - (dictDirective == usingDictCtx) ? - dictionary + dictSize - dictCtx->currentOffset : - dictionary + dictSize - startIndex; - - BYTE* op = (BYTE*) dest; - BYTE* const olimit = op + maxOutputSize; - - U32 offset = 0; - U32 forwardH; - - DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); - assert(ip != NULL); - /* If init conditions are not met, we don't have to mark stream - * as having dirty context, since no action was taken yet */ - if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ - if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ - if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ - assert(acceleration >= 1); - - lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); - - /* Update context state */ - if (dictDirective == usingDictCtx) { - /* Subsequent linked blocks can't use the dictionary. */ - /* Instead, they use the block we just compressed. */ - cctx->dictCtx = NULL; - cctx->dictSize = (U32)inputSize; - } else { - cctx->dictSize += (U32)inputSize; - } - cctx->currentOffset += (U32)inputSize; - cctx->tableType = (U32)tableType; - - if (inputSizehashTable, tableType, base); - ip++; forwardH = LZ4_hashPosition(ip, tableType); - - /* Main Loop */ - for ( ; ; ) { - const BYTE* match; - BYTE* token; - const BYTE* filledIp; - - /* Find a match */ - if (tableType == byPtr) { - const BYTE* forwardIp = ip; - int step = 1; - int searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); - - } while ( (match+LZ4_DISTANCE_MAX < ip) - || (LZ4_read32(match) != LZ4_read32(ip)) ); - - } else { /* byU32, byU16 */ - - const BYTE* forwardIp = ip; - int step = 1; - int searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - U32 const current = (U32)(forwardIp - base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex <= current); - assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - assert(tableType == byU32); - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - matchIndex += dictDelta; /* make dictCtx index comparable with current context */ - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else if (dictDirective == usingExtDict) { - if (matchIndex < startIndex) { - DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); - assert(startIndex - matchIndex >= MINMATCH); - assert(dictBase); - match = dictBase + matchIndex; - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else { /* single continuous memory segment */ - match = base + matchIndex; - } - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - - DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); - if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ - assert(matchIndex < current); - if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) - && (matchIndex+LZ4_DISTANCE_MAX < current)) { - continue; - } /* too far */ - assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ - - if (LZ4_read32(match) == LZ4_read32(ip)) { - if (maybe_extMem) offset = current - matchIndex; - break; /* match found */ - } - - } while(1); - } - - /* Catch up */ - filledIp = ip; - while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } - - /* Encode Literals */ - { unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ - (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - if ((outputDirective == fillOutput) && - (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { - op--; - goto _last_literals; - } - if (litLength >= RUN_MASK) { - int len = (int)(litLength - RUN_MASK); - *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; - *op++ = (BYTE)len; - } - else *token = (BYTE)(litLength< olimit)) { - /* the match was too close to the end, rewind and go to last literals */ - op = token; - goto _last_literals; - } - - /* Encode Offset */ - if (maybe_extMem) { /* static test */ - DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); - assert(offset <= LZ4_DISTANCE_MAX && offset > 0); - LZ4_writeLE16(op, (U16)offset); op+=2; - } else { - DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); - assert(ip-match <= LZ4_DISTANCE_MAX); - LZ4_writeLE16(op, (U16)(ip - match)); op+=2; - } - - /* Encode MatchLength */ - { unsigned matchCode; - - if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) - && (lowLimit==dictionary) /* match within extDict */ ) { - const BYTE* limit = ip + (dictEnd-match); - assert(dictEnd > match); - if (limit > matchlimit) limit = matchlimit; - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); - ip += (size_t)matchCode + MINMATCH; - if (ip==limit) { - unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); - matchCode += more; - ip += more; - } - DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); - } else { - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); - ip += (size_t)matchCode + MINMATCH; - DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); - } - - if ((outputDirective) && /* Check output buffer overflow */ - (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { - if (outputDirective == fillOutput) { - /* Match description too long : reduce it */ - U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; - ip -= matchCode - newMatchCode; - assert(newMatchCode < matchCode); - matchCode = newMatchCode; - if (unlikely(ip <= filledIp)) { - /* We have already filled up to filledIp so if ip ends up less than filledIp - * we have positions in the hash table beyond the current position. This is - * a problem if we reuse the hash table. So we have to remove these positions - * from the hash table. - */ - const BYTE* ptr; - DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); - for (ptr = ip; ptr <= filledIp; ++ptr) { - U32 const h = LZ4_hashPosition(ptr, tableType); - LZ4_clearHash(h, cctx->hashTable, tableType); - } - } - } else { - assert(outputDirective == limitedOutput); - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - } - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LZ4_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*255) { - op+=4; - LZ4_write32(op, 0xFFFFFFFF); - matchCode -= 4*255; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else - *token += (BYTE)(matchCode); - } - /* Ensure we have enough space for the last literals. */ - assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); - - anchor = ip; - - /* Test end of chunk */ - if (ip >= mflimitPlusOne) break; - - /* Fill table */ - LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); - - /* Test next position */ - if (tableType == byPtr) { - - match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); - LZ4_putPosition(ip, cctx->hashTable, tableType, base); - if ( (match+LZ4_DISTANCE_MAX >= ip) - && (LZ4_read32(match) == LZ4_read32(ip)) ) - { token=op++; *token=0; goto _next_match; } - - } else { /* byU32, byU16 */ - - U32 const h = LZ4_hashPosition(ip, tableType); - U32 const current = (U32)(ip-base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex < current); - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - matchIndex += dictDelta; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else if (dictDirective==usingExtDict) { - if (matchIndex < startIndex) { - assert(dictBase); - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else { /* single memory segment */ - match = base + matchIndex; - } - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - assert(matchIndex < current); - if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) - && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) - && (LZ4_read32(match) == LZ4_read32(ip)) ) { - token=op++; - *token=0; - if (maybe_extMem) offset = current - matchIndex; - DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", - (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); - goto _next_match; - } - } - - /* Prepare next loop */ - forwardH = LZ4_hashPosition(++ip, tableType); - - } - -_last_literals: - /* Encode Last Literals */ - { size_t lastRun = (size_t)(iend - anchor); - if ( (outputDirective) && /* Check output buffer overflow */ - (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { - if (outputDirective == fillOutput) { - /* adapt lastRun to fill 'dst' */ - assert(olimit >= op); - lastRun = (size_t)(olimit-op) - 1/*token*/; - lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ - } else { - assert(outputDirective == limitedOutput); - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - } - DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; - *op++ = (BYTE) accumulator; - } else { - *op++ = (BYTE)(lastRun< 0); - DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); - return result; -} - -/** LZ4_compress_generic() : - * inlined, to ensure branches are decided at compilation time; - * takes care of src == (NULL, 0) - * and forward the rest to LZ4_compress_generic_validated */ -LZ4_FORCE_INLINE int LZ4_compress_generic( - LZ4_stream_t_internal* const cctx, - const char* const src, - char* const dst, - const int srcSize, - int *inputConsumed, /* only written when outputDirective == fillOutput */ - const int dstCapacity, - const limitedOutput_directive outputDirective, - const tableType_t tableType, - const dict_directive dictDirective, - const dictIssue_directive dictIssue, - const int acceleration) -{ - DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", - srcSize, dstCapacity); - - if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ - if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ - if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ - DEBUGLOG(5, "Generating an empty block"); - assert(outputDirective == notLimited || dstCapacity >= 1); - assert(dst != NULL); - dst[0] = 0; - if (outputDirective == fillOutput) { - assert (inputConsumed != NULL); - *inputConsumed = 0; - } - return 1; - } - assert(src != NULL); - - return LZ4_compress_generic_validated(cctx, src, dst, srcSize, - inputConsumed, /* only written into if outputDirective == fillOutput */ - dstCapacity, outputDirective, - tableType, dictDirective, dictIssue, acceleration); -} - - -int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; - assert(ctx != NULL); - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - if (maxOutputSize >= LZ4_compressBound(inputSize)) { - if (inputSize < LZ4_64Klimit) { - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (inputSize < LZ4_64Klimit) { - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - -/** - * LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. It is only safe - * to call if the state buffer is known to be correctly initialized already - * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of - * "correctly initialized"). - */ -int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) -{ - LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - - if (dstCapacity >= LZ4_compressBound(srcSize)) { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - - -int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - int result; -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctxPtr == NULL) return 0; -#else - LZ4_stream_t ctx; - LZ4_stream_t* const ctxPtr = &ctx; -#endif - result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); - -#if (LZ4_HEAPMODE) - FREEMEM(ctxPtr); -#endif - return result; -} - - -int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) -{ - return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); -} - - -/* Note!: This function leaves the stream in an unclean/broken state! - * It is not safe to subsequently use the same state with a _fastReset() or - * _continue() call without resetting it. */ -static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) -{ - void* const s = LZ4_initStream(state, sizeof (*state)); - assert(s != NULL); (void)s; - - if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ - return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); - } else { - if (*srcSizePtr < LZ4_64Klimit) { - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); - } else { - tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); - } } -} - - -int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) -{ -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctx == NULL) return 0; -#else - LZ4_stream_t ctxBody; - LZ4_stream_t* ctx = &ctxBody; -#endif - - int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); - -#if (LZ4_HEAPMODE) - FREEMEM(ctx); -#endif - return result; -} - - - -/*-****************************** -* Streaming functions -********************************/ - -#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) -LZ4_stream_t* LZ4_createStream(void) -{ - LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); - LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal)); - DEBUGLOG(4, "LZ4_createStream %p", lz4s); - if (lz4s == NULL) return NULL; - LZ4_initStream(lz4s, sizeof(*lz4s)); - return lz4s; -} -#endif - -static size_t LZ4_stream_t_alignment(void) -{ -#if LZ4_ALIGN_TEST - typedef struct { char c; LZ4_stream_t t; } t_a; - return sizeof(t_a) - sizeof(LZ4_stream_t); -#else - return 1; /* effectively disabled */ -#endif -} - -LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) -{ - DEBUGLOG(5, "LZ4_initStream"); - if (buffer == NULL) { return NULL; } - if (size < sizeof(LZ4_stream_t)) { return NULL; } - if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; - MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); - return (LZ4_stream_t*)buffer; -} - -/* resetStream is now deprecated, - * prefer initStream() which is more general */ -void LZ4_resetStream (LZ4_stream_t* LZ4_stream) -{ - DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); - MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); -} - -void LZ4_resetStream_fast(LZ4_stream_t* ctx) { - LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); -} - -#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) -int LZ4_freeStream (LZ4_stream_t* LZ4_stream) -{ - if (!LZ4_stream) return 0; /* support free on NULL */ - DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); - FREEMEM(LZ4_stream); - return (0); -} -#endif - - -#define HASH_UNIT sizeof(reg_t) -int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) -{ - LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; - const tableType_t tableType = byU32; - const BYTE* p = (const BYTE*)dictionary; - const BYTE* const dictEnd = p + dictSize; - const BYTE* base; - - DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); - - /* It's necessary to reset the context, - * and not just continue it with prepareTable() - * to avoid any risk of generating overflowing matchIndex - * when compressing using this dictionary */ - LZ4_resetStream(LZ4_dict); - - /* We always increment the offset by 64 KB, since, if the dict is longer, - * we truncate it to the last 64k, and if it's shorter, we still want to - * advance by a whole window length so we can provide the guarantee that - * there are only valid offsets in the window, which allows an optimization - * in LZ4_compress_fast_continue() where it uses noDictIssue even when the - * dictionary isn't a full 64k. */ - dict->currentOffset += 64 KB; - - if (dictSize < (int)HASH_UNIT) { - return 0; - } - - if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; - base = dictEnd - dict->currentOffset; - dict->dictionary = p; - dict->dictSize = (U32)(dictEnd - p); - dict->tableType = (U32)tableType; - - while (p <= dictEnd-HASH_UNIT) { - LZ4_putPosition(p, dict->hashTable, tableType, base); - p+=3; - } - - return (int)dict->dictSize; -} - -void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) -{ - const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : - &(dictionaryStream->internal_donotuse); - - DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", - workingStream, dictionaryStream, - dictCtx != NULL ? dictCtx->dictSize : 0); - - if (dictCtx != NULL) { - /* If the current offset is zero, we will never look in the - * external dictionary context, since there is no value a table - * entry can take that indicate a miss. In that case, we need - * to bump the offset to something non-zero. - */ - if (workingStream->internal_donotuse.currentOffset == 0) { - workingStream->internal_donotuse.currentOffset = 64 KB; - } - - /* Don't actually attach an empty dictionary. - */ - if (dictCtx->dictSize == 0) { - dictCtx = NULL; - } - } - workingStream->internal_donotuse.dictCtx = dictCtx; -} - - -static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) -{ - assert(nextSize >= 0); - if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ - /* rescale hash table */ - U32 const delta = LZ4_dict->currentOffset - 64 KB; - const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; - int i; - DEBUGLOG(4, "LZ4_renormDictT"); - for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; - else LZ4_dict->hashTable[i] -= delta; - } - LZ4_dict->currentOffset = 64 KB; - if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; - LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; - } -} - - -int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, - const char* source, char* dest, - int inputSize, int maxOutputSize, - int acceleration) -{ - const tableType_t tableType = byU32; - LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse; - const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; - - DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); - - LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */ - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - - /* invalidate tiny dictionaries */ - if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */ - && (dictEnd != source) /* prefix mode */ - && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */ - && (streamPtr->dictCtx == NULL) /* usingDictCtx */ - ) { - DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); - /* remove dictionary existence from history, to employ faster prefix mode */ - streamPtr->dictSize = 0; - streamPtr->dictionary = (const BYTE*)source; - dictEnd = source; - } - - /* Check overlapping input/dictionary space */ - { const char* const sourceEnd = source + inputSize; - if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) { - streamPtr->dictSize = (U32)(dictEnd - sourceEnd); - if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; - if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; - streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize; - } - } - - /* prefix mode : source data follows dictionary */ - if (dictEnd == source) { - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) - return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); - else - return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); - } - - /* external dictionary mode */ - { int result; - if (streamPtr->dictCtx) { - /* We depend here on the fact that dictCtx'es (produced by - * LZ4_loadDict) guarantee that their tables contain no references - * to offsets between dictCtx->currentOffset - 64 KB and - * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe - * to use noDictIssue even when the dict isn't a full 64 KB. - */ - if (inputSize > 4 KB) { - /* For compressing large blobs, it is faster to pay the setup - * cost to copy the dictionary's tables into the active context, - * so that the compression loop is only looking into one table. - */ - LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); - } - } else { /* small data <= 4 KB */ - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } - } - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)inputSize; - return result; - } -} - - -/* Hidden debug function, to force-test external dictionary mode */ -int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) -{ - LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; - int result; - - LZ4_renormDictT(streamPtr, srcSize); - - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); - } - - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)srcSize; - - return result; -} - - -/*! LZ4_saveDict() : - * If previously compressed data block is not guaranteed to remain available at its memory location, - * save it into a safer place (char* safeBuffer). - * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable, - * one can therefore call LZ4_compress_fast_continue() right after. - * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. - */ -int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) -{ - LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; - - DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); - - if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ - if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } - - if (safeBuffer == NULL) assert(dictSize == 0); - if (dictSize > 0) { - const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; - assert(dict->dictionary); - LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize); - } - - dict->dictionary = (const BYTE*)safeBuffer; - dict->dictSize = (U32)dictSize; - - return dictSize; -} - - - -/*-******************************* - * Decompression functions - ********************************/ - -typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; - -#undef MIN -#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) - - -/* variant for decompress_unsafe() - * does not know end of input - * presumes input is well formed - * note : will consume at least one byte */ -size_t read_long_length_no_check(const BYTE** pp) -{ - size_t b, l = 0; - do { b = **pp; (*pp)++; l += b; } while (b==255); - DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1) - return l; -} - -/* core decoder variant for LZ4_decompress_fast*() - * for legacy support only : these entry points are deprecated. - * - Presumes input is correctly formed (no defense vs malformed inputs) - * - Does not know input size (presume input buffer is "large enough") - * - Decompress a full block (only) - * @return : nb of bytes read from input. - * Note : this variant is not optimized for speed, just for maintenance. - * the goal is to remove support of decompress_fast*() variants by v2.0 -**/ -LZ4_FORCE_INLINE int -LZ4_decompress_unsafe_generic( - const BYTE* const istart, - BYTE* const ostart, - int decompressedSize, - - size_t prefixSize, - const BYTE* const dictStart, /* only if dict==usingExtDict */ - const size_t dictSize /* note: =0 if dictStart==NULL */ - ) -{ - const BYTE* ip = istart; - BYTE* op = (BYTE*)ostart; - BYTE* const oend = ostart + decompressedSize; - const BYTE* const prefixStart = ostart - prefixSize; - - DEBUGLOG(5, "LZ4_decompress_unsafe_generic"); - if (dictStart == NULL) assert(dictSize == 0); - - while (1) { - /* start new sequence */ - unsigned token = *ip++; - - /* literals */ - { size_t ll = token >> ML_BITS; - if (ll==15) { - /* long literal length */ - ll += read_long_length_no_check(&ip); - } - if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */ - LZ4_memmove(op, ip, ll); /* support in-place decompression */ - op += ll; - ip += ll; - if ((size_t)(oend-op) < MFLIMIT) { - if (op==oend) break; /* end of block */ - DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op); - /* incorrect end of block : - * last match must start at least MFLIMIT==12 bytes before end of output block */ - return -1; - } } - - /* match */ - { size_t ml = token & 15; - size_t const offset = LZ4_readLE16(ip); - ip+=2; - - if (ml==15) { - /* long literal length */ - ml += read_long_length_no_check(&ip); - } - ml += MINMATCH; - - if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */ - - { const BYTE* match = op - offset; - - /* out of range */ - if (offset > (size_t)(op - prefixStart) + dictSize) { - DEBUGLOG(6, "offset out of range"); - return -1; - } - - /* check special case : extDict */ - if (offset > (size_t)(op - prefixStart)) { - /* extDict scenario */ - const BYTE* const dictEnd = dictStart + dictSize; - const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart)); - size_t const extml = (size_t)(dictEnd - extMatch); - if (extml > ml) { - /* match entirely within extDict */ - LZ4_memmove(op, extMatch, ml); - op += ml; - ml = 0; - } else { - /* match split between extDict & prefix */ - LZ4_memmove(op, extMatch, extml); - op += extml; - ml -= extml; - } - match = prefixStart; - } - - /* match copy - slow variant, supporting overlap copy */ - { size_t u; - for (u=0; u= ipmax before start of loop. Returns initial_error if so. - * @error (output) - error code. Must be set to 0 before call. -**/ -typedef size_t Rvl_t; -static const Rvl_t rvl_error = (Rvl_t)(-1); -LZ4_FORCE_INLINE Rvl_t -read_variable_length(const BYTE** ip, const BYTE* ilimit, - int initial_check) -{ - Rvl_t s, length = 0; - assert(ip != NULL); - assert(*ip != NULL); - assert(ilimit != NULL); - if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */ - return rvl_error; - } - do { - s = **ip; - (*ip)++; - length += s; - if (unlikely((*ip) > ilimit)) { /* read limit reached */ - return rvl_error; - } - /* accumulator overflow detection (32-bit mode only) */ - if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { - return rvl_error; - } - } while (s==255); - - return length; -} - -/*! LZ4_decompress_generic() : - * This generic decompression function covers all use cases. - * It shall be instantiated several times, using different sets of directives. - * Note that it is important for performance that this function really get inlined, - * in order to remove useless branches during compilation optimization. - */ -LZ4_FORCE_INLINE int -LZ4_decompress_generic( - const char* const src, - char* const dst, - int srcSize, - int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ - - earlyEnd_directive partialDecoding, /* full, partial */ - dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ - const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ - const BYTE* const dictStart, /* only if dict==usingExtDict */ - const size_t dictSize /* note : = 0 if noDict */ - ) -{ - if ((src == NULL) || (outputSize < 0)) { return -1; } - - { const BYTE* ip = (const BYTE*) src; - const BYTE* const iend = ip + srcSize; - - BYTE* op = (BYTE*) dst; - BYTE* const oend = op + outputSize; - BYTE* cpy; - - const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; - - const int checkOffset = (dictSize < (int)(64 KB)); - - - /* Set up the "end" pointers for the shortcut. */ - const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/; - const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/; - - const BYTE* match; - size_t offset; - unsigned token; - size_t length; - - - DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); - - /* Special cases */ - assert(lowPrefix <= op); - if (unlikely(outputSize==0)) { - /* Empty output buffer */ - if (partialDecoding) return 0; - return ((srcSize==1) && (*ip==0)) ? 0 : -1; - } - if (unlikely(srcSize==0)) { return -1; } - - /* LZ4_FAST_DEC_LOOP: - * designed for modern OoO performance cpus, - * where copying reliably 32-bytes is preferable to an unpredictable branch. - * note : fast loop may show a regression for some client arm chips. */ -#if LZ4_FAST_DEC_LOOP - if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { - DEBUGLOG(6, "skip fast decode loop"); - goto safe_decode; - } - - /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */ - while (1) { - /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ - assert(oend - op >= FASTLOOP_SAFE_DISTANCE); - assert(ip < iend); - token = *ip++; - length = token >> ML_BITS; /* literal length */ - - /* decode literal length */ - if (length == RUN_MASK) { - size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); - if (addl == rvl_error) { goto _output_error; } - length += addl; - if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ - - /* copy literals */ - cpy = op+length; - LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } - LZ4_wildCopy32(op, ip, cpy); - ip += length; op = cpy; - } else { - cpy = op+length; - DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); - /* We don't need to check oend, since we check it once for each loop below */ - if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } - /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */ - LZ4_memcpy(op, ip, 16); - ip += length; op = cpy; - } - - /* get offset */ - offset = LZ4_readLE16(ip); ip+=2; - match = op - offset; - assert(match <= op); /* overflow check */ - - /* get matchlength */ - length = token & ML_MASK; - - if (length == ML_MASK) { - size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); - if (addl == rvl_error) { goto _output_error; } - length += addl; - length += MINMATCH; - if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { - goto safe_match_copy; - } - } else { - length += MINMATCH; - if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { - goto safe_match_copy; - } - - /* Fastpath check: skip LZ4_wildCopy32 when true */ - if ((dict == withPrefix64k) || (match >= lowPrefix)) { - if (offset >= 8) { - assert(match >= lowPrefix); - assert(match <= op); - assert(op + 18 <= oend); - - LZ4_memcpy(op, match, 8); - LZ4_memcpy(op+8, match+8, 8); - LZ4_memcpy(op+16, match+16, 2); - op += length; - continue; - } } } - - if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - /* match starting within external dictionary */ - if ((dict==usingExtDict) && (match < lowPrefix)) { - assert(dictEnd != NULL); - if (unlikely(op+length > oend-LASTLITERALS)) { - if (partialDecoding) { - DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); - length = MIN(length, (size_t)(oend-op)); - } else { - goto _output_error; /* end-of-block condition violated */ - } } - - if (length <= (size_t)(lowPrefix-match)) { - /* match fits entirely within external dictionary : just copy */ - LZ4_memmove(op, dictEnd - (lowPrefix-match), length); - op += length; - } else { - /* match stretches into both external dictionary and current block */ - size_t const copySize = (size_t)(lowPrefix - match); - size_t const restSize = length - copySize; - LZ4_memcpy(op, dictEnd - copySize, copySize); - op += copySize; - if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ - BYTE* const endOfMatch = op + restSize; - const BYTE* copyFrom = lowPrefix; - while (op < endOfMatch) { *op++ = *copyFrom++; } - } else { - LZ4_memcpy(op, lowPrefix, restSize); - op += restSize; - } } - continue; - } - - /* copy match within block */ - cpy = op + length; - - assert((op <= oend) && (oend-op >= 32)); - if (unlikely(offset<16)) { - LZ4_memcpy_using_offset(op, match, cpy, offset); - } else { - LZ4_wildCopy32(op, match, cpy); - } - - op = cpy; /* wildcopy correction */ - } - safe_decode: -#endif - - /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ - while (1) { - assert(ip < iend); - token = *ip++; - length = token >> ML_BITS; /* literal length */ - - /* A two-stage shortcut for the most common case: - * 1) If the literal length is 0..14, and there is enough space, - * enter the shortcut and copy 16 bytes on behalf of the literals - * (in the fast mode, only 8 bytes can be safely copied this way). - * 2) Further if the match length is 4..18, copy 18 bytes in a similar - * manner; but we ensure that there's enough space in the output for - * those 18 bytes earlier, upon entering the shortcut (in other words, - * there is a combined check for both stages). - */ - if ( (length != RUN_MASK) - /* strictly "less than" on input, to re-enter the loop with at least one byte */ - && likely((ip < shortiend) & (op <= shortoend)) ) { - /* Copy the literals */ - LZ4_memcpy(op, ip, 16); - op += length; ip += length; - - /* The second stage: prepare for match copying, decode full info. - * If it doesn't work out, the info won't be wasted. */ - length = token & ML_MASK; /* match length */ - offset = LZ4_readLE16(ip); ip += 2; - match = op - offset; - assert(match <= op); /* check overflow */ - - /* Do not deal with overlapping matches. */ - if ( (length != ML_MASK) - && (offset >= 8) - && (dict==withPrefix64k || match >= lowPrefix) ) { - /* Copy the match. */ - LZ4_memcpy(op + 0, match + 0, 8); - LZ4_memcpy(op + 8, match + 8, 8); - LZ4_memcpy(op +16, match +16, 2); - op += length + MINMATCH; - /* Both stages worked, load the next token. */ - continue; - } - - /* The second stage didn't work out, but the info is ready. - * Propel it right to the point of match copying. */ - goto _copy_match; - } - - /* decode literal length */ - if (length == RUN_MASK) { - size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); - if (addl == rvl_error) { goto _output_error; } - length += addl; - if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ - } - - /* copy literals */ - cpy = op+length; -#if LZ4_FAST_DEC_LOOP - safe_literal_copy: -#endif - LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) { - /* We've either hit the input parsing restriction or the output parsing restriction. - * In the normal scenario, decoding a full block, it must be the last sequence, - * otherwise it's an error (invalid input or dimensions). - * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. - */ - if (partialDecoding) { - /* Since we are partial decoding we may be in this block because of the output parsing - * restriction, which is not valid since the output buffer is allowed to be undersized. - */ - DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") - DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); - DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); - DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); - /* Finishing in the middle of a literals segment, - * due to lack of input. - */ - if (ip+length > iend) { - length = (size_t)(iend-ip); - cpy = op + length; - } - /* Finishing in the middle of a literals segment, - * due to lack of output space. - */ - if (cpy > oend) { - cpy = oend; - assert(op<=oend); - length = (size_t)(oend-op); - } - } else { - /* We must be on the last sequence (or invalid) because of the parsing limitations - * so check that we exactly consume the input and don't overrun the output buffer. - */ - if ((ip+length != iend) || (cpy > oend)) { - DEBUGLOG(6, "should have been last run of literals") - DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); - DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); - goto _output_error; - } - } - LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */ - ip += length; - op += length; - /* Necessarily EOF when !partialDecoding. - * When partialDecoding, it is EOF if we've either - * filled the output buffer or - * can't proceed with reading an offset for following match. - */ - if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { - break; - } - } else { - LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */ - ip += length; op = cpy; - } - - /* get offset */ - offset = LZ4_readLE16(ip); ip+=2; - match = op - offset; - - /* get matchlength */ - length = token & ML_MASK; - - _copy_match: - if (length == ML_MASK) { - size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); - if (addl == rvl_error) { goto _output_error; } - length += addl; - if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ - } - length += MINMATCH; - -#if LZ4_FAST_DEC_LOOP - safe_match_copy: -#endif - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ - /* match starting within external dictionary */ - if ((dict==usingExtDict) && (match < lowPrefix)) { - assert(dictEnd != NULL); - if (unlikely(op+length > oend-LASTLITERALS)) { - if (partialDecoding) length = MIN(length, (size_t)(oend-op)); - else goto _output_error; /* doesn't respect parsing restriction */ - } - - if (length <= (size_t)(lowPrefix-match)) { - /* match fits entirely within external dictionary : just copy */ - LZ4_memmove(op, dictEnd - (lowPrefix-match), length); - op += length; - } else { - /* match stretches into both external dictionary and current block */ - size_t const copySize = (size_t)(lowPrefix - match); - size_t const restSize = length - copySize; - LZ4_memcpy(op, dictEnd - copySize, copySize); - op += copySize; - if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ - BYTE* const endOfMatch = op + restSize; - const BYTE* copyFrom = lowPrefix; - while (op < endOfMatch) *op++ = *copyFrom++; - } else { - LZ4_memcpy(op, lowPrefix, restSize); - op += restSize; - } } - continue; - } - assert(match >= lowPrefix); - - /* copy match within block */ - cpy = op + length; - - /* partialDecoding : may end anywhere within the block */ - assert(op<=oend); - if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { - size_t const mlen = MIN(length, (size_t)(oend-op)); - const BYTE* const matchEnd = match + mlen; - BYTE* const copyEnd = op + mlen; - if (matchEnd > op) { /* overlap copy */ - while (op < copyEnd) { *op++ = *match++; } - } else { - LZ4_memcpy(op, match, mlen); - } - op = copyEnd; - if (op == oend) { break; } - continue; - } - - if (unlikely(offset<8)) { - LZ4_write32(op, 0); /* silence msan warning when offset==0 */ - op[0] = match[0]; - op[1] = match[1]; - op[2] = match[2]; - op[3] = match[3]; - match += inc32table[offset]; - LZ4_memcpy(op+4, match, 4); - match -= dec64table[offset]; - } else { - LZ4_memcpy(op, match, 8); - match += 8; - } - op += 8; - - if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { - BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); - if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ - if (op < oCopyLimit) { - LZ4_wildCopy8(op, match, oCopyLimit); - match += oCopyLimit - op; - op = oCopyLimit; - } - while (op < cpy) { *op++ = *match++; } - } else { - LZ4_memcpy(op, match, 8); - if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } - } - op = cpy; /* wildcopy correction */ - } - - /* end of decoding */ - DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); - return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ - - /* Overflow error detected */ - _output_error: - return (int) (-(((const char*)ip)-src))-1; - } -} - - -/*===== Instantiate the API decoding functions. =====*/ - -LZ4_FORCE_O2 -int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, - decode_full_block, noDict, - (BYTE*)dest, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, - partial_decode, - noDict, (BYTE*)dst, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_fast(const char* source, char* dest, int originalSize) -{ - DEBUGLOG(5, "LZ4_decompress_fast"); - return LZ4_decompress_unsafe_generic( - (const BYTE*)source, (BYTE*)dest, originalSize, - 0, NULL, 0); -} - -/*===== Instantiate a few more decoding cases, used more than once. =====*/ - -LZ4_FORCE_O2 /* Exported, an obsolete API function. */ -int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - decode_full_block, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, - partial_decode, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); -} - -/* Another obsolete API function, paired with the previous one. */ -int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) -{ - return LZ4_decompress_unsafe_generic( - (const BYTE*)source, (BYTE*)dest, originalSize, - 64 KB, NULL, 0); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, - size_t prefixSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - decode_full_block, noDict, - (BYTE*)dest-prefixSize, NULL, 0); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, - size_t prefixSize) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, - partial_decode, noDict, - (BYTE*)dest-prefixSize, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, - int compressedSize, int maxOutputSize, - const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - decode_full_block, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, - int compressedSize, int targetOutputSize, int dstCapacity, - const void* dictStart, size_t dictSize) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, - partial_decode, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, - const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_unsafe_generic( - (const BYTE*)source, (BYTE*)dest, originalSize, - 0, (const BYTE*)dictStart, dictSize); -} - -/* The "double dictionary" mode, for use with e.g. ring buffers: the first part - * of the dictionary is passed as prefix, and the second via dictStart + dictSize. - * These routines are used only once, in LZ4_decompress_*_continue(). - */ -LZ4_FORCE_INLINE -int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, - size_t prefixSize, const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - decode_full_block, usingExtDict, - (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); -} - -/*===== streaming decompression functions =====*/ - -#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) -LZ4_streamDecode_t* LZ4_createStreamDecode(void) -{ - LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal)); - return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); -} - -int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) -{ - if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ - FREEMEM(LZ4_stream); - return 0; -} -#endif - -/*! LZ4_setStreamDecode() : - * Use this function to instruct where to find the dictionary. - * This function is not necessary if previous data is still available where it was decoded. - * Loading a size of 0 is allowed (same effect as no dictionary). - * @return : 1 if OK, 0 if error - */ -int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - lz4sd->prefixSize = (size_t)dictSize; - if (dictSize) { - assert(dictionary != NULL); - lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; - } else { - lz4sd->prefixEnd = (const BYTE*) dictionary; - } - lz4sd->externalDict = NULL; - lz4sd->extDictSize = 0; - return 1; -} - -/*! LZ4_decoderRingBufferSize() : - * when setting a ring buffer for streaming decompression (optional scenario), - * provides the minimum size of this ring buffer - * to be compatible with any source respecting maxBlockSize condition. - * Note : in a ring buffer scenario, - * blocks are presumed decompressed next to each other. - * When not enough space remains for next block (remainingSize < maxBlockSize), - * decoding resumes from beginning of ring buffer. - * @return : minimum ring buffer size, - * or 0 if there is an error (invalid maxBlockSize). - */ -int LZ4_decoderRingBufferSize(int maxBlockSize) -{ - if (maxBlockSize < 0) return 0; - if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; - if (maxBlockSize < 16) maxBlockSize = 16; - return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); -} - -/* -*_continue() : - These decoding functions allow decompression of multiple blocks in "streaming" mode. - Previously decoded blocks must still be available at the memory position where they were decoded. - If it's not possible, save the relevant part of decoded data into a safe buffer, - and indicate where it stands using LZ4_setStreamDecode() -*/ -LZ4_FORCE_O2 -int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - int result; - - if (lz4sd->prefixSize == 0) { - /* The first call, no dictionary yet. */ - assert(lz4sd->extDictSize == 0); - result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)result; - lz4sd->prefixEnd = (BYTE*)dest + result; - } else if (lz4sd->prefixEnd == (BYTE*)dest) { - /* They're rolling the current segment. */ - if (lz4sd->prefixSize >= 64 KB - 1) - result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); - else if (lz4sd->extDictSize == 0) - result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, - lz4sd->prefixSize); - else - result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += (size_t)result; - lz4sd->prefixEnd += result; - } else { - /* The buffer wraps around, or they're switching to another buffer. */ - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, - lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)result; - lz4sd->prefixEnd = (BYTE*)dest + result; - } - - return result; -} - -LZ4_FORCE_O2 int -LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, - const char* source, char* dest, int originalSize) -{ - LZ4_streamDecode_t_internal* const lz4sd = - (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse); - int result; - - DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize); - assert(originalSize >= 0); - - if (lz4sd->prefixSize == 0) { - DEBUGLOG(5, "first invocation : no prefix nor extDict"); - assert(lz4sd->extDictSize == 0); - result = LZ4_decompress_fast(source, dest, originalSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)originalSize; - lz4sd->prefixEnd = (BYTE*)dest + originalSize; - } else if (lz4sd->prefixEnd == (BYTE*)dest) { - DEBUGLOG(5, "continue using existing prefix"); - result = LZ4_decompress_unsafe_generic( - (const BYTE*)source, (BYTE*)dest, originalSize, - lz4sd->prefixSize, - lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += (size_t)originalSize; - lz4sd->prefixEnd += originalSize; - } else { - DEBUGLOG(5, "prefix becomes extDict"); - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_fast_extDict(source, dest, originalSize, - lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)originalSize; - lz4sd->prefixEnd = (BYTE*)dest + originalSize; - } - - return result; -} - - -/* -Advanced decoding functions : -*_usingDict() : - These decoding functions work the same as "_continue" ones, - the dictionary must be explicitly provided within parameters -*/ - -int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) -{ - if (dictSize==0) - return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); - if (dictStart+dictSize == dest) { - if (dictSize >= 64 KB - 1) { - return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); -} - -int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize) -{ - if (dictSize==0) - return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity); - if (dictStart+dictSize == dest) { - if (dictSize >= 64 KB - 1) { - return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize); -} - -int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) -{ - if (dictSize==0 || dictStart+dictSize == dest) - return LZ4_decompress_unsafe_generic( - (const BYTE*)source, (BYTE*)dest, originalSize, - (size_t)dictSize, NULL, 0); - assert(dictSize >= 0); - return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); -} - - -/*=************************************************* -* Obsolete Functions -***************************************************/ -/* obsolete compression functions */ -int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) -{ - return LZ4_compress_default(source, dest, inputSize, maxOutputSize); -} -int LZ4_compress(const char* src, char* dest, int srcSize) -{ - return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); -} -int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) -{ - return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); -} -int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) -{ - return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); -} -int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) -{ - return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); -} -int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) -{ - return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); -} - -/* -These decompression functions are deprecated and should no longer be used. -They are only provided here for compatibility with older user programs. -- LZ4_uncompress is totally equivalent to LZ4_decompress_fast -- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe -*/ -int LZ4_uncompress (const char* source, char* dest, int outputSize) -{ - return LZ4_decompress_fast(source, dest, outputSize); -} -int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) -{ - return LZ4_decompress_safe(source, dest, isize, maxOutputSize); -} - -/* Obsolete Streaming functions */ - -int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); } - -int LZ4_resetStreamState(void* state, char* inputBuffer) -{ - (void)inputBuffer; - LZ4_resetStream((LZ4_stream_t*)state); - return 0; -} - -#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) -void* LZ4_create (char* inputBuffer) -{ - (void)inputBuffer; - return LZ4_createStream(); -} -#endif - -char* LZ4_slideInputBuffer (void* state) -{ - /* avoid const char * -> char * conversion warning */ - return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; -} - -#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.h b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.h deleted file mode 100644 index 491c6087..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.h +++ /dev/null @@ -1,842 +0,0 @@ -/* - * LZ4 - Fast LZ compression algorithm - * Header File - * Copyright (C) 2011-2020, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ -#if defined (__cplusplus) -extern "C" { -#endif - -#ifndef LZ4_H_2983827168210 -#define LZ4_H_2983827168210 - -/* --- Dependency --- */ -#include /* size_t */ - - -/** - Introduction - - LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, - scalable with multi-cores CPU. It features an extremely fast decoder, with speed in - multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. - - The LZ4 compression library provides in-memory compression and decompression functions. - It gives full buffer control to user. - Compression can be done in: - - a single step (described as Simple Functions) - - a single step, reusing a context (described in Advanced Functions) - - unbounded multiple steps (described as Streaming compression) - - lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). - Decompressing such a compressed block requires additional metadata. - Exact metadata depends on exact decompression function. - For the typical case of LZ4_decompress_safe(), - metadata includes block's compressed size, and maximum bound of decompressed size. - Each application is free to encode and pass such metadata in whichever way it wants. - - lz4.h only handle blocks, it can not generate Frames. - - Blocks are different from Frames (doc/lz4_Frame_format.md). - Frames bundle both blocks and metadata in a specified manner. - Embedding metadata is required for compressed data to be self-contained and portable. - Frame format is delivered through a companion API, declared in lz4frame.h. - The `lz4` CLI can only manage frames. -*/ - -/*^*************************************************************** -* Export parameters -*****************************************************************/ -/* -* LZ4_DLL_EXPORT : -* Enable exporting of functions when building a Windows DLL -* LZ4LIB_VISIBILITY : -* Control library symbols visibility. -*/ -#ifndef LZ4LIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) -# else -# define LZ4LIB_VISIBILITY -# endif -#endif -#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) -# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY -#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) -# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ -#else -# define LZ4LIB_API LZ4LIB_VISIBILITY -#endif - -/*! LZ4_FREESTANDING : - * When this macro is set to 1, it enables "freestanding mode" that is - * suitable for typical freestanding environment which doesn't support - * standard C library. - * - * - LZ4_FREESTANDING is a compile-time switch. - * - It requires the following macros to be defined: - * LZ4_memcpy, LZ4_memmove, LZ4_memset. - * - It only enables LZ4/HC functions which don't use heap. - * All LZ4F_* functions are not supported. - * - See tests/freestanding.c to check its basic setup. - */ -#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1) -# define LZ4_HEAPMODE 0 -# define LZ4HC_HEAPMODE 0 -# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1 -# if !defined(LZ4_memcpy) -# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'." -# endif -# if !defined(LZ4_memset) -# error "LZ4_FREESTANDING requires macro 'LZ4_memset'." -# endif -# if !defined(LZ4_memmove) -# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'." -# endif -#elif ! defined(LZ4_FREESTANDING) -# define LZ4_FREESTANDING 0 -#endif - - -/*------ Version ------*/ -#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ -#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */ - -#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) - -#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE -#define LZ4_QUOTE(str) #str -#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) -#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */ - -LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */ -LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */ - - -/*-************************************ -* Tuning parameter -**************************************/ -#define LZ4_MEMORY_USAGE_MIN 10 -#define LZ4_MEMORY_USAGE_DEFAULT 14 -#define LZ4_MEMORY_USAGE_MAX 20 - -/*! - * LZ4_MEMORY_USAGE : - * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) - * Increasing memory usage improves compression ratio, at the cost of speed. - * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. - * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache - */ -#ifndef LZ4_MEMORY_USAGE -# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT -#endif - -#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) -# error "LZ4_MEMORY_USAGE is too small !" -#endif - -#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX) -# error "LZ4_MEMORY_USAGE is too large !" -#endif - -/*-************************************ -* Simple Functions -**************************************/ -/*! LZ4_compress_default() : - * Compresses 'srcSize' bytes from buffer 'src' - * into already allocated 'dst' buffer of size 'dstCapacity'. - * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). - * It also runs faster, so it's a recommended setting. - * If the function cannot compress 'src' into a more limited 'dst' budget, - * compression stops *immediately*, and the function result is zero. - * In which case, 'dst' content is undefined (invalid). - * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. - * dstCapacity : size of buffer 'dst' (which must be already allocated) - * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) - * or 0 if compression fails - * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). - */ -LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); - -/*! LZ4_decompress_safe() : - * compressedSize : is the exact complete size of the compressed block. - * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. - * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) - * If destination buffer is not large enough, decoding will stop and output an error code (negative value). - * If the source stream is detected malformed, the function will stop decoding and return a negative result. - * Note 1 : This function is protected against malicious data packets : - * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, - * even if the compressed block is maliciously modified to order the decoder to do these actions. - * In such case, the decoder stops immediately, and considers the compressed block malformed. - * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. - * The implementation is free to send / store / derive this information in whichever way is most beneficial. - * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. - */ -LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); - - -/*-************************************ -* Advanced Functions -**************************************/ -#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ -#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) - -/*! LZ4_compressBound() : - Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) - This function is primarily useful for memory allocation purposes (destination buffer size). - Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). - Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) - inputSize : max supported value is LZ4_MAX_INPUT_SIZE - return : maximum output size in a "worst case" scenario - or 0, if input size is incorrect (too large or negative) -*/ -LZ4LIB_API int LZ4_compressBound(int inputSize); - -/*! LZ4_compress_fast() : - Same as LZ4_compress_default(), but allows selection of "acceleration" factor. - The larger the acceleration value, the faster the algorithm, but also the lesser the compression. - It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. - An acceleration value of "1" is the same as regular LZ4_compress_default() - Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). - Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). -*/ -LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_fast_extState() : - * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. - * Use LZ4_sizeofState() to know how much memory must be allocated, - * and allocate it on 8-bytes boundaries (using `malloc()` typically). - * Then, provide this buffer as `void* state` to compression function. - */ -LZ4LIB_API int LZ4_sizeofState(void); -LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_destSize() : - * Reverse the logic : compresses as much data as possible from 'src' buffer - * into already allocated buffer 'dst', of size >= 'targetDestSize'. - * This function either compresses the entire 'src' content into 'dst' if it's large enough, - * or fill 'dst' buffer completely with as much data as possible from 'src'. - * note: acceleration parameter is fixed to "default". - * - * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. - * New value is necessarily <= input value. - * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) - * or 0 if compression fails. - * - * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): - * the produced compressed content could, in specific circumstances, - * require to be decompressed into a destination buffer larger - * by at least 1 byte than the content to decompress. - * If an application uses `LZ4_compress_destSize()`, - * it's highly recommended to update liblz4 to v1.9.2 or better. - * If this can't be done or ensured, - * the receiving decompression function should provide - * a dstCapacity which is > decompressedSize, by at least 1 byte. - * See https://github.com/lz4/lz4/issues/859 for details - */ -LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); - - -/*! LZ4_decompress_safe_partial() : - * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', - * into destination buffer 'dst' of size 'dstCapacity'. - * Up to 'targetOutputSize' bytes will be decoded. - * The function stops decoding on reaching this objective. - * This can be useful to boost performance - * whenever only the beginning of a block is required. - * - * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) - * If source stream is detected malformed, function returns a negative result. - * - * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. - * - * Note 2 : targetOutputSize must be <= dstCapacity - * - * Note 3 : this function effectively stops decoding on reaching targetOutputSize, - * so dstCapacity is kind of redundant. - * This is because in older versions of this function, - * decoding operation would still write complete sequences. - * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, - * it could write more bytes, though only up to dstCapacity. - * Some "margin" used to be required for this operation to work properly. - * Thankfully, this is no longer necessary. - * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. - * - * Note 4 : If srcSize is the exact size of the block, - * then targetOutputSize can be any value, - * including larger than the block's decompressed size. - * The function will, at most, generate block's decompressed size. - * - * Note 5 : If srcSize is _larger_ than block's compressed size, - * then targetOutputSize **MUST** be <= block's decompressed size. - * Otherwise, *silent corruption will occur*. - */ -LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); - - -/*-********************************************* -* Streaming Compression Functions -***********************************************/ -typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ - -/** - Note about RC_INVOKED - - - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio). - https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros - - - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars) - and reports warning "RC4011: identifier truncated". - - - To eliminate the warning, we surround long preprocessor symbol with - "#if !defined(RC_INVOKED) ... #endif" block that means - "skip this block when rc.exe is trying to read it". -*/ -#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ -#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) -LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); -LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); -#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ -#endif - -/*! LZ4_resetStream_fast() : v1.9.0+ - * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks - * (e.g., LZ4_compress_fast_continue()). - * - * An LZ4_stream_t must be initialized once before usage. - * This is automatically done when created by LZ4_createStream(). - * However, should the LZ4_stream_t be simply declared on stack (for example), - * it's necessary to initialize it first, using LZ4_initStream(). - * - * After init, start any new stream with LZ4_resetStream_fast(). - * A same LZ4_stream_t can be re-used multiple times consecutively - * and compress multiple streams, - * provided that it starts each new stream with LZ4_resetStream_fast(). - * - * LZ4_resetStream_fast() is much faster than LZ4_initStream(), - * but is not compatible with memory regions containing garbage data. - * - * Note: it's only useful to call LZ4_resetStream_fast() - * in the context of streaming compression. - * The *extState* functions perform their own resets. - * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. - */ -LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); - -/*! LZ4_loadDict() : - * Use this function to reference a static dictionary into LZ4_stream_t. - * The dictionary must remain available during compression. - * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. - * The same dictionary will have to be loaded on decompression side for successful decoding. - * Dictionary are useful for better compression of small data (KB range). - * While LZ4 accept any input as dictionary, - * results are generally better when using Zstandard's Dictionary Builder. - * Loading a size of 0 is allowed, and is the same as reset. - * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) - */ -LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); - -/*! LZ4_compress_fast_continue() : - * Compress 'src' content using data from previously compressed blocks, for better compression ratio. - * 'dst' buffer must be already allocated. - * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. - * - * @return : size of compressed block - * or 0 if there is an error (typically, cannot fit into 'dst'). - * - * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. - * Each block has precise boundaries. - * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. - * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. - * - * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! - * - * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. - * Make sure that buffers are separated, by at least one byte. - * This construction ensures that each block only depends on previous block. - * - * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. - * - * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. - */ -LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_saveDict() : - * If last 64KB data cannot be guaranteed to remain available at its current memory location, - * save it into a safer place (char* safeBuffer). - * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), - * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. - * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. - */ -LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); - - -/*-********************************************** -* Streaming Decompression Functions -* Bufferless synchronous API -************************************************/ -typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ - -/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : - * creation / destruction of streaming decompression tracking context. - * A tracking context can be re-used multiple times. - */ -#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ -#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) -LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); -LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); -#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ -#endif - -/*! LZ4_setStreamDecode() : - * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. - * Use this function to start decompression of a new stream of blocks. - * A dictionary can optionally be set. Use NULL or size 0 for a reset order. - * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. - * @return : 1 if OK, 0 if error - */ -LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); - -/*! LZ4_decoderRingBufferSize() : v1.8.2+ - * Note : in a ring buffer scenario (optional), - * blocks are presumed decompressed next to each other - * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), - * at which stage it resumes from beginning of ring buffer. - * When setting such a ring buffer for streaming decompression, - * provides the minimum size of this ring buffer - * to be compatible with any source respecting maxBlockSize condition. - * @return : minimum ring buffer size, - * or 0 if there is an error (invalid maxBlockSize). - */ -LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); -#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ - -/*! LZ4_decompress_*_continue() : - * These decoding functions allow decompression of consecutive blocks in "streaming" mode. - * A block is an unsplittable entity, it must be presented entirely to a decompression function. - * Decompression functions only accepts one block at a time. - * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. - * If less than 64KB of data has been decoded, all the data must be present. - * - * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : - * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). - * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. - * In which case, encoding and decoding buffers do not need to be synchronized. - * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. - * - Synchronized mode : - * Decompression buffer size is _exactly_ the same as compression buffer size, - * and follows exactly same update rule (block boundaries at same positions), - * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), - * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). - * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. - * In which case, encoding and decoding buffers do not need to be synchronized, - * and encoding ring buffer can have any size, including small ones ( < 64 KB). - * - * Whenever these conditions are not possible, - * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, - * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. -*/ -LZ4LIB_API int -LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, - const char* src, char* dst, - int srcSize, int dstCapacity); - - -/*! LZ4_decompress_*_usingDict() : - * These decoding functions work the same as - * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() - * They are stand-alone, and don't need an LZ4_streamDecode_t structure. - * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. - * Performance tip : Decompression speed can be substantially increased - * when dst == dictStart + dictSize. - */ -LZ4LIB_API int -LZ4_decompress_safe_usingDict(const char* src, char* dst, - int srcSize, int dstCapacity, - const char* dictStart, int dictSize); - -LZ4LIB_API int -LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, - int compressedSize, - int targetOutputSize, int maxOutputSize, - const char* dictStart, int dictSize); - -#endif /* LZ4_H_2983827168210 */ - - -/*^************************************* - * !!!!!! STATIC LINKING ONLY !!!!!! - ***************************************/ - -/*-**************************************************************************** - * Experimental section - * - * Symbols declared in this section must be considered unstable. Their - * signatures or semantics may change, or they may be removed altogether in the - * future. They are therefore only safe to depend on when the caller is - * statically linked against the library. - * - * To protect against unsafe usage, not only are the declarations guarded, - * the definitions are hidden by default - * when building LZ4 as a shared/dynamic library. - * - * In order to access these declarations, - * define LZ4_STATIC_LINKING_ONLY in your application - * before including LZ4's headers. - * - * In order to make their implementations accessible dynamically, you must - * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. - ******************************************************************************/ - -#ifdef LZ4_STATIC_LINKING_ONLY - -#ifndef LZ4_STATIC_3504398509 -#define LZ4_STATIC_3504398509 - -#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS -#define LZ4LIB_STATIC_API LZ4LIB_API -#else -#define LZ4LIB_STATIC_API -#endif - - -/*! LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. - * It is only safe to call if the state buffer is known to be correctly initialized already - * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). - * From a high level, the difference is that - * this function initializes the provided state with a call to something like LZ4_resetStream_fast() - * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). - */ -LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_attach_dictionary() : - * This is an experimental API that allows - * efficient use of a static dictionary many times. - * - * Rather than re-loading the dictionary buffer into a working context before - * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a - * working LZ4_stream_t, this function introduces a no-copy setup mechanism, - * in which the working stream references the dictionary stream in-place. - * - * Several assumptions are made about the state of the dictionary stream. - * Currently, only streams which have been prepared by LZ4_loadDict() should - * be expected to work. - * - * Alternatively, the provided dictionaryStream may be NULL, - * in which case any existing dictionary stream is unset. - * - * If a dictionary is provided, it replaces any pre-existing stream history. - * The dictionary contents are the only history that can be referenced and - * logically immediately precede the data compressed in the first subsequent - * compression call. - * - * The dictionary will only remain attached to the working stream through the - * first compression call, at the end of which it is cleared. The dictionary - * stream (and source buffer) must remain in-place / accessible / unchanged - * through the completion of the first compression call on the stream. - */ -LZ4LIB_STATIC_API void -LZ4_attach_dictionary(LZ4_stream_t* workingStream, - const LZ4_stream_t* dictionaryStream); - - -/*! In-place compression and decompression - * - * It's possible to have input and output sharing the same buffer, - * for highly constrained memory environments. - * In both cases, it requires input to lay at the end of the buffer, - * and decompression to start at beginning of the buffer. - * Buffer size must feature some margin, hence be larger than final size. - * - * |<------------------------buffer--------------------------------->| - * |<-----------compressed data--------->| - * |<-----------decompressed size------------------>| - * |<----margin---->| - * - * This technique is more useful for decompression, - * since decompressed size is typically larger, - * and margin is short. - * - * In-place decompression will work inside any buffer - * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). - * This presumes that decompressedSize > compressedSize. - * Otherwise, it means compression actually expanded data, - * and it would be more efficient to store such data with a flag indicating it's not compressed. - * This can happen when data is not compressible (already compressed, or encrypted). - * - * For in-place compression, margin is larger, as it must be able to cope with both - * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, - * and data expansion, which can happen when input is not compressible. - * As a consequence, buffer size requirements are much higher, - * and memory savings offered by in-place compression are more limited. - * - * There are ways to limit this cost for compression : - * - Reduce history size, by modifying LZ4_DISTANCE_MAX. - * Note that it is a compile-time constant, so all compressions will apply this limit. - * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, - * so it's a reasonable trick when inputs are known to be small. - * - Require the compressor to deliver a "maximum compressed size". - * This is the `dstCapacity` parameter in `LZ4_compress*()`. - * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, - * in which case, the return code will be 0 (zero). - * The caller must be ready for these cases to happen, - * and typically design a backup scheme to send data uncompressed. - * The combination of both techniques can significantly reduce - * the amount of margin required for in-place compression. - * - * In-place compression can work in any buffer - * which size is >= (maxCompressedSize) - * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. - * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, - * so it's possible to reduce memory requirements by playing with them. - */ - -#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) -#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ - -#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ -# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ -#endif - -#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ -#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ - -#endif /* LZ4_STATIC_3504398509 */ -#endif /* LZ4_STATIC_LINKING_ONLY */ - - - -#ifndef LZ4_H_98237428734687 -#define LZ4_H_98237428734687 - -/*-************************************************************ - * Private Definitions - ************************************************************** - * Do not use these definitions directly. - * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. - * Accessing members will expose user code to API and/or ABI break in future versions of the library. - **************************************************************/ -#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) -#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) -#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ - -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# include - typedef int8_t LZ4_i8; - typedef uint8_t LZ4_byte; - typedef uint16_t LZ4_u16; - typedef uint32_t LZ4_u32; -#else - typedef signed char LZ4_i8; - typedef unsigned char LZ4_byte; - typedef unsigned short LZ4_u16; - typedef unsigned int LZ4_u32; -#endif - -/*! LZ4_stream_t : - * Never ever use below internal definitions directly ! - * These definitions are not API/ABI safe, and may change in future versions. - * If you need static allocation, declare or allocate an LZ4_stream_t object. -**/ - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; - const LZ4_byte* dictionary; - const LZ4_stream_t_internal* dictCtx; - LZ4_u32 currentOffset; - LZ4_u32 tableType; - LZ4_u32 dictSize; - /* Implicit padding to ensure structure is aligned */ -}; - -#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ -union LZ4_stream_u { - char minStateSize[LZ4_STREAM_MINSIZE]; - LZ4_stream_t_internal internal_donotuse; -}; /* previously typedef'd to LZ4_stream_t */ - - -/*! LZ4_initStream() : v1.9.0+ - * An LZ4_stream_t structure must be initialized at least once. - * This is automatically done when invoking LZ4_createStream(), - * but it's not when the structure is simply declared on stack (for example). - * - * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. - * It can also initialize any arbitrary buffer of sufficient size, - * and will @return a pointer of proper type upon initialization. - * - * Note : initialization fails if size and alignment conditions are not respected. - * In which case, the function will @return NULL. - * Note2: An LZ4_stream_t structure guarantees correct alignment and size. - * Note3: Before v1.9.0, use LZ4_resetStream() instead -**/ -LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); - - -/*! LZ4_streamDecode_t : - * Never ever use below internal definitions directly ! - * These definitions are not API/ABI safe, and may change in future versions. - * If you need static allocation, declare or allocate an LZ4_streamDecode_t object. -**/ -typedef struct { - const LZ4_byte* externalDict; - const LZ4_byte* prefixEnd; - size_t extDictSize; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - -#define LZ4_STREAMDECODE_MINSIZE 32 -union LZ4_streamDecode_u { - char minStateSize[LZ4_STREAMDECODE_MINSIZE]; - LZ4_streamDecode_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_streamDecode_t */ - - - -/*-************************************ -* Obsolete Functions -**************************************/ - -/*! Deprecation warnings - * - * Deprecated functions make the compiler generate a warning when invoked. - * This is meant to invite users to update their source code. - * Should deprecation warnings be a problem, it is generally possible to disable them, - * typically with -Wno-deprecated-declarations for gcc - * or _CRT_SECURE_NO_WARNINGS in Visual. - * - * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS - * before including the header file. - */ -#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS -# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ -#else -# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define LZ4_DEPRECATED(message) [[deprecated(message)]] -# elif defined(_MSC_VER) -# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) -# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) -# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) -# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) -# define LZ4_DEPRECATED(message) __attribute__((deprecated)) -# else -# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") -# define LZ4_DEPRECATED(message) /* disabled */ -# endif -#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ - -/*! Obsolete compression functions (since v1.7.3) */ -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); - -/*! Obsolete decompression functions (since v1.8.0) */ -LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); -LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); - -/* Obsolete streaming functions (since v1.7.0) - * degraded functionality; do not use! - * - * In order to perform streaming compression, these functions depended on data - * that is no longer tracked in the state. They have been preserved as well as - * possible: using them will still produce a correct output. However, they don't - * actually retain any history between compression calls. The compression ratio - * achieved will therefore be no better than compressing each chunk - * independently. - */ -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); -LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); - -/*! Obsolete streaming decoding functions (since v1.7.0) */ -LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); -LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); - -/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : - * These functions used to be faster than LZ4_decompress_safe(), - * but this is no longer the case. They are now slower. - * This is because LZ4_decompress_fast() doesn't know the input size, - * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. - * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. - * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. - * - * The last remaining LZ4_decompress_fast() specificity is that - * it can decompress a block without knowing its compressed size. - * Such functionality can be achieved in a more secure manner - * by employing LZ4_decompress_safe_partial(). - * - * Parameters: - * originalSize : is the uncompressed size to regenerate. - * `dst` must be already allocated, its size must be >= 'originalSize' bytes. - * @return : number of bytes read from source buffer (== compressed size). - * The function expects to finish at block's end exactly. - * If the source stream is detected malformed, the function stops decoding and returns a negative result. - * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. - * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. - * Also, since match offsets are not validated, match reads from 'src' may underflow too. - * These issues never happen if input (compressed) data is correct. - * But they may happen if input data is invalid (error or intentional tampering). - * As a consequence, use these functions in trusted environments with trusted data **only**. - */ -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") -LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") -LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") -LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); - -/*! LZ4_resetStream() : - * An LZ4_stream_t structure must be initialized at least once. - * This is done with LZ4_initStream(), or LZ4_resetStream(). - * Consider switching to LZ4_initStream(), - * invoking LZ4_resetStream() will trigger deprecation warnings in the future. - */ -LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); - - -#endif /* LZ4_H_98237428734687 */ - - -#if defined (__cplusplus) -} -#endif diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.zip b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.zip deleted file mode 100644 index 6ed72c9a..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/lz4.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:37e63d56fb9cbe2e430c7f737a404cd4b98637b05e1467459d5c8fe1a4364cc3 -size 462886 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/readme.txt b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/readme.txt deleted file mode 100644 index 5161b3d0..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:359874042b2232244d3f73037a76b79b1b57f52d0b5f5892ab23b40305f2a48c -size 1734 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/ExampleGloveAdapterSingleton.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/ExampleGloveAdapterSingleton.obj deleted file mode 100644 index 39eb3f5f..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/ExampleGloveAdapterSingleton.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9e00b1239d1a04ca0fff91ed3a4865b8813dc4a9eeb09902760c0c8bf3c29827 -size 3526217 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/ExampleGloveDevice.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/ExampleGloveDevice.obj deleted file mode 100644 index 81b943bb..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/ExampleGloveDevice.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c290dc80b555e2ef8fe86a6c62745cad0f72b7aecbb4986839e3e12b33d4a9c -size 627880 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/GloveDeviceBase.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/GloveDeviceBase.obj deleted file mode 100644 index d501294e..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/GloveDeviceBase.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09210a0a98f96cfd282e39028c031c029f44473a1fc50ca4b27b23efcab670d0 -size 371495 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/LZ4Wrapper.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/LZ4Wrapper.obj deleted file mode 100644 index 89e61640..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/LZ4Wrapper.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c0ec6d23ca419873636cc3bd0dd5dfd2efcda0e3ae40f7717a645af0b1f52e1 -size 188070 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoDataConverter.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoDataConverter.obj deleted file mode 100644 index 7ba906df..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoDataConverter.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b1ceb38dc7a55b4452ae52bc745ae8dc3832c28fd056ee3ef78af16b12250248 -size 409270 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoDataParser.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoDataParser.obj deleted file mode 100644 index b25cf083..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoDataParser.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba5fe2378eb9493c6df8348f7d9ec755d3920386fce1bfe92c333879217d296b -size 5864194 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.command.1.tlog deleted file mode 100644 index 8f384092..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.command.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.read.1.tlog deleted file mode 100644 index 3c957845..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.read.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.write.1.tlog deleted file mode 100644 index ebcef3ae..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CL.write.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.command.1.tlog deleted file mode 100644 index 8500ffda..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.command.1.tlog +++ /dev/null @@ -1,2 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE\EXAMPLEGLOVEDATA.CSV -C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\x64\Debug\ExampleGloveData.csv diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.read.1.tlog deleted file mode 100644 index 91d479f4..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.read.1.tlog +++ /dev/null @@ -1 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE\EXAMPLEGLOVEDATA.CSV diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.write.1.tlog deleted file mode 100644 index 78e57e7f..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/CopyFile.write.1.tlog +++ /dev/null @@ -1,2 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE\EXAMPLEGLOVEDATA.CSV -C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\X64\DEBUG\EXAMPLEGLOVEDATA.CSV diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/RokokoGloveDevice.lastbuildstate b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/RokokoGloveDevice.lastbuildstate deleted file mode 100644 index 27ddd87a..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/RokokoGloveDevice.lastbuildstate +++ /dev/null @@ -1,2 +0,0 @@ -PlatformToolSet=v142:VCToolArchitecture=Native64Bit:VCToolsVersion=14.29.30133:TargetPlatformVersion=8.1: -Debug|x64|C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\| diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/RokokoGloveDevice.write.1u.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/RokokoGloveDevice.write.1u.tlog deleted file mode 100644 index 46ea407b..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/RokokoGloveDevice.write.1u.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.command.1.tlog deleted file mode 100644 index f6dbb298..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.command.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.read.1.tlog deleted file mode 100644 index 46e9fee1..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.read.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.write.1.tlog deleted file mode 100644 index 6af6286f..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.513E58BC.tlog/link.write.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.command.1.tlog deleted file mode 100644 index 86d1a4e9..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.command.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.read.1.tlog deleted file mode 100644 index e46cdddf..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.read.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.write.1.tlog deleted file mode 100644 index 9ebcf458..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CL.write.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.command.1.tlog deleted file mode 100644 index bedd38b5..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.command.1.tlog +++ /dev/null @@ -1,2 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE_FIXED\EXAMPLEGLOVEDATA.CSV -C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\x64\Debug\ExampleGloveData.csv diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.read.1.tlog deleted file mode 100644 index d500e0c9..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.read.1.tlog +++ /dev/null @@ -1 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE_FIXED\EXAMPLEGLOVEDATA.CSV diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.write.1.tlog deleted file mode 100644 index a422c589..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/CopyFile.write.1.tlog +++ /dev/null @@ -1,2 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE_FIXED\EXAMPLEGLOVEDATA.CSV -C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\X64\DEBUG\EXAMPLEGLOVEDATA.CSV diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.lastbuildstate b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.lastbuildstate deleted file mode 100644 index 27ddd87a..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.lastbuildstate +++ /dev/null @@ -1,2 +0,0 @@ -PlatformToolSet=v142:VCToolArchitecture=Native64Bit:VCToolsVersion=14.29.30133:TargetPlatformVersion=8.1: -Debug|x64|C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\| diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.write.1u.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.write.1u.tlog deleted file mode 100644 index 993ca4d0..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.write.1u.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.command.1.tlog deleted file mode 100644 index 8a68a716..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.command.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.read.1.tlog deleted file mode 100644 index 1fa84606..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.read.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.write.1.tlog deleted file mode 100644 index c3bb5dea..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGl.AAAAAAAA.tlog/link.write.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.dll.recipe b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.dll.recipe deleted file mode 100644 index 68ea365b..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.dll.recipe +++ /dev/null @@ -1,11 +0,0 @@ - - - - - C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\x64\Debug\RokokoGloveDevice.dll - - - - - - \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.ilk b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.ilk deleted file mode 100644 index 2d98dbde..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.ilk and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.vcxproj.FileListAbsolute.txt b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice.vcxproj.FileListAbsolute.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice_Fixed.dll.recipe b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice_Fixed.dll.recipe deleted file mode 100644 index 8ea86dad..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice_Fixed.dll.recipe +++ /dev/null @@ -1,11 +0,0 @@ - - - - - C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\x64\Debug\RokokoGloveDevice_Fixed.dll - - - - - - \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice_Fixed.ilk b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice_Fixed.ilk deleted file mode 100644 index 73b364af..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoGloveDevice_Fixed.ilk and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoUDPReceiver.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoUDPReceiver.obj deleted file mode 100644 index 59d65a2b..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/RokokoUDPReceiver.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b3793871ed2062b02be43b00a7c61cf0c639af27d5d7dc6eb1a20f4d16f65298 -size 617547 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/dllmain.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/dllmain.obj deleted file mode 100644 index 825f2208..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/dllmain.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fbdb66278d4bab5b4bd5cbc50e4d6031ce02548c0981ef7098168f08c8070004 -size 435430 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/vc142.idb b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/vc142.idb deleted file mode 100644 index 10cdc3cb..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Debug/vc142.idb and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/ExampleGloveAdapterSingleton.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/ExampleGloveAdapterSingleton.obj deleted file mode 100644 index 19ce0461..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/ExampleGloveAdapterSingleton.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9c64b45ad1121f5c51d1057a328443dca17165f4dcd0ba17c7bd76e750e73300 -size 4466185 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/ExampleGloveDevice.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/ExampleGloveDevice.obj deleted file mode 100644 index 4cf09fc1..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/ExampleGloveDevice.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ee6c686532959052d6520e9a60fdd30cf0def001a54552df18d27f509f31eb86 -size 3539857 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/GloveDeviceBase.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/GloveDeviceBase.obj deleted file mode 100644 index 78d96ff8..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/GloveDeviceBase.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ef00dab2aeb876e77fd485a4a698a07a95bc1d301816f53580ecbc553a5ebeae -size 728430 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/LZ4Wrapper.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/LZ4Wrapper.obj deleted file mode 100644 index 71ddba86..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/LZ4Wrapper.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:78064cbc94a492ffbd88dd3887aea4c2005afd1dfb807b5dde7cd84413d24e8d -size 408538 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoDataConverter.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoDataConverter.obj deleted file mode 100644 index 74c3495f..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoDataConverter.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1c4fcc703040c10f22ae4054bc8ea006945b70e768a6e541e7232598b907d607 -size 1222171 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoDataParser.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoDataParser.obj deleted file mode 100644 index acc5918e..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoDataParser.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad0350ccf87e2b81942fd83352d2824b588d5b95e79f31e08afbbbe292d8ada4 -size 3584145 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.command.1.tlog deleted file mode 100644 index 6fad39f2..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.command.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.read.1.tlog deleted file mode 100644 index cec42f6d..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.read.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.write.1.tlog deleted file mode 100644 index 9e978202..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CL.write.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.command.1.tlog deleted file mode 100644 index 8d6fa2dd..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.command.1.tlog +++ /dev/null @@ -1,2 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE_FIXED\EXAMPLEGLOVEDATA.CSV -C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\x64\Release\ExampleGloveData.csv diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.read.1.tlog deleted file mode 100644 index d500e0c9..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.read.1.tlog +++ /dev/null @@ -1 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE_FIXED\EXAMPLEGLOVEDATA.CSV diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.write.1.tlog deleted file mode 100644 index 1b95c7ee..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/CopyFile.write.1.tlog +++ /dev/null @@ -1,2 +0,0 @@ -^C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\ROKOKOGLOVEDEVICE_FIXED\EXAMPLEGLOVEDATA.CSV -C:\USERS\USER\DOCUMENTS\STREAMINGLE_URP\OPTITRACK ROKOKO GLOVE\X64\RELEASE\EXAMPLEGLOVEDATA.CSV diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.lastbuildstate b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.lastbuildstate deleted file mode 100644 index 64a25916..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.lastbuildstate +++ /dev/null @@ -1,2 +0,0 @@ -PlatformToolSet=v142:VCToolArchitecture=Native32Bit:VCToolsVersion=14.29.30133:TargetPlatformVersion=8.1: -Release|x64|C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\| diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.write.1u.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.write.1u.tlog deleted file mode 100644 index 4e7df3f1..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/RokokoGloveDevice_Fixed.write.1u.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.command.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.command.1.tlog deleted file mode 100644 index f875b26b..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.command.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.read.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.read.1.tlog deleted file mode 100644 index 6905241a..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.read.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.write.1.tlog b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.write.1.tlog deleted file mode 100644 index c00638f5..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGl.AAAAAAAA.tlog/link.write.1.tlog and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.dll.recipe b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.dll.recipe deleted file mode 100644 index dd50e382..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.dll.recipe +++ /dev/null @@ -1,11 +0,0 @@ - - - - - C:\Users\user\Documents\Streamingle_URP\Optitrack Rokoko Glove\x64\Release\RokokoGloveDevice_Fixed.dll - - - - - - \ No newline at end of file diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.iobj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.iobj deleted file mode 100644 index 48949e99..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.iobj and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.ipdb b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.ipdb deleted file mode 100644 index 0d82929d..00000000 Binary files a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoGloveDevice_Fixed.ipdb and /dev/null differ diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoUDPReceiver.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoUDPReceiver.obj deleted file mode 100644 index 19330cc3..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/RokokoUDPReceiver.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:28826eae994bb276f235c5f91af8fbaf7e79bf5a5e2c3851fa8cb954a63de59b -size 1381047 diff --git a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/dllmain.obj b/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/dllmain.obj deleted file mode 100644 index 8b70d98d..00000000 --- a/Optitrack Rokoko Glove/RokokoGloveDevice_Fixed/x64/Release/dllmain.obj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d03f921e16148d8cff270a4a2c51bfad2c83a5ab3bbe00e64c565dfb74d9def1 -size 3495471