Browse Source

support common transforming of shared PartBundles

David Rose 18 years ago
parent
commit
7c9997118c

+ 3 - 0
panda/src/chan/Sources.pp

@@ -26,6 +26,7 @@
     movingPartBase.I movingPartBase.h  \
     movingPartBase.I movingPartBase.h  \
     movingPartMatrix.I movingPartMatrix.h movingPartScalar.I  \
     movingPartMatrix.I movingPartMatrix.h movingPartScalar.I  \
     movingPartScalar.h partBundle.I partBundle.N partBundle.h  \
     movingPartScalar.h partBundle.I partBundle.N partBundle.h  \
+    partBundleHandle.I partBundleHandle.h \
     partBundleNode.I partBundleNode.h \
     partBundleNode.I partBundleNode.h \
     partGroup.I partGroup.h  \
     partGroup.I partGroup.h  \
     partSubset.I partSubset.h \
     partSubset.I partSubset.h \
@@ -45,6 +46,7 @@
     animControlCollection.cxx animGroup.cxx auto_bind.cxx  \
     animControlCollection.cxx animGroup.cxx auto_bind.cxx  \
     config_chan.cxx movingPartBase.cxx movingPartMatrix.cxx  \
     config_chan.cxx movingPartBase.cxx movingPartMatrix.cxx  \
     movingPartScalar.cxx partBundle.cxx \
     movingPartScalar.cxx partBundle.cxx \
+    partBundleHandle.cxx \
     partBundleNode.cxx \
     partBundleNode.cxx \
     partGroup.cxx \
     partGroup.cxx \
     partSubset.cxx \
     partSubset.cxx \
@@ -66,6 +68,7 @@
     movingPart.I movingPart.h movingPartBase.I \
     movingPart.I movingPart.h movingPartBase.I \
     movingPartBase.h movingPartMatrix.I movingPartMatrix.h \
     movingPartBase.h movingPartMatrix.I movingPartMatrix.h \
     movingPartScalar.I movingPartScalar.h partBundle.I partBundle.h \
     movingPartScalar.I movingPartScalar.h partBundle.I partBundle.h \
+    partBundleHandle.I partBundleHandle.h \
     partBundleNode.I partBundleNode.h \
     partBundleNode.I partBundleNode.h \
     partGroup.I partGroup.h \
     partGroup.I partGroup.h \
     partSubset.I partSubset.h \
     partSubset.I partSubset.h \

+ 40 - 0
panda/src/chan/partBundle.cxx

@@ -124,6 +124,46 @@ set_anim_blend_flag(bool anim_blend_flag) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundle::apply_transform
+//       Access: Published
+//  Description: Returns a PartBundle that is a duplicate of this one,
+//               but with the indicated transform applied.  If this is
+//               called multiple times with the same TransformState
+//               pointer, it returns the same PartBundle each time.
+////////////////////////////////////////////////////////////////////
+PT(PartBundle) PartBundle::
+apply_transform(const TransformState *transform) {
+  if (transform->is_identity()) {
+    // Trivial no-op.
+    return this;
+  }
+
+  AppliedTransforms::iterator ati = _applied_transforms.find(transform);
+  if (ati != _applied_transforms.end()) {
+    if ((*ati).first.is_valid_pointer() &&
+        (*ati).second.is_valid_pointer()) {
+      // Here's our cached result.
+      return (*ati).second.p();
+    }
+  }
+
+  PT(PartBundle) new_bundle = DCAST(PartBundle, copy_subgraph());
+  new_bundle->xform(transform->get_mat());
+
+  if (ati != _applied_transforms.end()) {
+    // A stale pointer to a deleted result.  Update it.
+    (*ati).first.refresh();
+    (*ati).second = new_bundle;
+  } else {
+    // No such result yet.  Store it.
+    bool inserted = _applied_transforms.insert(AppliedTransforms::value_type(transform, new_bundle)).second;
+    nassertr(inserted, new_bundle);
+  }
+
+  return new_bundle;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PartBundle::clear_control_effects
 //     Function: PartBundle::clear_control_effects
 //       Access: Published
 //       Access: Published

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

@@ -32,6 +32,8 @@
 #include "cycleDataWriter.h"
 #include "cycleDataWriter.h"
 #include "luse.h"
 #include "luse.h"
 #include "pvector.h"
 #include "pvector.h"
+#include "transformState.h"
+#include "weakPointerTo.h"
 
 
 class AnimBundle;
 class AnimBundle;
 class PartBundleNode;
 class PartBundleNode;
@@ -106,6 +108,7 @@ PUBLISHED:
   INLINE void set_root_xform(const LMatrix4f &root_xform);
   INLINE void set_root_xform(const LMatrix4f &root_xform);
   INLINE void xform(const LMatrix4f &mat);
   INLINE void xform(const LMatrix4f &mat);
   INLINE const LMatrix4f &get_root_xform() const;
   INLINE const LMatrix4f &get_root_xform() const;
+  PT(PartBundle) apply_transform(const TransformState *transform);
 
 
   INLINE int get_num_nodes() const;
   INLINE int get_num_nodes() const;
   INLINE PartBundleNode *get_node(int n) const;
   INLINE PartBundleNode *get_node(int n) const;
@@ -149,6 +152,9 @@ private:
   typedef pvector<PartBundleNode *> Nodes;
   typedef pvector<PartBundleNode *> Nodes;
   Nodes _nodes;
   Nodes _nodes;
 
 
+  typedef pmap<WCPT(TransformState), WPT(PartBundle) > AppliedTransforms;
+  AppliedTransforms _applied_transforms;
+
   // 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:

+ 60 - 0
panda/src/chan/partBundleHandle.I

@@ -0,0 +1,60 @@
+// Filename: partBundleHandle.I
+// Created by:  drose (01Oct07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundleHandle::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PartBundleHandle::
+PartBundleHandle(PartBundle *bundle) :
+  _bundle(bundle)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundleHandle::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PartBundleHandle::
+~PartBundleHandle() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundleHandle::get_bundle
+//       Access: Published
+//  Description: Returns the actual PartBundle embedded within the
+//               handle.
+////////////////////////////////////////////////////////////////////
+INLINE PartBundle *PartBundleHandle::
+get_bundle() {
+  return _bundle;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundleHandle::set_bundle
+//       Access: Published
+//  Description: Changes the actual PartBundle embedded within the
+//               handle.
+////////////////////////////////////////////////////////////////////
+INLINE void PartBundleHandle::
+set_bundle(PartBundle *bundle) {
+  _bundle = bundle;
+}

+ 19 - 0
panda/src/chan/partBundleHandle.cxx

@@ -0,0 +1,19 @@
+// Filename: partBundleHandle.cxx
+// Created by:  drose (01Oct07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "partBundleHandle.h"

+ 57 - 0
panda/src/chan/partBundleHandle.h

@@ -0,0 +1,57 @@
+// Filename: partBundleHandle.h
+// Created by:  drose (01Oct07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PARTBUNDLEHANDLE_H
+#define PARTBUNDLEHANDLE_H
+
+#include "pandabase.h"
+
+#include "partBundle.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PartBundleHandle
+// Description : This is a trivial class returned by
+//               PartBundleNode::get_bundle().  Its purpose is to hold
+//               the actual PartBundle pointer contained within the
+//               PartBundleNode, so that scene graph flatten
+//               operations can safely combine or duplicate
+//               PartBundles as necessary without affecting high-level
+//               bundle operations.
+//
+//               The high-level Actor class defined in
+//               direct/src/actor, for instance, will store a list of
+//               PartBundleHandles instead of on actual PartBundles,
+//               so that it will be immune to changes from these
+//               flatten operations.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_CHAN PartBundleHandle : public ReferenceCount {
+PUBLISHED:
+  INLINE PartBundleHandle(PartBundle *bundle);
+  INLINE ~PartBundleHandle();
+
+  INLINE PartBundle *get_bundle();
+  INLINE void set_bundle(PartBundle *bundle);
+
+private:
+  PT(PartBundle) _bundle;
+};
+
+#include "partBundleHandle.I"
+
+#endif

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

@@ -73,6 +73,20 @@ get_num_bundles() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PartBundle *PartBundleNode::
 INLINE PartBundle *PartBundleNode::
 get_bundle(int n) const {
 get_bundle(int n) const {
+  nassertr(n >= 0 && n < (int)_bundles.size(), NULL);
+  return _bundles[n]->get_bundle();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundleNode::get_bundle_handle
+//       Access: Public
+//  Description: Returns the PartBundleHandle that wraps around the
+//               actual nth PartBundle.  While the PartBundle pointer
+//               might later change due to a future flatten operation,
+//               the PartBundleHandle will not.
+////////////////////////////////////////////////////////////////////
+INLINE PartBundleHandle *PartBundleNode::
+get_bundle_handle(int n) const {
   nassertr(n >= 0 && n < (int)_bundles.size(), NULL);
   nassertr(n >= 0 && n < (int)_bundles.size(), NULL);
   return _bundles[n];
   return _bundles[n];
 }
 }

+ 79 - 26
panda/src/chan/partBundleNode.cxx

@@ -21,6 +21,7 @@
 #include "datagramIterator.h"
 #include "datagramIterator.h"
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "bamWriter.h"
+#include "sceneGraphReducer.h"
 
 
 TypeHandle PartBundleNode::_type_handle;
 TypeHandle PartBundleNode::_type_handle;
 
 
@@ -33,29 +34,34 @@ PartBundleNode::
 ~PartBundleNode() {
 ~PartBundleNode() {
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
-    (*bi)->remove_node(this);
+    (*bi)->get_bundle()->remove_node(this);
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PartBundleNode::safe_to_transform
+//     Function: PartBundleNode::apply_attribs_to_vertices
 //       Access: Public, Virtual
 //       Access: Public, Virtual
-//  Description: Returns true if it is generally safe to transform
-//               this particular kind of PandaNode by calling the
-//               xform() method, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool PartBundleNode::
-safe_to_transform() const {
-  // If any of our bundles appear on multiple nodes, we can't
-  // transform any of them without transforming all of them at once.
-  Bundles::const_iterator bi;
-  for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
-    if ((*bi)->get_num_nodes() > 1) {
-      return false;
+//  Description: Applies whatever attributes are specified in the
+//               AccumulatedAttribs object (and by the attrib_types
+//               bitmask) to the vertices on this node, if
+//               appropriate.  If this node uses geom arrays like a
+//               GeomNode, the supplied GeomTransformer may be used to
+//               unify shared arrays across multiple different nodes.
+//
+//               This is a generalization of xform().
+////////////////////////////////////////////////////////////////////
+void PartBundleNode::
+apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
+                          GeomTransformer &transformer) {
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    Bundles::iterator bi;
+    for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
+      PT(PartBundleHandle) handle = (*bi);
+      PartBundle *bundle = handle->get_bundle();
+      PT(PartBundle) new_bundle = bundle->apply_transform(attribs._transform);
+      update_bundle(handle, new_bundle);
     }
     }
   }
   }
-
-  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -67,6 +73,10 @@ safe_to_transform() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PartBundleNode::
 void PartBundleNode::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
+  // With plain xform(), we can't attempt to share bundles across
+  // different nodes.  Better to use apply_attribs_to_vertices(),
+  // instead.
+
   if (mat.almost_equal(LMatrix4f::ident_mat())) {
   if (mat.almost_equal(LMatrix4f::ident_mat())) {
     // Don't bother.
     // Don't bother.
     return;
     return;
@@ -74,7 +84,14 @@ xform(const LMatrix4f &mat) {
 
 
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
-    (*bi)->xform(mat);
+    PT(PartBundleHandle) handle = (*bi);
+    PartBundle *bundle = handle->get_bundle();
+    if (bundle->get_num_nodes() > 1) {
+      PT(PartBundle) new_bundle = DCAST(PartBundle, bundle->copy_subgraph());
+      update_bundle(handle, new_bundle);
+      bundle = new_bundle;
+    }
+    bundle->xform(mat);
   }
   }
 }
 }
 
 
@@ -85,8 +102,25 @@ xform(const LMatrix4f &mat) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PartBundleNode::
 void PartBundleNode::
 add_bundle(PartBundle *bundle) {
 add_bundle(PartBundle *bundle) {
-  _bundles.push_back(bundle);
-  bundle->add_node(this);
+  PT(PartBundleHandle) handle = new PartBundleHandle(bundle);
+  add_bundle_handle(handle);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundleNode::add_bundle_handle
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PartBundleNode::
+add_bundle_handle(PartBundleHandle *handle) {
+  Bundles::iterator bi = find(_bundles.begin(), _bundles.end(), handle);
+  if (bi != _bundles.end()) {
+    // This handle is already within the node.
+    return;
+  }
+
+  _bundles.push_back(handle);
+  handle->get_bundle()->add_node(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -97,16 +131,34 @@ add_bundle(PartBundle *bundle) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PartBundleNode::
 void PartBundleNode::
 steal_bundles(PartBundleNode *other) {
 steal_bundles(PartBundleNode *other) {
+  if (other == this) {
+    return;
+  }
+
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = other->_bundles.begin(); bi != other->_bundles.end(); ++bi) {
   for (bi = other->_bundles.begin(); bi != other->_bundles.end(); ++bi) {
-    PartBundle *bundle = (*bi);
-    _bundles.push_back(bundle);
-    bundle->remove_node(other);
-    bundle->add_node(this);
+    PartBundleHandle *handle = (*bi);
+    handle->get_bundle()->remove_node(other);
+    add_bundle_handle(handle);
   }
   }
   other->_bundles.clear();
   other->_bundles.clear();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundleNode::update_bundle
+//       Access: Protected, Virtual
+//  Description: Replaces the contents of the indicated
+//               PartBundleHandle (presumably stored within this node)
+//               with new_bundle.
+////////////////////////////////////////////////////////////////////
+void PartBundleNode::
+update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
+  PartBundle *old_bundle = old_bundle_handle->get_bundle();
+  old_bundle->remove_node(this);
+  old_bundle_handle->set_bundle(new_bundle);
+  new_bundle->add_node(this);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PartBundleNode::write_datagram
 //     Function: PartBundleNode::write_datagram
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -120,7 +172,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   dg.add_uint16(_bundles.size());
   dg.add_uint16(_bundles.size());
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
-    manager->write_pointer(dg, (*bi));
+    manager->write_pointer(dg, (*bi)->get_bundle());
   }
   }
 }
 }
 
 
@@ -137,8 +189,9 @@ complete_pointers(TypedWritable **p_list, BamReader* manager) {
 
 
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
-    (*bi) = DCAST(PartBundle, p_list[pi++]);
-    (*bi)->add_node(this);
+    PT(PartBundle) bundle = DCAST(PartBundle, p_list[pi++]);
+    bundle->add_node(this);
+    (*bi) = new PartBundleHandle(bundle);
   }
   }
 
 
   return pi;
   return pi;

+ 9 - 2
panda/src/chan/partBundleNode.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "partBundle.h"
 #include "partBundle.h"
+#include "partBundleHandle.h"
 
 
 #include "pandaNode.h"
 #include "pandaNode.h"
 #include "dcast.h"
 #include "dcast.h"
@@ -43,19 +44,25 @@ protected:
 
 
 public:
 public:
   virtual ~PartBundleNode();
   virtual ~PartBundleNode();
-  virtual bool safe_to_transform() const;
+  virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs,
+                                         int attrib_types,
+                                         GeomTransformer &transformer);
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);
 
 
 PUBLISHED:
 PUBLISHED:
   INLINE int get_num_bundles() const;
   INLINE int get_num_bundles() const;
   INLINE PartBundle *get_bundle(int n) const;
   INLINE PartBundle *get_bundle(int n) const;
+  INLINE PartBundleHandle *get_bundle_handle(int n) const;
 
 
 protected:
 protected:
   void add_bundle(PartBundle *bundle);
   void add_bundle(PartBundle *bundle);
+  void add_bundle_handle(PartBundleHandle *handle);
   void steal_bundles(PartBundleNode *other);
   void steal_bundles(PartBundleNode *other);
+  virtual void update_bundle(PartBundleHandle *old_bundle_handle, 
+                             PartBundle *new_bundle);
 
 
 protected:
 protected:
-  typedef pvector< PT(PartBundle) > Bundles;
+  typedef pvector< PT(PartBundleHandle) > Bundles;
   Bundles _bundles;
   Bundles _bundles;
 
 
 public:
 public:

+ 90 - 50
panda/src/char/character.cxx

@@ -230,6 +230,43 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
 //               pointer, and all geometry within this node will be
 //               pointer, and all geometry within this node will be
 //               updated to reference new_bundle.
 //               updated to reference new_bundle.
 //
 //
+//               This method is deprecated.  Use the newer version of
+//               this method, below.
+////////////////////////////////////////////////////////////////////
+void Character::
+merge_bundles(PartBundle *old_bundle, PartBundle *new_bundle) {
+  if (old_bundle == new_bundle) {
+    // Trivially return.
+    return;
+  }
+
+  // Find the PartBundleHandle of old_bundle.
+  PT(PartBundleHandle) old_bundle_handle;
+  Bundles::const_iterator bi;
+  for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
+    if ((*bi)->get_bundle() == old_bundle) {
+      old_bundle_handle = (*bi);
+      break;
+    }
+  }
+  nassertv(!old_bundle_handle.is_null());
+
+  PT(PartBundleHandle) new_bundle_handle = new PartBundleHandle(new_bundle);
+  merge_bundles(old_bundle_handle, new_bundle_handle);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Character::merge_bundles
+//       Access: Published
+//  Description: Merges old_bundle_handle->get_bundle() with
+//               new_bundle.  old_bundle_handle must be one of the
+//               PartBundleHandle within this node.  At the end of
+//               this call, the bundle pointer within the
+//               old_bundle_handle will be replaced with that within
+//               the new_bundle_handle pointer, and all geometry
+//               within this node will be updated to reference
+//               new_bundle.
+//
 //               Normally, this is called when the two bundles have
 //               Normally, this is called when the two bundles have
 //               the same, or nearly the same, hierarchies.  In this
 //               the same, or nearly the same, hierarchies.  In this
 //               case, new_bundle will simply be assigned over the
 //               case, new_bundle will simply be assigned over the
@@ -246,34 +283,9 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
 //               LOD's of the same model.
 //               LOD's of the same model.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Character::
 void Character::
-merge_bundles(PartBundle *old_bundle, PartBundle *new_bundle) {
-  // Find the index number of old_bundle.
-  size_t index = 0;
-  while (index < _bundles.size()) {
-    if (_bundles[index] == old_bundle) {
-      break;
-    }
-    ++index;
-  }
-  nassertv_always(index < _bundles.size());
-
-  if (old_bundle == new_bundle) {
-    // Trivially return.
-    return;
-  }
-
-  // First, merge the bundles themselves.
-  JointMap joint_map;
-  r_merge_bundles(joint_map, old_bundle, new_bundle);
-  old_bundle->remove_node(this);
-  _bundles[index] = new_bundle;
-  new_bundle->add_node(this);
-
-  // Now convert the geometry to use the new bundle.
-  GeomVertexMap gvmap;
-  GeomJointMap gjmap;
-  GeomSliderMap gsmap;
-  r_update_geom(this, joint_map, gvmap, gjmap, gsmap);
+merge_bundles(PartBundleHandle *old_bundle_handle, 
+              PartBundleHandle *new_bundle_handle) {
+  update_bundle(old_bundle_handle, new_bundle_handle->get_bundle());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -405,28 +417,6 @@ force_update() {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: Character::do_update
-//       Access: Private
-//  Description: The actual implementation of update().  Assumes the
-//               appropriate PStatCollector has already been started.
-////////////////////////////////////////////////////////////////////
-void Character::
-do_update() {
-  // Update all the joints and sliders.
-  if (even_animation) {
-    int num_bundles = get_num_bundles();
-    for (int i = 0; i < num_bundles; ++i) {
-      get_bundle(i)->force_update();
-    }
-  } else {
-    int num_bundles = get_num_bundles();
-    for (int i = 0; i < num_bundles; ++i) {
-      get_bundle(i)->update();
-    }
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Character::r_copy_children
 //     Function: Character::r_copy_children
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
@@ -475,6 +465,56 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Character::update_bundle
+//       Access: Protected, Virtual
+//  Description: Replaces the contents of the indicated
+//               PartBundleHandle (presumably stored within this node)
+//               with new_bundle.
+////////////////////////////////////////////////////////////////////
+void Character::
+update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
+  if (old_bundle_handle->get_bundle() == new_bundle) {
+    // Trivially return.
+    return;
+  }
+
+  // First, merge the bundles, to ensure we have the same set of
+  // joints in the new bundle.
+  JointMap joint_map;
+  r_merge_bundles(joint_map, old_bundle_handle->get_bundle(), new_bundle);
+
+  PartBundleNode::update_bundle(old_bundle_handle, new_bundle);
+
+  // Now convert the geometry to use the new bundle.
+  GeomVertexMap gvmap;
+  GeomJointMap gjmap;
+  GeomSliderMap gsmap;
+  r_update_geom(this, joint_map, gvmap, gjmap, gsmap);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Character::do_update
+//       Access: Private
+//  Description: The actual implementation of update().  Assumes the
+//               appropriate PStatCollector has already been started.
+////////////////////////////////////////////////////////////////////
+void Character::
+do_update() {
+  // Update all the joints and sliders.
+  if (even_animation) {
+    int num_bundles = get_num_bundles();
+    for (int i = 0; i < num_bundles; ++i) {
+      get_bundle(i)->force_update();
+    }
+  } else {
+    int num_bundles = get_num_bundles();
+    for (int i = 0; i < num_bundles; ++i) {
+      get_bundle(i)->update();
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Character::fill_joint_map
 //     Function: Character::fill_joint_map
 //       Access: Private
 //       Access: Private

+ 8 - 2
panda/src/char/character.h

@@ -65,6 +65,8 @@ public:
 PUBLISHED:
 PUBLISHED:
   INLINE CharacterJointBundle *get_bundle(int i) const;
   INLINE CharacterJointBundle *get_bundle(int i) const;
   void merge_bundles(PartBundle *old_bundle, PartBundle *other_bundle);
   void merge_bundles(PartBundle *old_bundle, PartBundle *other_bundle);
+  void merge_bundles(PartBundleHandle *old_bundle_handle, 
+                     PartBundleHandle *other_bundle_handle);
 
 
   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;
@@ -76,6 +78,12 @@ PUBLISHED:
   void update();
   void update();
   void force_update();
   void force_update();
 
 
+protected:
+  virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map,
+                               Thread *current_thread);
+  virtual void update_bundle(PartBundleHandle *old_bundle_handle, 
+                             PartBundle *new_bundle);
+
 private:
 private:
   void do_update();
   void do_update();
 
 
@@ -85,8 +93,6 @@ private:
   typedef pmap<const VertexTransform *, PT(JointVertexTransform) > GeomJointMap;
   typedef pmap<const VertexTransform *, PT(JointVertexTransform) > GeomJointMap;
   typedef pmap<const VertexSlider *, PT(CharacterVertexSlider) > GeomSliderMap;
   typedef pmap<const VertexSlider *, PT(CharacterVertexSlider) > GeomSliderMap;
 
 
-  virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map,
-                               Thread *current_thread);
   void fill_joint_map(JointMap &joint_map, PartGroup *copy, PartGroup *orig);
   void fill_joint_map(JointMap &joint_map, PartGroup *copy, PartGroup *orig);
   void r_merge_bundles(Character::JointMap &joint_map, 
   void r_merge_bundles(Character::JointMap &joint_map, 
                        PartGroup *old_group, PartGroup *new_group);
                        PartGroup *old_group, PartGroup *new_group);

+ 18 - 0
panda/src/express/weakPointerToBase.I

@@ -399,6 +399,24 @@ clear() {
   reassign((To *)NULL);
   reassign((To *)NULL);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: WeakPointerToBase::refresh
+//       Access: Published
+//  Description: Informs the WeakPointerTo object that its pointer is
+//               no longer deleted.  This may be used after a
+//               WeakPointerTo has deleted a deleted pointer, and then
+//               a new pointer has been reallocated.  It's equivalent
+//               to simply reassigning the pointer to its new
+//               (i.e. original) value, but has the advantage that it
+//               is const, so can be used for WeakPointers used as
+//               keys in STL maps and sets.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void WeakPointerToBase<T>::
+refresh() const {
+  ((WeakPointerToBase<T> *)this)->reassign((To *)_void_ptr);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: WeakPointerToBase::output
 //     Function: WeakPointerToBase::output
 //       Access: Published
 //       Access: Published

+ 1 - 0
panda/src/express/weakPointerToBase.h

@@ -83,6 +83,7 @@ public:
 
 
 PUBLISHED:
 PUBLISHED:
   INLINE void clear();
   INLINE void clear();
+  INLINE void refresh() const;
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
 };
 };

+ 4 - 2
panda/src/pgraph/sceneGraphReducer.cxx

@@ -116,7 +116,9 @@ flatten(PandaNode *root, int combine_siblings_bits) {
       num_pass_nodes += r_flatten(root, child_node, combine_siblings_bits);
       num_pass_nodes += r_flatten(root, child_node, combine_siblings_bits);
     }
     }
 
 
-    if (combine_siblings_bits != 0 && root->get_num_children() >= 2) {
+    if (combine_siblings_bits != 0 && 
+        root->get_num_children() >= 2 && 
+        root->safe_to_combine_children()) {
       num_pass_nodes += flatten_siblings(root, combine_siblings_bits);
       num_pass_nodes += flatten_siblings(root, combine_siblings_bits);
     }
     }
 
 
@@ -397,7 +399,7 @@ r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
 
 
     // Finally, if any of our remaining children are plain PandaNodes
     // Finally, if any of our remaining children are plain PandaNodes
     // with no children, just remove them.
     // with no children, just remove them.
-    {
+    if (parent_node->safe_to_combine_children()) {
       for (int i = parent_node->get_num_children() - 1; i >= 0; --i) {
       for (int i = parent_node->get_num_children() - 1; i >= 0; --i) {
         PandaNode *child_node = parent_node->get_child(i);
         PandaNode *child_node = parent_node->get_child(i);
         if (child_node->is_exact_type(PandaNode::get_class_type()) &&
         if (child_node->is_exact_type(PandaNode::get_class_type()) &&