setLODAnimation

This commit is contained in:
David Rose 2008-12-15 22:44:33 +00:00
parent cafd7c93f9
commit f3106c89e2
6 changed files with 268 additions and 19 deletions

View File

@ -196,6 +196,8 @@ class Actor(DirectObject, NodePath):
self.__subpartsComplete = False
self.__LODNode = None
self.__LODAnimation = None
self.__LODCenter = Point3(0, 0, 0)
self.switches = None
if (other == None):
@ -723,8 +725,60 @@ class Actor(DirectObject, NodePath):
return self.__hasLOD
def setCenter(self, center):
if center != None:
self.__LODNode.node().setCenter(center)
if center == None:
center = Point3(0, 0, 0)
self.__LODCenter = center
if self.__LODNode:
self.__LODNode.node().setCenter(self.__LODCenter)
if self.__LODAnimation:
self.setLODAnimation(*self.__LODAnimation)
def setLODAnimation(self, farDistance, nearDistance, delayFactor):
""" Activates a special mode in which the Actor animates less
frequently as it gets further from the camera. This is
intended as a simple optimization to minimize the effort of
computing animation for lots of characters that may not
necessarily be very important to animate every frame.
If the character is closer to the camera than near_distance,
then it is animated its normal rate, every frame. If the
character is exactly far_distance away, it is animated only
every delay_factor seconds (which should be a number greater
than 0). If the character is between near_distance and
far_distance, its animation rate is linearly interpolated
according to its distance between the two. The interpolation
function continues beyond far_distance, so that the character
is animated increasingly less frequently as it gets farther
away. """
self.__LODAnimation = (farDistance, nearDistance, delayFactor)
# Temporary hasattr for old Panda.
if not hasattr(Character, 'setLodAnimation'):
return
for lodData in self.__partBundleDict.values():
for partData in lodData.values():
char = partData.partBundleNP
char.node().setLodAnimation(self.__LODCenter, farDistance, nearDistance, delayFactor)
def clearLODAnimation(self):
""" Description: Undoes the effect of a recent call to
set_lod_animation(). Henceforth, the character will animate
every frame, regardless of its distance from the camera.
"""
self.__LODAnimation = None
# Temporary hasattr for old Panda.
if not hasattr(Character, 'setLodAnimation'):
return
for lodData in self.__partBundleDict.values():
for partData in lodData.values():
char = partData.partBundleNP
char.node().clearLodAnimation()
def update(self, lod=0, partName=None, lodName=None, force=False):
""" Updates all of the Actor's joints in the indicated LOD.

View File

@ -248,3 +248,17 @@ get_control_effect(AnimControl *control) const {
CDReader cdata(_cycler);
return do_get_control_effect(control, cdata);
}
////////////////////////////////////////////////////////////////////
// Function: PartBundle::set_update_delay
// Access: Public
// Description: Specifies the minimum amount of time, in seconds,
// that should elapse between any two consecutive
// updates. This is normally used by
// Character::set_lod_animation(), and should not be
// called directly.
////////////////////////////////////////////////////////////////////
INLINE void PartBundle::
set_update_delay(double delay) {
_update_delay = delay;
}

View File

@ -54,6 +54,7 @@ PartBundle(const PartBundle &copy) :
PartGroup(copy)
{
_anim_preload = copy._anim_preload;
_update_delay = 0.0;
CDWriter cdata(_cycler, true);
CDReader cdata_from(copy._cycler);
@ -74,6 +75,7 @@ PartBundle::
PartBundle(const string &name) :
PartGroup(name)
{
_update_delay = 0.0;
}
////////////////////////////////////////////////////////////////////
@ -483,24 +485,27 @@ release_joint(const string &joint_name) {
bool PartBundle::
update() {
Thread *current_thread = Thread::get_current_thread();
bool anim_changed;
bool frame_blend_flag;
CDWriter cdata(_cycler, false, current_thread);
anim_changed = cdata->_anim_changed;
frame_blend_flag = cdata->_frame_blend_flag;
bool any_changed = false;
bool any_changed = do_update(this, cdata, NULL, false, anim_changed,
current_thread);
double now = ClockObject::get_global_clock()->get_frame_time(current_thread);
if (now > cdata->_last_update + _update_delay || cdata->_anim_changed) {
bool anim_changed = cdata->_anim_changed;
bool frame_blend_flag = cdata->_frame_blend_flag;
// Now update all the controls for next time.
// CDWriter cdata(_cycler, false, current_thread);
ChannelBlend::const_iterator cbi;
for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
AnimControl *control = (*cbi).first;
control->mark_channels(frame_blend_flag);
any_changed = do_update(this, cdata, NULL, false, anim_changed,
current_thread);
// Now update all the controls for next time.
ChannelBlend::const_iterator cbi;
for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
AnimControl *control = (*cbi).first;
control->mark_channels(frame_blend_flag);
}
cdata->_anim_changed = false;
cdata->_last_update = now;
}
cdata->_anim_changed = false;
return any_changed;
}
@ -860,6 +865,7 @@ CData() {
_last_control_set = NULL;
_net_blend = 0.0f;
_anim_changed = false;
_last_update = 0.0;
}
////////////////////////////////////////////////////////////////////
@ -876,7 +882,8 @@ CData(const PartBundle::CData &copy) :
_last_control_set(copy._last_control_set),
_blend(copy._blend),
_net_blend(copy._net_blend),
_anim_changed(copy._anim_changed)
_anim_changed(copy._anim_changed),
_last_update(copy._last_update)
{
// Note that this copy constructor is not used by the PartBundle
// copy constructor! Any elements that must be copied between

View File

@ -147,6 +147,7 @@ public:
// interface; they're just public so we don't have to declare a
// bunch of friends.
virtual void control_activated(AnimControl *control);
INLINE void set_update_delay(double delay);
bool do_bind_anim(AnimControl *control, AnimBundle *anim,
int hierarchy_match_flags, const PartSubset &subset);
@ -171,6 +172,8 @@ private:
typedef pmap<WCPT(TransformState), WPT(PartBundle) > AppliedTransforms;
AppliedTransforms _applied_transforms;
double _update_delay;
// This is the data that must be cycled between pipeline stages.
class CData : public CycleData {
public:
@ -192,6 +195,7 @@ private:
ChannelBlend _blend;
float _net_blend;
bool _anim_changed;
double _last_update;
};
PipelineCycler<CData> _cycler;

View File

@ -25,6 +25,9 @@
#include "animControl.h"
#include "clockObject.h"
#include "pStatTimer.h"
#include "camera.h"
#include "cullTraverser.h"
#include "cullTraverserData.h"
TypeHandle Character::_type_handle;
@ -38,6 +41,11 @@ PStatCollector Character::_animation_pcollector("*:Animation");
Character::
Character(const Character &copy, bool copy_bundles) :
PartBundleNode(copy),
_lod_center(copy._lod_center),
_lod_far_distance(copy._lod_far_distance),
_lod_near_distance(copy._lod_near_distance),
_lod_delay_factor(copy._lod_delay_factor),
_do_lod_animation(copy._do_lod_animation),
_joints_pcollector(copy._joints_pcollector),
_skinning_pcollector(copy._skinning_pcollector)
{
@ -60,6 +68,8 @@ Character(const Character &copy, bool copy_bundles) :
}
}
_last_auto_update = -1.0;
_view_frame = -1;
_view_distance2 = 0.0f;
}
////////////////////////////////////////////////////////////////////
@ -74,6 +84,10 @@ Character(const string &name) :
_skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices")
{
set_cull_callback();
clear_lod_animation();
_last_auto_update = -1.0;
_view_frame = -1;
_view_distance2 = 0.0f;
}
////////////////////////////////////////////////////////////////////
@ -174,12 +188,42 @@ combine_with(PandaNode *other) {
// visible, or false if it should be culled.
////////////////////////////////////////////////////////////////////
bool Character::
cull_callback(CullTraverser *, CullTraverserData &) {
cull_callback(CullTraverser *trav, CullTraverserData &data) {
// For now, we update the character during the cull traversal; this
// prevents us from needlessly updating characters that aren't in
// the view frustum. We may need a better way to do this
// optimization later, to handle characters that might animate
// themselves in front of the view frustum.
if (_do_lod_animation) {
int this_frame = ClockObject::get_global_clock()->get_frame_count();
CPT(TransformState) rel_transform = get_rel_transform(trav, data);
LPoint3f center = _lod_center * rel_transform->get_mat();
float dist2 = center.dot(center);
if (this_frame != _view_frame || dist2 < _view_distance2) {
_view_frame = this_frame;
_view_distance2 = dist2;
// Now compute the lod delay.
float dist = sqrt(dist2);
double delay = 0.0;
if (dist > _lod_near_distance) {
delay = _lod_delay_factor * (dist - _lod_near_distance) / (_lod_far_distance - _lod_near_distance);
nassertr(delay > 0.0, false);
}
set_lod_current_delay(delay);
if (char_cat.is_spam()) {
char_cat.spam()
<< "Distance to " << NodePath::any_path(this) << " in frame "
<< this_frame << " is " << dist << ", computed delay is " << delay
<< "\n";
}
}
}
update();
return true;
}
@ -288,6 +332,71 @@ merge_bundles(PartBundleHandle *old_bundle_handle,
update_bundle(old_bundle_handle, new_bundle);
}
////////////////////////////////////////////////////////////////////
// Function: Character::set_lod_animation
// Access: Published
// Description: Activates a special mode in which the character
// animates less frequently as it gets further from the
// camera. This is intended as a simple optimization to
// minimize the effort of computing animation for lots
// of characters that may not necessarily be very
// important to animate every frame.
//
// If the character is closer to the camera than
// near_distance, then it is animated its normal rate,
// every frame. If the character is exactly
// far_distance away, it is animated only every
// delay_factor seconds (which should be a number
// greater than 0). If the character is between
// near_distance and far_distance, its animation rate is
// linearly interpolated according to its distance
// between the two. The interpolation function
// continues beyond far_distance, so that the character
// is animated increasingly less frequently as it gets
// farther away.
//
// The distance calculations are made from center, which
// is a fixed point relative to the character node, to
// the camera's lod center or cull center node (or to
// the camera node itself).
//
// If multiple cameras are viewing the character in any
// given frame, the closest one counts.
////////////////////////////////////////////////////////////////////
void Character::
set_lod_animation(const LPoint3f &center,
float far_distance, float near_distance,
float delay_factor) {
nassertv(far_distance >= near_distance);
nassertv(delay_factor >= 0.0f);
_lod_center = center;
_lod_far_distance = far_distance;
_lod_near_distance = near_distance;
_lod_delay_factor = delay_factor;
_do_lod_animation = (_lod_far_distance > _lod_near_distance && _lod_delay_factor > 0.0);
if (!_do_lod_animation) {
set_lod_current_delay(0.0);
}
}
////////////////////////////////////////////////////////////////////
// Function: Character::clear_lod_animation
// Access: Published
// Description: Undoes the effect of a recent call to
// set_lod_animation(). Henceforth, the character will
// animate every frame, regardless of its distance from
// the camera.
////////////////////////////////////////////////////////////////////
void Character::
clear_lod_animation() {
_lod_center = LPoint3f::zero();
_lod_far_distance = 0.0f;
_lod_near_distance = 0.0f;
_lod_delay_factor = 0.0f;
_do_lod_animation = false;
set_lod_current_delay(0.0);
}
////////////////////////////////////////////////////////////////////
// Function: Character::find_joint
// Access: Published
@ -390,13 +499,13 @@ update() {
if (now != _last_auto_update) {
_last_auto_update = now;
PStatTimer timer(_joints_pcollector);
if (char_cat.is_spam()) {
char_cat.spam()
<< "Animating " << NodePath::any_path(this)
<< " at time " << now << "\n";
}
PStatTimer timer(_joints_pcollector);
do_update();
}
}
@ -495,6 +604,37 @@ update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
r_update_geom(this, joint_map, gvmap, gjmap, gsmap);
}
////////////////////////////////////////////////////////////////////
// Function: Character::get_rel_transform
// Access: Protected
// Description: Returns the relative transform to convert from the
// LODNode space to the camera space.
////////////////////////////////////////////////////////////////////
CPT(TransformState) Character::
get_rel_transform(CullTraverser *trav, CullTraverserData &data) {
// Get a pointer to the camera node.
Camera *camera = trav->get_scene()->get_camera_node();
// Get the camera space transform.
CPT(TransformState) rel_transform;
NodePath lod_center = camera->get_lod_center();
if (!lod_center.is_empty()) {
rel_transform =
lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
} else {
NodePath cull_center = camera->get_cull_center();
if (!cull_center.is_empty()) {
rel_transform =
cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
} else {
rel_transform = data.get_modelview_transform(trav);
}
}
return rel_transform;
}
////////////////////////////////////////////////////////////////////
// Function: Character::do_update
// Access: Private
@ -517,6 +657,20 @@ do_update() {
}
}
////////////////////////////////////////////////////////////////////
// Function: Character::set_lod_current_delay
// Access: Private
// Description: Changes the amount of delay we should impose due to
// the LOD animation setting.
////////////////////////////////////////////////////////////////////
void Character::
set_lod_current_delay(double delay) {
int num_bundles = get_num_bundles();
for (int i = 0; i < num_bundles; ++i) {
get_bundle(i)->set_update_delay(delay);
}
}
////////////////////////////////////////////////////////////////////
// Function: Character::fill_joint_map
// Access: Private

View File

@ -66,6 +66,11 @@ PUBLISHED:
void merge_bundles(PartBundleHandle *old_bundle_handle,
PartBundleHandle *other_bundle_handle);
void set_lod_animation(const LPoint3f &center,
float far_distance, float near_distance,
float delay_factor);
void clear_lod_animation();
CharacterJoint *find_joint(const string &name) const;
CharacterSlider *find_slider(const string &name) const;
@ -81,9 +86,11 @@ protected:
Thread *current_thread);
virtual void update_bundle(PartBundleHandle *old_bundle_handle,
PartBundle *new_bundle);
CPT(TransformState) get_rel_transform(CullTraverser *trav, CullTraverserData &data);
private:
void do_update();
void set_lod_current_delay(double delay);
typedef pmap<const PandaNode *, PandaNode *> NodeMap;
typedef pmap<const PartGroup *, PartGroup *> JointMap;
@ -129,6 +136,15 @@ private:
double _last_auto_update;
int _view_frame;
double _view_distance2;
LPoint3f _lod_center;
float _lod_far_distance;
float _lod_near_distance;
float _lod_delay_factor;
bool _do_lod_animation;
// Statistics
PStatCollector _joints_pcollector;
PStatCollector _skinning_pcollector;