xoreos  0.0.5
animation.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include "glm/gtc/type_ptr.hpp"
26 #include "glm/gtc/matrix_transform.hpp"
27 
28 #include "src/common/readstream.h"
29 #include "src/common/debug.h"
30 
31 #include "src/graphics/graphics.h"
32 #include "src/graphics/camera.h"
33 
38 
40 
41 namespace Graphics {
42 
43 namespace Aurora {
44 
45 Animation::Animation() : _length(0.0f), _transtime(0.0f) {
46 
47 }
48 
50  for (NodeList::iterator n = nodeList.begin(); n != nodeList.end(); ++n)
51  delete *n;
52 }
53 
55  return _name;
56 }
57 
59  _name = name;
60 }
61 
62 void Animation::setLength(float length) {
63  _length = length;
64 }
65 
66 float Animation::getLength() const {
67  return _length;
68 }
69 
70 void Animation::setTransTime(float transtime) {
71  _transtime = transtime;
72 }
73 
75  float UNUSED(lastFrame),
76  float nextFrame,
77  const std::vector<ModelNode *> &modelNodeMap) {
78  // TODO: Also need to fire off associated events
79  // for event in _events event->fire()
80 
81 
82  float scale = model->getAnimationScale(_name);
83  for (NodeList::iterator n = nodeList.begin(); n != nodeList.end(); ++n) {
84  ModelNode *animNode = (*n)->_nodedata;
85  ModelNode *target = modelNodeMap[animNode->_nodeNumber];
86  if (!target)
87  continue;
88 
89  // Update position and orientation based on time
90  if (!animNode->_positionFrames.empty())
91  interpolatePosition(animNode, target, nextFrame, scale, model->_positionRelative);
92  if (!animNode->_orientationFrames.empty())
93  interpolateOrientation(animNode, target, nextFrame);
94  }
95 
96  if (model->_skinned)
97  updateSkinnedModel(model);
98 }
99 
101  nodeList.push_back(node);
102  nodeMap.insert(std::make_pair(node->getName(), node));
103 }
104 
105 bool Animation::hasNode(const Common::UString &node) const {
106  return (nodeMap.find(node) != nodeMap.end());
107 }
108 
110  NodeMap::iterator n = nodeMap.find(node);
111  if (n == nodeMap.end())
112  return 0;
113 
114  return n->second->_nodedata;
115 }
116 
117 const ModelNode *Animation::getNode(const Common::UString &node) const {
118  NodeMap::const_iterator n = nodeMap.find(node);
119  if (n == nodeMap.end())
120  return 0;
121 
122  return n->second->_nodedata;
123 }
124 
125 const std::list<AnimNode *> &Animation::getNodes() const {
126  return nodeList;
127 }
128 
130 static float dotQuaternion(float x1, float y1, float z1, float q1,
131  float x2, float y2, float z2, float q2) {
132 
133  return x1 * x2 + y1 * y2 + z1 * z2 + q1 * q2;
134 }
135 
137 static void normQuaternion(float xIn , float yIn , float zIn , float qIn,
138  float &xOut, float &yOut, float &zOut, float &qOut) {
139 
140  const float magnitude = sqrt(dotQuaternion(xIn, yIn, zIn, qIn, xIn, yIn, zIn, qIn));
141 
142  xOut = xIn / magnitude;
143  yOut = yIn / magnitude;
144  zOut = zIn / magnitude;
145  qOut = qIn / magnitude;
146 }
147 
148 void Animation::interpolatePosition(ModelNode *animNode, ModelNode *target, float time, float scale,
149  bool relative) const {
150  float dx = 0;
151  float dy = 0;
152  float dz = 0;
153  if (relative) {
154  const PositionKeyFrame &pos = target->_positionFrames[0];
155  dx = pos.x;
156  dy = pos.y;
157  dz = pos.z;
158  }
159 
160  // If only one keyframe, don't interpolate, just set the only position
161  if (animNode->_positionFrames.size() == 1) {
162  const PositionKeyFrame &pos = animNode->_positionFrames[0];
163  target->setBufferedPosition((dx + pos.x) * scale,
164  (dy + pos.y) * scale,
165  (dz + pos.z) * scale);
166  return;
167  }
168 
169  size_t lastFrame = 0;
170  for (size_t i = 0; i < animNode->_positionFrames.size(); i++) {
171  const PositionKeyFrame &pos = animNode->_positionFrames[i];
172  if (pos.time >= time)
173  break;
174 
175  lastFrame = i;
176  }
177 
178  const PositionKeyFrame &last = animNode->_positionFrames[lastFrame];
179  if (lastFrame + 1 >= animNode->_positionFrames.size() || last.time >= time) {
180  target->setBufferedPosition((dx + last.x) * scale,
181  (dy + last.y) * scale,
182  (dz + last.z) * scale);
183  return;
184  }
185 
186  const PositionKeyFrame &next = animNode->_positionFrames[lastFrame + 1];
187 
188  const float f = (time - last.time) / (next.time - last.time);
189  const float x = f * next.x + (1.0f - f) * last.x;
190  const float y = f * next.y + (1.0f - f) * last.y;
191  const float z = f * next.z + (1.0f - f) * last.z;
192 
193  target->setBufferedPosition((dx + x) * scale,
194  (dy + y) * scale,
195  (dz + z) * scale);
196 }
197 
198 void Animation::interpolateOrientation(ModelNode *animNode, ModelNode *target, float time) const {
199  // If only one keyframe, don't interpolate just set the only orientation
200  if (animNode->_orientationFrames.size() == 1) {
201  const QuaternionKeyFrame &ori = animNode->_orientationFrames[0];
202  target->setBufferedOrientation(ori.x, ori.y, ori.z, Common::rad2deg(acos(ori.q) * 2.0));
203  return;
204  }
205 
206  size_t lastFrame = 0;
207  for (size_t i = 0; i < animNode->_orientationFrames.size(); i++) {
208  const QuaternionKeyFrame &ori = animNode->_orientationFrames[i];
209  if (ori.time >= time)
210  break;
211 
212  lastFrame = i;
213  }
214 
215  const QuaternionKeyFrame &last = animNode->_orientationFrames[lastFrame];
216  if (lastFrame + 1 >= animNode->_orientationFrames.size() || last.time >= time) {
217  target->setBufferedOrientation(last.x, last.y, last.z, Common::rad2deg(acos(last.q) * 2.0));
218  return;
219  }
220 
221  const QuaternionKeyFrame &next = animNode->_orientationFrames[lastFrame + 1];
222 
223  const float f = (time - last.time) / (next.time - last.time);
224 
225  /* If the angle is > 90°, we need to flip the direction of one quaternion to
226  get a smooth transition instead of wild jumps. */
227  const float angle = acos(dotQuaternion(last.x, last.y, last.z, last.q, next.x, next.y, next.z, next.q));
228  const float dir = (angle >= (M_PI / 2)) ? -1.0f : 1.0f;
229 
230  float x = f * dir * next.x + (1.0f - f) * last.x;
231  float y = f * dir * next.y + (1.0f - f) * last.y;
232  float z = f * dir * next.z + (1.0f - f) * last.z;
233  float q = f * dir * next.q + (1.0f - f) * last.q;
234 
235  // Normalize the result for slightly better results
236  normQuaternion(x, y, z, q, x, y, z, q);
237 
238  target->setBufferedOrientation(x, y, z, Common::rad2deg(acos(q) * 2.0));
239 }
240 
241 static void multiply(const float *v, const glm::mat4 &m, float *rv) {
242  rv[0] = v[0] * m[0][0] + v[1] * m[1][0] + v[2] * m[2][0] + m[3][0];
243  rv[1] = v[0] * m[0][1] + v[1] * m[1][1] + v[2] * m[2][1] + m[3][1];
244  rv[2] = v[0] * m[0][2] + v[1] * m[1][2] + v[2] * m[2][2] + m[3][2];
245  float w = v[0] * m[0][3] + v[1] * m[1][3] + v[2] * m[2][3] + m[3][3];
246  rv[0] /= w;
247  rv[1] /= w;
248  rv[2] /= w;
249 }
250 
252  const std::list<ModelNode *> &nodes = model->getNodes();
253  for (std::list<ModelNode *>::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
254  ModelNode *node = *it;
255  if (!node->_mesh || !node->_mesh->skin)
256  continue;
257 
258  // TODO:
259  // Handmaiden model in KotOR 2 has a node that is different
260  // from all the others in that it's parent is a bone. Because
261  // of this it has transformations applied twice to it: by the
262  // renderer and by the skeletal animation routine. This should
263  // probably be handled the other way.
264  if (node->_parent && node->_parent->_name.stricmp("f_jaw_g") == 0)
265  continue;
266 
267  glm::mat4 invTransform = node->_invBindPose;
268  glm::mat4 transform = glm::inverse(invTransform);
269 
270  ModelNode::Skin *skin = node->_mesh->skin;
271 
272  for (uint16 i = 0; i < skin->boneMappingCount; ++i) {
273  int index = static_cast<int>(skin->boneMapping[i]);
274  if (index != -1)
275  skin->boneNodeMap[index]->computeAbsoluteTransform();
276  }
277 
278  // TODO: Use vertex shader
279 
280  ModelNode::MeshData *meshData = node->_mesh->data;
281  VertexBuffer &vertexBuffer = *(meshData->rawMesh->getVertexBuffer());
282  uint32 vertexCount = vertexBuffer.getCount();
283 
284  std::vector<float> &vcb = node->_vertexCoordsBuffer;
285  vcb.resize(3 * vertexCount);
286  float *v = &vcb[0];
287 
288  float *iv = &meshData->initialVertexCoords[0];
289  float *boneWeights = &skin->boneWeights[0];
290  float *boneMappingId = &skin->boneMappingId[0];
291 
292  for (uint32 i = 0; i < vertexCount; ++i) {
293  v[0] = 0;
294  v[1] = 0;
295  v[2] = 0;
296  for (uint8 j = 0; j < 4; ++j) {
297  int index = static_cast<int>(boneMappingId[j]);
298  if (index != -1) {
299  float rv[3];
300  float tv[3];
301  const glm::mat4 &invBindPose = skin->boneNodeMap[index]->_invBindPose;
302  const glm::mat4 &boneTransform = skin->boneNodeMap[index]->_absoluteTransform;
303 
304  multiply(iv, transform, rv);
305  multiply(rv, invBindPose, tv);
306  multiply(tv, boneTransform, rv);
307  multiply(rv, invTransform, tv);
308 
309  v[0] += tv[0] * boneWeights[j];
310  v[1] += tv[1] * boneWeights[j];
311  v[2] += tv[2] * boneWeights[j];
312  }
313  }
314  v += 3;
315  iv += 3;
316  boneWeights += 4;
317  boneMappingId += 4;
318  }
319 
320  node->_vertexCoordsBuffered = true;
321  }
322 
323  for (std::map<Common::UString, Model *>::iterator m = model->_attachedModels.begin();
324  m != model->_attachedModels.end(); ++m) {
325  Model *attachedModel = m->second;
326  if (attachedModel->_skinned)
327  updateSkinnedModel(attachedModel);
328  }
329 }
330 
331 } // End of namespace Aurora
332 
333 } // End of namespace Graphics
void setBufferedPosition(float x, float y, float z)
Definition: modelnode.cpp:842
A node within an animation.
Common::UString _name
The model&#39;s name.
Definition: animation.h:91
std::vector< float > boneMapping
Definition: modelnode.h:149
const Common::UString & getName() const
Get the animation&#39;s name.
Definition: animation.cpp:54
The global graphics manager.
void updateSkinnedModel(Model *model)
Transform vertices for each node of the specified model based on current animation.
Definition: animation.cpp:251
A class holding an UTF-8 string.
Definition: ustring.h:48
void update(Model *model, float lastFrame, float nextFrame, const std::vector< ModelNode *> &modelNodeMap)
Update the model position and orientation.
Definition: animation.cpp:74
void setLength(float length)
Definition: animation.cpp:62
uint32 getCount() const
Get vertex count.
An animation to be applied to a model.
VertexBuffer * getVertexBuffer()
Definition: mesh.cpp:39
Utility functions for debug output.
std::vector< float > boneWeights
Definition: modelnode.h:151
uint8_t uint8
Definition: types.h:200
std::map< Common::UString, Model * > _attachedModels
Definition: model.h:282
void setBufferedOrientation(float x, float y, float z, float angle)
Definition: modelnode.cpp:849
glm::mat4 _invBindPose
Inverse bind pose matrix used for animations.
Definition: modelnode.h:247
Camera management.
float getAnimationScale(const Common::UString &anim)
Determine what animation scaling applies.
Definition: model.cpp:513
#define M_PI
Definition: maths.h:39
std::vector< ModelNode * > boneNodeMap
Definition: modelnode.h:153
static void multiply(const float *v, const glm::mat4 &m, float *rv)
Definition: animation.cpp:241
static void normQuaternion(float xIn, float yIn, float zIn, float qIn, float &xOut, float &yOut, float &zOut, float &qOut)
Normalize a quaternion.
Definition: animation.cpp:137
std::vector< float > initialVertexCoords
Initial node vertex coordinates.
Definition: modelnode.h:161
uint16_t uint16
Definition: types.h:202
#define UNUSED(x)
Definition: system.h:170
void setTransTime(float transtime)
Definition: animation.cpp:70
"GGraphics", global, non-engine graphics.
Definition: debugman.h:42
void interpolatePosition(ModelNode *animNode, ModelNode *target, float time, float scale, bool relative) const
Definition: animation.cpp:148
const std::list< AnimNode * > & getNodes() const
Get all animation nodes.
Definition: animation.cpp:125
A node within a 3D model.
ModelNode * getNode(const Common::UString &node)
Get the specified node.
Definition: animation.cpp:109
A 3D model of an object.
const std::list< ModelNode * > & getNodes()
Get all nodes in the current state.
Definition: model.cpp:472
Buffer containing vertex data.
Definition: vertexbuffer.h:68
std::vector< QuaternionKeyFrame > _orientationFrames
Keyframes for orientation animation.
Definition: modelnode.h:229
void interpolateOrientation(ModelNode *animNode, ModelNode *target, float time) const
Definition: animation.cpp:198
Basic reading stream interfaces.
std::vector< float > boneMappingId
Definition: modelnode.h:152
static float dotQuaternion(float x1, float y1, float z1, float q1, float x2, float y2, float z2, float q2)
Return the dot product of two quaternions.
Definition: animation.cpp:130
Graphics::Mesh::Mesh * rawMesh
Node raw mesh data.
Definition: modelnode.h:159
Common::UString _name
The node&#39;s name.
Definition: modelnode.h:216
NodeMap nodeMap
The nodes within the state, indexed by name.
Definition: animation.h:87
uint32_t uint32
Definition: types.h:204
static glm::mat4 inverse(const glm::mat4 &m)
Definition: graphics.cpp:1363
float getLength() const
Get the animations length.
Definition: animation.cpp:66
void setName(Common::UString &name)
Definition: animation.cpp:58
bool hasNode(const Common::UString &node) const
Does the specified node exist?
Definition: animation.cpp:105
const Common::UString & getName() const
Get the node&#39;s name.
Definition: animnode.cpp:61
std::vector< float > _vertexCoordsBuffer
Definition: modelnode.h:255
void addAnimNode(AnimNode *node)
Definition: animation.cpp:100
static float rad2deg(float rad)
Definition: maths.h:93
std::vector< PositionKeyFrame > _positionFrames
Keyframes for position animation.
Definition: modelnode.h:228
ModelNode * _parent
The node&#39;s parent.
Definition: modelnode.h:209
int stricmp(const UString &str) const
Definition: ustring.cpp:192
NodeList nodeList
The nodes within the state.
Definition: animation.h:86