//====================================================================================================== // 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::shared_ptr& joint) { if (bodyJson.contains(jointName)) { auto& jointJson = bodyJson[jointName]; joint = std::make_shared(); // Position 파싱 if (jointJson.contains("position")) { auto& pos = jointJson["position"]; joint->position.x = pos.value("x", 0.0f); joint->position.y = pos.value("y", 0.0f); joint->position.z = pos.value("z", 0.0f); } // Rotation 파싱 if (jointJson.contains("rotation")) { auto& rot = jointJson["rotation"]; joint->rotation.x = rot.value("x", 0.0f); joint->rotation.y = rot.value("y", 0.0f); joint->rotation.z = rot.value("z", 0.0f); joint->rotation.w = rot.value("w", 1.0f); // 쿼터니언 정규화 NormalizeQuaternion(joint->rotation.x, joint->rotation.y, joint->rotation.z, joint->rotation.w); } } } bool RokokoDataParser::ValidateFrameData(const RokokoData::LiveFrame_v4& frame) { try { // 기본 검증 if (frame.scene.actors.empty()) { return false; } const auto& actor = frame.scene.actors[0]; // 손가락 데이터 검증 (왼손 엄지손가락만 체크) if (!actor.body.leftThumbMedial || !actor.body.leftThumbDistal || !actor.body.leftThumbTip) { 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]; // 왼손 데이터 추출 (20개 관절) if (actor.body.leftThumbProximal) leftHandFingers.push_back(*actor.body.leftThumbProximal); if (actor.body.leftThumbMedial) leftHandFingers.push_back(*actor.body.leftThumbMedial); if (actor.body.leftThumbDistal) leftHandFingers.push_back(*actor.body.leftThumbDistal); if (actor.body.leftThumbTip) leftHandFingers.push_back(*actor.body.leftThumbTip); if (actor.body.leftIndexProximal) leftHandFingers.push_back(*actor.body.leftIndexProximal); if (actor.body.leftIndexMedial) leftHandFingers.push_back(*actor.body.leftIndexMedial); if (actor.body.leftIndexDistal) leftHandFingers.push_back(*actor.body.leftIndexDistal); if (actor.body.leftIndexTip) leftHandFingers.push_back(*actor.body.leftIndexTip); if (actor.body.leftMiddleProximal) leftHandFingers.push_back(*actor.body.leftMiddleProximal); if (actor.body.leftMiddleMedial) leftHandFingers.push_back(*actor.body.leftMiddleMedial); if (actor.body.leftMiddleDistal) leftHandFingers.push_back(*actor.body.leftMiddleDistal); if (actor.body.leftMiddleTip) leftHandFingers.push_back(*actor.body.leftMiddleTip); if (actor.body.leftRingProximal) leftHandFingers.push_back(*actor.body.leftRingProximal); if (actor.body.leftRingMedial) leftHandFingers.push_back(*actor.body.leftRingMedial); if (actor.body.leftRingDistal) leftHandFingers.push_back(*actor.body.leftRingDistal); if (actor.body.leftRingTip) leftHandFingers.push_back(*actor.body.leftRingTip); if (actor.body.leftLittleProximal) leftHandFingers.push_back(*actor.body.leftLittleProximal); if (actor.body.leftLittleMedial) leftHandFingers.push_back(*actor.body.leftLittleMedial); if (actor.body.leftLittleDistal) leftHandFingers.push_back(*actor.body.leftLittleDistal); if (actor.body.leftLittleTip) leftHandFingers.push_back(*actor.body.leftLittleTip); // 오른손 데이터 추출 (20개 관절) if (actor.body.rightThumbProximal) rightHandFingers.push_back(*actor.body.rightThumbProximal); if (actor.body.rightThumbMedial) rightHandFingers.push_back(*actor.body.rightThumbMedial); if (actor.body.rightThumbDistal) rightHandFingers.push_back(*actor.body.rightThumbDistal); if (actor.body.rightThumbTip) rightHandFingers.push_back(*actor.body.rightThumbTip); if (actor.body.rightIndexProximal) rightHandFingers.push_back(*actor.body.rightIndexProximal); if (actor.body.rightIndexMedial) rightHandFingers.push_back(*actor.body.rightIndexMedial); if (actor.body.rightIndexDistal) rightHandFingers.push_back(*actor.body.rightIndexDistal); if (actor.body.rightIndexTip) rightHandFingers.push_back(*actor.body.rightIndexTip); if (actor.body.rightMiddleProximal) rightHandFingers.push_back(*actor.body.rightMiddleProximal); if (actor.body.rightMiddleMedial) rightHandFingers.push_back(*actor.body.rightMiddleMedial); if (actor.body.rightMiddleDistal) rightHandFingers.push_back(*actor.body.rightMiddleDistal); if (actor.body.rightMiddleTip) rightHandFingers.push_back(*actor.body.rightMiddleTip); if (actor.body.rightRingProximal) rightHandFingers.push_back(*actor.body.rightRingProximal); if (actor.body.rightRingMedial) rightHandFingers.push_back(*actor.body.rightRingMedial); if (actor.body.rightRingDistal) rightHandFingers.push_back(*actor.body.rightRingDistal); if (actor.body.rightRingTip) rightHandFingers.push_back(*actor.body.rightRingTip); if (actor.body.rightLittleProximal) rightHandFingers.push_back(*actor.body.rightLittleProximal); if (actor.body.rightLittleMedial) rightHandFingers.push_back(*actor.body.rightLittleMedial); if (actor.body.rightLittleDistal) rightHandFingers.push_back(*actor.body.rightLittleDistal); if (actor.body.rightLittleTip) 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; } } }