Bubba-3D  0.9.0
Awesome game engine!
BoneTransformer.cpp
1 /*
2  * This file is part of Bubba-3D.
3  *
4  * Bubba-3D is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Bubba-3D is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with Bubba-3D. If not, see http://www.gnu.org/licenses/.
16  */
17 #include "linmath/float3x3.h"
18 #include <ResourceManager.h>
19 #include <Utils.h>
20 #include "BoneTransformer.h"
21 
22 
23 BoneTransformer::BoneTransformer(aiScene *aiScene){
24  numberOfBones = 0;
25  globalInverseTransform = convertAiMatrixToFloat4x4(aiScene->mRootNode->mTransformation.Inverse());
26  this->assimpScene = aiScene;
27 }
28 
29 void BoneTransformer::readNodeHierarchyAndUpdateBoneTransformations(float currentAnimationTick,
30  aiNode *currentAssimpNode,
31  chag::float4x4 parentMatrix) {
32  std::string nodeName(currentAssimpNode->mName.data);
33 
34  chag::float4x4 nodeTransformationMatrix = getCurrentNodeTransformation(currentAnimationTick, currentAssimpNode, nodeName);
35  chag::float4x4 globalTransformation = parentMatrix * nodeTransformationMatrix;
36 
37  // If the node isn't a bone we dont need to update any bones (duuuh)
38  if(nodeIsABone(nodeName)) {
39  updateBoneTransformation(nodeName, globalTransformation);
40  }
41 
42  for (unsigned int i = 0; i < currentAssimpNode->mNumChildren; i++) {
43  readNodeHierarchyAndUpdateBoneTransformations(currentAnimationTick, currentAssimpNode->mChildren[i],
44  globalTransformation);
45  }
46 }
47 
48 void BoneTransformer::updateBoneTransformation(const std::string &nodeName, const chag::float4x4 &globalTransformation) {
49  uint boneIndex = boneNameToIndexMapping[nodeName];
50 
51  boneInfos[boneIndex]->finalTransformation = globalInverseTransform * globalTransformation * boneInfos[boneIndex]->boneOffset;
52 }
53 
54 bool BoneTransformer::nodeIsABone(const std::string &nodeName) const {
55  return boneNameToIndexMapping.find(nodeName) != boneNameToIndexMapping.end();
56 }
57 
58 chag::float4x4 BoneTransformer::getCurrentNodeTransformation(float currentAnimationTick,
59  const aiNode *currentAssimpNode,
60  const std::string nodeName) {
61  const aiNodeAnim *nodeAnimation = getAnimationNode(nodeName);
62  if(nodeAnimation == nullptr) {
68  return convertAiMatrixToFloat4x4(currentAssimpNode->mTransformation);
69  } else {
70  return getInterpolatedAnimationMatrix(currentAnimationTick, nodeAnimation);
71 
72  }
73 }
74 
75 chag::float4x4 BoneTransformer::getInterpolatedAnimationMatrix(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
76  chag::float4x4 scalingMatrix = getInterpolatedScalingMatrix(currentAnimationTick, nodeAnimation);
77  chag::float4x4 rotationMatrix = getInterpolatedRotationMatrix(currentAnimationTick, nodeAnimation);
78  chag::float4x4 translationMatrix = getInterpolatedTranslationMatrix(currentAnimationTick, nodeAnimation);
79 
80  chag::float4x4 interpolatedMatrix = translationMatrix * rotationMatrix * scalingMatrix;
81  return interpolatedMatrix;
82 }
83 
84 chag::float4x4 BoneTransformer::getInterpolatedTranslationMatrix(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
85  aiVector3D translation = calculateTranslationInterpolation(currentAnimationTick, nodeAnimation);
86  chag::float4x4 translationMatrix =
87  chag::make_translation(chag::make_vector(
88  translation.x,
89  translation.y,
90  translation.z));
91  return translationMatrix;
92 }
93 
94 chag::float4x4 BoneTransformer::getInterpolatedRotationMatrix(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
95  aiQuaternion rotationQuaternion = calculateRotationInterpolation(currentAnimationTick, nodeAnimation);
96  chag::float4x4 rotationMatrix = chag::make_matrix(
97  convertAiMatrixToFloat3x3(rotationQuaternion.GetMatrix()),
98  chag::make_vector(0.0f, 0.0f, 0.0f)
99  );
100  return rotationMatrix;
101 }
102 
103 chag::float4x4 BoneTransformer::getInterpolatedScalingMatrix(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
104  aiVector3D scaling = calculateScalingInterpolation(currentAnimationTick, nodeAnimation);
105  chag::float4x4 scalingMatrix = chag::make_scale<chag::float4x4>(chag::make_vector(scaling.x, scaling.y, scaling.z));
106  return scalingMatrix;
107 }
108 
109 const aiNodeAnim *BoneTransformer::getAnimationNode(const std::string &nodeName) {
110  const aiAnimation* animation = assimpScene->mAnimations[0];
111 
112  const aiNodeAnim* nodeAnimation = findNodeAnim(animation, nodeName);
113  return nodeAnimation;
114 }
115 
116 const aiNodeAnim* BoneTransformer::findNodeAnim(const aiAnimation* animation, const std::string nodeName) {
117  for (unsigned int i = 0; i < animation->mNumChannels; i++) {
118  const aiNodeAnim* nodeAnimation = animation->mChannels[i];
119  if(std::string(nodeAnimation->mNodeName.data) == nodeName) {
120  return nodeAnimation;
121  }
122  }
123  return nullptr;
124 }
125 
126 aiVector3D BoneTransformer::calculateScalingInterpolation(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
127  if(nodeAnimation->mNumScalingKeys <= 1) {
128  return nodeAnimation->mScalingKeys[0].mValue;
129  }
130 
131  unsigned int currentScalingIndex = findScalingIndexRightBeforeTick(currentAnimationTick, nodeAnimation);
132  unsigned int nextScalingIndex = currentScalingIndex + 1;
133  assert(nextScalingIndex < nodeAnimation->mNumScalingKeys);
134 
135  double deltaTimeBetweenAnimations = nodeAnimation->mScalingKeys[nextScalingIndex].mTime -
136  nodeAnimation->mScalingKeys[currentScalingIndex].mTime;
137  double deltaFactor = (currentAnimationTick - nodeAnimation->mScalingKeys[currentScalingIndex].mTime) /
138  deltaTimeBetweenAnimations;
139  assert(deltaFactor >= 0.0f && deltaFactor <= 1.0f);
140  const aiVector3D &currentScalingVector = nodeAnimation->mScalingKeys[currentScalingIndex].mValue;
141  const aiVector3D &nextScalingVector = nodeAnimation->mScalingKeys[nextScalingIndex].mValue;
142 
143  return (nextScalingVector - currentScalingVector) * (float)deltaFactor;
144 }
145 
146 aiQuaternion BoneTransformer::calculateRotationInterpolation(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
147  if(nodeAnimation->mNumRotationKeys <= 1) {
148  return nodeAnimation->mRotationKeys[0].mValue;
149  }
150 
151  unsigned int currentRotationIndex = findRotationIndexRightBeforeTick(currentAnimationTick, nodeAnimation);
152  unsigned int nextRotationIndex = currentRotationIndex + 1;
153  assert(nextRotationIndex < nodeAnimation->mNumRotationKeys);
154 
155  double deltaTimeBetweenAnimations = nodeAnimation->mRotationKeys[nextRotationIndex].mTime -
156  nodeAnimation->mRotationKeys[currentRotationIndex].mTime;
157  double deltaFactor = (currentAnimationTick - nodeAnimation->mRotationKeys[currentRotationIndex].mTime) /
158  deltaTimeBetweenAnimations;
159  assert(deltaFactor >= 0.0f && deltaFactor <= 1.0f);
160  const aiQuaternion &currentRotationQuaternion = nodeAnimation->mRotationKeys[currentRotationIndex].mValue;
161  const aiQuaternion &nextRotationQuaternion = nodeAnimation->mRotationKeys[nextRotationIndex].mValue;
162  aiQuaternion interpolatedQuaternion;
163  aiQuaterniont<float>::Interpolate(interpolatedQuaternion, currentRotationQuaternion, nextRotationQuaternion, deltaFactor);
164 
165  return interpolatedQuaternion;
166 }
167 
168 aiVector3D BoneTransformer::calculateTranslationInterpolation(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
169  if(nodeAnimation->mNumPositionKeys <= 1) {
170  return nodeAnimation->mPositionKeys[0].mValue;
171  }
172 
173  unsigned int currentTranslationIndex = findTranslationIndexRightBeforeTick(currentAnimationTick, nodeAnimation);
174  unsigned int nextTranslationIndex = currentTranslationIndex + 1;
175  assert(nextTranslationIndex < nodeAnimation->mNumPositionKeys);
176 
177  double deltaTimeBetweenAnimations = nodeAnimation->mPositionKeys[nextTranslationIndex].mTime -
178  nodeAnimation->mPositionKeys[currentTranslationIndex].mTime;
179  double deltaFactor = (currentAnimationTick - nodeAnimation->mPositionKeys[currentTranslationIndex].mTime) /
180  deltaTimeBetweenAnimations;
181  assert(deltaFactor >= 0.0f && deltaFactor <= 1.0f);
182  const aiVector3D &currentTranslationVector = nodeAnimation->mPositionKeys[currentTranslationIndex].mValue;
183  const aiVector3D &nextTranslationVector = nodeAnimation->mPositionKeys[nextTranslationIndex].mValue;
184 
185  return currentTranslationVector + ((nextTranslationVector - currentTranslationVector) * (float)deltaFactor);
186 
187 }
188 
189 unsigned int BoneTransformer::findScalingIndexRightBeforeTick(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
190  for (unsigned int i = 0; i < nodeAnimation->mNumScalingKeys - 1; i++) {
191  if(currentAnimationTick < nodeAnimation->mScalingKeys[i + 1].mTime) {
192  return i;
193  }
194  }
195 
196  throw std::invalid_argument("Tried to find a scaling index in an animation, but couldn't find it");
197 }
198 
199 unsigned int BoneTransformer::findRotationIndexRightBeforeTick(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
200  for (unsigned int i = 0; i < nodeAnimation->mNumRotationKeys - 1; i++) {
201  if(currentAnimationTick < nodeAnimation->mRotationKeys[i + 1].mTime) {
202  return i;
203  }
204  }
205 
206  throw std::invalid_argument("Tried to find a rotation index in an animation, but couldn't find it");
207 }
208 
209 unsigned int BoneTransformer::findTranslationIndexRightBeforeTick(float currentAnimationTick, const aiNodeAnim *nodeAnimation) {
210  for (unsigned int i = 0; i < nodeAnimation->mNumPositionKeys - 1; i++) {
211  if(currentAnimationTick < nodeAnimation->mPositionKeys[i + 1].mTime) {
212  return i;
213  }
214  }
215 
216  throw std::invalid_argument("Tried to find a translation index in an animation, but couldn't find it");
217 }
218 
219 std::vector<chag::float4x4> BoneTransformer::calculateBoneTransforms(float totalElapsedTimeInSeconds) {
220 
221  chag::float4x4 rootMatrix = chag::make_identity<chag::float4x4>();
222 
223  double currentAnimationTick = getCurrentAnimationTick(totalElapsedTimeInSeconds);
224 
225  readNodeHierarchyAndUpdateBoneTransformations((float)currentAnimationTick, assimpScene->mRootNode, rootMatrix);
226 
227  std::vector<chag::float4x4> boneTransformMatrices;
228  boneTransformMatrices.resize(numberOfBones);
229  for (int bone = 0; bone < numberOfBones; bone++) {
230  boneTransformMatrices[bone] = boneInfos[bone]->finalTransformation;
231  }
232  return boneTransformMatrices;
233 }
234 
236  int boneIndex;
237  std::string boneName(bone->mName.data);
238 
239  if(boneNameToIndexMapping.find(boneName) == boneNameToIndexMapping.end()) {
240 
241  boneNameToIndexMapping.insert(std::pair<std::string, int>(boneName, numberOfBones));
242 
243  BoneMatrices* boneInfo = new BoneMatrices();
244  boneInfos.push_back(boneInfo);
245  boneInfo->boneOffset = convertAiMatrixToFloat4x4(bone->mOffsetMatrix);
246 
247  boneIndex = numberOfBones;
248  numberOfBones++;
249  } else {
250  boneIndex = boneNameToIndexMapping[boneName];
251  }
252  return boneIndex;
253 }
254 
255 double BoneTransformer::getCurrentAnimationTick(float totalElapsedTimeInSeconds) const {
256  double ticksPerSecond = assimpScene->mAnimations[0]->mTicksPerSecond;
257  ticksPerSecond = ticksPerSecond == 0 ? 25 : ticksPerSecond;
258 
259  double elapsedTimeInTicks = totalElapsedTimeInSeconds * ticksPerSecond;
260  double animationDurationInTicks = assimpScene->mAnimations[0]->mDuration;
261  double currentAnimationTick = fmod(elapsedTimeInTicks, animationDurationInTicks);
262  return currentAnimationTick;
263 }
264 
std::vector< chag::float4x4 > calculateBoneTransforms(float totalElapsedTimeInSeconds)
int createBoneIndexIfAbsent(const aiBone *bone)