diff --git a/Editor/resources/Shaders/gbuffer_skinned.shader b/Editor/resources/Shaders/gbuffer_skinned.shader index 5adc13b9..c13922ce 100644 --- a/Editor/resources/Shaders/gbuffer_skinned.shader +++ b/Editor/resources/Shaders/gbuffer_skinned.shader @@ -23,9 +23,15 @@ out vec2 UV; void main() { - vec3 T = normalize((u_Model * vec4(Tangent, 0.0f)).xyz); - vec3 N = normalize((u_Model * vec4(Normal, 0.0f)).xyz); - vec3 B = normalize((u_Model * vec4(Bitangent, 0.0f)).xyz); + mat4 boneTransform = u_FinalBonesMatrice[BoneIDs.x] * Weights.x; + boneTransform += u_FinalBonesMatrice[BoneIDs.y] * Weights.y; + boneTransform += u_FinalBonesMatrice[BoneIDs.z] * Weights.z; + boneTransform += u_FinalBonesMatrice[BoneIDs.w] * Weights.w; + + mat3 normalMatrix = transpose(inverse(mat3(boneTransform))); + vec3 T = normalize(normalMatrix * Tangent); + vec3 N = normalize(normalMatrix * Normal); + vec3 B = normalize(normalMatrix * Bitangent); TBN = mat3(T, B, N); UV = UVPosition; @@ -37,19 +43,19 @@ void main() { continue; } - + if (BoneIDs[i] >= MAX_BONES) { totalPosition = vec4(VertexPosition, 1.0f); break; } - vec4 localPosition = vec4(VertexPosition, 1.0f); + vec4 localPosition = u_FinalBonesMatrice[BoneIDs[i]] * vec4(VertexPosition, 1.0f); totalPosition += localPosition * Weights[i]; // vec3 localNormal = mat3(u_FinalBonesMatrice[BoneIDs[i]]) * Normal; } - gl_Position = u_Projection * u_View * u_Model * vec4(VertexPosition, 1.0f); + gl_Position = u_Projection * u_View * u_Model * totalPosition; } #shader fragment diff --git a/Nuake/src/Rendering/Mesh/Bone.cpp b/Nuake/src/Rendering/Mesh/Bone.cpp new file mode 100644 index 00000000..e0a95295 --- /dev/null +++ b/Nuake/src/Rendering/Mesh/Bone.cpp @@ -0,0 +1,152 @@ +#include "Bone.h" +#include "src/Core/Logger.h" + +namespace Nuake +{ + Bone::Bone(const std::string& name, int id) + { + Name = name; + Id = id; + m_LocalTransform = Matrix4(1.0f); + } + + void Bone::PushPositionKeyframe(KeyPosition& key) + { + m_Positions.push_back(std::move(key)); + } + + void Bone::PushRotationframe(KeyRotation& key) + { + m_Rotations.push_back(std::move(key)); + } + + void Bone::PushScaleKeyframe(KeyScale& key) + { + m_Scales.push_back(std::move(key)); + } + + void Bone::Update(float time) + { + Matrix4 translation = InterpolatePosition(time); + Matrix4 rotation = InterpolateRotation(time); + Matrix4 scale = InterpolateScaling(time); + m_LocalTransform = translation * rotation * scale; + } + + Matrix4 Bone::GetLocalTransform() const + { + return m_LocalTransform; + } + + int Bone::GetPositionIndex(float time) const + { + for (uint32_t i = 0; i < m_NumPositions - 1; i++) + { + if (time < m_Positions[i + 1].Timestamp) + { + return i; + } + } + + Logger::Log("Bone position keyframe not found", "Bone system", CRITICAL); + assert(false); + + return 0; + } + + int Bone::GetRotationIndex(float time) const + { + for (uint32_t i = 0; i < m_NumRotations - 1; i++) + { + if (time < m_Rotations[i + 1].Timestamp) + { + return i; + } + } + + Logger::Log("Bone rotation keyframe not found", "Bone system", CRITICAL); + assert(false); + + return 0; + } + + int Bone::GetScaleIndex(float time) const + { + for (uint32_t i = 0; i < m_NumScales - 1; i++) + { + if (time < m_Scales[i + 1].Timestamp) + { + return i; + } + } + + Logger::Log("Bone scale keyframe not found", "Bone system", CRITICAL); + assert(false); + + return 0; + } + + float Bone::GetScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime) const + { + float scaleFactor = 0.0f; + float midWayLength = animationTime - lastTimeStamp; + float framesDiff = nextTimeStamp - lastTimeStamp; + scaleFactor = midWayLength / framesDiff; + return scaleFactor; + } + + Matrix4 Bone::InterpolatePosition(float animationTime) const + { + if (m_NumPositions == 1) + { + return glm::translate(Matrix4(1.0f), m_Positions[0].Position); + } + + const int keyFramePresentIndex = GetPositionIndex(animationTime); + const int keyFrameFutureIndex = keyFramePresentIndex + 1; + + const KeyPosition& keyFramePresent = m_Positions[keyFramePresentIndex]; + const KeyPosition& keyFrameFuture = m_Positions[keyFrameFutureIndex]; + + const float scaleFactor = GetScaleFactor(keyFramePresent.Timestamp, keyFrameFuture.Timestamp, animationTime); + const Vector3 finalPosition = glm::mix(keyFramePresent.Position, keyFrameFuture.Position, scaleFactor); + return glm::translate(Matrix4(1.0f), finalPosition); + } + + Matrix4 Bone::InterpolateRotation(float animationTime) const + { + if (m_NumRotations == 1) + { + auto rotation = glm::normalize(m_Rotations[0].Orientation); + return glm::toMat4(rotation); + } + + const int keyFramePresentIndex = GetRotationIndex(animationTime); + const int keyFrameFutureIndex = keyFramePresentIndex + 1; + + const KeyRotation& keyFramePresent = m_Rotations[keyFramePresentIndex]; + const KeyRotation& keyFrameFuture = m_Rotations[keyFrameFutureIndex]; + + const float scaleFactor = GetScaleFactor(keyFramePresent.Timestamp, keyFrameFuture.Timestamp, animationTime); + const Quat& finalRotation = glm::slerp(keyFramePresent.Orientation, keyFramePresent.Orientation, scaleFactor); + return glm::toMat4(finalRotation); + } + + Matrix4 Bone::InterpolateScaling(float animationTime) const + { + if (m_NumScales == 1) + { + return glm::scale(Matrix4(1.0f), m_Scales[0].Scale); + } + + const int keyFramePresentIndex = GetScaleIndex(animationTime); + const int keyFrameFutureIndex = keyFramePresentIndex + 1; + + const KeyScale& keyFramePresent = m_Scales[keyFramePresentIndex]; + const KeyScale& keyFrameFuture = m_Scales[keyFrameFutureIndex]; + + const float scaleFactor = GetScaleFactor(keyFramePresent.Timestamp, keyFrameFuture.Timestamp, animationTime); + const Vector3 finalScale = glm::mix(keyFramePresent.Scale, keyFramePresent.Scale, scaleFactor); + return glm::scale(Matrix4(1.0f), finalScale); + } +} \ No newline at end of file diff --git a/Nuake/src/Rendering/Mesh/Bone.h b/Nuake/src/Rendering/Mesh/Bone.h new file mode 100644 index 00000000..6f55092f --- /dev/null +++ b/Nuake/src/Rendering/Mesh/Bone.h @@ -0,0 +1,72 @@ +#pragma once +#include "src/Core/Core.h" +#include "src/Core/Maths.h" + + +namespace Nuake +{ + struct KeyPosition + { + Vector3 Position; + float Timestamp; + }; + + struct KeyRotation + { + Quat Orientation; + float Timestamp; + }; + + struct KeyScale + { + Vector3 Scale; + float Timestamp; + }; + + struct BoneVertexWeight + { + uint32_t VertexID; + float Weight; + }; + + class Bone + { + public: + std::string Name; + uint32_t Id; + Matrix4 Offset; + std::vector VertexWeights; + + private: + std::vector m_Positions; + std::vector m_Rotations; + std::vector m_Scales; + uint32_t m_NumPositions; + uint32_t m_NumRotations; + uint32_t m_NumScales; + + Matrix4 m_LocalTransform; + + public: + Bone() = default; + Bone(const std::string& name, int id); + + void PushPositionKeyframe(KeyPosition& key); + void PushRotationframe(KeyRotation& key); + void PushScaleKeyframe(KeyScale& key); + + void Update(float time); + + Matrix4 GetLocalTransform() const; + int GetPositionIndex(float time) const; + int GetRotationIndex(float time) const; + int GetScaleIndex(float time) const; + + private: + float GetScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime) const; + + Matrix4 InterpolatePosition(float animationTime) const ; + Matrix4 InterpolateRotation(float animationTime) const; + Matrix4 InterpolateScaling(float animationTime) const; + }; +} diff --git a/Nuake/src/Rendering/Mesh/SkinnedMesh.h b/Nuake/src/Rendering/Mesh/SkinnedMesh.h index 55de6bce..7c0a2017 100644 --- a/Nuake/src/Rendering/Mesh/SkinnedMesh.h +++ b/Nuake/src/Rendering/Mesh/SkinnedMesh.h @@ -3,7 +3,10 @@ #include "src/Rendering/AABB.h" #include "src/Resource/Resource.h" #include "src/Resource/Serializable.h" + #include "src/Rendering/Vertex.h" +#include "src/Rendering/Mesh/Bone.h" + namespace Nuake { @@ -13,19 +16,6 @@ namespace Nuake struct Vertex; class Shader; - struct BoneVertexWeight - { - uint32_t VertexID; - float Weight; - }; - - struct Bone - { - std::string Name; - Matrix4 Offset; - std::vector VertexWeights; - }; - class SkinnedMesh : ISerializable, Resource { public: diff --git a/Nuake/src/Resource/ModelLoader.cpp b/Nuake/src/Resource/ModelLoader.cpp index 2a64dd3b..d7b26160 100644 --- a/Nuake/src/Resource/ModelLoader.cpp +++ b/Nuake/src/Resource/ModelLoader.cpp @@ -120,36 +120,52 @@ namespace Nuake auto& indices = ProcessIndices(node); auto& material = ProcessMaterials(scene, node); auto& bones = std::vector(); + auto& bonesMap = std::unordered_map(); + if (node->HasBones()) { + uint32_t boneCounter = 0; for (uint32_t i = 0; i < node->mNumBones; i++) { aiBone* bone = node->mBones[i]; const std::string& boneName = bone->mName.C_Str(); - const auto& boneMatrix = bone->mOffsetMatrix; - Bone newBone; - newBone.Name = boneName; - newBone.Offset = ConvertMatrixToGLMFormat(bone->mOffsetMatrix); + int32_t boneId = -1; + if (bonesMap.find(boneName) == bonesMap.end()) + { + boneId = boneCounter; + boneCounter++; + + Bone newBone = Bone(boneName, boneId); + newBone.Offset = ConvertMatrixToGLMFormat(bone->mOffsetMatrix); + bones.push_back(newBone); + + bonesMap[boneName] = newBone; + } + else + { + boneId = bonesMap[boneName].Id; + } + + assert(boneId != -1); const uint32_t numWeight = bone->mNumWeights; - newBone.VertexWeights.reserve(numWeight); for (uint32_t j = 0; j < numWeight; j++) { aiVertexWeight vertexWeight = bone->mWeights[j]; const uint32_t vertexWeightVertexId = vertexWeight.mVertexId; - vertices[vertexWeightVertexId].boneIDs[j] = i; - vertices[vertexWeightVertexId].weights[j] = vertexWeight.mWeight; - BoneVertexWeight boneVertexWeight - { - vertexWeightVertexId, - vertexWeight.mWeight - }; - newBone.VertexWeights.push_back(std::move(boneVertexWeight)); + SetVertexBoneData(vertices[vertexWeightVertexId], boneId, vertexWeight.mWeight); + + //BoneVertexWeight boneVertexWeight + //{ + // vertexWeightVertexId, + // vertexWeight.mWeight + //}; + // + //Bone& newBone = bones[boneId]; + //newBone.VertexWeights.push_back(std::move(boneVertexWeight)); } - - bones.push_back(std::move(newBone)); } } else @@ -189,30 +205,30 @@ namespace Nuake // Position current.x = mesh->mVertices[i].x; - current.y = mesh->mVertices[i].y; - current.z = mesh->mVertices[i].z; + current.y = mesh->mVertices[i].z; + current.z = mesh->mVertices[i].y; vertex.position = current; // Normals current.x = mesh->mNormals[i].x; - current.y = mesh->mNormals[i].y; - current.z = mesh->mNormals[i].z; + current.y = mesh->mNormals[i].z; + current.z = mesh->mNormals[i].y; vertex.normal = current; // Tangents if (mesh->mTangents) { current.x = mesh->mTangents[i].x; - current.y = mesh->mTangents[i].y; - current.z = mesh->mTangents[i].z; + current.y = mesh->mTangents[i].z; + current.z = mesh->mTangents[i].y; vertex.tangent = current; } if (mesh->mBitangents) { current.x = mesh->mBitangents[i].x; - current.y = mesh->mBitangents[i].y; - current.z = mesh->mBitangents[i].z; + current.y = mesh->mBitangents[i].z; + current.z = mesh->mBitangents[i].y; vertex.bitangent = current; } @@ -291,6 +307,19 @@ namespace Nuake return vertices; } + void ModelLoader::SetVertexBoneData(SkinnedVertex& vertex, int boneID, float weight) + { + for (int i = 0; i < MAX_BONE_INFLUENCE; ++i) + { + if (vertex.boneIDs[i] < 0) + { + vertex.weights[i] = weight; + vertex.boneIDs[i] = boneID; + break; + } + } + } + std::vector ModelLoader::ProcessIndices(aiMesh* mesh) { auto indices = std::vector(); diff --git a/Nuake/src/Resource/ModelLoader.h b/Nuake/src/Resource/ModelLoader.h index a80adf61..32340eb6 100644 --- a/Nuake/src/Resource/ModelLoader.h +++ b/Nuake/src/Resource/ModelLoader.h @@ -45,16 +45,31 @@ namespace Nuake void ProcessSkinnedNode(aiNode* node, const aiScene* scene); Ref ProcessSkinnedMesh(aiMesh* node, const aiScene* scene); std::vector ProcessSkinnedVertices(aiMesh* mesh); + void SetVertexBoneData(SkinnedVertex& vertex, int boneId, float weight); static inline Matrix4 ConvertMatrixToGLMFormat(const aiMatrix4x4& from) { - Matrix4 to; - //the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column - to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4; - to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4; - to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4; - to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4; - return to; + Matrix4 result; + for (auto i = 0; i < 3; i++) + { + for (auto j = 0; j < 3; j++) + { + result[i][j] = from[i][j]; + } + } + + // The rest would be zero, other than the 4,4. + result[0][3] = 0.0f; + result[1][3] = 0.0f; + result[2][3] = 0.0f; + + result[3][0] = 0.0f; + result[3][1] = 0.0f; + result[3][2] = 0.0f; + + result[3][3] = 1.0f; + + return result; } }; } \ No newline at end of file diff --git a/Nuake/src/Resource/SkeletalAnimation.h b/Nuake/src/Resource/SkeletalAnimation.h new file mode 100644 index 00000000..af3109f8 --- /dev/null +++ b/Nuake/src/Resource/SkeletalAnimation.h @@ -0,0 +1,21 @@ +#pragma once +#include + +namespace Nuake +{ + class Animation + { + private: + float m_Duration; + int m_TicksPerSecond; + std::vector m_Bones; + //std::map m_BoneInfoMap; + public: + Animation() = default; + Animation(); + + Bone& FindBone(const std::string& boneName); + + float GetTicksPerSecond() const { return m_TicksPerSecond; } + }; +}