#include "BoneAnimatedModel.h" #include #include #include #include #include #include namespace ZL { int getIndexByValue(const std::string& name, const std::vector& words) { for (int i = 0; i < words.size(); i++) { if (words[i] == name) { return i; } } std::cout << "Bone name not found: " << name << std::endl; throw std::runtime_error("Bone name not found: " + name); return -1; } void BoneSystem::LoadFromFile(const std::string& fileName, const std::string& ZIPFileName) { std::ifstream filestream; std::istringstream zipStream; if (!ZIPFileName.empty()) { std::vector fileData = readFileFromZIP(fileName, ZIPFileName); std::string fileContents(fileData.begin(), fileData.end()); zipStream.str(fileContents); } else { filestream.open(fileName); } std::istream& f = (!ZIPFileName.empty()) ? static_cast(zipStream) : static_cast(filestream); //Skip first 5 lines std::string tempLine; for (int i = 0; i < 5; i++) { std::getline(f, tempLine); } std::getline(f, tempLine); static const std::regex pattern_count(R"(\d+)"); static const std::regex pattern_float(R"([-]?\d+\.\d+)"); static const std::regex pattern_int(R"([-]?\d+)"); static const std::regex pattern_boneChildren(R"(\'([^\']+)\')"); static const std::regex pattern_bone_weight(R"(\'([^\']+)\'.*?([-]?\d+\.\d+))"); std::smatch match; int numberBones; if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); numberBones = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } std::vector bones; std::vector boneNames; std::vector boneParentNames; std::unordered_map> boneChildren; bones.resize(numberBones); boneNames.resize(numberBones); boneParentNames.resize(numberBones); for (int i = 0; i < numberBones; i++) { std::getline(f, tempLine); std::string boneName = tempLine.substr(6); boneNames[i] = boneName; std::getline(f, tempLine); std::vector floatValues; auto b = tempLine.cbegin(); auto e = tempLine.cend(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } bones[i].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] }; std::getline(f, tempLine); //skip tail std::getline(f, tempLine); //len if (std::regex_search(tempLine, match, pattern_float)) { std::string len_str = match.str(); bones[i].boneLength = std::stof(len_str); } else { throw std::runtime_error("No number found in the input string."); } //---------- matrix begin std::getline(f, tempLine); b = tempLine.cbegin(); e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } bones[i].boneMatrixWorld.data()[0] = floatValues[0]; bones[i].boneMatrixWorld.data()[0 + 1 * 3] = floatValues[1]; bones[i].boneMatrixWorld.data()[0 + 2 * 3] = floatValues[2]; std::getline(f, tempLine); b = tempLine.cbegin(); e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } bones[i].boneMatrixWorld.data()[1] = floatValues[0]; bones[i].boneMatrixWorld.data()[1 + 1 * 3] = floatValues[1]; bones[i].boneMatrixWorld.data()[1 + 2 * 3] = floatValues[2]; std::getline(f, tempLine); b = tempLine.cbegin(); e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } bones[i].boneMatrixWorld.data()[2] = floatValues[0]; bones[i].boneMatrixWorld.data()[2 + 1 * 3] = floatValues[1]; bones[i].boneMatrixWorld.data()[2 + 2 * 3] = floatValues[2]; //----------- matrix end std::getline(f, tempLine); //parent if (tempLine == " Parent: None") { bones[i].parent = -1; } else { std::string boneParent = tempLine.substr(10); boneParentNames[i] = boneParent; } std::getline(f, tempLine); //children b = tempLine.cbegin(); e = tempLine.cend(); while (std::regex_search(b, e, match, pattern_boneChildren)) { boneChildren[boneName].push_back(match.str(1)); b = match.suffix().first; } } std::cout << "bone names count : " << boneNames.size() << std::endl; //Now process all the bones: for (int i = 0; i < numberBones; i++) { std::string boneName = boneNames[i]; std::string boneParent = boneParentNames[i]; if (boneParent == "" || boneParent == "None") { bones[i].parent = -1; } else { bones[i].parent = getIndexByValue(boneParent, boneNames); } for (int j = 0; j < boneChildren[boneName].size(); j++) { bones[i].children.push_back(getIndexByValue(boneChildren[boneName][j], boneNames)); } } startBones = bones; currentBones = bones; std::getline(f, tempLine); //vertice count int numberVertices; if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); numberVertices = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } std::vector vertices; vertices.resize(numberVertices); for (int i = 0; i < numberVertices; i++) { std::getline(f, tempLine); std::vector floatValues; auto b = tempLine.cbegin(); auto e = tempLine.cend(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } vertices[i] = Vector3f{floatValues[0], floatValues[1], floatValues[2]}; } //==== process uv and normals begin std::getline(f, tempLine); //===UV Coordinates: std::getline(f, tempLine); //triangle count int numberTriangles; if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); numberTriangles = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } // Now process UVs std::vector> uvCoords; uvCoords.resize(numberTriangles); for (int i = 0; i < numberTriangles; i++) { std::getline(f, tempLine); //Face 0 int uvCount; std::getline(f, tempLine); if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); uvCount = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } if (uvCount != 3) { throw std::runtime_error("more than 3 uvs"); } std::vector floatValues; for (int j = 0; j < 3; j++) { std::getline(f, tempLine); //UV auto b = tempLine.cbegin(); auto e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } if (floatValues.size() != 2) { throw std::runtime_error("more than 2 uvs---"); } uvCoords[i][j] = Vector2f{ floatValues[0],floatValues[1] }; } } std::getline(f, tempLine); //===Normals: std::vector normals; normals.resize(numberVertices); for (int i = 0; i < numberVertices; i++) { std::getline(f, tempLine); std::vector floatValues; auto b = tempLine.cbegin(); auto e = tempLine.cend(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] }; } //==== process uv and normals end std::getline(f, tempLine); //triangle count. //numberTriangles; //Need to check if new value is the same as was read before if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); numberTriangles = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } std::vector> triangles; triangles.resize(numberTriangles); for (int i = 0; i < numberTriangles; i++) { std::getline(f, tempLine); std::vector intValues; auto b = tempLine.cbegin(); auto e = tempLine.cend(); while (std::regex_search(b, e, match, pattern_int)) { intValues.push_back(std::stoi(match.str())); b = match.suffix().first; } triangles[i] = { intValues[0], intValues[1], intValues[2] }; } std::getline(f, tempLine);//=== Vertex Weights === std::vector> localVerticesBoneWeight; localVerticesBoneWeight.resize(numberVertices); for (int i = 0; i < numberVertices; i++) { std::getline(f, tempLine); //skip Vertex 0: std::getline(f, tempLine); //vertex group count int boneCount; if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); boneCount = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } if (boneCount > MAX_BONE_COUNT) { throw std::runtime_error("more than 5 bones"); } float sumWeights = 0; for (int j = 0; j < boneCount; j++) { std::getline(f, tempLine); //Group: 'Bone', Weight: 0.9929084181785583 if (std::regex_search(tempLine, match, pattern_bone_weight)) { std::string word = match.str(1); double weight = std::stod(match.str(2)); //std::cout << "Bone name: " << word << ", weight: " << weight << std::endl; int boneNumber = getIndexByValue(word, boneNames); localVerticesBoneWeight[i][j].boneIndex = boneNumber; localVerticesBoneWeight[i][j].weight = weight; sumWeights += weight; } else { throw std::runtime_error("No match found in the input string."); } } //Normalize weights: for (int j = 0; j < boneCount; j++) { localVerticesBoneWeight[i][j].weight = localVerticesBoneWeight[i][j].weight / sumWeights; } } std::getline(f, tempLine);//=== Animation Keyframes === std::getline(f, tempLine);//=== Bone Transforms per Keyframe === std::getline(f, tempLine); int numberKeyFrames; if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); numberKeyFrames = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } animations.resize(1); animations[0].keyFrames.resize(numberKeyFrames); for (int i = 0; i < numberKeyFrames; i++) { std::getline(f, tempLine); int numberFrame; if (std::regex_search(tempLine, match, pattern_count)) { std::string number_str = match.str(); numberFrame = std::stoi(number_str); } else { throw std::runtime_error("No number found in the input string."); } animations[0].keyFrames[i].frame = numberFrame; animations[0].keyFrames[i].bones.resize(numberBones); for (int j = 0; j < numberBones; j++) { std::getline(f, tempLine); std::string boneName = tempLine.substr(8); int boneNumber = getIndexByValue(boneName, boneNames); animations[0].keyFrames[i].bones[boneNumber] = startBones[boneNumber]; std::getline(f, tempLine); // Location: std::vector floatValues; auto b = tempLine.cbegin(); auto e = tempLine.cend(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } animations[0].keyFrames[i].bones[boneNumber].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] }; std::getline(f, tempLine); // Rotation std::getline(f, tempLine); // Matrix //=============== Matrix begin ================== std::getline(f, tempLine); b = tempLine.cbegin(); e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0] = floatValues[0]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0 + 1 * 4] = floatValues[1]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0 + 2 * 4] = floatValues[2]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0 + 3 * 4] = floatValues[3]; std::getline(f, tempLine); b = tempLine.cbegin(); e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1] = floatValues[0]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1 + 1 * 4] = floatValues[1]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1 + 2 * 4] = floatValues[2]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1 + 3 * 4] = floatValues[3]; std::getline(f, tempLine); b = tempLine.cbegin(); e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2] = floatValues[0]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2 + 1 * 4] = floatValues[1]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2 + 2 * 4] = floatValues[2]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2 + 3 * 4] = floatValues[3]; std::getline(f, tempLine); b = tempLine.cbegin(); e = tempLine.cend(); floatValues.clear(); while (std::regex_search(b, e, match, pattern_float)) { floatValues.push_back(std::stof(match.str())); b = match.suffix().first; } animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3] = floatValues[0]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3 + 1 * 4] = floatValues[1]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3 + 2 * 4] = floatValues[2]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3 + 3 * 4] = floatValues[3]; //=============== Matrix end ================== } } // Now let's process bone weights and vertices for (int i = 0; i < numberTriangles; i++) { mesh.PositionData.push_back(vertices[triangles[i][0]]); mesh.PositionData.push_back(vertices[triangles[i][1]]); mesh.PositionData.push_back(vertices[triangles[i][2]]); verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]); verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]); verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]); mesh.TexCoordData.push_back(uvCoords[i][0]); mesh.TexCoordData.push_back(uvCoords[i][1]); mesh.TexCoordData.push_back(uvCoords[i][2]); } startMesh = mesh; if (startBones.size() > MAX_GPU_BONES) { std::cout << "Warning: model has " << startBones.size() << " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl; //throw std::runtime_error("Too many bones for GPU skinning"); } } void BoneSystem::LoadFromBinaryFile(const std::string& fileName, const std::string& ZIPFileName) { std::vector fileData; if (!ZIPFileName.empty()) { fileData = readFileFromZIP(fileName, ZIPFileName); } else { std::ifstream f(fileName, std::ios::binary | std::ios::ate); if (!f.is_open()) throw std::runtime_error("Failed to open binary file: " + fileName); std::streamsize fileSize = f.tellg(); f.seekg(0); fileData.resize(static_cast(fileSize)); f.read(fileData.data(), fileSize); } const char* ptr = fileData.data(); auto readRaw = [&](void* dst, size_t n) { std::memcpy(dst, ptr, n); ptr += n; }; auto readUint32 = [&]() -> uint32_t { uint32_t v; readRaw(&v, 4); return v; }; auto readInt32 = [&]() -> int32_t { int32_t v; readRaw(&v, 4); return v; }; auto readFloat = [&]() -> float { float v; readRaw(&v, 4); return v; }; auto readVec3 = [&]() -> Vector3f { return Vector3f{readFloat(), readFloat(), readFloat()}; }; auto readVec2 = [&]() -> Vector2f { return Vector2f{readFloat(), readFloat()}; }; // Header char magic[4]; readRaw(magic, 4); if (std::memcmp(magic, "BSAF", 4) != 0) throw std::runtime_error("Invalid binary animation file (bad magic)"); uint32_t version = readUint32(); if (version != 1) throw std::runtime_error("Unsupported binary animation file version"); // ---- Bones ---- uint32_t numBones = readUint32(); std::vector bones(numBones); for (uint32_t i = 0; i < numBones; i++) { bones[i].boneStartWorld = readVec3(); bones[i].boneLength = readFloat(); // 3x3 matrix (row-major in file). // Stored with stride-3 into Matrix4f to match the text loader. float m[9]; for (int j = 0; j < 9; j++) m[j] = readFloat(); bones[i].boneMatrixWorld = Matrix4f::Zero(); for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) bones[i].boneMatrixWorld.data()[r + c * 3] = m[r * 3 + c]; bones[i].parent = readInt32(); uint32_t numChildren = readUint32(); bones[i].children.resize(numChildren); for (uint32_t j = 0; j < numChildren; j++) bones[i].children[j] = readInt32(); } startBones = bones; currentBones = bones; // ---- Vertices ---- uint32_t numVertices = readUint32(); std::vector vertices(numVertices); for (uint32_t i = 0; i < numVertices; i++) vertices[i] = readVec3(); // ---- UV Coordinates ---- uint32_t numFaces = readUint32(); std::vector> uvCoords(numFaces); for (uint32_t i = 0; i < numFaces; i++) for (int j = 0; j < 3; j++) uvCoords[i][j] = readVec2(); // ---- Normals (read but not currently used by mesh) ---- std::vector normals(numVertices); for (uint32_t i = 0; i < numVertices; i++) normals[i] = readVec3(); // ---- Triangles ---- uint32_t numTriangles = readUint32(); std::vector> triangles(numTriangles); for (uint32_t i = 0; i < numTriangles; i++) triangles[i] = { readInt32(), readInt32(), readInt32() }; // ---- Vertex Weights ---- std::vector> localVerticesBoneWeight(numVertices); for (uint32_t i = 0; i < numVertices; i++) { uint32_t numGroups = readUint32(); float sumWeights = 0; for (uint32_t j = 0; j < numGroups; j++) { int boneIdx = readInt32(); float weight = readFloat(); if (j < MAX_BONE_COUNT) { localVerticesBoneWeight[i][j].boneIndex = boneIdx; localVerticesBoneWeight[i][j].weight = weight; sumWeights += weight; } } // Normalize weights uint32_t cap = (numGroups < MAX_BONE_COUNT) ? numGroups : MAX_BONE_COUNT; for (uint32_t j = 0; j < cap; j++) localVerticesBoneWeight[i][j].weight /= sumWeights; } // ---- Animation Keyframes ---- uint32_t numKeyframes = readUint32(); animations.resize(1); animations[0].keyFrames.resize(numKeyframes); for (uint32_t i = 0; i < numKeyframes; i++) { animations[0].keyFrames[i].frame = readInt32(); animations[0].keyFrames[i].bones.resize(numBones); for (uint32_t j = 0; j < numBones; j++) { animations[0].keyFrames[i].bones[j] = startBones[j]; animations[0].keyFrames[i].bones[j].boneStartWorld = readVec3(); // 4x4 matrix (row-major in file, stored with stride-4 into Matrix4f) float m[16]; for (int k = 0; k < 16; k++) m[k] = readFloat(); for (int r = 0; r < 4; r++) for (int c = 0; c < 4; c++) animations[0].keyFrames[i].bones[j].boneMatrixWorld.data()[r + c * 4] = m[r * 4 + c]; } } // ---- Build per-triangle mesh (same expansion as text loader) ---- for (uint32_t i = 0; i < numTriangles; i++) { mesh.PositionData.push_back(vertices[triangles[i][0]]); mesh.PositionData.push_back(vertices[triangles[i][1]]); mesh.PositionData.push_back(vertices[triangles[i][2]]); verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]); verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]); verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]); mesh.TexCoordData.push_back(uvCoords[i][0]); mesh.TexCoordData.push_back(uvCoords[i][1]); mesh.TexCoordData.push_back(uvCoords[i][2]); } startMesh = mesh; if (startBones.size() > MAX_GPU_BONES) { std::cout << "Warning: model has " << startBones.size() << " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl; //throw std::runtime_error("Too many bones for GPU skinning"); } } void GpuBoneData::PrepareGpuSkinningData(const std::vector>& verticesBoneWeight) { size_t vertexCount = verticesBoneWeight.size(); boneIndices0.resize(vertexCount); boneIndices1.resize(vertexCount); boneWeights0.resize(vertexCount); boneWeights1.resize(vertexCount); for (size_t i = 0; i < vertexCount; i++) { boneIndices0[i] = Vector4f( static_cast(max(0, verticesBoneWeight[i][0].boneIndex)), static_cast(max(0, verticesBoneWeight[i][1].boneIndex)), static_cast(max(0, verticesBoneWeight[i][2].boneIndex)), static_cast(max(0, verticesBoneWeight[i][3].boneIndex)) ); boneIndices1[i] = Vector2f( static_cast(max(0, verticesBoneWeight[i][4].boneIndex)), static_cast(max(0, verticesBoneWeight[i][5].boneIndex)) ); boneWeights0[i] = Vector4f( verticesBoneWeight[i][0].weight, verticesBoneWeight[i][1].weight, verticesBoneWeight[i][2].weight, verticesBoneWeight[i][3].weight ); boneWeights1[i] = Vector2f( verticesBoneWeight[i][4].weight, verticesBoneWeight[i][5].weight ); } prepared = true; } /* void BoneSystem::ComputeSkinningMatrices(int frame, std::vector& outMatrices) const { int startingKeyFrame = -1; for (size_t i = 0; i < animations[0].keyFrames.size() - 1; i++) { int oldFrame = animations[0].keyFrames[i].frame; int nextFrame = animations[0].keyFrames[i + 1].frame; if (frame >= oldFrame && frame < nextFrame) { startingKeyFrame = static_cast(i); break; } } if (startingKeyFrame == -1) { outMatrices.resize(startBones.size()); for (auto& m : outMatrices) m = Matrix4f::Identity(); return; } int modifiedFrameNumber = frame - animations[0].keyFrames[startingKeyFrame].frame; int diffFrames = animations[0].keyFrames[startingKeyFrame + 1].frame - animations[0].keyFrames[startingKeyFrame].frame; float t = (modifiedFrameNumber + 0.f) / diffFrames; const std::vector& oneFrameBones = animations[0].keyFrames[startingKeyFrame].bones; const std::vector& nextFrameBones = animations[0].keyFrames[startingKeyFrame + 1].bones; outMatrices.resize(startBones.size()); for (size_t i = 0; i < startBones.size(); i++) { Vector3f interpPos; interpPos(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0)); interpPos(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1)); interpPos(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2)); Matrix3f oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0); Matrix3f nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0); Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized(); Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized(); Eigen::Quaternionf result = q1.slerp(t, q2); Matrix3f boneMatrixWorld3 = result.toRotationMatrix(); Matrix4f currentBoneMatrixWorld4 = Eigen::Matrix4f::Identity(); currentBoneMatrixWorld4.block<3, 3>(0, 0) = boneMatrixWorld3; currentBoneMatrixWorld4.block<3, 1>(0, 3) = interpPos; Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld; Matrix4f invertedStartBoneMatrixWorld4 = startBoneMatrixWorld4.inverse(); outMatrices[i] = currentBoneMatrixWorld4 * invertedStartBoneMatrixWorld4; } } */ void BoneSystem::Interpolate(int frame) { int startingFrame = -1; for (int i = 0; i < animations[0].keyFrames.size() - 1; i++) { int oldFrame = animations[0].keyFrames[i].frame; int nextFrame = animations[0].keyFrames[i + 1].frame; if (frame >= oldFrame && frame < nextFrame) { startingFrame = i; break; } } if (startingFrame == -1) { std::cout << "Exception here: frame number is out of range of keyframes. Frame: " << frame << std::endl; throw std::runtime_error("Exception here"); } int modifiedFrameNumber = frame - animations[0].keyFrames[startingFrame].frame; int diffFrames = animations[0].keyFrames[startingFrame + 1].frame - animations[0].keyFrames[startingFrame].frame; float t = (modifiedFrameNumber + 0.f) / diffFrames; std::vector& oneFrameBones = animations[0].keyFrames[startingFrame].bones; std::vector& nextFrameBones = animations[0].keyFrames[startingFrame+1].bones; std::vector skinningMatrixForEachBone; skinningMatrixForEachBone.resize(currentBones.size()); for (int i = 0; i < currentBones.size(); i++) { currentBones[i].boneStartWorld(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0)); currentBones[i].boneStartWorld(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1)); currentBones[i].boneStartWorld(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2)); Matrix3f oneFrameBonesMatrix; oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0); Matrix3f nextFrameBonesMatrix; nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0); Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized(); Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized(); Eigen::Quaternionf q1_norm = q1.normalized(); Eigen::Quaternionf q2_norm = q2.normalized(); Eigen::Quaternionf result = q1_norm.slerp(t, q2_norm); Matrix3f boneMatrixWorld3 = result.toRotationMatrix(); currentBones[i].boneMatrixWorld = Eigen::Matrix4f::Identity(); // Копируем 3x3 матрицу в верхний левый угол currentBones[i].boneMatrixWorld.block<3, 3>(0, 0) = boneMatrixWorld3; // Копируем позицию в последний столбец (первые 3 элемента) currentBones[i].boneMatrixWorld.block<3, 1>(0, 3) = currentBones[i].boneStartWorld; Matrix4f currentBoneMatrixWorld4 = currentBones[i].boneMatrixWorld; Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld; Matrix4f inverstedStartBoneMatrixWorld4 = startBoneMatrixWorld4.inverse(); skinningMatrixForEachBone[i] = currentBoneMatrixWorld4 * inverstedStartBoneMatrixWorld4; } for (int i = 0; i < mesh.PositionData.size(); i++) { Vector4f originalPos = { startMesh.PositionData[i](0), startMesh.PositionData[i](1), startMesh.PositionData[i](2), 1.0}; Vector4f finalPos = Vector4f{0.f, 0.f, 0.f, 0.f}; bool vMoved = false; for (int j = 0; j < MAX_BONE_COUNT; j++) { if (verticesBoneWeight[i][j].weight != 0) { if (verticesBoneWeight[i][j].boneIndex == -1) { std::cout << "Exception here: bone index is -1 but weight is > 0" << std::endl; throw std::runtime_error("Bones loaded incorrectly - bone index is -1 but weight is > 0"); } vMoved = true; finalPos = finalPos + (skinningMatrixForEachBone[verticesBoneWeight[i][j].boneIndex] * originalPos) * verticesBoneWeight[i][j].weight; } } if (!vMoved) { finalPos = originalPos; } mesh.PositionData[i](0) = finalPos(0); mesh.PositionData[i](1) = finalPos(1); mesh.PositionData[i](2) = finalPos(2); } } void GpuSkinningShaderData::prepareGpuSkinningVBOs(BoneSystem& model) { if (gpuSkinningPrepared) { return; } gpuBoneData.PrepareGpuSkinningData(model.verticesBoneWeight); // Upload bind-pose mesh (static, done once) bindPoseMutable.AssignFrom(model.startMesh); auto& gpu = gpuBoneData; boneIndices0VBO = std::make_shared(); glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer()); glBufferData(GL_ARRAY_BUFFER, gpu.boneIndices0.size() * sizeof(Eigen::Vector4f), gpu.boneIndices0.data(), GL_STATIC_DRAW); boneIndices1VBO = std::make_shared(); glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer()); glBufferData(GL_ARRAY_BUFFER, gpu.boneIndices1.size() * sizeof(Eigen::Vector2f), gpu.boneIndices1.data(), GL_STATIC_DRAW); boneWeights0VBO = std::make_shared(); glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer()); glBufferData(GL_ARRAY_BUFFER, gpu.boneWeights0.size() * sizeof(Eigen::Vector4f), gpu.boneWeights0.data(), GL_STATIC_DRAW); boneWeights1VBO = std::make_shared(); glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer()); glBufferData(GL_ARRAY_BUFFER, gpu.boneWeights1.size() * sizeof(Eigen::Vector2f), gpu.boneWeights1.data(), GL_STATIC_DRAW); gpuSkinningPrepared = true; } void GpuSkinningShaderData::RenderVBO(Renderer& renderer) { // Bind position and texcoord VBOs glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.positionVBO->getBuffer()); renderer.VertexAttribPointer3fv("vPosition", 0, NULL); if (bindPoseMutable.texCoordVBO) { glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.texCoordVBO->getBuffer()); renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL); } // Bind bone index VBOs glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer()); renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL); glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer()); renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL); // Bind bone weight VBOs glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer()); renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL); glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer()); renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL); glDrawArrays(GL_TRIANGLES, 0, static_cast(bindPoseMutable.data.PositionData.size())); } void GpuSkinningShaderData::ComputeSkinningMatrices(const std::vector& startBones, const std::vector& keyFrames, int frame) { int startingKeyFrame = -1; for (size_t i = 0; i < keyFrames.size() - 1; i++) { int oldFrame = keyFrames[i].frame; int nextFrame = keyFrames[i + 1].frame; if (frame >= oldFrame && frame < nextFrame) { startingKeyFrame = static_cast(i); break; } } skinningMatrices.resize(startBones.size()); if (startingKeyFrame == -1) { for (auto& m : skinningMatrices) m = Matrix4f::Identity(); return; } int modifiedFrameNumber = frame - keyFrames[startingKeyFrame].frame; int diffFrames = keyFrames[startingKeyFrame + 1].frame - keyFrames[startingKeyFrame].frame; float t = (modifiedFrameNumber + 0.f) / diffFrames; const std::vector& oneFrameBones = keyFrames[startingKeyFrame].bones; const std::vector& nextFrameBones = keyFrames[startingKeyFrame + 1].bones; for (size_t i = 0; i < startBones.size(); i++) { Vector3f interpPos; interpPos(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0)); interpPos(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1)); interpPos(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2)); Matrix3f oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0); Matrix3f nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0); Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized(); Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized(); Eigen::Quaternionf result = q1.slerp(t, q2); Matrix3f boneMatrixWorld3 = result.toRotationMatrix(); Matrix4f currentBoneMatrixWorld4 = Eigen::Matrix4f::Identity(); currentBoneMatrixWorld4.block<3, 3>(0, 0) = boneMatrixWorld3; currentBoneMatrixWorld4.block<3, 1>(0, 3) = interpPos; Matrix4f startBoneMatrixWorld4 = keyFrames[0].bones[i].boneMatrixWorld; Matrix4f invertedStartBoneMatrixWorld4 = startBoneMatrixWorld4.inverse(); skinningMatrices[i] = currentBoneMatrixWorld4 * invertedStartBoneMatrixWorld4; } } }