setLODAnimation
This commit is contained in:
parent
cafd7c93f9
commit
f3106c89e2
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ PartBundle(const PartBundle ©) :
|
|||
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 ©) :
|
|||
_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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ©, 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 ©, 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 ¢er,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ PUBLISHED:
|
|||
void merge_bundles(PartBundleHandle *old_bundle_handle,
|
||||
PartBundleHandle *other_bundle_handle);
|
||||
|
||||
void set_lod_animation(const LPoint3f ¢er,
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue