Jelajahi Sumber

merge_bundles

David Rose 18 tahun lalu
induk
melakukan
826406eebc

+ 1 - 1
panda/src/chan/partBundleNode.h

@@ -54,7 +54,7 @@ protected:
   void add_bundle(PartBundle *bundle);
   void steal_bundles(PartBundleNode *other);
 
-private:
+protected:
   typedef pvector< PT(PartBundle) > Bundles;
   Bundles _bundles;
 

+ 202 - 7
panda/src/char/character.cxx

@@ -220,6 +220,52 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
                                       found_any, transform, current_thread);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Character::merge_bundles
+//       Access: Published
+//  Description: Merges the other_ith bundle into the this_ith within
+//               this node.  At the end of this call, this node and
+//               the other node will share the same bundle pointer at
+//               their corresponding positions (usually zero).
+//
+//               Normally, this is called when the two bundles have
+//               the same, or nearly the same, hierarchies.  In this
+//               case, the indicated bundle will simply be assigned to
+//               the this_ith bundle position.  However, if any joints
+//               are present in one bundle or the other, the new
+//               bundle will contain the union of all joints.
+//               
+//               The geometry below this node is also updated to
+//               reference the indicated bundle, instead of the
+//               original bundle.
+//
+//               This method is intended to unify two different models
+//               that share a common skeleton, for instance, different
+//               LOD's of the same model.
+////////////////////////////////////////////////////////////////////
+void Character::
+merge_bundles(Character *other, int this_i, int other_i) {
+  nassertv(this_i >= 0 && this_i < (int)_bundles.size());
+  nassertv(other_i >= 0 && other_i < (int)other->_bundles.size());
+  CharacterJointBundle *old_bundle = DCAST(CharacterJointBundle, _bundles[this_i]);
+  CharacterJointBundle *new_bundle = DCAST(CharacterJointBundle, other->_bundles[other_i]);
+  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);
+  _bundles[this_i] = 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::find_joint
 //       Access: Published
@@ -452,6 +498,119 @@ fill_joint_map(Character::JointMap &joint_map,
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Character::r_merge_bundles
+//       Access: Private
+//  Description: Recursively checks the two bundles for a matching
+//               hierarchy, and adds nodes as necessary to "new_group"
+//               where they are not already present.  Also fills
+//               joint_map in the same manner as fill_joint_map().
+////////////////////////////////////////////////////////////////////
+void Character::
+r_merge_bundles(Character::JointMap &joint_map, 
+                PartGroup *old_group, PartGroup *new_group) {
+  joint_map[old_group] = new_group;
+
+  if (new_group->is_of_type(CharacterJoint::get_class_type())) {
+    CharacterJoint *new_joint;
+    DCAST_INTO_V(new_joint, new_group);
+
+    // Make sure the new_joint references this as its new character.
+    new_joint->_character = this;
+
+    if (old_group != new_group &&
+        old_group->is_of_type(CharacterJoint::get_class_type())) {
+      CharacterJoint *old_joint;
+      DCAST_INTO_V(old_joint, old_group);
+
+      // Since the old_joint will be getting dropped, reset its
+      // character reference.
+      old_joint->_character = NULL;
+
+      // Copy any _net_transform and _local_transform operations to the
+      // new joint.
+      CharacterJoint::NodeList::iterator ni;
+      for (ni = old_joint->_net_transform_nodes.begin();
+           ni != old_joint->_net_transform_nodes.end();
+           ++ni) {
+        new_joint->_net_transform_nodes.insert(*ni);
+      }
+      for (ni = old_joint->_local_transform_nodes.begin();
+           ni != old_joint->_local_transform_nodes.end();
+           ++ni) {
+        new_joint->_local_transform_nodes.insert(*ni);
+      }
+    }
+  }
+
+  if (old_group == new_group) {
+    return;
+  }
+    
+  int i = 0, j = 0;
+  int old_num_children = old_group->get_num_children();
+  int new_num_children = new_group->get_num_children();
+
+  PartGroup::Children new_children;
+  new_children.reserve(max(old_num_children, new_num_children));
+
+  while (i < old_num_children && j < new_num_children) {
+    PartGroup *pc = old_group->get_child(i);
+    PartGroup *ac = new_group->get_child(j);
+
+    if (pc->get_name() < ac->get_name()) {
+      // Here is a group that exists in old_group, but not in
+      // new_group.  Duplicate it.
+      PartGroup *new_pc = pc->make_copy();
+      new_children.push_back(new_pc);
+
+      r_merge_bundles(joint_map, pc, new_pc);
+      i++;
+
+    } else if (ac->get_name() < pc->get_name()) {
+      // Here is a group that exists in new_group, but not in
+      // old_group.  Preserve it.
+      new_children.push_back(ac);
+
+      r_merge_bundles(joint_map, ac, ac);
+      j++;
+
+    } else {
+      // Here is a group that exists in both.  Preserve it.
+      new_children.push_back(ac);
+
+      r_merge_bundles(joint_map, pc, ac);
+      i++;
+      j++;
+    }
+  }
+
+  while (i < old_num_children) {
+    PartGroup *pc = old_group->get_child(i);
+
+    // Here is a group that exists in old_group, but not in
+    // new_group.  Duplicate it.
+    PartGroup *new_pc = pc->make_copy();
+    new_children.push_back(new_pc);
+    
+    r_merge_bundles(joint_map, pc, new_pc);
+    i++;
+  }
+
+  while (j < new_num_children) {
+    PartGroup *ac = new_group->get_child(j);
+
+    // Here is a group that exists in new_group, but not in
+    // old_group.  Preserve it.
+    new_children.push_back(ac);
+
+    r_merge_bundles(joint_map, ac, ac);
+    j++;
+  }
+
+  new_group->_children.swap(new_children);
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Character::r_copy_char
@@ -479,7 +638,7 @@ r_copy_char(PandaNode *dest, const PandaNode *source,
     for (int i = 0; i < num_geoms; i++) {
       const Geom *geom = source_gnode->get_geom(i);
       const RenderState *state = source_gnode->get_geom_state(i);
-      dest_gnode->add_geom(copy_geom(geom, from, joint_map, gvmap, gjmap, gsmap), state);
+      dest_gnode->add_geom(copy_geom(geom, joint_map, gvmap, gjmap, gsmap), state);
     }
   }
 
@@ -510,6 +669,38 @@ r_copy_char(PandaNode *dest, const PandaNode *source,
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Character::r_update_geom
+//       Access: Private
+//  Description: Walks the hierarchy, updating any GeomNodes in-place
+//               to reference the new animation tables within this
+//               Character.
+////////////////////////////////////////////////////////////////////
+void Character::
+r_update_geom(PandaNode *node, const Character::JointMap &joint_map,
+              Character::GeomVertexMap &gvmap,
+              Character::GeomJointMap &gjmap, 
+              Character::GeomSliderMap &gsmap) {
+  if (node->is_geom_node()) {
+    GeomNode *gnode;
+    DCAST_INTO_V(gnode, node);
+
+    int num_geoms = gnode->get_num_geoms();
+    for (int i = 0; i < num_geoms; i++) {
+      CPT(Geom) geom = gnode->get_geom(i);
+      PT(Geom) new_geom = copy_geom(geom, joint_map, gvmap, gjmap, gsmap);
+      gnode->set_geom(i, new_geom);
+    }
+  }
+
+  int num_children = node->get_num_children();
+  for (int i = 0; i < num_children; i++) {
+    PandaNode *child = node->get_child(i);
+
+    r_update_geom(child, joint_map, gvmap, gjmap, gsmap);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Character::copy_geom
 //       Access: Private
@@ -519,10 +710,9 @@ r_copy_char(PandaNode *dest, const PandaNode *source,
 //               returns the same Geom.
 ////////////////////////////////////////////////////////////////////
 PT(Geom) Character::
-copy_geom(const Geom *source, const Character *from,
-          const Character::JointMap &joint_map,
-          Character::GeomVertexMap &gvmap,
-          Character::GeomJointMap &gjmap, Character::GeomSliderMap &gsmap) {
+copy_geom(const Geom *source, const Character::JointMap &joint_map,
+          Character::GeomVertexMap &gvmap, Character::GeomJointMap &gjmap, 
+          Character::GeomSliderMap &gsmap) {
   CPT(GeomVertexFormat) format = source->get_vertex_data()->get_format();
   if (format->get_animation().get_animation_type() == Geom::AT_none) {
     // Not animated, so never mind.
@@ -800,8 +990,13 @@ r_clear_joint_characters(PartGroup *part) {
   if (part->is_of_type(CharacterJoint::get_class_type())) {
     CharacterJoint *joint = DCAST(CharacterJoint, part);
 
-    nassertv(joint->get_character() == this || joint->get_character() == NULL);
-    joint->set_character(NULL);
+    // It is possible for the joint to reference a different Character
+    // here--after merge_bundles() has been called, a particular joint
+    // will be listed within more than one Character node, but it can
+    // only point back to one of them.
+    if (joint->get_character() == this) {
+      joint->set_character(NULL);
+    }
   }
 
   int num_children = part->get_num_children();

+ 7 - 1
panda/src/char/character.h

@@ -64,6 +64,7 @@ public:
 
 PUBLISHED:
   INLINE CharacterJointBundle *get_bundle(int i) const;
+  void merge_bundles(Character *other, int this_i, int other_i);
 
   CharacterJoint *find_joint(const string &name) const;
   CharacterSlider *find_slider(const string &name) const;
@@ -87,11 +88,16 @@ private:
   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 r_merge_bundles(Character::JointMap &joint_map, 
+                       PartGroup *old_group, PartGroup *new_group);
   void r_copy_char(PandaNode *dest, const PandaNode *source,
                    const Character *from, NodeMap &node_map,
                    const JointMap &joint_map, GeomVertexMap &gvmap,
                    GeomJointMap &gjmap, GeomSliderMap &gsmap);
-  PT(Geom) copy_geom(const Geom *source, const Character *from,
+  void r_update_geom(PandaNode *node,
+                     const JointMap &joint_map, GeomVertexMap &gvmap,
+                     GeomJointMap &gjmap, GeomSliderMap &gsmap);
+  PT(Geom) copy_geom(const Geom *source, 
                      const JointMap &joint_map, GeomVertexMap &gvmap,
                      GeomJointMap &gjmap, GeomSliderMap &gsmap);
   void copy_node_pointers(const Character::NodeMap &node_map,

+ 9 - 0
panda/src/char/characterJointBundle.cxx

@@ -33,6 +33,15 @@ CharacterJointBundle::
 CharacterJointBundle(const string &name) : PartBundle(name) {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CharacterJointBundle::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CharacterJointBundle::
+~CharacterJointBundle() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CharacterJointBundle::make_copy
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/char/characterJointBundle.h

@@ -38,6 +38,7 @@ protected:
 
 public:
   CharacterJointBundle(const string &name = "");
+  virtual ~CharacterJointBundle();
 
 PUBLISHED:
   INLINE Character *get_node(int n) const;