#include "BoneAnimatedModelNew.h" #include #include #include #include #include #include namespace ZL { #ifdef EMSCRIPTEN using std::min; using std::max; #endif 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; } static std::string trimRight(const std::string& s) { size_t end = s.size(); while (end > 0 && (s[end - 1] == '\r' || s[end - 1] == '\n' || s[end - 1] == ' ' || s[end - 1] == '\t')) end--; return s.substr(0, end); } 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 BoneSystemNew::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); 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; std::string tempLine; // ---- Armature matrix (4 lines after the header) ---- std::getline(f, tempLine); // === Armature Matrix === armatureMatrix = Matrix4f::Identity(); for (int r = 0; r < 4; r++) { 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; } if (floatValues.size() >= 4) { for (int c = 0; c < 4; c++) armatureMatrix.data()[r + c * 4] = floatValues[c]; } } // ---- Bones ---- std::getline(f, tempLine); // === Armature Bones: N int numberBones; if (std::regex_search(tempLine, match, pattern_count)) { numberBones = std::stoi(match.str()); } else { throw std::runtime_error("Armature bones count not found"); } 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); // Bone: name std::string boneName = trimRight(tempLine.substr(6)); boneNames[i] = boneName; std::getline(f, tempLine); // HEAD_LOCAL 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); // TAIL_LOCAL std::getline(f, tempLine); // Length if (std::regex_search(tempLine, match, pattern_float)) { bones[i].boneLength = std::stof(match.str()); } else { throw std::runtime_error("Bone length not found"); } // 3x3 matrix for (int r = 0; r < 3; r++) { 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()[r] = floatValues[0]; bones[i].boneMatrixWorld.data()[r + 1 * 3] = floatValues[1]; bones[i].boneMatrixWorld.data()[r + 2 * 3] = floatValues[2]; } std::getline(f, tempLine); // Parent std::string parentLine = trimRight(tempLine); if (parentLine == " Parent: None") { bones[i].parent = -1; } else { boneParentNames[i] = parentLine.substr(10); } std::getline(f, tempLine); // Children auto bc = tempLine.cbegin(); auto ec = tempLine.cend(); while (std::regex_search(bc, ec, match, pattern_boneChildren)) { boneChildren[boneName].push_back(match.str(1)); bc = match.suffix().first; } } // Resolve parent/child indices for (int i = 0; i < numberBones; i++) { const std::string& boneName = boneNames[i]; const std::string& boneParent = boneParentNames[i]; if (boneParent == "" || boneParent == "None") bones[i].parent = -1; else bones[i].parent = getIndexByValue(boneParent, boneNames); for (size_t j = 0; j < boneChildren[boneName].size(); j++) bones[i].children.push_back(getIndexByValue(boneChildren[boneName][j], boneNames)); } startBones = bones; currentBones = bones; this->boneNames = boneNames; // ---- Multi-mesh header ---- std::getline(f, tempLine); // === TOTAL MESHES TO EXPORT: N === int numberMeshes; if (std::regex_search(tempLine, match, pattern_count)) { numberMeshes = std::stoi(match.str()); } else { throw std::runtime_error("Total meshes count not found"); } for (int meshIdx = 0; meshIdx < numberMeshes; meshIdx++) { // === Mesh Object: Name === std::getline(f, tempLine); std::string meshHeader = trimRight(tempLine); // Extract mesh name: strip "=== Mesh Object: " prefix and " ===" suffix const std::string prefix = "=== Mesh Object: "; const std::string suffix = " ==="; std::string meshName; if (meshHeader.size() >= prefix.size() + suffix.size() && meshHeader.compare(0, prefix.size(), prefix) == 0 && meshHeader.compare(meshHeader.size() - suffix.size(), suffix.size(), suffix) == 0) { meshName = meshHeader.substr(prefix.size(), meshHeader.size() - prefix.size() - suffix.size()); } else { throw std::runtime_error("Invalid mesh header: " + meshHeader); } MeshBoneData meshData; // Vertices std::getline(f, tempLine); // ===Vertices: N int numberVertices; if (std::regex_search(tempLine, match, pattern_count)) { numberVertices = std::stoi(match.str()); } else { throw std::runtime_error("Vertex count not found for mesh " + meshName); } std::vector vertices(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] }; } // UV coordinates std::getline(f, tempLine); // ===UV Coordinates: std::getline(f, tempLine); // Face count: M int numberFaces; if (std::regex_search(tempLine, match, pattern_count)) { numberFaces = std::stoi(match.str()); } else { throw std::runtime_error("Face count not found for mesh " + meshName); } std::vector> uvCoords(numberFaces); for (int i = 0; i < numberFaces; i++) { std::getline(f, tempLine); // Face X std::getline(f, tempLine); // UV Count: 3 int uvCount; if (std::regex_search(tempLine, match, pattern_count)) { uvCount = std::stoi(match.str()); } else { throw std::runtime_error("UV count not found"); } if (uvCount != 3) throw std::runtime_error("more than 3 uvs"); for (int j = 0; j < 3; j++) { std::getline(f, tempLine); // UV 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; } if (floatValues.size() != 2) throw std::runtime_error("more than 2 uvs---"); uvCoords[i][j] = Vector2f{ floatValues[0], floatValues[1] }; } } // Normals std::getline(f, tempLine); // ===Normals: std::vector normals(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] }; } // Triangles std::getline(f, tempLine); // ===Triangles: M int numberTriangles; if (std::regex_search(tempLine, match, pattern_count)) { numberTriangles = std::stoi(match.str()); } else { throw std::runtime_error("Triangle count not found for mesh " + meshName); } std::vector> triangles(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] }; } // Vertex weights std::getline(f, tempLine); // === Vertex Weights (Max 5 bones per vertex) === std::vector> localVerticesBoneWeight(numberVertices); for (int i = 0; i < numberVertices; i++) { std::getline(f, tempLine); // Vertex X: std::getline(f, tempLine); // Vertex groups: K int boneCount; if (std::regex_search(tempLine, match, pattern_count)) { boneCount = std::stoi(match.str()); } else { throw std::runtime_error("Vertex group count not found"); } 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); if (std::regex_search(tempLine, match, pattern_bone_weight)) { std::string word = match.str(1); double weight = std::stod(match.str(2)); int boneNumber = getIndexByValue(word, boneNames); localVerticesBoneWeight[i][j].boneIndex = boneNumber; localVerticesBoneWeight[i][j].weight = static_cast(weight); sumWeights += static_cast(weight); } else { throw std::runtime_error("No match found in bone weight line"); } } for (int j = 0; j < boneCount; j++) localVerticesBoneWeight[i][j].weight /= sumWeights; } // Build per-triangle expanded mesh for (int i = 0; i < numberTriangles; i++) { meshData.mesh.PositionData.push_back(vertices[triangles[i][0]]); meshData.mesh.PositionData.push_back(vertices[triangles[i][1]]); meshData.mesh.PositionData.push_back(vertices[triangles[i][2]]); meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]); meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]); meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]); meshData.mesh.TexCoordData.push_back(uvCoords[i][0]); meshData.mesh.TexCoordData.push_back(uvCoords[i][1]); meshData.mesh.TexCoordData.push_back(uvCoords[i][2]); } meshData.startMesh = meshData.mesh; meshes[meshName] = std::move(meshData); meshNamesOrdered.push_back(meshName); } std::cout << "Loaded " << numberMeshes << " meshes from " << fileName << std::endl; // ---- Animation Keyframes ---- std::getline(f, tempLine); // === Animation Keyframes === std::getline(f, tempLine); // === Bone Transforms per Keyframe === std::getline(f, tempLine); // Keyframes: N int numberKeyFrames; if (std::regex_search(tempLine, match, pattern_count)) { numberKeyFrames = std::stoi(match.str()); } else { throw std::runtime_error("Keyframe count not found"); } animations.resize(1); animations[0].keyFrames.resize(numberKeyFrames); for (int i = 0; i < numberKeyFrames; i++) { std::getline(f, tempLine); // Frame: N int numberFrame; if (std::regex_search(tempLine, match, pattern_count)) { numberFrame = std::stoi(match.str()); } else { throw std::runtime_error("Frame number not found"); } animations[0].keyFrames[i].frame = numberFrame; animations[0].keyFrames[i].bones.resize(numberBones); for (int j = 0; j < numberBones; j++) { std::getline(f, tempLine); // Bone: name std::string boneName = trimRight(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: for (int r = 0; r < 4; r++) { 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()[r] = floatValues[0]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 1 * 4] = floatValues[1]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 2 * 4] = floatValues[2]; animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 3 * 4] = floatValues[3]; } } } if (startBones.size() > MAX_GPU_BONES) { std::cout << "Warning: model has " << startBones.size() << " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl; } } void BoneSystemNew::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(); const char* end = ptr + fileData.size(); auto readRaw = [&](void* dst, size_t n) { if (ptr + n > end) throw std::runtime_error("Unexpected EOF in binary animation file"); 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()}; }; auto readString = [&]() -> std::string { uint32_t len = readUint32(); std::string s(len, '\0'); if (len > 0) readRaw(&s[0], len); return s; }; // Header char magic[4]; readRaw(magic, 4); if (std::memcmp(magic, "BSMF", 4) != 0) throw std::runtime_error("Invalid multi-mesh binary animation file (bad magic)"); uint32_t version = readUint32(); if (version != 2) throw std::runtime_error("Unsupported multi-mesh binary animation file version"); // Armature matrix (row-major in file, stored with stride-4 into Matrix4f) { float m[16]; for (int k = 0; k < 16; k++) m[k] = readFloat(); armatureMatrix = Matrix4f::Identity(); for (int r = 0; r < 4; r++) for (int c = 0; c < 4; c++) armatureMatrix.data()[r + c * 4] = m[r * 4 + c]; } // 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(); 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; // Bone names boneNames.resize(numBones); for (uint32_t i = 0; i < numBones; i++) boneNames[i] = readString(); // Meshes uint32_t numMeshes = readUint32(); for (uint32_t meshIdx = 0; meshIdx < numMeshes; meshIdx++) { std::string meshName = readString(); MeshBoneData meshData; uint32_t numVertices = readUint32(); std::vector vertices(numVertices); for (uint32_t i = 0; i < numVertices; i++) vertices[i] = readVec3(); 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(); std::vector normals(numVertices); for (uint32_t i = 0; i < numVertices; i++) normals[i] = readVec3(); uint32_t numTriangles = readUint32(); std::vector> triangles(numTriangles); for (uint32_t i = 0; i < numTriangles; i++) triangles[i] = { readInt32(), readInt32(), readInt32() }; 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; } } uint32_t cap = (numGroups < MAX_BONE_COUNT) ? numGroups : MAX_BONE_COUNT; for (uint32_t j = 0; j < cap; j++) localVerticesBoneWeight[i][j].weight /= sumWeights; } for (uint32_t i = 0; i < numTriangles; i++) { meshData.mesh.PositionData.push_back(vertices[triangles[i][0]]); meshData.mesh.PositionData.push_back(vertices[triangles[i][1]]); meshData.mesh.PositionData.push_back(vertices[triangles[i][2]]); meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]); meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]); meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]); meshData.mesh.TexCoordData.push_back(uvCoords[i][0]); meshData.mesh.TexCoordData.push_back(uvCoords[i][1]); meshData.mesh.TexCoordData.push_back(uvCoords[i][2]); } meshData.startMesh = meshData.mesh; meshes[meshName] = std::move(meshData); meshNamesOrdered.push_back(meshName); } // 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(); 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]; } } if (startBones.size() > MAX_GPU_BONES) { std::cout << "Warning: model has " << startBones.size() << " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl; } } int BoneSystemNew::findBoneIndex(const std::string& name) const { for (size_t i = 0; i < boneNames.size(); i++) { if (boneNames[i] == name) return static_cast(i); } return -1; } void BoneSystemNew::Interpolate(int frame) { int startingKeyFrame = -1; for (int i = 0; i < static_cast(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 = i; break; } } if (startingKeyFrame == -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[startingKeyFrame].frame; int diffFrames = animations[0].keyFrames[startingKeyFrame + 1].frame - animations[0].keyFrames[startingKeyFrame].frame; float t = (modifiedFrameNumber + 0.f) / diffFrames; std::vector& oneFrameBones = animations[0].keyFrames[startingKeyFrame].bones; std::vector& nextFrameBones = animations[0].keyFrames[startingKeyFrame + 1].bones; std::vector skinningMatrixForEachBone(currentBones.size()); for (size_t 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 = 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(); currentBones[i].boneMatrixWorld = Eigen::Matrix4f::Identity(); currentBones[i].boneMatrixWorld.block<3, 3>(0, 0) = boneMatrixWorld3; currentBones[i].boneMatrixWorld.block<3, 1>(0, 3) = currentBones[i].boneStartWorld; Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld; skinningMatrixForEachBone[i] = currentBones[i].boneMatrixWorld * startBoneMatrixWorld4.inverse(); } for (const auto& name : meshNamesOrdered) { auto it = meshes.find(name); if (it == meshes.end()) continue; MeshBoneData& md = it->second; for (size_t i = 0; i < md.mesh.PositionData.size(); i++) { Vector4f originalPos = { md.startMesh.PositionData[i](0), md.startMesh.PositionData[i](1), md.startMesh.PositionData[i](2), 1.0f }; Vector4f finalPos = Vector4f{ 0.f, 0.f, 0.f, 0.f }; bool vMoved = false; for (int j = 0; j < MAX_BONE_COUNT; j++) { if (md.verticesBoneWeight[i][j].weight != 0) { if (md.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[md.verticesBoneWeight[i][j].boneIndex] * originalPos) * md.verticesBoneWeight[i][j].weight; } } if (!vMoved) finalPos = originalPos; md.mesh.PositionData[i](0) = finalPos(0); md.mesh.PositionData[i](1) = finalPos(1); md.mesh.PositionData[i](2) = finalPos(2); } } } void MeshGpuSkinningData::prepareGpuSkinningVBOs(MeshBoneData& meshData) { if (gpuSkinningPrepared) return; gpuBoneData.PrepareGpuSkinningData(meshData.verticesBoneWeight); bindPoseMutable.AssignFrom(meshData.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 MeshGpuSkinningData::RenderVBO(Renderer& renderer) { CheckGlError(__FILE__, __LINE__); glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.positionVBO->getBuffer()); renderer.VertexAttribPointer3fv("vPosition", 0, NULL); CheckGlError(__FILE__, __LINE__); if (bindPoseMutable.texCoordVBO) { glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.texCoordVBO->getBuffer()); renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL); CheckGlError(__FILE__, __LINE__); } if (bindPoseMutable.normalVBO) { glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.normalVBO->getBuffer()); renderer.VertexAttribPointer3fv("vNormal", 0, NULL); CheckGlError(__FILE__, __LINE__); } else { renderer.DisableVertexAttribArray("vNormal"); CheckGlError(__FILE__, __LINE__); } glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer()); renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL); CheckGlError(__FILE__, __LINE__); glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer()); renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL); CheckGlError(__FILE__, __LINE__); glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer()); renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL); CheckGlError(__FILE__, __LINE__); glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer()); renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL); CheckGlError(__FILE__, __LINE__); glDrawArrays(GL_TRIANGLES, 0, static_cast(bindPoseMutable.data.PositionData.size())); } void GpuSkinningShaderDataNew::prepareGpuSkinningVBOs(BoneSystemNew& model) { for (const auto& name : model.meshNamesOrdered) { auto it = model.meshes.find(name); if (it == model.meshes.end()) continue; perMesh[name].prepareGpuSkinningVBOs(it->second); } } void GpuSkinningShaderDataNew::RenderVBO(Renderer& renderer, const std::vector& meshNamesOrdered) { for (const auto& name : meshNamesOrdered) { auto it = perMesh.find(name); if (it == perMesh.end()) continue; it->second.RenderVBO(renderer); } } void GpuSkinningShaderDataNew::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; skinningMatrices[i] = currentBoneMatrixWorld4 * startBoneMatrixWorld4.inverse(); } } }