GRIM: Apply animations in priority order.

This commit is contained in:
Joni Vähämäki 2011-05-24 19:47:39 +03:00
parent d1d0dfe909
commit 6a40b43f16
9 changed files with 215 additions and 118 deletions

View File

@ -1205,6 +1205,11 @@ void Actor::update() {
c->update();
}
for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
Costume *c = *i;
c->animate();
}
for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
Costume *c = *i;
c->moveHead(_lookingMode, _lookAtVector, _lookAtRate);

View File

@ -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<Model> _obj;
Model::HierNode *_hier;
Graphics::Matrix4 _matrix;
Common::List<AnimationEntry> *_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<AnimationEntry>();
}
// 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<AnimationEntry>::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<AnimationEntry>::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<AnimationEntry>::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<MainModelComponent *>(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<ModelComponent *>(_parent);
if (mc)
mc->addActiveAnimation(&_anim, _priority1, _priority2);
}
void KeyframeComponent::deactivate() {
ModelComponent *mc = dynamic_cast<ModelComponent *>(_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;
}

View File

@ -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() { }

View File

@ -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());

View File

@ -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());

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;