don't recompute character vertices if animation is unchanged

This commit is contained in:
David Rose 2002-02-19 18:50:32 +00:00
parent 54c440e9e1
commit 2fc11e2a9f
12 changed files with 151 additions and 52 deletions

View File

@ -87,11 +87,19 @@ write_with_value(ostream &out, int indent_level) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: MovingPartBase::do_update // Function: MovingPartBase::do_update
// Access: Public, Virtual // Access: Public, Virtual
// Description: // Description: Recursively update this particular part and all of
// its descendents for the current frame. This is not
// really public and is not intended to be called
// directly; it is called from the top of the tree by
// PartBundle::update().
//
// The return value is true if any part has changed,
// false otherwise.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void MovingPartBase:: bool MovingPartBase::
do_update(PartBundle *root, PartGroup *parent, do_update(PartBundle *root, PartGroup *parent,
bool parent_changed, bool anim_changed) { bool parent_changed, bool anim_changed) {
bool any_changed = false;
bool needs_update = anim_changed; bool needs_update = anim_changed;
// See if any of the channel values have changed since last time. // See if any of the channel values have changed since last time.
@ -102,9 +110,9 @@ do_update(PartBundle *root, PartGroup *parent,
++bci) { ++bci) {
AnimControl *control = (*bci); AnimControl *control = (*bci);
int channel_index = control->get_channel_index(); int channel_index = control->get_channel_index();
nassertv(channel_index >= 0 && channel_index < (int)_channels.size()); nassertr(channel_index >= 0 && channel_index < (int)_channels.size(), false);
AnimChannelBase *channel = _channels[channel_index]; AnimChannelBase *channel = _channels[channel_index];
nassertv(channel != (AnimChannelBase*)0L); nassertr(channel != (AnimChannelBase*)0L, false);
needs_update = control->channel_has_changed(channel); needs_update = control->channel_has_changed(channel);
} }
@ -115,15 +123,19 @@ do_update(PartBundle *root, PartGroup *parent,
} }
if (parent_changed || needs_update) { if (parent_changed || needs_update) {
update_internals(parent, needs_update, parent_changed); any_changed = update_internals(parent, needs_update, parent_changed);
} }
// Now recurse. // Now recurse.
Children::iterator ci; Children::iterator ci;
for (ci = _children.begin(); ci != _children.end(); ++ci) { for (ci = _children.begin(); ci != _children.end(); ++ci) {
(*ci)->do_update(root, this, parent_changed || needs_update, if ((*ci)->do_update(root, this, parent_changed || needs_update,
anim_changed); anim_changed)) {
any_changed = true;
}
} }
return any_changed;
} }
@ -134,9 +146,13 @@ do_update(PartBundle *root, PartGroup *parent,
// some ancestor has changed values. It is a hook for // some ancestor has changed values. It is a hook for
// derived classes to update whatever cache they may // derived classes to update whatever cache they may
// have that depends on these. // have that depends on these.
//
// The return value is true if the part has changed as a
// result of the update, or false otherwise.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void MovingPartBase:: bool MovingPartBase::
update_internals(PartGroup *, bool, bool) { update_internals(PartGroup *, bool, bool) {
return false;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////

View File

@ -48,11 +48,11 @@ public:
virtual void write_with_value(ostream &out, int indent_level) const; virtual void write_with_value(ostream &out, int indent_level) const;
virtual void output_value(ostream &out) const=0; virtual void output_value(ostream &out) const=0;
virtual void do_update(PartBundle *root, PartGroup *parent, virtual bool do_update(PartBundle *root, PartGroup *parent,
bool parent_changed, bool anim_changed); bool parent_changed, bool anim_changed);
virtual void get_blend_value(const PartBundle *root)=0; virtual void get_blend_value(const PartBundle *root)=0;
virtual void update_internals(PartGroup *parent, bool self_changed, virtual bool update_internals(PartGroup *parent, bool self_changed,
bool parent_changed); bool parent_changed);
protected: protected:

View File

@ -344,11 +344,16 @@ advance_time(double time) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PartBundle::update // Function: PartBundle::update
// Access: Public // Access: Public
// Description: // Description: Updates all the parts in the bundle to reflect the
// data for the current frame (as set in each of the
// AnimControls).
//
// Returns true if any part has changed as a result of
// this, or false otherwise.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void PartBundle:: bool PartBundle::
update() { update() {
do_update(this, NULL, false, _anim_changed); bool any_changed = do_update(this, NULL, false, _anim_changed);
// Now update all the controls for next time. // Now update all the controls for next time.
ChannelBlend::const_iterator cbi; ChannelBlend::const_iterator cbi;
@ -357,6 +362,8 @@ update() {
control->mark_channels(); control->mark_channels();
} }
_anim_changed = false; _anim_changed = false;
return any_changed;
} }

View File

@ -122,7 +122,7 @@ public:
// bunch of friends. // bunch of friends.
void advance_time(double time); void advance_time(double time);
void update(); bool update();
virtual void control_activated(AnimControl *control); virtual void control_activated(AnimControl *control);
protected: protected:

View File

@ -350,15 +350,28 @@ write_with_value(ostream &out, int indent_level) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PartGroup::do_update // Function: PartGroup::do_update
// Access: Public, Virtual // Access: Public, Virtual
// Description: // Description: Recursively update this particular part and all of
// its descendents for the current frame. This is not
// really public and is not intended to be called
// directly; it is called from the top of the tree by
// PartBundle::update().
//
// The return value is true if any part has changed,
// false otherwise.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void PartGroup:: bool PartGroup::
do_update(PartBundle *root, PartGroup *, do_update(PartBundle *root, PartGroup *,
bool parent_changed, bool anim_changed) { bool parent_changed, bool anim_changed) {
bool any_changed = false;
Children::iterator ci; Children::iterator ci;
for (ci = _children.begin(); ci != _children.end(); ++ci) { for (ci = _children.begin(); ci != _children.end(); ++ci) {
(*ci)->do_update(root, this, parent_changed, anim_changed); if ((*ci)->do_update(root, this, parent_changed, anim_changed)) {
any_changed = true;
}
} }
return any_changed;
} }

View File

@ -82,7 +82,7 @@ public:
const PartGroup *parent, const PartGroup *parent,
int hierarchy_match_flags = 0) const; int hierarchy_match_flags = 0) const;
virtual void do_update(PartBundle *root, PartGroup *parent, virtual bool do_update(PartBundle *root, PartGroup *parent,
bool parent_changed, bool anim_changed); bool parent_changed, bool anim_changed);
protected: protected:

View File

@ -21,16 +21,16 @@
#include "computedVertices.h" #include "computedVertices.h"
#include "config_char.h" #include "config_char.h"
#include <geomNode.h> #include "geomNode.h"
#include <datagram.h> #include "datagram.h"
#include <datagramIterator.h> #include "datagramIterator.h"
#include <bamReader.h> #include "bamReader.h"
#include <bamWriter.h> #include "bamWriter.h"
#include <pStatTimer.h> #include "pStatTimer.h"
#include <geomNode.h> #include "geomNode.h"
#include <animControl.h> #include "animControl.h"
#include <clockObject.h> #include "clockObject.h"
#include <pStatTimer.h> #include "pStatTimer.h"
TypeHandle Character::_type_handle; TypeHandle Character::_type_handle;
@ -140,11 +140,14 @@ update() {
PStatTimer timer(_char_pcollector); PStatTimer timer(_char_pcollector);
// First, update all the joints and sliders. // First, update all the joints and sliders.
get_bundle()->update(); bool any_changed = get_bundle()->update();
// Now update the vertices. // Now update the vertices, if we need to. This is likely to be a
if (_computed_vertices != (ComputedVertices *)NULL) { // slow operation.
_computed_vertices->update(this); if (any_changed || even_animation) {
if (_computed_vertices != (ComputedVertices *)NULL) {
_computed_vertices->update(this);
}
} }
} }
@ -299,7 +302,7 @@ copy_geom(Geom *source, const Character *from) {
PT(Geom) dest = source; PT(Geom) dest = source;
source->get_coords(coords, index); source->get_coords(coords, index);
if ((coords != NULL) && (coords == (from->_cv._coords))) { if ((coords != (void *)NULL) && (coords == (from->_cv._coords))) {
if (dest == source) { if (dest == source) {
dest = source->make_copy(); dest = source->make_copy();
} }

View File

@ -19,16 +19,16 @@
#ifndef CHARACTER_H #ifndef CHARACTER_H
#define CHARACTER_H #define CHARACTER_H
#include <pandabase.h> #include "pandabase.h"
#include "computedVertices.h" #include "computedVertices.h"
#include <partBundleNode.h> #include "partBundleNode.h"
#include <namedNode.h> #include "namedNode.h"
#include <vector_PartGroupStar.h> #include "vector_PartGroupStar.h"
#include <pointerTo.h> #include "pointerTo.h"
#include <geom.h> #include "geom.h"
#include <pStatCollector.h> #include "pStatCollector.h"
class CharacterJointBundle; class CharacterJointBundle;
class ComputedVertices; class ComputedVertices;

View File

@ -84,22 +84,40 @@ make_copy() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: CharacterJoint::update_internals // Function: CharacterJoint::update_internals
// Access: Public, Virtual // Access: Public, Virtual
// Description: // Description: This is called by do_update() whenever the part or
// some ancestor has changed values. It is a hook for
// derived classes to update whatever cache they may
// have that depends on these.
//
// The return value is true if the part has changed as a
// result of the update, or false otherwise.
//
// In the case of a CharacterJoint, of course, it means
// to recompute the joint angles and associated
// transforms for this particular joint.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CharacterJoint:: bool CharacterJoint::
update_internals(PartGroup *parent, bool self_changed, bool) { update_internals(PartGroup *parent, bool self_changed, bool parent_changed) {
nassertv(parent != NULL); nassertr(parent != (PartGroup *)NULL, false);
bool net_changed = false; bool net_changed = false;
if (parent->is_of_type(CharacterJoint::get_class_type())) { if (parent->is_of_type(CharacterJoint::get_class_type())) {
CharacterJoint *parent_joint = DCAST(CharacterJoint, parent); // The joint is not a toplevel joint; its parent therefore affects
// its net transform.
if (parent_changed || self_changed) {
CharacterJoint *parent_joint = DCAST(CharacterJoint, parent);
_net_transform = _value * parent_joint->_net_transform;
net_changed = true;
}
_net_transform = _value * parent_joint->_net_transform; } else {
net_changed = true; // The joint *is* a toplevel joint, and the only thing that
// affects its net transform is the joint itself.
} else if (self_changed) { if (self_changed) {
_net_transform = _value; _net_transform = _value;
net_changed = true; net_changed = true;
}
} }
if (net_changed && !_net_transform_arcs.empty()) { if (net_changed && !_net_transform_arcs.empty()) {
@ -141,6 +159,8 @@ update_internals(PartGroup *parent, bool self_changed, bool) {
} }
} }
} }
return self_changed || net_changed;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////

View File

@ -43,7 +43,7 @@ public:
virtual PartGroup *make_copy() const; virtual PartGroup *make_copy() const;
virtual void update_internals(PartGroup *parent, bool self_changed, virtual bool update_internals(PartGroup *parent, bool self_changed,
bool parent_changed); bool parent_changed);
PUBLISHED: PUBLISHED:

View File

@ -31,6 +31,41 @@ Configure(config_char);
NotifyCategoryDef(char, ""); NotifyCategoryDef(char, "");
ConfigureFn(config_char) { ConfigureFn(config_char) {
init_libchar();
}
// The animation system, by default, only recomputes the characters'
// vertices on frames for which there is some change in the animation
// cycle. Since this doesn't happen every frame, and since the
// recomputing of vertices can take significant time, this might
// result in irregular patterns of slow frames and fast frames,
// resulting in an unsteady frame rate. Sometimes it is preferable to
// achieve a more even frame rate, even if the average frame rate is
// slower overall.
// Set even-animation to true to achieve this. When this is true,
// characters' vertices will be recomputed every frame, whether they
// need it or not. This will tend to balance out the frame rate so
// that it is more uniformly slow.
const bool even_animation = config_char.GetBool("even-animation", false);
////////////////////////////////////////////////////////////////////
// Function: init_libchar
// Description: Initializes the library. This must be called at
// least once before any of the functions or classes in
// this library can be used. Normally it will be
// called by the static initializers and need not be
// called explicitly, but special cases exist.
////////////////////////////////////////////////////////////////////
void
init_libchar() {
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
Character::init_type(); Character::init_type();
CharacterJoint::init_type(); CharacterJoint::init_type();
CharacterJointBundle::init_type(); CharacterJointBundle::init_type();

View File

@ -27,4 +27,9 @@
NotifyCategoryDecl(char, EXPCL_PANDA, EXPTP_PANDA); NotifyCategoryDecl(char, EXPCL_PANDA, EXPTP_PANDA);
#endif #endif
// Configure variables for char package.
extern const bool EXPCL_PANDA even_animation;
extern EXPCL_PANDA void init_libchar();
#endif #endif