// ---------------------------------------------------------------- // From Game Programming in C++ by Sanjay Madhav // Copyright (C) 2017 Sanjay Madhav. All rights reserved. // // Released under the BSD License // See LICENSE in root directory for full details. // ---------------------------------------------------------------- #include "Skeleton.h" #include #include #include #include #include "MatrixPalette.h" bool Skeleton::Load(const std::string& fileName) { std::ifstream file(fileName); if (!file.is_open()) { SDL_Log("File not found: Skeleton %s", fileName.c_str()); return false; } std::stringstream fileStream; fileStream << file.rdbuf(); std::string contents = fileStream.str(); rapidjson::StringStream jsonStr(contents.c_str()); rapidjson::Document doc; doc.ParseStream(jsonStr); if (!doc.IsObject()) { SDL_Log("Skeleton %s is not valid json", fileName.c_str()); return false; } int ver = doc["version"].GetInt(); // Check the metadata if (ver != 1) { SDL_Log("Skeleton %s unknown format", fileName.c_str()); return false; } const rapidjson::Value& bonecount = doc["bonecount"]; if (!bonecount.IsUint()) { SDL_Log("Skeleton %s doesn't have a bone count.", fileName.c_str()); return false; } size_t count = bonecount.GetUint(); if (count > MAX_SKELETON_BONES) { SDL_Log("Skeleton %s exceeds maximum bone count.", fileName.c_str()); return false; } mBones.reserve(count); const rapidjson::Value& bones = doc["bones"]; if (!bones.IsArray()) { SDL_Log("Skeleton %s doesn't have a bone array?", fileName.c_str()); return false; } if (bones.Size() != count) { SDL_Log("Skeleton %s has a mismatch between the bone count and number of bones", fileName.c_str()); return false; } Bone temp; for (rapidjson::SizeType i = 0; i < count; i++) { if (!bones[i].IsObject()) { SDL_Log("Skeleton %s: Bone %d is invalid.", fileName.c_str(), i); return false; } const rapidjson::Value& name = bones[i]["name"]; temp.mName = name.GetString(); const rapidjson::Value& parent = bones[i]["parent"]; temp.mParent = parent.GetInt(); const rapidjson::Value& bindpose = bones[i]["bindpose"]; if (!bindpose.IsObject()) { SDL_Log("Skeleton %s: Bone %d is invalid.", fileName.c_str(), i); return false; } const rapidjson::Value& rot = bindpose["rot"]; const rapidjson::Value& trans = bindpose["trans"]; if (!rot.IsArray() || !trans.IsArray()) { SDL_Log("Skeleton %s: Bone %d is invalid.", fileName.c_str(), i); return false; } temp.mLocalBindPose.mRotation.x = rot[0].GetDouble(); temp.mLocalBindPose.mRotation.y = rot[1].GetDouble(); temp.mLocalBindPose.mRotation.z = rot[2].GetDouble(); temp.mLocalBindPose.mRotation.w = rot[3].GetDouble(); temp.mLocalBindPose.mTranslation.x = trans[0].GetDouble(); temp.mLocalBindPose.mTranslation.y = trans[1].GetDouble(); temp.mLocalBindPose.mTranslation.z = trans[2].GetDouble(); mBones.emplace_back(temp); } // Now that we have the bones ComputeGlobalInvBindPose(); return true; } void Skeleton::ComputeGlobalInvBindPose() { // Resize to number of bones, which automatically fills identity mGlobalInvBindPoses.resize(GetNumBones()); // Step 1: Compute global bind pose for each bone // The global bind pose for root is just the local bind pose mGlobalInvBindPoses[0] = mBones[0].mLocalBindPose.ToMatrix(); // Each remaining bone's global bind pose is its local pose // multiplied by the parent's global bind pose for (size_t i = 1; i < mGlobalInvBindPoses.size(); i++) { Matrix4 localMat = mBones[i].mLocalBindPose.ToMatrix(); mGlobalInvBindPoses[i] = localMat * mGlobalInvBindPoses[mBones[i].mParent]; } // Step 2: Invert for (size_t i = 0; i < mGlobalInvBindPoses.size(); i++) { mGlobalInvBindPoses[i].Invert(); } }