Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

Commit

Permalink
GRIM: Use quaternions for animation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Akz- committed Jul 2, 2014
1 parent b13c379 commit dbfec3b
Show file tree
Hide file tree
Showing 14 changed files with 77 additions and 85 deletions.
46 changes: 16 additions & 30 deletions engines/grim/animation.cpp
Expand Up @@ -250,52 +250,38 @@ void AnimManager::removeAnimation(const Animation *anim) {
void AnimManager::animate(ModelNode *hier, int numNodes) {
// Apply animation to each hierarchy node separately.
for (int i = 0; i < numNodes; i++) {
Math::Vector3d tempPos;
Math::Angle tempYaw = 0.0f, tempPitch = 0.0f, tempRoll = 0.0f;
float totalWeight = 0.0f;
float remainingWeight = 1.0f;
int currPriority = -1;
float layerWeight = 0.0f;

// The animations are layered so that animations with a higher priority
// are played regardless of the blend weights of lower priority animations.
// The highest priority layer gets as much weight as it wants, while the
// next layer gets the remaining amount and so on.
for (Common::List<AnimationEntry>::iterator j = _activeAnims.begin(); j != _activeAnims.end(); ++j) {
if (currPriority != j->_priority) {
remainingWeight *= 1.0f - layerWeight;
layerWeight = 0.0f;
for (Common::List<AnimationEntry>::iterator k = j; k != _activeAnims.end(); ++k) {
if (j->_priority != k->_priority)
break;
float time = k->_anim->_time / 1000.0f;
if (k->_anim->_keyframe->isNodeAnimated(hier, i, time, k->_tagged))
layerWeight += k->_anim->_fade;
}

currPriority = j->_priority;
remainingWeight *= 1 - totalWeight;
if (remainingWeight <= 0.0f)
break;

float weightFactor = 1.0f;
if (totalWeight > 1.0f) {
weightFactor = 1.0f / totalWeight;
}
tempPos += hier[i]._animPos * weightFactor;
tempYaw += hier[i]._animYaw * weightFactor;
tempPitch += hier[i]._animPitch * weightFactor;
tempRoll += hier[i]._animRoll * weightFactor;
hier[i]._animPos.set(0, 0, 0);
hier[i]._animYaw = 0.0f;
hier[i]._animPitch = 0.0f;
hier[i]._animRoll = 0.0f;
totalWeight = 0.0f;
}

float time = j->_anim->_time / 1000.0f;
float weight = j->_anim->_fade * remainingWeight;
if (j->_anim->_keyframe->animate(hier, i, time, weight, j->_tagged))
totalWeight += j->_anim->_fade;
}

float weightFactor = 1.0f;
if (totalWeight > 1.0f) {
weightFactor = 1.0f / totalWeight;
float weight = j->_anim->_fade;
if (layerWeight > 1.0f)
weight /= layerWeight;
weight *= remainingWeight;
j->_anim->_keyframe->animate(hier, i, time, weight, j->_tagged);
}
hier[i]._animPos = hier[i]._animPos * weightFactor + tempPos;
hier[i]._animYaw = hier[i]._animYaw * weightFactor + tempYaw;
hier[i]._animPitch = hier[i]._animPitch * weightFactor + tempPitch;
hier[i]._animRoll = hier[i]._animRoll * weightFactor + tempRoll;
}
}

Expand Down
19 changes: 4 additions & 15 deletions engines/grim/costume/head.cpp
Expand Up @@ -144,22 +144,11 @@ void Head::Joint::orientTowards(bool entering, const Math::Vector3d &point, floa
_yaw = y;
_roll = r;

// Assemble ypr back to a matrix.
// This matrix is the head orientation with respect to parent-with-keyframe-animation space.
lookAtTM.buildFromXYZ(y, pt, r, Math::EO_ZXY);

// What follows is a hack: Since translateObject(ModelNode *node, bool reset) in this file,
// and GfxOpenGL/GfxTinyGL::drawHierachyNode concatenate transforms incorrectly, by summing up
// euler angles, do a hack here where we do the proper transform here already, and *subtract off*
// the YPR scalars from the animYPR scalars to cancel out the values that those pieces of code
// will later accumulate. After those pieces of code have been fixed, the following lines can
// be deleted, and this function can simply output the contents of pt, y and r variables above.
lookAtTM = animFrame * lookAtTM;
// Assemble ypr to a quaternion.
// This is the head orientation with respect to parent-with-keyframe-animation space.
Math::Quaternion lookAtQuat = Math::Quaternion::fromXYZ(y, pt, r, Math::EO_ZXY);

lookAtTM.getXYZ(&y, &pt, &r, Math::EO_ZXY);
_node->_animYaw = y - _node->_yaw;
_node->_animPitch = pt - _node->_pitch;
_node->_animRoll = r - _node->_roll;
_node->_animRot = _node->_animRot * lookAtQuat;
}

void Head::Joint::saveState(SaveGame *state) const {
Expand Down
6 changes: 2 additions & 4 deletions engines/grim/costume/model_component.cpp
Expand Up @@ -125,10 +125,8 @@ AnimManager *ModelComponent::getAnimManager() const {
int ModelComponent::update(uint time) {
// First reset the current animation.
for (int i = 0; i < getNumNodes(); i++) {
_hier[i]._animPos.set(0, 0, 0);
_hier[i]._animPitch = 0;
_hier[i]._animYaw = 0;
_hier[i]._animRoll = 0;
_hier[i]._animPos = _hier[i]._pos;
_hier[i]._animRot = _hier[i]._rot;
}

_animated = false;
Expand Down
1 change: 1 addition & 0 deletions engines/grim/gfx_base.h
Expand Up @@ -139,6 +139,7 @@ class GfxBase {
virtual void translateViewpointStart() = 0;
virtual void translateViewpoint(const Math::Vector3d &vec) = 0;
virtual void rotateViewpoint(const Math::Angle &angle, const Math::Vector3d &axis) = 0;
virtual void rotateViewpoint(const Math::Matrix4 &rot) = 0;
virtual void translateViewpointFinish() = 0;

virtual void drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face) = 0;
Expand Down
4 changes: 4 additions & 0 deletions engines/grim/gfx_opengl.cpp
Expand Up @@ -852,6 +852,10 @@ void GfxOpenGL::rotateViewpoint(const Math::Angle &angle, const Math::Vector3d &
glRotatef(angle.getDegrees(), axis.x(), axis.y(), axis.z());
}

void GfxOpenGL::rotateViewpoint(const Math::Matrix4 &rot) {
glMultMatrixf(rot.getData());
}

void GfxOpenGL::translateViewpointFinish() {
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
Expand Down
1 change: 1 addition & 0 deletions engines/grim/gfx_opengl.h
Expand Up @@ -79,6 +79,7 @@ class GfxOpenGL : public GfxBase {
void translateViewpointStart() override;
void translateViewpoint(const Math::Vector3d &vec) override;
void rotateViewpoint(const Math::Angle &angle, const Math::Vector3d &axis) override;
void rotateViewpoint(const Math::Matrix4 &rot) override;
void translateViewpointFinish() override;

void drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face) override;
Expand Down
5 changes: 5 additions & 0 deletions engines/grim/gfx_opengl_shaders.cpp
Expand Up @@ -765,6 +765,11 @@ void GfxOpenGLS::rotateViewpoint(const Math::Angle &angle, const Math::Vector3d
_matrixStack.top() = temp;
}

void GfxOpenGLS::rotateViewpoint(const Math::Matrix4 &rot) {
Math::Matrix4 temp = rot * _matrixStack.top();
_matrixStack.top() = temp;
}

void GfxOpenGLS::translateViewpointFinish() {
_matrixStack.pop();
}
Expand Down
1 change: 1 addition & 0 deletions engines/grim/gfx_opengl_shaders.h
Expand Up @@ -82,6 +82,7 @@ class GfxOpenGLS : public GfxBase {
virtual void translateViewpointStart() override;
virtual void translateViewpoint(const Math::Vector3d &vec) override;
virtual void rotateViewpoint(const Math::Angle &angle, const Math::Vector3d &axis) override;
virtual void rotateViewpoint(const Math::Matrix4 &rot) override;
virtual void translateViewpointFinish() override;

virtual void drawEMIModelFace(const EMIModel* model, const EMIMeshFace* face) override;
Expand Down
4 changes: 4 additions & 0 deletions engines/grim/gfx_tinygl.cpp
Expand Up @@ -974,6 +974,10 @@ void GfxTinyGL::rotateViewpoint(const Math::Angle &angle, const Math::Vector3d &
tglRotatef(angle.getDegrees(), axis.x(), axis.y(), axis.z());
}

void GfxTinyGL::rotateViewpoint(const Math::Matrix4 &rot) {
tglMultMatrixf(rot.getData());
}

void GfxTinyGL::translateViewpointFinish() {
tglPopMatrix();
}
Expand Down
1 change: 1 addition & 0 deletions engines/grim/gfx_tinygl.h
Expand Up @@ -76,6 +76,7 @@ class GfxTinyGL : public GfxBase {
void translateViewpointStart() override;
void translateViewpoint(const Math::Vector3d &vec) override;
void rotateViewpoint(const Math::Angle &angle, const Math::Vector3d &axis) override;
void rotateViewpoint(const Math::Matrix4 &matrix) override;
void translateViewpointFinish() override;

void drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face) override;
Expand Down
37 changes: 23 additions & 14 deletions engines/grim/keyframe.cpp
Expand Up @@ -160,7 +160,7 @@ KeyframeAnim::~KeyframeAnim() {
g_resourceloader->uncacheKeyframe(this);
}

bool KeyframeAnim::animate(ModelNode *nodes, int num, float time, float fade, bool tagged) const {
bool KeyframeAnim::isNodeAnimated(ModelNode *nodes, int num, float time, bool tagged) const {
// Without this sending the bread down the tube in "mo" often crashes,
// because it goes outside the bounds of the array of the nodes.
if (num >= _numJoints)
Expand All @@ -172,12 +172,28 @@ bool KeyframeAnim::animate(ModelNode *nodes, int num, float time, float fade, bo
frame = _numFrames;

if (_nodes[num] && tagged == ((_type & nodes[num]._type) != 0)) {
return _nodes[num]->animate(nodes[num], frame, fade, (_flags & 256) == 0);
return _nodes[num]->_numEntries != 0;
} else {
return false;
}
}

void KeyframeAnim::animate(ModelNode *nodes, int num, float time, float fade, bool tagged) const {
// Without this sending the bread down the tube in "mo" often crashes,
// because it goes outside the bounds of the array of the nodes.
if (num >= _numJoints)
return;

float frame = time * _fps;

if (frame > _numFrames)
frame = _numFrames;

if (_nodes[num] && tagged == ((_type & nodes[num]._type) != 0)) {
_nodes[num]->animate(nodes[num], frame, fade, (_flags & 256) == 0);
}
}

int KeyframeAnim::getMarker(float startTime, float stopTime) const {
if (!_markers)
return 0;
Expand Down Expand Up @@ -247,9 +263,9 @@ KeyframeAnim::KeyframeNode::~KeyframeNode() {
delete[] _entries;
}

bool KeyframeAnim::KeyframeNode::animate(ModelNode &node, float frame, float fade, bool useDelta) const {
void KeyframeAnim::KeyframeNode::animate(ModelNode &node, float frame, float fade, bool useDelta) const {
if (_numEntries == 0)
return false;
return;

// Do a binary search for the nearest previous frame
// Loop invariant: entries_[low].frame_ <= frame < entries_[high].frame_
Expand Down Expand Up @@ -283,16 +299,9 @@ bool KeyframeAnim::KeyframeNode::animate(ModelNode &node, float frame, float fad

node._animPos += (pos - node._pos) * fade;

Math::Angle dpitch = pitch - node._pitch;
node._animPitch += dpitch.normalize(-180) * fade;

Math::Angle dyaw = yaw - node._yaw;
node._animYaw += dyaw.normalize(-180) * fade;

Math::Angle droll = roll - node._roll;
node._animRoll += droll.normalize(-180) * fade;

return true;
Math::Quaternion rotQuat = Math::Quaternion::fromXYZ(yaw, pitch, roll, Math::EO_ZXY);
rotQuat = node._animRot * node._rot.inverse() * rotQuat;
node._animRot = node._animRot.slerpQuat(rotQuat, fade);
}

} // end of namespace Grim
5 changes: 3 additions & 2 deletions engines/grim/keyframe.h
Expand Up @@ -43,7 +43,8 @@ class KeyframeAnim : public Object {

void loadBinary(Common::SeekableReadStream *data);
void loadText(TextSplitter &ts);
bool animate(ModelNode *nodes, int num, float time, float fade, bool tagged) const;
bool isNodeAnimated(ModelNode *nodes, int num, float time, bool tagged) const;
void animate(ModelNode *nodes, int num, float time, float fade, bool tagged) const;
int getMarker(float startTime, float stopTime) const;

float getLength() const { return _numFrames / _fps; }
Expand Down Expand Up @@ -83,7 +84,7 @@ class KeyframeAnim : public Object {
void loadText(TextSplitter &ts);
~KeyframeNode();

bool animate(ModelNode &node, float frame, float fade, bool useDelta) const;
void animate(ModelNode &node, float frame, float fade, bool useDelta) const;

char _meshName[32];
int _numEntries;
Expand Down
28 changes: 9 additions & 19 deletions engines/grim/model.cpp
Expand Up @@ -629,10 +629,8 @@ void ModelNode::loadBinary(Common::SeekableReadStream *data, ModelNode *hierNode
_yaw = get_float(f);
data->read(f, 4);
_roll = get_float(f);
_animPos.set(0, 0, 0);
_animPitch = 0;
_animYaw = 0;
_animRoll = 0;
_rot = Math::Quaternion::fromXYZ(_yaw, _pitch, _roll, Math::EO_ZXY);
_animPos = _pos;
_sprite = nullptr;

data->seek(48, SEEK_CUR);
Expand Down Expand Up @@ -740,13 +738,8 @@ void ModelNode::update() {
return;

if (_hierVisible && _needsUpdate) {
Math::Vector3d animPos = _pos + _animPos;
Math::Angle animPitch = _pitch + _animPitch;
Math::Angle animYaw = _yaw + _animYaw;
Math::Angle animRoll = _roll + _animRoll;

_localMatrix.setPosition(animPos);
_localMatrix.buildFromXYZ(animYaw, animPitch, animRoll, Math::EO_ZXY);
_localMatrix = _animRot.toMatrix();
_localMatrix.setPosition(_animPos);

_matrix = _matrix * _localMatrix;

Expand Down Expand Up @@ -791,16 +784,13 @@ void ModelNode::removeSprite(const Sprite *sprite) {
}

void ModelNode::translateViewpoint() const {
Math::Vector3d animPos = _pos + _animPos;
Math::Angle animPitch = _pitch + _animPitch;
Math::Angle animYaw = _yaw + _animYaw;
Math::Angle animRoll = _roll + _animRoll;
g_driver->translateViewpointStart();

g_driver->translateViewpoint(animPos);
g_driver->rotateViewpoint(animYaw, Math::Vector3d(0, 0, 1));
g_driver->rotateViewpoint(animPitch, Math::Vector3d(1, 0, 0));
g_driver->rotateViewpoint(animRoll, Math::Vector3d(0, 1, 0));
g_driver->translateViewpoint(_animPos);

Math::Matrix4 rot = _animRot.toMatrix();
rot.transpose();
g_driver->rotateViewpoint(rot);
}

void ModelNode::translateViewpointBack() const {
Expand Down
4 changes: 3 additions & 1 deletion engines/grim/model.h
Expand Up @@ -25,6 +25,7 @@

#include "engines/grim/object.h"
#include "math/matrix4.h"
#include "math/quat.h"

namespace Common {
class SeekableReadStream;
Expand Down Expand Up @@ -190,8 +191,9 @@ class ModelNode {
// Specifies the bind pose YPR values for this node. This data
// is read from the model file and never altered (could be const).
Math::Angle _pitch, _yaw, _roll;
Math::Quaternion _rot;
Math::Vector3d _animPos;
Math::Angle _animPitch, _animYaw, _animRoll;
Math::Quaternion _animRot;
bool _meshVisible, _hierVisible;
bool _initialized;
bool _needsUpdate;
Expand Down

0 comments on commit dbfec3b

Please sign in to comment.