mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-22 20:21:06 +00:00
Merge pull request #949 from Akz-/grim-quat
GRIM: Use quaternions for animation.
This commit is contained in:
commit
5f87912e91
@ -250,11 +250,9 @@ 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.
|
||||
@ -262,40 +260,28 @@ void AnimManager::animate(ModelNode *hier, int numNodes) {
|
||||
// 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 weight = j->_anim->_fade;
|
||||
if (layerWeight > 1.0f)
|
||||
weight /= layerWeight;
|
||||
weight *= remainingWeight;
|
||||
j->_anim->_keyframe->animate(hier, i, time, weight, j->_tagged);
|
||||
}
|
||||
|
||||
float weightFactor = 1.0f;
|
||||
if (totalWeight > 1.0f) {
|
||||
weightFactor = 1.0f / totalWeight;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
|
||||
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 {
|
||||
|
@ -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;
|
||||
|
@ -139,6 +139,7 @@ public:
|
||||
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;
|
||||
|
@ -853,6 +853,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();
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ public:
|
||||
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;
|
||||
|
@ -941,6 +941,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() {
|
||||
//glMatrixMode(GL_MODELVIEW); // exist in opengl but doesn't work properly here
|
||||
tglPopMatrix();
|
||||
|
@ -76,6 +76,7 @@ public:
|
||||
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;
|
||||
|
@ -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)
|
||||
@ -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;
|
||||
@ -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_
|
||||
@ -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
|
||||
|
@ -43,7 +43,8 @@ public:
|
||||
|
||||
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; }
|
||||
@ -83,7 +84,7 @@ private:
|
||||
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;
|
||||
|
@ -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);
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "engines/grim/object.h"
|
||||
#include "math/matrix4.h"
|
||||
#include "math/quat.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
@ -190,8 +191,9 @@ public:
|
||||
// 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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user