From 6a40b43f16a88b113045f9b927ec1d25fac593c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joni=20V=C3=A4h=C3=A4m=C3=A4ki?= Date: Tue, 24 May 2011 19:47:39 +0300 Subject: [PATCH] GRIM: Apply animations in priority order. --- engines/grim/actor.cpp | 5 + engines/grim/costume.cpp | 230 ++++++++++++++++++++++++++++-------- engines/grim/costume.h | 2 + engines/grim/gfx_opengl.cpp | 14 +-- engines/grim/gfx_tinygl.cpp | 14 +-- engines/grim/keyframe.cpp | 42 ++----- engines/grim/keyframe.h | 4 +- engines/grim/model.cpp | 20 +--- engines/grim/model.h | 2 - 9 files changed, 215 insertions(+), 118 deletions(-) diff --git a/engines/grim/actor.cpp b/engines/grim/actor.cpp index c9746c55172..df13956824d 100644 --- a/engines/grim/actor.cpp +++ b/engines/grim/actor.cpp @@ -1205,6 +1205,11 @@ void Actor::update() { c->update(); } + for (Common::List::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) { + Costume *c = *i; + c->animate(); + } + for (Common::List::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) { Costume *c = *i; c->moveHead(_lookingMode, _lookAtVector, _lookAtRate); diff --git a/engines/grim/costume.cpp b/engines/grim/costume.cpp index c8995f12514..a41c308e302 100644 --- a/engines/grim/costume.cpp +++ b/engines/grim/costume.cpp @@ -132,16 +132,24 @@ public: void init(); }; +struct AnimationState { + KeyframeAnimPtr _keyf; + int _time; + float _fade; +}; + class ModelComponent : public Costume::Component { public: ModelComponent(Costume::Component *parent, int parentID, const char *filename, Costume::Component *prevComponent, tag32 tag); void init(); void setKey(int val); - void update(); + void animate(); void reset(); void resetColormap(); void setMatrix(Graphics::Matrix4 matrix) { _matrix = matrix; }; void restoreState(SaveGame *state); + void addActiveAnimation(AnimationState *anim, int priority1, int priority2); + void removeActiveAnimation(AnimationState *anim); ~ModelComponent(); Model::HierNode *getHierarchy() { return _hier; } @@ -150,10 +158,17 @@ public: void draw(); protected: + struct AnimationEntry { + AnimationState *anim; + int priority; + bool tagged; + }; + Common::String _filename; ObjectPtr _obj; Model::HierNode *_hier; Graphics::Matrix4 _matrix; + Common::List *_activeAnims; }; class MainModelComponent : public ModelComponent { @@ -307,7 +322,7 @@ void SpriteComponent::restoreState(SaveGame *state) { ModelComponent::ModelComponent(Costume::Component *p, int parentID, const char *filename, Costume::Component *prevComponent, tag32 t) : Costume::Component(p, parentID, t), _filename(filename), - _obj(NULL), _hier(NULL) { + _obj(NULL), _hier(NULL), _activeAnims(NULL) { const char *comma = strchr(filename, ','); // Can be called with a comma and a numeric parameter afterward, but @@ -356,6 +371,10 @@ void ModelComponent::init() { setKey(0); } + if (!_activeAnims) { + _activeAnims = new Common::List(); + } + // If we're the child of a mesh component, put our nodes in the // parent object's tree. if (_parent) { @@ -383,16 +402,103 @@ void ModelComponent::reset() { _hier->_hierVisible = _visible; } -// Reset the hierarchy nodes for any keyframe animations (which -// are children of this component and therefore get updated later). -void ModelComponent::update() { - for (int i = 0; i < _obj->getNumNodes(); i++) { - _hier[i]._priority = -1; +void ModelComponent::addActiveAnimation(AnimationState *anim, int priority1, int priority2) +{ + // Keep the list of animations sorted by priorities in descending order. Because + // the animations have two different priorities, we add the animation to the list + // with both priorities. + Common::List::iterator i; + AnimationEntry entry; + entry.anim = anim; + entry.priority = priority1; + entry.tagged = false; + for (i = _activeAnims->begin(); i != _activeAnims->end(); ++i) { + if (i->priority < entry.priority) { + _activeAnims->insert(i, entry); + break; + } + } + if (i == _activeAnims->end()) + _activeAnims->push_back(entry); + + entry.priority = priority2; + entry.tagged = true; + for (i = _activeAnims->begin(); i != _activeAnims->end(); ++i) { + if (i->priority < entry.priority) { + _activeAnims->insert(i, entry); + break; + } + } + if (i == _activeAnims->end()) + _activeAnims->push_back(entry); +} + +void ModelComponent::removeActiveAnimation(AnimationState *anim) +{ + Common::List::iterator i; + for (i = _activeAnims->begin(); i != _activeAnims->end(); ++i) { + if (i->anim == anim) + i = _activeAnims->erase(i); + } +} + +void ModelComponent::animate() { + // 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]._totalWeight = 0; + } + + // Apply animation to each hierarchy node separately. + for (int i = 0; i < getNumNodes(); i++) { + Graphics::Vector3d tempPos; + float tempYaw = 0.0f, tempPitch = 0.0f, tempRoll = 0.0f; + float totalWeight = 0.0f; + float remainingWeight = 1.0f; + int currPriority = -1; + + // 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::iterator j = _activeAnims->begin(); j != _activeAnims->end(); ++j) { + if (currPriority != j->priority) { + 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; + j->anim->_keyf->animate(_hier, i, time, weight, j->tagged); + totalWeight += weight; + } + + 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; } } @@ -423,15 +529,11 @@ void translateObject(Model::HierNode *node, bool reset) { if (reset) { g_driver->translateViewpointFinish(); } else { - if (node->_totalWeight > 0) { - Graphics::Vector3d animPos = node->_pos + node->_animPos / node->_totalWeight; - float animPitch = node->_pitch + node->_animPitch / node->_totalWeight; - float animYaw = node->_yaw + node->_animYaw / node->_totalWeight; - float animRoll = node->_roll + node->_animRoll / node->_totalWeight; - g_driver->translateViewpointStart(animPos, animPitch, animYaw, animRoll); - } else { - g_driver->translateViewpointStart(node->_pos, node->_pitch, node->_yaw, node->_roll); - } + Graphics::Vector3d animPos = node->_pos + node->_animPos; + float animPitch = node->_pitch + node->_animPitch; + float animYaw = node->_yaw + node->_animYaw; + float animRoll = node->_roll + node->_animRoll; + g_driver->translateViewpointStart(animPos, animPitch, animYaw, animRoll); } } @@ -459,6 +561,7 @@ MainModelComponent::MainModelComponent(Costume::Component *p, int parentID, cons MainModelComponent *mmc = dynamic_cast(prevComponent); if (mmc && mmc->_filename == filename) { + _activeAnims = mmc->_activeAnims; _obj = mmc->_obj; _hier = mmc->_hier; _hierShared = true; @@ -536,16 +639,17 @@ public: void reset(); void saveState(SaveGame *state); void restoreState(SaveGame *state); + void activate(); + void deactivate(); ~KeyframeComponent() {} private: - KeyframeAnimPtr _keyf; + AnimationState _anim; int _priority1, _priority2; Model::HierNode *_hier; int _numNodes; bool _active; int _repeatMode; - int _currTime; Common::String _fname; friend class Costume; @@ -557,10 +661,10 @@ KeyframeComponent::KeyframeComponent(Costume::Component *p, int parentID, const const char *comma = strchr(filename, ','); if (comma) { Common::String realName(filename, comma); - _keyf = g_resourceloader->getKeyframe(realName); + _anim._keyf = g_resourceloader->getKeyframe(realName.c_str()); sscanf(comma + 1, "%d,%d", &_priority1, &_priority2); } else - _keyf = g_resourceloader->getKeyframe(filename); + _anim._keyf = g_resourceloader->getKeyframe(filename); } void KeyframeComponent::setKey(int val) { @@ -570,58 +674,70 @@ void KeyframeComponent::setKey(int val) { case 2: case 3: if (!_active || val != 1) { - _active = true; - _currTime = -1; + if (!_active) { + activate(); + _active = true; + } + _anim._time = -1; } _repeatMode = val; break; case 5: - warning("Key 5 (meaning uncertain) used for keyframe %s", _keyf->getFilename().c_str()); + warning("Key 5 (meaning uncertain) used for keyframe %s", _anim._keyf->getFilename().c_str()); case 4: - _active = false; + if (_active) { + deactivate(); + _active = false; + } break; default: if (gDebugLevel == DEBUG_MODEL || gDebugLevel == DEBUG_WARN || gDebugLevel == DEBUG_ALL) - warning("Unknown key %d for keyframe %s", val, _keyf->getFilename().c_str()); + warning("Unknown key %d for keyframe %s", val, _anim._keyf->getFilename()); } } void KeyframeComponent::reset() { - _active = false; + if (_active) { + deactivate(); + _active = false; + } } void KeyframeComponent::update() { if (!_active) return; - if (_currTime < 0) // For first time through - _currTime = 0; + if (_anim._time < 0) // For first time through + _anim._time = 0; else - _currTime += g_grim->getFrameTime(); + _anim._time += g_grim->getFrameTime(); - int animLength = (int)(_keyf->getLength() * 1000); + int animLength = (int)(_anim._keyf->getLength() * 1000); - if (_currTime > animLength) { // What to do at end? + if (_anim._time > animLength) { // What to do at end? switch (_repeatMode) { case 0: // Stop case 3: // Fade at end - _active = false; + if (_active) { + deactivate(); + _active = false; + } return; case 1: // Loop do - _currTime -= animLength; - while (_currTime > animLength); + _anim._time -= animLength; + while (_anim._time > animLength); break; case 2: // Hold at end - _currTime = animLength; + _anim._time = animLength; break; default: if (gDebugLevel == DEBUG_MODEL || gDebugLevel == DEBUG_WARN || gDebugLevel == DEBUG_ALL) - warning("Unknown repeat mode %d for keyframe %s", _repeatMode, _keyf->getFilename().c_str()); + warning("Unknown repeat mode %d for keyframe %s", _repeatMode, _anim._keyf->getFilename()); } } - _keyf->animate(_hier, _numNodes, _currTime / 1000.0f, _priority1, _priority2, _fade); + _anim._fade = _fade; } void KeyframeComponent::init() { @@ -631,7 +747,7 @@ void KeyframeComponent::init() { _numNodes = mc->getNumNodes(); } else { if (gDebugLevel == DEBUG_MODEL || gDebugLevel == DEBUG_WARN || gDebugLevel == DEBUG_ALL) - warning("Parent of %s was not a model", _keyf->getFilename().c_str()); + warning("Parent of %s was not a model", _anim._keyf->getFilename()); _hier = NULL; _numNodes = 0; } @@ -640,13 +756,28 @@ void KeyframeComponent::init() { void KeyframeComponent::saveState(SaveGame *state) { state->writeLESint32(_active); state->writeLESint32(_repeatMode); - state->writeLESint32(_currTime); + state->writeLESint32(_anim._time); } void KeyframeComponent::restoreState(SaveGame *state) { _active = state->readLESint32(); _repeatMode = state->readLESint32(); - _currTime = state->readLESint32(); + _anim._time = state->readLESint32(); + + if (_active) + activate(); +} + +void KeyframeComponent::activate() { + ModelComponent *mc = dynamic_cast(_parent); + if (mc) + mc->addActiveAnimation(&_anim, _priority1, _priority2); +} + +void KeyframeComponent::deactivate() { + ModelComponent *mc = dynamic_cast(_parent); + if (mc) + mc->removeActiveAnimation(&_anim); } MeshComponent::MeshComponent(Costume::Component *p, int parentID, const char *name, tag32 t) : @@ -1384,16 +1515,19 @@ void Costume::update() { } } +void Costume::animate() { + for (int i = 0; i < _numComponents; i++) { + if (_components[i]) { + _components[i]->animate(); + } + } +} + void Costume::moveHead(bool lookingMode, const Graphics::Vector3d &lookAt, float rate) { if (_joint1Node) { float step = g_grim->getPerSecond(rate); float yawStep = step; float pitchStep = step / 3.f; - - _joint1Node->_totalWeight = 1; - _joint2Node->_totalWeight = 1; - _joint3Node->_totalWeight = 1; - if (!lookingMode) { //animate yaw if (_headYaw > yawStep) { @@ -1448,9 +1582,7 @@ void Costume::moveHead(bool lookingMode, const Graphics::Vector3d &lookAt, float float bodyYaw = _matrix._rot.getYaw(); p = _joint1Node->_parent; while (p) { - bodyYaw += p->_yaw; - if (p->_totalWeight > 0) - bodyYaw += p->_animYaw / p->_totalWeight; + bodyYaw += p->_yaw + p->_animYaw; p = p->_parent; } diff --git a/engines/grim/costume.h b/engines/grim/costume.h index 9f9d447ec03..0942e04a4ad 100644 --- a/engines/grim/costume.h +++ b/engines/grim/costume.h @@ -67,6 +67,7 @@ public: void moveHead(bool lookingMode, const Graphics::Vector3d &lookAt, float rate); void update(); + void animate(); void setupTextures(); void draw(); void setPosRotate(Graphics::Vector3d pos, float pitch, float yaw, float roll); @@ -91,6 +92,7 @@ public: virtual void setKey(int) { } virtual void setMapName(char *) { } virtual void update() { } + virtual void animate() { } virtual void setupTexture() { } virtual void draw() { } virtual void reset() { } diff --git a/engines/grim/gfx_opengl.cpp b/engines/grim/gfx_opengl.cpp index 5e438afddc6..8d748ab05c4 100644 --- a/engines/grim/gfx_opengl.cpp +++ b/engines/grim/gfx_opengl.cpp @@ -489,15 +489,11 @@ void GfxOpenGL::translateViewpointFinish() { } void GfxOpenGL::drawHierachyNode(const Model::HierNode *node) { - if (node->_totalWeight > 0) { - Graphics::Vector3d animPos = node->_pos + node->_animPos / node->_totalWeight; - float animPitch = node->_pitch + node->_animPitch / node->_totalWeight; - float animYaw = node->_yaw + node->_animYaw / node->_totalWeight; - float animRoll = node->_roll + node->_animRoll / node->_totalWeight; - translateViewpointStart(animPos, animPitch, animYaw, animRoll); - } else { - translateViewpointStart(node->_pos, node->_pitch, node->_yaw, node->_roll); - } + Graphics::Vector3d animPos = node->_pos + node->_animPos; + float animPitch = node->_pitch + node->_animPitch; + float animYaw = node->_yaw + node->_animYaw; + float animRoll = node->_roll + node->_animRoll; + translateViewpointStart(animPos, animPitch, animYaw, animRoll); if (node->_hierVisible) { glPushMatrix(); glTranslatef(node->_pivot.x(), node->_pivot.y(), node->_pivot.z()); diff --git a/engines/grim/gfx_tinygl.cpp b/engines/grim/gfx_tinygl.cpp index ea6b9f8fe40..c900330764b 100644 --- a/engines/grim/gfx_tinygl.cpp +++ b/engines/grim/gfx_tinygl.cpp @@ -526,15 +526,11 @@ void GfxTinyGL::translateViewpointFinish() { } void GfxTinyGL::drawHierachyNode(const Model::HierNode *node) { - if (node->_totalWeight > 0) { - Graphics::Vector3d animPos = node->_pos + node->_animPos / node->_totalWeight; - float animPitch = node->_pitch + node->_animPitch / node->_totalWeight; - float animYaw = node->_yaw + node->_animYaw / node->_totalWeight; - float animRoll = node->_roll + node->_animRoll / node->_totalWeight; - translateViewpointStart(animPos, animPitch, animYaw, animRoll); - } else { - translateViewpointStart(node->_pos, node->_pitch, node->_yaw, node->_roll); - } + Graphics::Vector3d animPos = node->_pos + node->_animPos; + float animPitch = node->_pitch + node->_animPitch; + float animYaw = node->_yaw + node->_animYaw; + float animRoll = node->_roll + node->_animRoll; + translateViewpointStart(animPos, animPitch, animYaw, animRoll); if (node->_hierVisible) { tglPushMatrix(); tglTranslatef(node->_pivot.x(), node->_pivot.y(), node->_pivot.z()); diff --git a/engines/grim/keyframe.cpp b/engines/grim/keyframe.cpp index 5cd9409a571..ddaf275cff6 100644 --- a/engines/grim/keyframe.cpp +++ b/engines/grim/keyframe.cpp @@ -149,22 +149,19 @@ KeyframeAnim::~KeyframeAnim() { g_resourceloader->uncacheKeyframe(this); } -void KeyframeAnim::animate(Model::HierNode *nodes, int num, float time, int priority1, int priority2, float fade) const { +void KeyframeAnim::animate(Model::HierNode *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; - // 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 (_numJoints < num) { - num = _numJoints; - } - - for (int i = 0; i < num; i++) { - if (_nodes[i]) - _nodes[i]->animate(nodes[i], frame, ((_type & nodes[i]._type) != 0 ? priority2 : priority1), fade); - } + if (_nodes[num] && tagged == ((_type & nodes[num]._type) != 0)) + _nodes[num]->animate(nodes[num], frame, fade); } void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) { @@ -222,11 +219,9 @@ KeyframeAnim::KeyframeNode::~KeyframeNode() { delete[] _entries; } -void KeyframeAnim::KeyframeNode::animate(Model::HierNode &node, float frame, int priority, float fade) const { +void KeyframeAnim::KeyframeNode::animate(Model::HierNode &node, float frame, float fade) const { if (_numEntries == 0) return; - if (priority < node._priority) - return; // Do a binary search for the nearest previous frame // Loop invariant: entries_[low].frame_ <= frame < entries_[high].frame_ @@ -245,23 +240,6 @@ void KeyframeAnim::KeyframeNode::animate(Model::HierNode &node, float frame, int float yaw = _entries[low]._yaw + dt * _entries[low]._dyaw; float roll = _entries[low]._roll + dt * _entries[low]._droll; - if (priority > node._priority) { - node._priority = priority; - if (node._totalWeight > 0) { - node._animPos = node._animPos * (1 - fade) / node._totalWeight; - node._animPitch = node._animPitch * (1 - fade) / node._totalWeight; - node._animYaw = node._animYaw * (1 - fade) / node._totalWeight; - node._animRoll = node._animRoll * (1 - fade) / node._totalWeight; - node._totalWeight = 1 - fade; - } else { - node._animPos.set(0,0,0); - node._animPitch = 0; - node._animYaw = 0; - node._animRoll = 0; - node._totalWeight = 0; - } - } - node._animPos += (pos - node._pos) * fade; float dpitch = pitch - node._pitch; @@ -284,8 +262,6 @@ void KeyframeAnim::KeyframeNode::animate(Model::HierNode &node, float frame, int while (droll < -180) droll += 360; node._animRoll += droll * fade; - - node._totalWeight += fade; } } // end of namespace Grim diff --git a/engines/grim/keyframe.h b/engines/grim/keyframe.h index 64a27c32231..4bfa724de63 100644 --- a/engines/grim/keyframe.h +++ b/engines/grim/keyframe.h @@ -34,7 +34,7 @@ public: void loadBinary(const char *data, int len); void loadText(TextSplitter &ts); - void animate(Model::HierNode *nodes, int num, float time, int priority1 = 1, int priority2 = 5, float fade = 1) const; + void animate(Model::HierNode *nodes, int num, float time, float fade, bool tagged) const; float getLength() const { return _numFrames / _fps; } const Common::String &getFilename() const { return _fname; } @@ -66,7 +66,7 @@ private: void loadText(TextSplitter &ts); ~KeyframeNode(); - void animate(Model::HierNode &node, float frame, int priority, float fade) const; + void animate(Model::HierNode &node, float frame, float fade) const; char _meshName[32]; int _numEntries; diff --git a/engines/grim/model.cpp b/engines/grim/model.cpp index 694c50c4cf3..3459774b898 100644 --- a/engines/grim/model.cpp +++ b/engines/grim/model.cpp @@ -273,8 +273,6 @@ void Model::HierNode::loadBinary(const char *&data, Model::HierNode *hierNodes, _animPitch = 0; _animYaw = 0; _animRoll = 0; - _priority = -1; - _totalWeight = 0; _sprite = NULL; data += 184; @@ -393,7 +391,6 @@ void Model::loadText(TextSplitter *ts, CMap *cmap) { _rootHierNode[num]._pivot = Graphics::Vector3d(pivotx, pivoty, pivotz); _rootHierNode[num]._meshVisible = true; _rootHierNode[num]._hierVisible = true; - _rootHierNode[num]._totalWeight = 0; _rootHierNode[num]._sprite = NULL; } @@ -551,18 +548,13 @@ void Model::HierNode::update() { if (!_initialized) return; - if (_totalWeight > 0) { - Graphics::Vector3d animPos = _pos + _animPos / _totalWeight; - float animPitch = _pitch + _animPitch / _totalWeight; - float animYaw = _yaw + _animYaw / _totalWeight; - float animRoll = _roll + _animRoll / _totalWeight; + Graphics::Vector3d animPos = _pos + _animPos; + float animPitch = _pitch + _animPitch; + float animYaw = _yaw + _animYaw; + float animRoll = _roll + _animRoll; - _localMatrix._pos.set(animPos.x(), animPos.y(), animPos.z()); - _localMatrix._rot.buildFromPitchYawRoll(animPitch, animYaw, animRoll); - } else { - _localMatrix._pos.set(_pos.x(), _pos.y(), _pos.z()); - _localMatrix._rot.buildFromPitchYawRoll(_pitch, _yaw, _roll); - } + _localMatrix._pos.set(animPos.x(), animPos.y(), animPos.z()); + _localMatrix._rot.buildFromPitchYawRoll(animPitch, animYaw, animRoll); _matrix *= _localMatrix; diff --git a/engines/grim/model.h b/engines/grim/model.h index 6a330369a95..259def6d04d 100644 --- a/engines/grim/model.h +++ b/engines/grim/model.h @@ -83,8 +83,6 @@ public: Graphics::Vector3d _animPos; float _animPitch, _animYaw, _animRoll; bool _meshVisible, _hierVisible; - int _priority; - float _totalWeight; bool _initialized; Graphics::Matrix4 _matrix; Graphics::Matrix4 _localMatrix;