diff --git a/src/BoneAnimatedModel.cpp b/src/BoneAnimatedModel.cpp index 3c03541..070bbec 100644 --- a/src/BoneAnimatedModel.cpp +++ b/src/BoneAnimatedModel.cpp @@ -758,7 +758,7 @@ namespace ZL prepared = true; } - + /* void BoneSystem::ComputeSkinningMatrices(int frame, std::vector& outMatrices) const { int startingKeyFrame = -1; @@ -815,7 +815,7 @@ namespace ZL outMatrices[i] = currentBoneMatrixWorld4 * invertedStartBoneMatrixWorld4; } } - + */ void BoneSystem::Interpolate(int frame) { int startingFrame = -1; @@ -928,18 +928,18 @@ namespace ZL } - void BoneAnimationData::prepareGpuSkinningVBOs() { + void GpuSkinningShaderData::prepareGpuSkinningVBOs(BoneSystem& model) { if (gpuSkinningPrepared) { return; } - model.gpuBoneData.PrepareGpuSkinningData(model.verticesBoneWeight); + gpuBoneData.PrepareGpuSkinningData(model.verticesBoneWeight); // Upload bind-pose mesh (static, done once) bindPoseMutable.AssignFrom(model.startMesh); - auto& gpu = model.gpuBoneData; + auto& gpu = gpuBoneData; boneIndices0VBO = std::make_shared(); glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer()); @@ -964,5 +964,90 @@ namespace ZL 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; + } + } + } \ No newline at end of file diff --git a/src/BoneAnimatedModel.h b/src/BoneAnimatedModel.h index 5540851..fd1a1d5 100644 --- a/src/BoneAnimatedModel.h +++ b/src/BoneAnimatedModel.h @@ -12,8 +12,6 @@ namespace ZL Vector3f boneStartWorld; float boneLength; Matrix4f boneMatrixWorld; - // boneVector = boneLength * (0, 1, 0) � ���� �������� - // Then multiply by boneMatrixWorld � �� �������� �������� ����� int parent; std::vector children; @@ -59,22 +57,18 @@ namespace ZL std::vector animations; int startingFrame = 0; - GpuBoneData gpuBoneData; - void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = ""); void LoadFromBinaryFile(const std::string& fileName, const std::string& ZIPFileName = ""); void Interpolate(int frame); // GPU skinning: compute skinning matrices without modifying the mesh - void ComputeSkinningMatrices(int frame, std::vector& outMatrices) const; + //void ComputeSkinningMatrices(int frame, std::vector& outMatrices) const; }; - struct BoneAnimationData { - BoneSystem model; - float currentFrame = 0.f; - int lastFrame = -1; - int totalFrames = 1; + + struct GpuSkinningShaderData { + GpuBoneData gpuBoneData; // GPU skinning data (lazily initialized) VertexRenderStruct bindPoseMutable; @@ -85,10 +79,22 @@ namespace ZL bool gpuSkinningPrepared = false; std::vector skinningMatrices; - void prepareGpuSkinningVBOs(); + void prepareGpuSkinningVBOs(BoneSystem& model); + void RenderVBO(Renderer& renderer); + void ComputeSkinningMatrices(const std::vector& startBones, const std::vector& keyFrames, int frame); + }; + + struct BoneAnimationData { + BoneSystem model; + float currentFrame = 0.f; + int lastFrame = -1; + int totalFrames = 1; + + GpuSkinningShaderData gpuSkinningShaderData; }; + }; \ No newline at end of file diff --git a/src/Character.cpp b/src/Character.cpp index d4e1ff2..3360992 100644 --- a/src/Character.cpp +++ b/src/Character.cpp @@ -27,9 +27,6 @@ void Character::loadBinaryAnimation(AnimationState state, const std::string& fil data.totalFrames = 1; } } -/*void Character::setTexture(std::shared_ptr tex) { - texture = tex; -}*/ void Character::setTarget(const Eigen::Vector3f& target, std::function onArrived) { @@ -259,7 +256,7 @@ void Character::update(int64_t deltaMs) { if (static_cast(anim.currentFrame) != anim.lastFrame) { if (useGpuSkinning) { - anim.model.ComputeSkinningMatrices(static_cast(anim.currentFrame), anim.skinningMatrices); + anim.gpuSkinningShaderData.ComputeSkinningMatrices(anim.model.startBones, anim.model.animations[0].keyFrames, static_cast(anim.currentFrame)); } else { anim.model.Interpolate(static_cast(anim.currentFrame)); } @@ -310,14 +307,15 @@ void Character::drawGpuSkinning(Renderer& renderer) { } auto& anim = it->second; - anim.prepareGpuSkinningVBOs(); + anim.gpuSkinningShaderData.prepareGpuSkinningVBOs(anim.model); - if (anim.skinningMatrices.empty()) + if (anim.gpuSkinningShaderData.skinningMatrices.empty()) { if (anim.model.animations.empty() || anim.model.animations[0].keyFrames.empty()) return; - anim.model.ComputeSkinningMatrices( - anim.model.animations[0].keyFrames[0].frame, anim.skinningMatrices); - if (anim.skinningMatrices.empty()) return; + + anim.gpuSkinningShaderData.ComputeSkinningMatrices(anim.model.startBones, anim.model.animations[0].keyFrames, static_cast(anim.currentFrame)); + + if (anim.gpuSkinningShaderData.skinningMatrices.empty()) return; } static const std::string skinningShaderName = "skinning"; static const std::string boneMatricesUniform = "uBoneMatrices[0]"; @@ -337,51 +335,56 @@ void Character::drawGpuSkinning(Renderer& renderer) { // Upload bone skinning matrices renderer.RenderUniformMatrix4fvArray(boneMatricesUniform, - static_cast(anim.skinningMatrices.size()), false, - anim.skinningMatrices[0].data()); + static_cast(anim.gpuSkinningShaderData.skinningMatrices.size()), false, + anim.gpuSkinningShaderData.skinningMatrices[0].data()); glBindTexture(GL_TEXTURE_2D, texture->getTexID()); // Bind VAO (desktop only) #ifndef EMSCRIPTEN #ifndef __ANDROID__ - if (anim.bindPoseMutable.vao) { - glBindVertexArray(anim.bindPoseMutable.vao->getBuffer()); + if (anim.gpuSkinningShaderData.bindPoseMutable.vao) { + glBindVertexArray(anim.gpuSkinningShaderData.bindPoseMutable.vao->getBuffer()); renderer.shaderManager.EnableVertexAttribArrays(); } #endif #endif - // Bind position and texcoord VBOs - glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.positionVBO->getBuffer()); - renderer.VertexAttribPointer3fv("vPosition", 0, NULL); - - if (anim.bindPoseMutable.texCoordVBO) { - glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.texCoordVBO->getBuffer()); - renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL); - } - - // Bind bone index VBOs - glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices0VBO->getBuffer()); - renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL); - - glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices1VBO->getBuffer()); - renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL); - - // Bind bone weight VBOs - glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights0VBO->getBuffer()); - renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL); - - glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights1VBO->getBuffer()); - renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL); - - glDrawArrays(GL_TRIANGLES, 0, static_cast(anim.bindPoseMutable.data.PositionData.size())); - + anim.gpuSkinningShaderData.RenderVBO(renderer); + renderer.PopMatrix(); renderer.PopProjectionMatrix(); renderer.shaderManager.PopShader(); } +bool Character::prepareGpuSkinning() +{ + AnimationState drawState = resolveActiveState(); + auto it = animations.find(drawState); + if (it == animations.end()) + { + return false; + } + + auto& anim = it->second; + anim.gpuSkinningShaderData.prepareGpuSkinningVBOs(anim.model); + if (anim.gpuSkinningShaderData.skinningMatrices.empty()) { + if (anim.model.animations.empty() || anim.model.animations[0].keyFrames.empty()) + { + return false; + } + + anim.gpuSkinningShaderData.ComputeSkinningMatrices(anim.model.startBones, anim.model.animations[0].keyFrames, static_cast(anim.currentFrame)); + + if (anim.gpuSkinningShaderData.skinningMatrices.empty()) + { + return false; + } + } + + return true; +} + // ==================== Shadow depth pass ==================== void Character::drawShadowDepth(Renderer& renderer) { @@ -418,14 +421,7 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) { auto it = animations.find(drawState); if (it == animations.end()) return; - auto& anim = it->second; - anim.prepareGpuSkinningVBOs(); - if (anim.skinningMatrices.empty()) { - if (anim.model.animations.empty() || anim.model.animations[0].keyFrames.empty()) return; - anim.model.ComputeSkinningMatrices( - anim.model.animations[0].keyFrames[0].frame, anim.skinningMatrices); - if (anim.skinningMatrices.empty()) return; - } + if (!prepareGpuSkinning()) return; static const std::string shadowSkinningShader = "shadow_depth_skinning"; static const std::string boneMatricesUniform = "uBoneMatrices[0]"; @@ -440,36 +436,10 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) { renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix()); renderer.RenderUniformMatrix4fvArray(boneMatricesUniform, - static_cast(anim.skinningMatrices.size()), false, - anim.skinningMatrices[0].data()); - -#ifndef EMSCRIPTEN -#ifndef __ANDROID__ - if (anim.bindPoseMutable.vao) { - glBindVertexArray(anim.bindPoseMutable.vao->getBuffer()); - renderer.shaderManager.EnableVertexAttribArrays(); - } -#endif -#endif - - glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.positionVBO->getBuffer()); - renderer.VertexAttribPointer3fv("vPosition", 0, NULL); - - if (anim.bindPoseMutable.texCoordVBO) { - glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.texCoordVBO->getBuffer()); - renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL); - } - - glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices0VBO->getBuffer()); - renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL); - glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices1VBO->getBuffer()); - renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL); - glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights0VBO->getBuffer()); - renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL); - glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights1VBO->getBuffer()); - renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL); - - glDrawArrays(GL_TRIANGLES, 0, static_cast(anim.bindPoseMutable.data.PositionData.size())); + static_cast(it->second.gpuSkinningShaderData.skinningMatrices.size()), false, + it->second.gpuSkinningShaderData.skinningMatrices[0].data()); + + it->second.gpuSkinningShaderData.RenderVBO(renderer); renderer.PopMatrix(); renderer.shaderManager.PopShader(); @@ -528,15 +498,7 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri auto it = animations.find(drawState); if (it == animations.end() || !texture) return; - auto& anim = it->second; - anim.prepareGpuSkinningVBOs(); - - if (anim.skinningMatrices.empty()) { - if (anim.model.animations.empty() || anim.model.animations[0].keyFrames.empty()) return; - anim.model.ComputeSkinningMatrices( - anim.model.animations[0].keyFrames[0].frame, anim.skinningMatrices); - if (anim.skinningMatrices.empty()) return; - } + if (!prepareGpuSkinning()) return; static const std::string skinningShadowShader = "skinning_shadow"; static const std::string boneMatricesUniform = "uBoneMatrices[0]"; @@ -562,44 +524,12 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix()); renderer.RenderUniformMatrix4fvArray(boneMatricesUniform, - static_cast(anim.skinningMatrices.size()), false, - anim.skinningMatrices[0].data()); + static_cast(it->second.gpuSkinningShaderData.skinningMatrices.size()), false, + it->second.gpuSkinningShaderData.skinningMatrices[0].data()); glBindTexture(GL_TEXTURE_2D, texture->getTexID()); -#ifndef EMSCRIPTEN -#ifndef __ANDROID__ - if (anim.bindPoseMutable.vao) { - glBindVertexArray(anim.bindPoseMutable.vao->getBuffer()); - renderer.shaderManager.EnableVertexAttribArrays(); - } -#endif -#endif - - glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.positionVBO->getBuffer()); - renderer.VertexAttribPointer3fv("vPosition", 0, NULL); - - if (anim.bindPoseMutable.texCoordVBO) { - glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.texCoordVBO->getBuffer()); - renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL); - } - - // Bind normals for diffuse lighting (skinning_shadow shader skins them) - if (anim.bindPoseMutable.normalVBO) { - glBindBuffer(GL_ARRAY_BUFFER, anim.bindPoseMutable.normalVBO->getBuffer()); - renderer.VertexAttribPointer3fv("vNormal", 0, NULL); - } - - glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices0VBO->getBuffer()); - renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL); - glBindBuffer(GL_ARRAY_BUFFER, anim.boneIndices1VBO->getBuffer()); - renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL); - glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights0VBO->getBuffer()); - renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL); - glBindBuffer(GL_ARRAY_BUFFER, anim.boneWeights1VBO->getBuffer()); - renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL); - - glDrawArrays(GL_TRIANGLES, 0, static_cast(anim.bindPoseMutable.data.PositionData.size())); + it->second.gpuSkinningShaderData.RenderVBO(renderer); renderer.PopMatrix(); renderer.PopProjectionMatrix(); diff --git a/src/Character.h b/src/Character.h index cd290f0..7a8669b 100644 --- a/src/Character.h +++ b/src/Character.h @@ -70,8 +70,8 @@ public: bool canAttack = false; Character* attackTarget = nullptr; bool isPlayer = false; - bool useGpuSkinning = true; - //bool useGpuSkinning = false; + //bool useGpuSkinning = true; + bool useGpuSkinning = false; float interactionRadius = 0.0f; @@ -97,6 +97,8 @@ private: // if the requested state has no loaded animation. AnimationState resolveActiveState() const; + bool prepareGpuSkinning(); + // GPU skinning: draw using shader-based skinning void drawGpuSkinning(Renderer& renderer); // Shadow: draw into depth map (no texture, no projection push)