Browse Source

setLODAnimation

David Rose 17 years ago
parent
commit
f3106c89e2

+ 56 - 2
direct/src/actor/Actor.py

@@ -196,6 +196,8 @@ class Actor(DirectObject, NodePath):
         self.__subpartsComplete = False
         self.__subpartsComplete = False
 
 
         self.__LODNode = None
         self.__LODNode = None
+        self.__LODAnimation = None
+        self.__LODCenter = Point3(0, 0, 0)
         self.switches = None
         self.switches = None
 
 
         if (other == None):
         if (other == None):
@@ -723,8 +725,60 @@ class Actor(DirectObject, NodePath):
         return self.__hasLOD
         return self.__hasLOD
 
 
     def setCenter(self, center):
     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):
     def update(self, lod=0, partName=None, lodName=None, force=False):
         """ Updates all of the Actor's joints in the indicated LOD.
         """ Updates all of the Actor's joints in the indicated LOD.

+ 14 - 0
panda/src/chan/partBundle.I

@@ -248,3 +248,17 @@ get_control_effect(AnimControl *control) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
   return do_get_control_effect(control, cdata);
   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;
+}

+ 23 - 16
panda/src/chan/partBundle.cxx

@@ -54,6 +54,7 @@ PartBundle(const PartBundle &copy) :
   PartGroup(copy)
   PartGroup(copy)
 {
 {
   _anim_preload = copy._anim_preload;
   _anim_preload = copy._anim_preload;
+  _update_delay = 0.0;
 
 
   CDWriter cdata(_cycler, true);
   CDWriter cdata(_cycler, true);
   CDReader cdata_from(copy._cycler);
   CDReader cdata_from(copy._cycler);
@@ -74,6 +75,7 @@ PartBundle::
 PartBundle(const string &name) : 
 PartBundle(const string &name) : 
   PartGroup(name)
   PartGroup(name)
 {
 {
+  _update_delay = 0.0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -483,24 +485,27 @@ release_joint(const string &joint_name) {
 bool PartBundle::
 bool PartBundle::
 update() {
 update() {
   Thread *current_thread = Thread::get_current_thread();
   Thread *current_thread = Thread::get_current_thread();
-  bool anim_changed;
-  bool frame_blend_flag;
   CDWriter cdata(_cycler, false, current_thread);
   CDWriter cdata(_cycler, false, current_thread);
-  anim_changed = cdata->_anim_changed;
-  frame_blend_flag = cdata->_frame_blend_flag;
-
-  bool any_changed = do_update(this, cdata, NULL, false, anim_changed, 
-                               current_thread);
+  bool any_changed = false;
 
 
-  // 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);
+  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;
+
+    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;
   return any_changed;
 }
 }
@@ -860,6 +865,7 @@ CData() {
   _last_control_set = NULL;
   _last_control_set = NULL;
   _net_blend = 0.0f;
   _net_blend = 0.0f;
   _anim_changed = false;
   _anim_changed = false;
+  _last_update = 0.0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -876,7 +882,8 @@ CData(const PartBundle::CData &copy) :
   _last_control_set(copy._last_control_set),
   _last_control_set(copy._last_control_set),
   _blend(copy._blend),
   _blend(copy._blend),
   _net_blend(copy._net_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
   // Note that this copy constructor is not used by the PartBundle
   // copy constructor!  Any elements that must be copied between
   // copy constructor!  Any elements that must be copied between

+ 4 - 0
panda/src/chan/partBundle.h

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

+ 156 - 2
panda/src/char/character.cxx

@@ -25,6 +25,9 @@
 #include "animControl.h"
 #include "animControl.h"
 #include "clockObject.h"
 #include "clockObject.h"
 #include "pStatTimer.h"
 #include "pStatTimer.h"
+#include "camera.h"
+#include "cullTraverser.h"
+#include "cullTraverserData.h"
 
 
 TypeHandle Character::_type_handle;
 TypeHandle Character::_type_handle;
 
 
@@ -38,6 +41,11 @@ PStatCollector Character::_animation_pcollector("*:Animation");
 Character::
 Character::
 Character(const Character &copy, bool copy_bundles) :
 Character(const Character &copy, bool copy_bundles) :
   PartBundleNode(copy),
   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),
   _joints_pcollector(copy._joints_pcollector),
   _skinning_pcollector(copy._skinning_pcollector)
   _skinning_pcollector(copy._skinning_pcollector)
 {
 {
@@ -60,6 +68,8 @@ Character(const Character &copy, bool copy_bundles) :
     }
     }
   }    
   }    
   _last_auto_update = -1.0;
   _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")
   _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices")
 {
 {
   set_cull_callback();
   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.
 //               visible, or false if it should be culled.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Character::
 bool Character::
-cull_callback(CullTraverser *, CullTraverserData &) {
+cull_callback(CullTraverser *trav, CullTraverserData &data) {
   // For now, we update the character during the cull traversal; this
   // For now, we update the character during the cull traversal; this
   // prevents us from needlessly updating characters that aren't in
   // prevents us from needlessly updating characters that aren't in
   // the view frustum.  We may need a better way to do this
   // the view frustum.  We may need a better way to do this
   // optimization later, to handle characters that might animate
   // optimization later, to handle characters that might animate
   // themselves in front of the view frustum.
   // 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();
   update();
   return true;
   return true;
 }
 }
@@ -288,6 +332,71 @@ merge_bundles(PartBundleHandle *old_bundle_handle,
   update_bundle(old_bundle_handle, new_bundle);
   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
 //     Function: Character::find_joint
 //       Access: Published
 //       Access: Published
@@ -390,13 +499,13 @@ update() {
   if (now != _last_auto_update) {
   if (now != _last_auto_update) {
     _last_auto_update = now;
     _last_auto_update = now;
 
 
-    PStatTimer timer(_joints_pcollector);
     if (char_cat.is_spam()) {
     if (char_cat.is_spam()) {
       char_cat.spam() 
       char_cat.spam() 
         << "Animating " << NodePath::any_path(this)
         << "Animating " << NodePath::any_path(this)
         << " at time " << now << "\n";
         << " at time " << now << "\n";
     }
     }
     
     
+    PStatTimer timer(_joints_pcollector);
     do_update();
     do_update();
   }
   }
 }
 }
@@ -495,6 +604,37 @@ update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
   r_update_geom(this, joint_map, gvmap, gjmap, gsmap);
   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
 //     Function: Character::do_update
 //       Access: Private
 //       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
 //     Function: Character::fill_joint_map
 //       Access: Private
 //       Access: Private

+ 16 - 0
panda/src/char/character.h

@@ -66,6 +66,11 @@ PUBLISHED:
   void merge_bundles(PartBundleHandle *old_bundle_handle, 
   void merge_bundles(PartBundleHandle *old_bundle_handle, 
                      PartBundleHandle *other_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;
   CharacterJoint *find_joint(const string &name) const;
   CharacterSlider *find_slider(const string &name) const;
   CharacterSlider *find_slider(const string &name) const;
 
 
@@ -81,9 +86,11 @@ protected:
                                Thread *current_thread);
                                Thread *current_thread);
   virtual void update_bundle(PartBundleHandle *old_bundle_handle, 
   virtual void update_bundle(PartBundleHandle *old_bundle_handle, 
                              PartBundle *new_bundle);
                              PartBundle *new_bundle);
+  CPT(TransformState) get_rel_transform(CullTraverser *trav, CullTraverserData &data);
 
 
 private:
 private:
   void do_update();
   void do_update();
+  void set_lod_current_delay(double delay);
 
 
   typedef pmap<const PandaNode *, PandaNode *> NodeMap;
   typedef pmap<const PandaNode *, PandaNode *> NodeMap;
   typedef pmap<const PartGroup *, PartGroup *> JointMap;
   typedef pmap<const PartGroup *, PartGroup *> JointMap;
@@ -129,6 +136,15 @@ private:
 
 
   double _last_auto_update;
   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
   // Statistics
   PStatCollector _joints_pcollector;
   PStatCollector _joints_pcollector;
   PStatCollector _skinning_pcollector;
   PStatCollector _skinning_pcollector;