Browse Source

pgraph characters

David Rose 24 years ago
parent
commit
4d552344fe
42 changed files with 1895 additions and 152 deletions
  1. 29 20
      panda/src/chan/Sources.pp
  2. 150 12
      panda/src/chan/auto_bind.cxx
  3. 5 0
      panda/src/chan/auto_bind.h
  4. 1 0
      panda/src/chan/chan_composite1.cxx
  5. 1 0
      panda/src/chan/chan_composite2.cxx
  6. 7 2
      panda/src/chan/config_chan.cxx
  7. 11 0
      panda/src/chan/partBundle.I
  8. 7 3
      panda/src/chan/partBundle.h
  9. 1 0
      panda/src/chan/partGroup.h
  10. 67 0
      panda/src/chan/qpanimBundleNode.I
  11. 110 0
      panda/src/chan/qpanimBundleNode.cxx
  12. 82 0
      panda/src/chan/qpanimBundleNode.h
  13. 69 0
      panda/src/chan/qppartBundleNode.I
  14. 80 0
      panda/src/chan/qppartBundleNode.cxx
  15. 80 0
      panda/src/chan/qppartBundleNode.h
  16. 15 9
      panda/src/char/Sources.pp
  17. 1 0
      panda/src/char/char_composite1.cxx
  18. 1 0
      panda/src/char/characterJoint.h
  19. 153 9
      panda/src/char/computedVertices.cxx
  20. 6 3
      panda/src/char/computedVertices.h
  21. 3 0
      panda/src/char/config_char.cxx
  22. 87 0
      panda/src/char/qpcharacter.I
  23. 538 0
      panda/src/char/qpcharacter.cxx
  24. 134 0
      panda/src/char/qpcharacter.h
  25. 11 4
      panda/src/egg2pg/Sources.pp
  26. 31 20
      panda/src/egg2pg/animBundleMaker.cxx
  27. 4 5
      panda/src/egg2pg/animBundleMaker.h
  28. 14 14
      panda/src/egg2pg/characterMaker.cxx
  29. 6 8
      panda/src/egg2pg/characterMaker.h
  30. 0 0
      panda/src/egg2pg/computedVerticesMaker.I
  31. 141 6
      panda/src/egg2pg/computedVerticesMaker.cxx
  32. 14 10
      panda/src/egg2pg/computedVerticesMaker.h
  33. 0 1
      panda/src/egg2pg/computedVerticesMakerEntity.I
  34. 6 5
      panda/src/egg2pg/computedVerticesMakerEntity.h
  35. 4 2
      panda/src/egg2pg/egg2pg_composite1.cxx
  36. 2 0
      panda/src/egg2pg/egg2pg_composite2.cxx
  37. 9 11
      panda/src/egg2pg/qpeggLoader.cxx
  38. 3 1
      panda/src/egg2pg/qpeggLoader.h
  39. 1 3
      panda/src/egg2sg/Sources.pp
  40. 0 3
      panda/src/egg2sg/egg2sg_composite1.cxx
  41. 2 1
      panda/src/egg2sg/eggLoader.h
  42. 9 0
      panda/src/testbed/pview.cxx

+ 29 - 20
panda/src/chan/Sources.pp

@@ -9,30 +9,37 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
 
   #define SOURCES \
-     animBundle.I animBundle.h animBundleNode.I animBundleNode.h  \
-     animChannel.I animChannel.h animChannelBase.I  \
-     animChannelBase.h animChannelMatrixXfmTable.I  \
-     animChannelMatrixXfmTable.h animChannelScalarTable.I  \
-     animChannelScalarTable.h animControl.I animControl.N  \
-     animControl.h animControlCollection.I  \
-     animControlCollection.h animGroup.I animGroup.h auto_bind.h  \
-     config_chan.h movingPartBase.I movingPartBase.h  \
-     movingPartMatrix.I movingPartMatrix.h movingPartScalar.I  \
-     movingPartScalar.h partBundle.I partBundle.N partBundle.h  \
-     partBundleNode.I partBundleNode.h partGroup.I partGroup.h  \
-     vector_PartGroupStar.h 
+    animBundle.I animBundle.h animBundleNode.I animBundleNode.h  \
+    qpanimBundleNode.I qpanimBundleNode.h \
+    animChannel.I animChannel.h animChannelBase.I  \
+    animChannelBase.h animChannelMatrixXfmTable.I  \
+    animChannelMatrixXfmTable.h animChannelScalarTable.I  \
+    animChannelScalarTable.h animControl.I animControl.N  \
+    animControl.h animControlCollection.I  \
+    animControlCollection.h animGroup.I animGroup.h auto_bind.h  \
+    config_chan.h movingPartBase.I movingPartBase.h  \
+    movingPartMatrix.I movingPartMatrix.h movingPartScalar.I  \
+    movingPartScalar.h partBundle.I partBundle.N partBundle.h  \
+    partBundleNode.I partBundleNode.h \
+    qppartBundleNode.I qppartBundleNode.h \
+    partGroup.I partGroup.h  \
+    vector_PartGroupStar.h 
 
   #define INCLUDED_SOURCES  \
-     animBundle.cxx animBundleNode.cxx animChannel.cxx  \
-     animChannelBase.cxx animChannelMatrixXfmTable.cxx  \
-     animChannelScalarTable.cxx animControl.cxx  \
-     animControlCollection.cxx animGroup.cxx auto_bind.cxx  \
-     config_chan.cxx movingPartBase.cxx movingPartMatrix.cxx  \
-     movingPartScalar.cxx partBundle.cxx partBundleNode.cxx  \
-     partGroup.cxx vector_PartGroupStar.cxx 
+    animBundle.cxx animBundleNode.cxx \
+    qpanimBundleNode.cxx \
+    animChannel.cxx  \
+    animChannelBase.cxx animChannelMatrixXfmTable.cxx  \
+    animChannelScalarTable.cxx animControl.cxx  \
+    animControlCollection.cxx animGroup.cxx auto_bind.cxx  \
+    config_chan.cxx movingPartBase.cxx movingPartMatrix.cxx  \
+    movingPartScalar.cxx partBundle.cxx partBundleNode.cxx  \
+    qppartBundleNode.cxx \
+    partGroup.cxx vector_PartGroupStar.cxx 
 
   #define INSTALL_HEADERS \
     animBundle.I animBundle.h animBundleNode.I animBundleNode.h \
+    qpanimBundleNode.I qpanimBundleNode.h \
     animChannel.I animChannel.h animChannelBase.I animChannelBase.h \
     animChannelFixed.I animChannelFixed.h animChannelMatrixXfmTable.I \
     animChannelMatrixXfmTable.h animChannelScalarTable.I \
@@ -42,7 +49,9 @@
     movingPart.I movingPart.h movingPartBase.I \
     movingPartBase.h movingPartMatrix.I movingPartMatrix.h \
     movingPartScalar.I movingPartScalar.h partBundle.I partBundle.h \
-    partBundleNode.I partBundleNode.h partGroup.I partGroup.h \
+    partBundleNode.I partBundleNode.h \
+    qppartBundleNode.I qppartBundleNode.h \
+    partGroup.I partGroup.h \
     vector_PartGroupStar.h
     
   #define IGATESCAN all

+ 150 - 12
panda/src/chan/auto_bind.cxx

@@ -17,17 +17,19 @@
 ////////////////////////////////////////////////////////////////////
 
 
-#include "animBundleNode.h"
-#include "partBundleNode.h"
+#include "auto_bind.h"
+#include "qpanimBundleNode.h"
+#include "qppartBundleNode.h"
 #include "config_chan.h"
+#include "string_utils.h"
 
-#include <renderRelation.h>
-#include <traverserVisitor.h>
-#include <dftraverser.h>
-#include <string_utils.h>
-#include <nullLevelState.h>
-#include <nullTransitionWrapper.h>
-#include "auto_bind.h"
+#include "animBundleNode.h"
+#include "partBundleNode.h"
+#include "renderRelation.h"
+#include "traverserVisitor.h"
+#include "dftraverser.h"
+#include "nullLevelState.h"
+#include "nullTransitionWrapper.h"
 
 typedef pset<AnimBundleNode *> AnimNodes;
 typedef pmap<string, AnimNodes> Anims;
@@ -36,7 +38,6 @@ typedef pset<PartBundleNode *> PartNodes;
 typedef pmap<string, PartNodes> Parts;
 
 
-
 ////////////////////////////////////////////////////////////////////
 //       Class : CollectNodes
 // Description : This is a traverser visitor that locates bundle nodes
@@ -133,8 +134,9 @@ bind_anims(const PartNodes &parts, const AnimNodes &anims,
 //               The list of all resulting AnimControls created is
 //               filled into controls.
 ////////////////////////////////////////////////////////////////////
-void auto_bind(Node *root_node, AnimControlCollection &controls,
-               int hierarchy_match_flags) {
+void
+auto_bind(Node *root_node, AnimControlCollection &controls,
+          int hierarchy_match_flags) {
 
   // First, locate all the bundles in the subgraph.
   CollectNodes cn;
@@ -169,3 +171,139 @@ void auto_bind(Node *root_node, AnimControlCollection &controls,
 }
 
 
+typedef pset<qpAnimBundleNode *> qpAnimNodes;
+typedef pmap<string, qpAnimNodes> qpAnims;
+
+typedef pset<qpPartBundleNode *> qpPartNodes;
+typedef pmap<string, qpPartNodes> qpParts;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: bind_anims
+//  Description: A support function for auto_bind(), below.  Given a
+//               set of AnimBundles and a set of PartBundles that all
+//               share the same name, perform whatever bindings make
+//               sense.
+////////////////////////////////////////////////////////////////////
+static void
+qpbind_anims(const qpPartNodes &parts, const qpAnimNodes &anims,
+           AnimControlCollection &controls,
+           int hierarchy_match_flags) {
+
+  qpPartNodes::const_iterator pni;
+
+  for (pni = parts.begin(); pni != parts.end(); ++pni) {
+    PartBundle *part = (*pni)->get_bundle();
+
+    qpAnimNodes::const_iterator ani;
+    for (ani = anims.begin(); ani != anims.end(); ++ani) {
+      AnimBundle *anim = (*ani)->get_bundle();
+
+      if (chan_cat.is_info()) {
+        chan_cat.info()
+          << "Attempting to bind " << *part << " to " << *anim << "\n";
+      }
+
+      PT(AnimControl) control =
+        part->bind_anim(anim, hierarchy_match_flags);
+      string name = anim->get_name();
+      if (control != (AnimControl *)NULL) {
+        if (controls.find_anim(name) != (AnimControl *)NULL) {
+          // That name's already used; synthesize another one.
+          int index = 0;
+          string new_name;
+          do {
+            index++;
+            new_name = name + '.' + format_string(index);
+          } while (controls.find_anim(new_name) != (AnimControl *)NULL);
+          name = new_name;
+        }
+
+        controls.store_anim(control, name);
+      }
+
+      if (chan_cat.is_info()) {
+        if (control == NULL) {
+          chan_cat.info()
+            << "Bind failed.\n";
+        } else {
+          chan_cat.info()
+            << "Bind succeeded, index "
+            << control->get_channel_index() << "; accessible as "
+            << name << "\n";
+        }
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: r_find_bundles
+//  Description: A support function for auto_bind(), below.  Walks
+//               through the hierarchy and finds all of the
+//               PartBundles and AnimBundles.
+////////////////////////////////////////////////////////////////////
+static void 
+r_find_bundles(PandaNode *node, qpAnims &anims, qpParts &parts) {
+  if (node->is_of_type(qpAnimBundleNode::get_class_type())) {
+    qpAnimBundleNode *bn = DCAST(qpAnimBundleNode, node);
+    anims[bn->get_bundle()->get_name()].insert(bn);
+    
+  } else if (node->is_of_type(qpPartBundleNode::get_class_type())) {
+    qpPartBundleNode *bn = DCAST(qpPartBundleNode, node);
+    parts[bn->get_bundle()->get_name()].insert(bn);
+  }
+
+  PandaNode::Children cr = node->get_children();
+  int num_children = cr.get_num_children();
+  for (int i = 0; i < num_children; i++) {
+    r_find_bundles(cr.get_child(i), anims, parts);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: auto_bind
+//  Description: Walks the scene graph or subgraph beginning at the
+//               indicated node, and attempts to bind any AnimBundles
+//               found to their matching PartBundles, when possible.
+//
+//               The list of all resulting AnimControls created is
+//               filled into controls.
+////////////////////////////////////////////////////////////////////
+void
+auto_bind(PandaNode *root_node, AnimControlCollection &controls,
+          int hierarchy_match_flags) {
+  // First, locate all the bundles in the subgraph.
+  qpAnims anims;
+  qpParts parts;
+  r_find_bundles(root_node, anims, parts);
+
+  // Now, match up the bundles by name.
+
+  qpAnims::const_iterator ai = anims.begin();
+  qpParts::const_iterator pi = parts.begin();
+
+  while (ai != anims.end() && pi != parts.end()) {
+    if ((*ai).first < (*pi).first) {
+      // Here's an anim with no matching parts.
+      ++ai;
+
+    } else if ((*pi).first < (*ai).first) {
+      // And here's a part with no matching anims.
+      ++pi;
+
+    } else {
+      // But here we have (at least one) match!
+      qpbind_anims((*pi).second, (*ai).second, controls,
+                   hierarchy_match_flags);
+      ++pi;
+
+      // We don't increment the anim counter yet.  That way, the same
+      // anim may bind to multiple parts, if they all share the same
+      // name.
+    }
+  }
+}
+
+

+ 5 - 0
panda/src/chan/auto_bind.h

@@ -25,6 +25,7 @@
 #include "animControlCollection.h"
 
 class Node;
+class PandaNode;
 
 BEGIN_PUBLISH
 ////////////////////////////////////////////////////////////////////
@@ -39,6 +40,10 @@ BEGIN_PUBLISH
 EXPCL_PANDA void
 auto_bind(Node *root_node, AnimControlCollection &controls,
           int hierarchy_match_flags = 0);
+
+EXPCL_PANDA void
+auto_bind(PandaNode *root_node, AnimControlCollection &controls,
+          int hierarchy_match_flags = 0);
 END_PUBLISH
 
 #endif

+ 1 - 0
panda/src/chan/chan_composite1.cxx

@@ -6,6 +6,7 @@
 #include "movingPartScalar.cxx"
 #include "partBundle.cxx"
 #include "partBundleNode.cxx"
+#include "qppartBundleNode.cxx"
 #include "partGroup.cxx"
 #include "vector_PartGroupStar.cxx"
 

+ 1 - 0
panda/src/chan/chan_composite2.cxx

@@ -1,6 +1,7 @@
 
 #include "animBundle.cxx"
 #include "animBundleNode.cxx"
+#include "qpanimBundleNode.cxx"
 #include "animChannel.cxx"
 #include "animChannelBase.cxx"
 #include "animChannelMatrixXfmTable.cxx"

+ 7 - 2
panda/src/chan/config_chan.cxx

@@ -20,6 +20,7 @@
 #include "config_chan.h"
 #include "animBundle.h"
 #include "animBundleNode.h"
+#include "qpanimBundleNode.h"
 #include "animChannelBase.h"
 #include "animChannelMatrixXfmTable.h"
 #include "animChannelScalarTable.h"
@@ -30,10 +31,11 @@
 #include "movingPartScalar.h"
 #include "partBundle.h"
 #include "partBundleNode.h"
+#include "qppartBundleNode.h"
 #include "partGroup.h"
 
-#include <luse.h>
-#include <dconfig.h>
+#include "luse.h"
+#include "dconfig.h"
 
 Configure(config_chan);
 NotifyCategoryDef(chan, "");
@@ -79,6 +81,7 @@ bool read_compressed_channels = config_chan.GetBool("read-compressed-channels",
 ConfigureFn(config_chan) {
   AnimBundle::init_type();
   AnimBundleNode::init_type();
+  qpAnimBundleNode::init_type();
   AnimChannelBase::init_type();
   AnimChannelMatrixXfmTable::init_type();
   AnimChannelScalarTable::init_type();
@@ -89,6 +92,7 @@ ConfigureFn(config_chan) {
   MovingPartScalar::init_type();
   PartBundle::init_type();
   PartBundleNode::init_type();
+  qpPartBundleNode::init_type();
   PartGroup::init_type();
 
   // This isn't defined in this package, but it *is* essential that it
@@ -107,6 +111,7 @@ ConfigureFn(config_chan) {
   AnimGroup::register_with_read_factory();
   AnimBundle::register_with_read_factory();
   AnimBundleNode::register_with_read_factory();
+  qpAnimBundleNode::register_with_read_factory();
   AnimChannelMatrixXfmTable::register_with_read_factory();
   AnimChannelScalarTable::register_with_read_factory();
 }

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

@@ -39,6 +39,17 @@ get_node() const {
   return _node;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundle::get_qpnode
+//       Access: Published
+//  Description: Returns the PartBundleNode associated with this
+//               PartBundle.
+////////////////////////////////////////////////////////////////////
+INLINE qpPartBundleNode *PartBundle::
+get_qpnode() const {
+  return _qpnode;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PartBundle::control_begin
 //       Access: Public

+ 7 - 3
panda/src/chan/partBundle.h

@@ -19,17 +19,18 @@
 #ifndef PARTBUNDLE_H
 #define PARTBUNDLE_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 #include "partGroup.h"
 #include "animControl.h"
 #include "animControlCollection.h"
 
-#include <pointerTo.h>
-#include <iterator_types.h>
+#include "pointerTo.h"
+#include "iterator_types.h"
 
 class AnimBundle;
 class PartBundleNode;
+class qpPartBundleNode;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PartBundle
@@ -93,6 +94,7 @@ PUBLISHED:
   INLINE BlendType get_blend_type() const;
 
   INLINE PartBundleNode *get_node() const;
+  INLINE qpPartBundleNode *get_qpnode() const;
 
   void clear_control_effects();
   void set_control_effect(AnimControl *control, float effect);
@@ -131,6 +133,7 @@ protected:
 
   BlendType _blend_type;
   PartBundleNode *_node;
+  qpPartBundleNode *_qpnode;
 
   AnimControl *_last_control_set;
   ChannelBlend _blend;
@@ -162,6 +165,7 @@ private:
   static TypeHandle _type_handle;
 
   friend class PartBundleNode;
+  friend class qpPartBundleNode;
 };
 
 inline ostream &operator <<(ostream &out, const PartBundle &bundle) {

+ 1 - 0
panda/src/chan/partGroup.h

@@ -130,6 +130,7 @@ private:
   static TypeHandle _type_handle;
 
   friend class Character;
+  friend class qpCharacter;
 };
 
 #include "partGroup.I"

+ 67 - 0
panda/src/chan/qpanimBundleNode.I

@@ -0,0 +1,67 @@
+// Filename: qpanimBundleNode.I
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::Constructor
+//       Access: Public
+//  Description: The AnimBundle and its node should be constructed
+//               together.  Generally, the derived classes of
+//               qpAnimBundleNode will automatically create a AnimBundle
+//               of the appropriate type, and pass it up to this
+//               constructor.
+////////////////////////////////////////////////////////////////////
+INLINE qpAnimBundleNode::
+qpAnimBundleNode(const string &name, AnimBundle *bundle) :
+  PandaNode(name),
+  _bundle(bundle)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::Default Constructor
+//       Access: Protected
+//  Description: For internal use only.
+////////////////////////////////////////////////////////////////////
+INLINE qpAnimBundleNode::
+qpAnimBundleNode() : PandaNode("") {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::Copy Constructor
+//       Access: Protected
+//  Description: Use make_copy() or copy_subgraph() to copy one of
+//               these.  Copying a qpAnimBundleNode will always force a
+//               deep copy of the PartGroup hierarchy.
+////////////////////////////////////////////////////////////////////
+INLINE qpAnimBundleNode::
+qpAnimBundleNode(const qpAnimBundleNode &copy) :
+  PandaNode(copy),
+  _bundle(copy._bundle)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::get_bundle
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE AnimBundle *qpAnimBundleNode::
+get_bundle() const {
+  return _bundle;
+}

+ 110 - 0
panda/src/chan/qpanimBundleNode.cxx

@@ -0,0 +1,110 @@
+// Filename: qpanimBundleNode.cxx
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpanimBundleNode.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle qpAnimBundleNode::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::safe_to_flatten
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to flatten out
+//               this particular kind of Node by duplicating
+//               instances, false otherwise (for instance, a Camera
+//               cannot be safely flattened, because the Camera
+//               pointer itself is meaningful).
+////////////////////////////////////////////////////////////////////
+bool qpAnimBundleNode::
+safe_to_flatten() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               qpAnimBundleNode.
+////////////////////////////////////////////////////////////////////
+void qpAnimBundleNode::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpAnimBundleNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+  manager->write_pointer(dg, _bundle);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpAnimBundleNode::
+complete_pointers(TypedWritable **p_list, BamReader* manager) {
+  int pi = PandaNode::complete_pointers(p_list, manager);
+  _bundle = DCAST(AnimBundle, p_list[pi++]);
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of this type is encountered
+//               in the Bam file.  It should create the object
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *qpAnimBundleNode::
+make_from_bam(const FactoryParams &params) {
+  qpAnimBundleNode *node = new qpAnimBundleNode;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpAnimBundleNode::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new PandaNode.
+////////////////////////////////////////////////////////////////////
+void qpAnimBundleNode::
+fillin(DatagramIterator &scan, BamReader* manager) {
+  PandaNode::fillin(scan, manager);
+  manager->read_pointer(scan, this);
+}

+ 82 - 0
panda/src/chan/qpanimBundleNode.h

@@ -0,0 +1,82 @@
+// Filename: qpanimBundleNode.h
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpANIMBUNDLENODE_H
+#define qpANIMBUNDLENODE_H
+
+#include "pandabase.h"
+
+#include "animBundle.h"
+
+#include "pandaNode.h"
+#include "dcast.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpAnimBundleNode
+// Description : This is a node that contains a pointer to an
+//               AnimBundle.  Like AnimBundleNode, it exists solely to
+//               make it easy to store AnimBundles in the scene graph.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpAnimBundleNode : public PandaNode {
+public:
+  INLINE qpAnimBundleNode(const string &name, AnimBundle *bundle);
+
+protected:
+  INLINE qpAnimBundleNode();
+  INLINE qpAnimBundleNode(const qpAnimBundleNode &copy);
+
+public:
+  virtual bool safe_to_flatten() const;
+
+PUBLISHED:
+  INLINE AnimBundle *get_bundle() const;
+
+private:
+  PT(AnimBundle) _bundle;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter* manager, Datagram &me);
+  virtual int complete_pointers(TypedWritable **p_list,
+                                BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator& scan, BamReader* manager);
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "qpAnimBundleNode",
+                  PandaNode::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qpanimBundleNode.I"
+
+#endif

+ 69 - 0
panda/src/chan/qppartBundleNode.I

@@ -0,0 +1,69 @@
+// Filename: qppartBundleNode.I
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::Constructor
+//       Access: Public
+//  Description: The PartBundle and its node should be constructed
+//               together.  Generally, the derived classes of
+//               qpPartBundleNode will automatically create a PartBundle
+//               of the appropriate type, and pass it up to this
+//               constructor.
+////////////////////////////////////////////////////////////////////
+INLINE qpPartBundleNode::
+qpPartBundleNode(const string &name, PartBundle *bundle) :
+  PandaNode(name),
+  _bundle(bundle)
+{
+  _bundle->_qpnode = this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::Default Constructor
+//       Access: Protected
+//  Description: For internal use only.
+////////////////////////////////////////////////////////////////////
+INLINE qpPartBundleNode::
+qpPartBundleNode() : PandaNode("") {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::Copy Constructor
+//       Access: Protected
+//  Description: Use make_copy() or copy_subgraph() to copy one of
+//               these.  Copying a qpPartBundleNode will always force a
+//               deep copy of the PartGroup hierarchy.
+////////////////////////////////////////////////////////////////////
+INLINE qpPartBundleNode::
+qpPartBundleNode(const qpPartBundleNode &copy) :
+  PandaNode(copy),
+  _bundle(DCAST(PartBundle, copy._bundle->copy_subgraph()))
+{
+  _bundle->_qpnode = this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::get_bundle
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PartBundle *qpPartBundleNode::
+get_bundle() const {
+  return _bundle;
+}

+ 80 - 0
panda/src/chan/qppartBundleNode.cxx

@@ -0,0 +1,80 @@
+// Filename: qppartBundleNode.cxx
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qppartBundleNode.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+
+TypeHandle qpPartBundleNode::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::safe_to_flatten
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to flatten out
+//               this particular kind of Node by duplicating
+//               instances, false otherwise (for instance, a Camera
+//               cannot be safely flattened, because the Camera
+//               pointer itself is meaningful).
+////////////////////////////////////////////////////////////////////
+bool qpPartBundleNode::
+safe_to_flatten() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpPartBundleNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+  manager->write_pointer(dg, _bundle);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpPartBundleNode::
+complete_pointers(TypedWritable **p_list, BamReader* manager) {
+  int pi = PandaNode::complete_pointers(p_list, manager);
+  _bundle = DCAST(PartBundle, p_list[pi++]);
+  _bundle->_qpnode = this;
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpPartBundleNode::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new PandaNode.
+////////////////////////////////////////////////////////////////////
+void qpPartBundleNode::
+fillin(DatagramIterator &scan, BamReader* manager) {
+  PandaNode::fillin(scan, manager);
+  manager->read_pointer(scan, this);
+}

+ 80 - 0
panda/src/chan/qppartBundleNode.h

@@ -0,0 +1,80 @@
+// Filename: qppartBundleNode.h
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpPARTBUNDLENODE_H
+#define qpPARTBUNDLENODE_H
+
+#include "pandabase.h"
+
+#include "partBundle.h"
+
+#include "pandaNode.h"
+#include "dcast.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpPartBundleNode
+// Description : This is a node that contains a pointer to an
+//               PartBundle.  Like AnimBundleNode, it exists solely to
+//               make it easy to store PartBundles in the scene graph.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpPartBundleNode : public PandaNode {
+public:
+  INLINE qpPartBundleNode(const string &name, PartBundle *bundle);
+
+protected:
+  INLINE qpPartBundleNode();
+  INLINE qpPartBundleNode(const qpPartBundleNode &copy);
+
+public:
+  virtual bool safe_to_flatten() const;
+
+PUBLISHED:
+  INLINE PartBundle *get_bundle() const;
+
+private:
+  PT(PartBundle) _bundle;
+
+public:
+  virtual void write_datagram(BamWriter* manager, Datagram &me);
+  virtual int complete_pointers(TypedWritable **p_list,
+                                BamReader *manager);
+
+protected:
+  void fillin(DatagramIterator& scan, BamReader* manager);
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "qpPartBundleNode",
+                  PandaNode::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "qppartBundleNode.I"
+
+#endif

+ 15 - 9
panda/src/char/Sources.pp

@@ -10,19 +10,25 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
 
   #define SOURCES \
-     character.I character.h characterJoint.h characterJointBundle.I  \
-     characterJointBundle.h characterSlider.h computedVertices.I  \
-     computedVertices.h computedVerticesMorph.I  \
-     computedVerticesMorph.h config_char.h dynamicVertices.h
+    character.I character.h \
+    qpcharacter.I qpcharacter.h \
+    characterJoint.h characterJointBundle.I  \
+    characterJointBundle.h characterSlider.h computedVertices.I  \
+    computedVertices.h computedVerticesMorph.I  \
+    computedVerticesMorph.h config_char.h dynamicVertices.h
     
   #define INCLUDED_SOURCES \
-     character.cxx characterJoint.cxx characterJointBundle.cxx  \
-     characterSlider.cxx computedVertices.cxx  \
-     computedVerticesMorph.cxx config_char.cxx  \
-     dynamicVertices.cxx
+    character.cxx \
+    qpcharacter.cxx \
+    characterJoint.cxx characterJointBundle.cxx  \
+    characterSlider.cxx computedVertices.cxx  \
+    computedVerticesMorph.cxx config_char.cxx  \
+    dynamicVertices.cxx
 
   #define INSTALL_HEADERS \
-    character.I character.h characterJoint.h characterJointBundle.I \
+    character.I character.h \
+    qpcharacter.I qpcharacter.h \
+    characterJoint.h characterJointBundle.I \
     characterJointBundle.h characterSlider.h computedVertices.I \
     computedVertices.h computedVerticesMorph.I computedVerticesMorph.h \
     config_char.h dynamicVertices.h

+ 1 - 0
panda/src/char/char_composite1.cxx

@@ -1,6 +1,7 @@
 
 #include "config_char.cxx"
 #include "character.cxx"
+#include "qpcharacter.cxx"
 #include "characterJoint.cxx"
 #include "characterJointBundle.cxx"
 

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

@@ -82,6 +82,7 @@ public:
   // character.  It does not store any meaningful value after
   // creation is complete.
   PT_NamedNode _geom_node;
+  PT(PandaNode) _qpgeom_node;
 
   // These are filled in as the joint animates.
   LMatrix4f _net_transform;

+ 153 - 9
panda/src/char/computedVertices.cxx

@@ -20,6 +20,7 @@
 #include "computedVertices.h"
 #include "characterJoint.h"
 #include "character.h"
+#include "qpcharacter.h"
 #include "config_char.h"
 
 #include <datagram.h>
@@ -53,28 +54,29 @@ VertexTransform(const VertexTransform &copy) :
 //               vertices in the table, according to the values of the
 //               relevant slider.
 ////////////////////////////////////////////////////////////////////
-template<class ValueType, class MorphType>
+// QP
+template<class ValueType, class MorphType, class Character>
 static void
 compute_morphs(ValueType *table, const pvector<MorphType> &morph_list,
-           Character *character) {
+               Character *character) {
   pvector<MorphType>::const_iterator mli;
   for (mli = morph_list.begin(); mli != morph_list.end(); ++mli) {
     const MorphType &morph = (*mli);
     const CharacterSlider *slider;
     DCAST_INTO_V(slider, character->get_part(morph._slider_index));
-
+    
     float slider_value = slider->_value;
-
+    
     if (slider_value != 0.0f) {
       typedef TYPENAME MorphType::Morphs Morphs;
       typedef TYPENAME MorphType::MorphValue MorphValue;
       TYPENAME Morphs::const_iterator mi;
       for (mi = morph._morphs.begin(); mi != morph._morphs.end(); ++mi) {
-    typedef typename MorphValue::VecType VecType;
-    ushort index = (*mi)._index;
-    const VecType &v = (*mi)._vector;
-
-    table[index] += v * slider_value;
+        typedef typename MorphValue::VecType VecType;
+        ushort index = (*mi)._index;
+        const VecType &v = (*mi)._vector;
+        
+        table[index] += v * slider_value;
       }
     }
   }
@@ -229,6 +231,108 @@ update(Character *character) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ComputedVertices::update
+//       Access: Public
+//  Description: Recomputes all of the _coords, _norms, etc. values
+//               based on the values in _orig_coords, _orig_norms,
+//               etc., and the current positions of all of the joints.
+////////////////////////////////////////////////////////////////////
+void ComputedVertices::
+update(qpCharacter *character) {
+  nassertv(character != (qpCharacter *)NULL);
+  nassertv(character->_cv._coords.size() == _orig_coords.size());
+  nassertv(character->_cv._norms.size() == _orig_norms.size());
+  nassertv(character->_cv._texcoords.size() == _orig_texcoords.size());
+  nassertv(character->_cv._colors.size() == _orig_colors.size());
+
+  const Vertexf *orig_coords = _orig_coords;
+  const Normalf *orig_norms = _orig_norms;
+
+  memset(character->_cv._coords, 0, sizeof(Vertexf) * character->_cv._coords.size());
+  memset(character->_cv._norms, 0, sizeof(Normalf) * character->_cv._norms.size());
+
+  if (!_vertex_morphs.empty()) {
+    // We have some vertex morphs.  Compute them first.
+    int table_size = sizeof(Vertexf) * _orig_coords.size();
+    Vertexf *morphed_coords = (Vertexf *)alloca(table_size);
+    memcpy(morphed_coords, _orig_coords, table_size);
+
+    compute_morphs(morphed_coords, _vertex_morphs, character);
+    orig_coords = morphed_coords;
+  }
+
+  if (!_normal_morphs.empty()) {
+    // We also have some normal morphs.  Compute them too.
+    int table_size = sizeof(Normalf) * _orig_norms.size();
+    Normalf *morphed_norms = (Normalf *)alloca(table_size);
+    memcpy(morphed_norms, _orig_norms, table_size);
+
+    compute_morphs(morphed_norms, _normal_morphs, character);
+    orig_norms = morphed_norms;
+  }
+
+  if (!_texcoord_morphs.empty()) {
+    // We have some uv morphs.  These don't particularly need to be
+    // done before the joints are computed, but do them now anyway.
+    int table_size = sizeof(TexCoordf) * _orig_texcoords.size();
+
+    // **** Is this right?  Test it!
+    //    TexCoordf *morphed_texcoords = (TexCoordf *)alloca(table_size);
+    memcpy(character->_cv._texcoords, _orig_texcoords, table_size);
+
+    compute_morphs(character->_cv._texcoords.p(), _texcoord_morphs, character);
+  }
+
+  if (!_color_morphs.empty()) {
+    // We have some color morphs.  Do these now too.
+    int table_size = sizeof(Colorf) * _orig_colors.size();
+
+    // **** Is this right?  Test it!
+    // Colorf *morphed_colors = (Colorf *)alloca(table_size);
+    memcpy(character->_cv._colors, _orig_colors, table_size);
+
+    compute_morphs(character->_cv._colors.p(), _color_morphs, character);
+  }
+
+  // Now that we've computed all the morphs, it's safe to transform
+  // vertices into their proper coordinate space, according to the
+  // current positions of all the joints.
+
+  LMatrix4f mat = LMatrix4f::ident_mat();
+  int last_joint_index = -1;
+
+  VertexTransforms::const_iterator vti;
+  for (vti = _transforms.begin(); vti != _transforms.end(); ++vti) {
+    const VertexTransform &vt = (*vti);
+
+    // We cache the matrix from the last joint, because we are likely
+    // to encounter the same joint several times in a row.
+    if (vt._joint_index != last_joint_index) {
+      last_joint_index = vt._joint_index;
+
+      // We won't encounter -1 after the first few joints.
+      nassertv(vt._joint_index >= 0);
+      CharacterJoint *joint;
+      DCAST_INTO_V(joint, character->get_part(vt._joint_index));
+
+      mat =
+        joint->_initial_net_transform_inverse *
+        joint->_net_transform;
+    }
+
+    Vertices::const_iterator vi;
+    for (vi = vt._vindex.begin(); vi != vt._vindex.end(); ++vi) {
+      int i = (*vi);
+      character->_cv._coords[i] += (orig_coords[i] * mat) * vt._effect;
+    }
+    for (vi = vt._nindex.begin(); vi != vt._nindex.end(); ++vi) {
+      int i = (*vi);
+      character->_cv._norms[i] += (orig_norms[i] * mat) * vt._effect;
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ComputedVertices::make_orig
 //       Access: Public
@@ -269,6 +373,46 @@ make_orig(Character *character) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ComputedVertices::make_orig
+//       Access: Public
+//  Description: Copies all the values loaded in the _coords, _norms,
+//               etc. arrays into the corresponding _orig_coords,
+//               etc. arrays.
+////////////////////////////////////////////////////////////////////
+void ComputedVertices::
+make_orig(qpCharacter *character) {
+  nassertv(character != (qpCharacter *)NULL);
+
+  if (character->_cv._coords.empty()) {
+    _orig_coords.clear();
+  } else {
+    _orig_coords = PTA_Vertexf::empty_array(0);
+    _orig_coords.v() = character->_cv._coords.v();
+  }
+
+  if (character->_cv._norms.empty()) {
+    _orig_norms.clear();
+  } else {
+    _orig_norms = PTA_Normalf::empty_array(0);
+    _orig_norms.v() = character->_cv._norms.v();
+  }
+
+  if (character->_cv._colors.empty()) {
+    _orig_colors.clear();
+  } else {
+    _orig_colors = PTA_Colorf::empty_array(0);
+    _orig_colors.v() = character->_cv._colors.v();
+  }
+
+  if (character->_cv._texcoords.empty()) {
+    _orig_texcoords.clear();
+  } else {
+    _orig_texcoords = PTA_TexCoordf::empty_array(0);
+    _orig_texcoords.v() = character->_cv._texcoords.v();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ComputedVertices::write
 //       Access: Public

+ 6 - 3
panda/src/char/computedVertices.h

@@ -19,15 +19,16 @@
 #ifndef COMPUTEDVERTICES_H
 #define COMPUTEDVERTICES_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 #include "dynamicVertices.h"
 #include "computedVerticesMorph.h"
 
-#include <vector_ushort.h>
-#include <typedWritableReferenceCount.h>
+#include "vector_ushort.h"
+#include "typedWritableReferenceCount.h"
 
 class Character;
+class qpCharacter;
 class CharacterJoint;
 
 ////////////////////////////////////////////////////////////////////
@@ -42,7 +43,9 @@ public:
   INLINE ComputedVertices();
 
   void update(Character *character);
+  void update(qpCharacter *character);
   void make_orig(Character *character);
+  void make_orig(qpCharacter *character);
 
   void write(ostream &out, Character *character) const;
 

+ 3 - 0
panda/src/char/config_char.cxx

@@ -19,6 +19,7 @@
 
 #include "config_char.h"
 #include "character.h"
+#include "qpcharacter.h"
 #include "characterJoint.h"
 #include "characterJointBundle.h"
 #include "characterSlider.h"
@@ -67,6 +68,7 @@ init_libchar() {
   initialized = true;
 
   Character::init_type();
+  qpCharacter::init_type();
   CharacterJoint::init_type();
   CharacterJointBundle::init_type();
   CharacterSlider::init_type();
@@ -82,6 +84,7 @@ init_libchar() {
   //Registration of writeable object's creation
   //functions with BamReader's factory
   Character::register_with_read_factory();
+  qpCharacter::register_with_read_factory();
   CharacterJoint::register_with_read_factory();
   CharacterJointBundle::register_with_read_factory();
   CharacterSlider::register_with_read_factory();

+ 87 - 0
panda/src/char/qpcharacter.I

@@ -0,0 +1,87 @@
+// Filename: qpcharacter.I
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "characterJointBundle.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::get_bundle
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CharacterJointBundle *qpCharacter::
+get_bundle() const {
+  return DCAST(CharacterJointBundle, qpPartBundleNode::get_bundle());
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::get_computed_vertices
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ComputedVertices *qpCharacter::
+get_computed_vertices() const {
+  return _computed_vertices;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::get_num_parts
+//       Access: Public
+//  Description: Returns the total number of moving parts (e.g. joints
+//               and sliders) associated with the qpCharacter.
+////////////////////////////////////////////////////////////////////
+INLINE int qpCharacter::
+get_num_parts() const {
+  return _parts.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::get_part
+//       Access: Public
+//  Description: Returns the nth moving part associated with the
+//               qpCharacter.
+////////////////////////////////////////////////////////////////////
+INLINE PartGroup *qpCharacter::
+get_part(int n) const {
+  nassertr(n >= 0 && n < (int)_parts.size(), NULL);
+  return _parts[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::write_parts
+//       Access: Public
+//  Description: Writes a list of the qpCharacter's joints and sliders,
+//               in their hierchical structure, to the indicated
+//               output stream.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCharacter::
+write_parts(ostream &out) const {
+  get_bundle()->write(out, 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::write_part_values
+//       Access: Public
+//  Description: Writes a list of the qpCharacter's joints and sliders,
+//               along with each current position, in their hierchical
+//               structure, to the indicated output stream.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCharacter::
+write_part_values(ostream &out) const {
+  get_bundle()->write_with_value(out, 0);
+}

+ 538 - 0
panda/src/char/qpcharacter.cxx

@@ -0,0 +1,538 @@
+// Filename: qpcharacter.cxx
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpcharacter.h"
+#include "characterJoint.h"
+#include "computedVertices.h"
+#include "config_char.h"
+
+#include "geomNode.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "pStatTimer.h"
+#include "geomNode.h"
+#include "animControl.h"
+#include "clockObject.h"
+#include "pStatTimer.h"
+
+TypeHandle qpCharacter::_type_handle;
+
+#ifndef CPPPARSER
+PStatCollector qpCharacter::_anim_pcollector("App:Animation");
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::Copy Constructor
+//       Access: Protected
+//  Description: Use make_copy() or copy_subgraph() to copy a qpCharacter.
+////////////////////////////////////////////////////////////////////
+qpCharacter::
+qpCharacter(const qpCharacter &copy) :
+  qpPartBundleNode(copy.get_name(), new CharacterJointBundle(copy.get_bundle()->get_name())),
+  _cv(DynamicVertices::deep_copy(copy._cv)),
+  _computed_vertices(copy._computed_vertices),
+  _parts(copy._parts),
+  _char_pcollector(copy._char_pcollector)
+{
+  // Now make a copy of the joint/slider hierarchy.  We could just use
+  // the PartBundleNode's copy constructor, but if we do it ourselves
+  // we can simultaneously update our _parts list.
+
+  copy_joints(get_bundle(), copy.get_bundle());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpCharacter::
+qpCharacter(const string &name) :
+  qpPartBundleNode(name, new CharacterJointBundle(name)),
+  _char_pcollector(_anim_pcollector, name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpCharacter::
+~qpCharacter() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::make_copy
+//       Access: Public, Virtual
+//  Description: The qpCharacter make_copy() function will make a new
+//               copy of the qpCharacter, with all of its joints copied,
+//               and with a new set of dynamic vertex arrays all ready
+//               to go, but it will not copy any of the original
+//               qpCharacter's geometry, so the new qpCharacter won't look
+//               like much.  Use copy_subgraph() to make a full copy
+//               of the qpCharacter.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpCharacter::
+make_copy() const {
+  return new qpCharacter(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::safe_to_transform
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to transform
+//               this particular kind of Node by calling the xform()
+//               method, false otherwise.  For instance, it's usually
+//               a bad idea to attempt to xform a qpCharacter.
+////////////////////////////////////////////////////////////////////
+bool qpCharacter::
+safe_to_transform() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::has_cull_callback
+//       Access: Public, Virtual
+//  Description: Should be overridden by derived classes to return
+//               true if cull_callback() has been defined.  Otherwise,
+//               returns false to indicate cull_callback() does not
+//               need to be called for this node during the cull
+//               traversal.
+////////////////////////////////////////////////////////////////////
+bool qpCharacter::
+has_cull_callback() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::cull_callback
+//       Access: Public, Virtual
+//  Description: If has_cull_callback() returns true, this function
+//               will be called during the cull traversal to perform
+//               any additional operations that should be performed at
+//               cull time.  This may include additional manipulation
+//               of render state or additional visible/invisible
+//               decisions, or any other arbitrary operation.
+//
+//               By the time this function is called, the node has
+//               already passed the bounding-volume test for the
+//               viewing frustum, and the node's transform and state
+//               have already been applied to the indicated
+//               CullTraverserData object.
+//
+//               The return value is true if this node should be
+//               visible, or false if it should be culled.
+////////////////////////////////////////////////////////////////////
+bool qpCharacter::
+cull_callback(CullTraverserData &) {
+  // 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.
+  double now = ClockObject::get_global_clock()->get_frame_time();
+  get_bundle()->advance_time(now);
+
+  if (char_cat.is_spam()) {
+    char_cat.spam() << "Animating " << *this << " at time " << now << "\n";
+  }
+
+  update();
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::update
+//       Access: Public
+//  Description: Recalculates the qpCharacter's joints and vertices for
+//               the current frame.  Normally this is performed
+//               automatically during the render and need not be
+//               called explicitly.
+////////////////////////////////////////////////////////////////////
+void qpCharacter::
+update() {
+  // Statistics
+  PStatTimer timer(_char_pcollector);
+
+  // First, update all the joints and sliders.
+  bool any_changed = get_bundle()->update();
+
+  // Now update the vertices, if we need to.  This is likely to be a
+  // slow operation.
+  if (any_changed || even_animation) {
+    if (_computed_vertices != (ComputedVertices *)NULL) {
+      _computed_vertices->update(this);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::copy_joints
+//       Access: Private
+//  Description: Recursively walks the joint/slider hierarchy and
+//               creates a new copy of the hierarchy.
+////////////////////////////////////////////////////////////////////
+void qpCharacter::
+copy_joints(PartGroup *copy, PartGroup *orig) {
+  if (copy->get_type() != orig->get_type()) {
+    char_cat.warning()
+      << "Don't know how to copy " << orig->get_type() << "\n";
+  }
+
+  PartGroup::Children::const_iterator ci;
+  for (ci = orig->_children.begin(); ci != orig->_children.end(); ++ci) {
+    PartGroup *orig_child = (*ci);
+    PartGroup *copy_child = orig_child->make_copy();
+    copy->_children.push_back(copy_child);
+    copy_joints(copy_child, orig_child);
+  }
+
+  Parts::iterator pi = find(_parts.begin(), _parts.end(), orig);
+  if (pi != _parts.end()) {
+    (*pi) = copy;
+  }
+}
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::r_copy_subgraph
+//       Access: Private, Virtual
+//  Description: This is a virtual function inherited from Node.  It's
+//               called when a copy_subgraph() operation reaches the
+//               qpCharacter node.  In the case of a qpCharacter, it's
+//               overridden to do the all right things to copy the
+//               dynamic geometry to the new qpCharacter.
+//
+//               Note that it includes the parameter inst_map, which
+//               is a map type, and is not (and cannot be) exported
+//               from PANDA.DLL.  Thus, any derivative of Node that is
+//               not also a member of PANDA.DLL *cannot* access this
+//               map.
+////////////////////////////////////////////////////////////////////
+Node *qpCharacter::
+r_copy_subgraph(TypeHandle graph_type, Node::InstanceMap &) const {
+  Node *copy = make_copy();
+  nassertr(copy != (Node *)NULL, NULL);
+  if (copy->get_type() != get_type()) {
+    graph_cat.warning()
+      << "Don't know how to copy nodes of type " << get_type() << "\n";
+  }
+
+  // We assume there will be no instancing going on below the
+  // qpCharacter node.  If there is, too bad; it will get flattened out.
+
+  // Now we preempt the node's r_copy_subgraph() operation with our
+  // own function that keeps track of the old vs. new arcs and also
+  // updates any Geoms we find with our new dynamic vertices.
+
+  qpCharacter *char_copy;
+  DCAST_INTO_R(char_copy, copy, NULL);
+  ArcMap arc_map;
+  char_copy->r_copy_char(char_copy, this, graph_type, this, arc_map);
+  char_copy->copy_arc_pointers(this, arc_map);
+
+  return copy;
+}
+*/
+
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::r_copy_char
+//       Access: Private
+//  Description: Recursively walks the scene graph hierarchy below the
+//               qpCharacter node, duplicating it while noting the
+//               orig:copy arc mappings, and also updates any
+//               GeomNodes found.
+////////////////////////////////////////////////////////////////////
+void qpCharacter::
+r_copy_char(Node *dest, const Node *source, TypeHandle graph_type,
+            const qpCharacter *from, qpCharacter::ArcMap &arc_map) {
+  if (source->is_of_type(GeomNode::get_class_type())) {
+    const GeomNode *source_gnode;
+    GeomNode *dest_gnode;
+    DCAST_INTO_V(source_gnode, source);
+    DCAST_INTO_V(dest_gnode, dest);
+
+    dest_gnode->clear();
+    int num_geoms = source_gnode->get_num_geoms();
+    for (int i = 0; i < num_geoms; i++) {
+      dDrawable *d = source_gnode->get_geom(i);
+      if (d->is_of_type(Geom::get_class_type())) {
+        dest_gnode->add_geom(copy_geom(DCAST(Geom, d), from));
+      } else {
+        dest_gnode->add_geom(d);
+      }
+    }
+  }
+
+  int num_children = source->get_num_children(graph_type);
+  for (int i = 0; i < num_children; i++) {
+    NodeRelation *source_arc = source->get_child(graph_type, i);
+    const Node *source_child = source_arc->get_child();
+    nassertv(source_child != (Node *)NULL);
+
+    Node *dest_child;
+    if (source_child->is_of_type(qpCharacter::get_class_type())) {
+      // We make a special case for nodes of type qpCharacter.  If we
+      // encounter one of these, we have a qpCharacter under a
+      // qpCharacter, and the nested qpCharacter's copy should be called
+      // instead of ours.
+      dest_child = source_child->copy_subgraph(graph_type);
+
+    } else {
+      // Otherwise, we assume that make_copy() will make a suitable
+      // copy of the node.  This does limit the sorts of things we can
+      // have parented to a qpCharacter and expect copy_subgraph() to
+      // work correctly.  Too bad.
+      dest_child = source_child->make_copy();
+      r_copy_char(dest_child, source_child, graph_type, from, arc_map);
+    }
+
+    NodeRelation *dest_arc =
+      NodeRelation::create_typed_arc(graph_type, dest, dest_child);
+    nassertv(dest_arc != (NodeRelation *)NULL);
+    nassertv(dest_arc->is_exact_type(graph_type));
+
+    dest_arc->copy_transitions_from(source_arc);
+    arc_map[source_arc] = dest_arc;
+  }
+}
+*/
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::copy_geom
+//       Access: Private
+//  Description: Makes a new copy of the Geom with the dynamic vertex
+//               arrays replaced to reference this qpCharacter instead
+//               of the other one.  If no arrays have changed, simply
+//               returns the same Geom.
+////////////////////////////////////////////////////////////////////
+PT(Geom) qpCharacter::
+copy_geom(Geom *source, const qpCharacter *from) {
+  GeomBindType bind;
+  PTA_ushort index;
+
+  PTA_Vertexf coords;
+  PTA_Normalf norms;
+  PTA_Colorf colors;
+  PTA_TexCoordf texcoords;
+
+  PT(Geom) dest = source;
+
+  source->get_coords(coords, index);
+  if ((coords != (void *)NULL) && (coords == (from->_cv._coords))) {
+    if (dest == source) {
+      dest = source->make_copy();
+    }
+    dest->set_coords(_cv._coords, index);
+  }
+
+  source->get_normals(norms, bind, index);
+  if (bind != G_OFF && norms == from->_cv._norms) {
+    if (dest == source) {
+      dest = source->make_copy();
+    }
+    dest->set_normals(_cv._norms, bind, index);
+  }
+
+  source->get_colors(colors, bind, index);
+  if (bind != G_OFF && colors == from->_cv._colors) {
+    if (dest == source) {
+      dest = source->make_copy();
+    }
+    dest->set_colors(_cv._colors, bind, index);
+  }
+
+  source->get_texcoords(texcoords, bind, index);
+  if (bind != G_OFF && texcoords == from->_cv._texcoords) {
+    if (dest == source) {
+      dest = source->make_copy();
+    }
+    dest->set_texcoords(_cv._texcoords, bind, index);
+  }
+
+  return dest;
+}
+*/
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::copy_arc_pointers
+//       Access: Public
+//  Description: Creates _net_transform_arcs and _local_transform_arcs
+//               as appropriate in each of the qpCharacter's joints, as
+//               copied from the other qpCharacter.
+////////////////////////////////////////////////////////////////////
+void qpCharacter::
+copy_arc_pointers(const qpCharacter *from, const qpCharacter::ArcMap &arc_map) {
+  nassertv(_parts.size() == from->_parts.size());
+  for (int i = 0; i < (int)_parts.size(); i++) {
+    if (_parts[i]->is_of_type(CharacterJoint::get_class_type())) {
+      nassertv(_parts[i] != from->_parts[i]);
+      CharacterJoint *source_joint;
+      CharacterJoint *dest_joint;
+      DCAST_INTO_V(source_joint, from->_parts[i]);
+      DCAST_INTO_V(dest_joint, _parts[i]);
+
+      CharacterJoint::ArcList::const_iterator ai;
+      for (ai = source_joint->_net_transform_arcs.begin();
+           ai != source_joint->_net_transform_arcs.end();
+           ++ai) {
+        NodeRelation *source_arc = (*ai);
+
+        ArcMap::const_iterator mi;
+        mi = arc_map.find(source_arc);
+        if (mi != arc_map.end()) {
+          NodeRelation *dest_arc = (*mi).second;
+
+          // Here's an internal joint that the source qpCharacter was
+          // animating directly.  We'll animate our corresponding
+          // joint the same way.
+          dest_joint->add_net_transform(dest_arc);
+        }
+      }
+
+      for (ai = source_joint->_local_transform_arcs.begin();
+           ai != source_joint->_local_transform_arcs.end();
+           ++ai) {
+        NodeRelation *source_arc = (*ai);
+
+        ArcMap::const_iterator mi;
+        mi = arc_map.find(source_arc);
+        if (mi != arc_map.end()) {
+          NodeRelation *dest_arc = (*mi).second;
+
+          // Here's an internal joint that the source qpCharacter was
+          // animating directly.  We'll animate our corresponding
+          // joint the same way.
+          dest_joint->add_local_transform(dest_arc);
+        }
+      }
+    }
+  }
+}
+*/
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               qpCharacter.
+////////////////////////////////////////////////////////////////////
+void qpCharacter::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpCharacter::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  qpPartBundleNode::write_datagram(manager, dg);
+  _cv.write_datagram(manager, dg);
+  manager->write_pointer(dg, _computed_vertices);
+
+  dg.add_uint16(_parts.size());
+  Parts::const_iterator pi;
+  for (pi = _parts.begin(); pi != _parts.end(); pi++) {
+    manager->write_pointer(dg, (*pi));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpCharacter::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = qpPartBundleNode::complete_pointers(p_list, manager);
+  _computed_vertices = DCAST(ComputedVertices, p_list[pi++]);
+
+  int num_parts = _parts.size();
+  for (int i = 0; i < num_parts; i++) {
+    _parts[i] = DCAST(PartGroup, p_list[pi++]);
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type qpCharacter is encountered
+//               in the Bam file.  It should create the qpCharacter
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *qpCharacter::
+make_from_bam(const FactoryParams &params) {
+  qpCharacter *node = new qpCharacter("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCharacter::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpCharacter.
+////////////////////////////////////////////////////////////////////
+void qpCharacter::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  qpPartBundleNode::fillin(scan, manager);
+  _cv.fillin(scan, manager);
+  manager->read_pointer(scan, this);
+
+  // Read the number of parts to expect in the _parts list, and then
+  // fill the array up with NULLs.  We'll fill in the actual values in
+  // complete_pointers, later.
+  int num_parts = scan.get_uint16();
+  _parts.clear();
+  _parts.reserve(num_parts);
+  for (int i = 0; i < num_parts; i++) {
+    manager->read_pointer(scan, this);
+    _parts.push_back((PartGroup *)NULL);
+  }
+
+#ifdef DO_PSTATS
+  // Reinitialize our collector with our name, now that we know it.
+  if (has_name()) {
+    _char_pcollector =
+      PStatCollector(_anim_pcollector, get_name());
+  }
+#endif
+}

+ 134 - 0
panda/src/char/qpcharacter.h

@@ -0,0 +1,134 @@
+// Filename: qpcharacter.h
+// Created by:  drose (06Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpCHARACTER_H
+#define qpCHARACTER_H
+
+#include "pandabase.h"
+
+#include "computedVertices.h"
+
+#include "qppartBundleNode.h"
+#include "vector_PartGroupStar.h"
+#include "pointerTo.h"
+#include "geom.h"
+#include "pStatCollector.h"
+
+class CharacterJointBundle;
+class ComputedVertices;
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpCharacter
+// Description : An animated character, with skeleton-morph animation
+//               and either soft-skinned or hard-skinned vertices.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpCharacter : public qpPartBundleNode {
+protected:
+  qpCharacter(const qpCharacter &copy);
+
+public:
+  qpCharacter(const string &name);
+  virtual ~qpCharacter();
+
+  virtual PandaNode *make_copy() const;
+
+  virtual bool safe_to_transform() const;
+  virtual bool has_cull_callback() const;
+  virtual bool cull_callback(CullTraverserData &data);
+
+PUBLISHED:
+  INLINE CharacterJointBundle *get_bundle() const;
+
+  INLINE ComputedVertices *get_computed_vertices() const;
+  INLINE int get_num_parts() const;
+  INLINE PartGroup *get_part(int n) const;
+
+  INLINE void write_parts(ostream &out) const;
+  INLINE void write_part_values(ostream &out) const;
+
+  void update();
+
+private:
+  void copy_joints(PartGroup *copy, PartGroup *orig);
+
+  /*
+  typedef pmap<NodeRelation *, NodeRelation *> ArcMap;
+  virtual Node *r_copy_subgraph(TypeHandle graph_type,
+                                InstanceMap &inst_map) const;
+  void r_copy_char(Node *dest, const Node *source, TypeHandle graph_type,
+                   const qpCharacter *from, ArcMap &arc_map);
+  PT(Geom) copy_geom(Geom *source, const qpCharacter *from);
+  void copy_arc_pointers(const qpCharacter *from, const ArcMap &arc_map);
+  */
+
+  // These are the actual dynamic vertex pools for this qpCharacter's
+  // ComputedVertices--the vertices that it will recompute each frame
+  // based on the soft-skinning and morphing requirements.  Note that
+  // we store this concretely, instead of as a pointer, just because
+  // we don't really need to make it a pointer.
+  DynamicVertices _cv;
+
+  // And this is the object that animates them.  It *is* a pointer, so
+  // it can be shared between multiple instances of this qpCharacter.
+  PT(ComputedVertices) _computed_vertices;
+
+  // This vector is used by the ComputedVertices object to index back
+  // into our joints and sliders.
+  typedef vector_PartGroupStar Parts;
+  Parts _parts;
+
+  // Statistics
+  PStatCollector _char_pcollector;
+  static PStatCollector _anim_pcollector;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist,
+                                BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    qpPartBundleNode::init_type();
+    register_type(_type_handle, "qpCharacter",
+                  qpPartBundleNode::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class qpCharacterMaker;
+  friend class ComputedVerticesMaker;
+  friend class ComputedVertices;
+};
+
+#include "qpcharacter.I"
+
+#endif
+

+ 11 - 4
panda/src/egg2pg/Sources.pp

@@ -9,12 +9,22 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
   #define SOURCES \
+    animBundleMaker.h \
+    characterMaker.h \
+    qpcharacterMaker.h \
+    computedVerticesMaker.I computedVerticesMaker.h \
+    computedVerticesMakerEntity.I computedVerticesMakerEntity.h \
     config_egg2pg.h \
     eggBinner.h \
+    eggLoaderBase.h \
     qpeggLoader.h \
     qpload_egg_file.h
 
   #define INCLUDED_SOURCES \
+    animBundleMaker.cxx \
+    characterMaker.cxx \
+    qpcharacterMaker.cxx \
+    computedVerticesMaker.cxx \
     config_egg2pg.cxx \
     eggBinner.cxx \
     qpeggLoader.cxx \
@@ -27,9 +37,6 @@
   #endif
 
   #define INSTALL_HEADERS \
-    config_egg2pg.h \
-    eggBinner.h \
-    qpeggLoader.h \
-    qpload_egg_file.h
+    qpload_egg_file.h config_egg2pg.h
 
 #end lib_target

+ 31 - 20
panda/src/egg2sg/animBundleMaker.cxx → panda/src/egg2pg/animBundleMaker.cxx

@@ -17,18 +17,19 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "animBundleMaker.h"
-#include "config_egg2sg.h"
-
-#include <eggTable.h>
-#include <eggAnimData.h>
-#include <eggSAnimData.h>
-#include <eggXfmAnimData.h>
-#include <eggXfmSAnim.h>
-#include <eggGroupNode.h>
-#include <animBundle.h>
-#include <animBundleNode.h>
-#include <animChannelMatrixXfmTable.h>
-#include <animChannelScalarTable.h>
+#include "config_egg2pg.h"
+
+#include "eggTable.h"
+#include "eggAnimData.h"
+#include "eggSAnimData.h"
+#include "eggXfmAnimData.h"
+#include "eggXfmSAnim.h"
+#include "eggGroupNode.h"
+#include "animBundle.h"
+#include "animBundleNode.h"
+#include "qpanimBundleNode.h"
+#include "animChannelMatrixXfmTable.h"
+#include "animChannelScalarTable.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: AnimBundleMaker::Construtor
@@ -46,18 +47,18 @@ AnimBundleMaker(EggTable *root) : _root(root) {
   inspect_tree(root);
 
   if (!_ok_fps) {
-    egg2sg_cat.warning()
+    egg2pg_cat.warning()
       << "AnimBundle " << _root->get_name()
       << " specifies contradictory frame rates.\n";
   } else if (_fps == 0.0f) {
-    egg2sg_cat.warning()
+    egg2pg_cat.warning()
       << "AnimBundle " << _root->get_name()
       << " does not specify a frame rate.\n";
     _fps = 24.0f;
   }
 
   if (!_ok_num_frames) {
-    egg2sg_cat.warning()
+    egg2pg_cat.warning()
       << "AnimBundle " << _root->get_name()
       << " specifies contradictory number of frames.\n";
   }
@@ -74,6 +75,16 @@ make_node() {
   return new AnimBundleNode(make_bundle());
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AnimBundleMaker::make_qpnode
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpAnimBundleNode *AnimBundleMaker::
+make_qpnode() {
+  return new qpAnimBundleNode("", make_bundle());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: AnimBundleMaker::make_bundle
 //       Access: Private
@@ -196,7 +207,7 @@ build_hierarchy(EggTable *egg_table, AnimGroup *parent) {
       if (this_node == NULL) {
         this_node = create_xfm_channel((*ci), egg_table->get_name(), parent);
       } else {
-        egg2sg_cat.warning()
+        egg2pg_cat.warning()
           << "Duplicate xform table under node "
           << egg_table->get_name() << "\n";
       }
@@ -271,7 +282,7 @@ create_xfm_channel(EggNode *egg_node, const string &name,
     return create_xfm_channel(egg_anim, name, parent);
   }
 
-  egg2sg_cat.warning()
+  egg2pg_cat.warning()
     << "Inappropriate node named xform under node "
     << name << "\n";
   return NULL;
@@ -304,7 +315,7 @@ create_xfm_channel(EggXfmSAnim *egg_anim, const string &name,
       EggSAnimData *child = DCAST(EggSAnimData, *ci);
 
       if (child->get_name().empty()) {
-        egg2sg_cat.warning()
+        egg2pg_cat.warning()
           << "Unnamed subtable of <Xfm$Anim_S$> " << name
           << "\n";
       } else {
@@ -312,12 +323,12 @@ create_xfm_channel(EggXfmSAnim *egg_anim, const string &name,
 
         if (child->get_name().length() > 1 ||
             !table->is_valid_id(table_id)) {
-          egg2sg_cat.warning()
+          egg2pg_cat.warning()
             << "Unexpected table name " << child->get_name()
             << ", child of " << name << "\n";
 
         } else if (table->has_table(table_id)) {
-          egg2sg_cat.warning()
+          egg2pg_cat.warning()
             << "Duplicate table definition for " << table_id
             << " under " << name << "\n";
 

+ 4 - 5
panda/src/egg2sg/animBundleMaker.h → panda/src/egg2pg/animBundleMaker.h

@@ -19,11 +19,8 @@
 #ifndef ANIMBUNDLEMAKER_H
 #define ANIMBUNDLEMAKER_H
 
-#include <pandabase.h>
-
-#include <typedef.h>
-
-#include <string>
+#include "pandabase.h"
+#include "typedef.h"
 
 class EggNode;
 class EggGroupNode;
@@ -33,6 +30,7 @@ class EggSAnimData;
 class AnimGroup;
 class AnimBundle;
 class AnimBundleNode;
+class qpAnimBundleNode;
 class AnimChannelScalarTable;
 class AnimChannelMatrixXfmTable;
 
@@ -46,6 +44,7 @@ public:
   AnimBundleMaker(EggTable *root);
 
   AnimBundleNode *make_node();
+  qpAnimBundleNode *make_qpnode();
 
 private:
   AnimBundle *make_bundle();

+ 14 - 14
panda/src/egg2sg/characterMaker.cxx → panda/src/egg2pg/characterMaker.cxx

@@ -17,19 +17,19 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "characterMaker.h"
-#include "eggLoader.h"
-#include "config_egg2sg.h"
-
-#include <computedVertices.h>
-#include <eggGroup.h>
-#include <eggPrimitive.h>
-#include <partGroup.h>
-#include <characterJoint.h>
-#include <characterJointBundle.h>
-#include <characterSlider.h>
-#include <character.h>
-#include <renderRelation.h>
-#include <transformTransition.h>
+#include "qpeggLoader.h"
+#include "config_egg2pg.h"
+
+#include "computedVertices.h"
+#include "eggGroup.h"
+#include "eggPrimitive.h"
+#include "partGroup.h"
+#include "characterJoint.h"
+#include "characterJointBundle.h"
+#include "characterSlider.h"
+#include "character.h"
+#include "renderRelation.h"
+#include "transformTransition.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CharacterMaker::Construtor
@@ -37,7 +37,7 @@
 //  Description:
 ////////////////////////////////////////////////////////////////////
 CharacterMaker::
-CharacterMaker(EggGroup *root, EggLoader &loader)
+CharacterMaker(EggGroup *root, EggLoaderBase &loader)
   : _loader(loader), _egg_root(root) {
 
   _character_node = new Character(_egg_root->get_name());

+ 6 - 8
panda/src/egg2sg/characterMaker.h → panda/src/egg2pg/characterMaker.h

@@ -19,14 +19,12 @@
 #ifndef CHARACTERMAKER_H
 #define CHARACTERMAKER_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 #include "computedVerticesMaker.h"
 
-#include <vector_PartGroupStar.h>
-#include <typedef.h>
-
-#include <string>
+#include "vector_PartGroupStar.h"
+#include "typedef.h"
 #include "pmap.h"
 
 class EggNode;
@@ -39,7 +37,7 @@ class Character;
 class CharacterSlider;
 class MovingPartBase;
 class NamedNode;
-class EggLoader;
+class EggLoaderBase;
 
 ///////////////////////////////////////////////////////////////////
 //       Class : CharacterMaker
@@ -49,7 +47,7 @@ class EggLoader;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDAEGG CharacterMaker {
 public:
-  CharacterMaker(EggGroup *root, EggLoader &loader);
+  CharacterMaker(EggGroup *root, EggLoaderBase &loader);
 
   Character *make_node();
 
@@ -78,7 +76,7 @@ private:
   typedef vector_PartGroupStar Parts;
   Parts _parts;
 
-  EggLoader &_loader;
+  EggLoaderBase &_loader;
   EggGroup *_egg_root;
   Character *_character_node;
   CharacterJointBundle *_bundle;

+ 0 - 0
panda/src/egg2sg/computedVerticesMaker.I → panda/src/egg2pg/computedVerticesMaker.I


+ 141 - 6
panda/src/egg2sg/computedVerticesMaker.cxx → panda/src/egg2pg/computedVerticesMaker.cxx

@@ -19,12 +19,12 @@
 #include "computedVerticesMaker.h"
 #include "characterMaker.h"
 
-#include <characterJoint.h>
-#include <character.h>
-#include <computedVertices.h>
-#include <eggNode.h>
-#include <eggGroup.h>
-#include <eggVertex.h>
+#include "characterJoint.h"
+#include "character.h"
+#include "computedVertices.h"
+#include "eggNode.h"
+#include "eggGroup.h"
+#include "eggVertex.h"
 
 #include <algorithm>
 
@@ -447,6 +447,141 @@ make_computed_vertices(Character *character, CharacterMaker &char_maker) {
   return comp_verts;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ComputedVerticesMaker::make_computed_vertices
+//       Access: Public
+//  Description: After all spaces have been defined and all vertices
+//               added, creates a new ComputedVertices object and
+//               returns it.
+////////////////////////////////////////////////////////////////////
+ComputedVertices *ComputedVerticesMaker::
+make_computed_vertices(qpCharacter *character, qpCharacterMaker &char_maker) {
+  // We must first build up a set of all the unique kinds of vertex
+  // transforms.
+  typedef pset<ComputedVertices::VertexTransform> VertexTransforms;
+  VertexTransforms transforms;
+
+  TransformSpaces::const_iterator tsi;
+  for (tsi = _transforms.begin();
+       tsi != _transforms.end();
+       ++tsi) {
+    const JointWeights &jw = (*tsi).first;
+    const VertexCollection &vc = (*tsi).second;
+
+    JointWeights::const_iterator jwi;
+    for (jwi = jw.begin(); jwi != jw.end(); ++jwi) {
+      double weight = (*jwi).second;
+      EggNode *egg_joint = (*jwi).first;
+      int joint_index = char_maker.egg_to_index(egg_joint);
+
+      // Look for a VertexTransform that matches this template.
+      ComputedVertices::VertexTransform new_vt;
+      new_vt._joint_index = joint_index;
+      new_vt._effect = (float)weight;
+
+      // This will either insert the VertexTransform into the set and
+      // return its newly-created iterator, or it will return the
+      // iterator referring to the previously-inserted VertexTransform
+      // like this.
+      VertexTransforms::iterator vti = transforms.insert(new_vt).first;
+
+      // We can discard the const-ness of the set's iterator, because
+      // we will only be changing a part of the VertexTransform that
+      // doesn't affect its sort order within the set.
+      ComputedVertices::VertexTransform &insert_vt =
+        (ComputedVertices::VertexTransform &)*vti;
+
+      // Now add in all the vertices and normals.
+      copy(vc._vindex.begin(), vc._vindex.end(),
+           back_inserter(insert_vt._vindex));
+      copy(vc._nindex.begin(), vc._nindex.end(),
+           back_inserter(insert_vt._nindex));
+    }
+  }
+
+  // Ok, now we have the set of all VertexTransforms.  Create a
+  // ComputedVertices object that reflects this.
+  ComputedVertices *comp_verts = new ComputedVertices;
+  copy(transforms.begin(), transforms.end(),
+       back_inserter(comp_verts->_transforms));
+
+  character->_cv._coords = _coords;
+  character->_cv._norms = _norms;
+  character->_cv._colors = _colors;
+  character->_cv._texcoords = _texcoords;
+
+  // Finally, add in all the morph definitions.
+  Morphs::const_iterator mi;
+  for (mi = _morphs.begin(); mi != _morphs.end(); ++mi) {
+    const string &name = (*mi).first;
+    const MorphList &mlist = (*mi).second;
+
+    int slider_index = char_maker.create_slider(name);
+
+    if (!mlist._vmorphs.empty()) {
+      // We push an empty MorphVertex object and then modify it,
+      // rather than filling it first and then pushing it, just to
+      // avoid unnecessary copying of data.
+      comp_verts->_vertex_morphs.push_back(ComputedVerticesMorphVertex());
+      ComputedVerticesMorphVertex &mv = comp_verts->_vertex_morphs.back();
+      mv._slider_index = slider_index;
+
+      VertexMorphList::const_iterator vmi;
+      for (vmi = mlist._vmorphs.begin();
+           vmi != mlist._vmorphs.end();
+           ++vmi) {
+        mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first,
+                                                         (*vmi).second));
+      }
+    }
+
+    if (!mlist._nmorphs.empty()) {
+      comp_verts->_normal_morphs.push_back(ComputedVerticesMorphNormal());
+      ComputedVerticesMorphNormal &mv = comp_verts->_normal_morphs.back();
+      mv._slider_index = slider_index;
+
+      NormalMorphList::const_iterator vmi;
+      for (vmi = mlist._nmorphs.begin();
+           vmi != mlist._nmorphs.end();
+           ++vmi) {
+        mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first,
+                                                         (*vmi).second));
+      }
+    }
+
+    if (!mlist._tmorphs.empty()) {
+      comp_verts->_texcoord_morphs.push_back(ComputedVerticesMorphTexCoord());
+      ComputedVerticesMorphTexCoord &mv = comp_verts->_texcoord_morphs.back();
+      mv._slider_index = slider_index;
+
+      TexCoordMorphList::const_iterator vmi;
+      for (vmi = mlist._tmorphs.begin();
+           vmi != mlist._tmorphs.end();
+           ++vmi) {
+        mv._morphs.push_back(ComputedVerticesMorphValue2((*vmi).first,
+                                                         (*vmi).second));
+      }
+    }
+
+    if (!mlist._cmorphs.empty()) {
+      comp_verts->_color_morphs.push_back(ComputedVerticesMorphColor());
+      ComputedVerticesMorphColor &mv = comp_verts->_color_morphs.back();
+      mv._slider_index = slider_index;
+
+      ColorMorphList::const_iterator vmi;
+      for (vmi = mlist._cmorphs.begin();
+           vmi != mlist._cmorphs.end();
+           ++vmi) {
+        mv._morphs.push_back(ComputedVerticesMorphValue4((*vmi).first,
+                                                         (*vmi).second));
+      }
+    }
+  }
+
+  comp_verts->make_orig(character);
+  return comp_verts;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ComputedVerticesMaker::write

+ 14 - 10
panda/src/egg2sg/computedVerticesMaker.h → panda/src/egg2pg/computedVerticesMaker.h

@@ -19,25 +19,26 @@
 #ifndef COMPUTEDVERTICESMAKER_H
 #define COMPUTEDVERTICESMAKER_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 #include "computedVerticesMakerEntity.h"
 
-#include <computedVerticesMorph.h>
-#include <pointerToArray.h>
-#include <luse.h>
-#include <typedef.h>
-#include <eggMorphList.h>
-#include <pta_Vertexf.h>
-#include <pta_Normalf.h>
-#include <pta_Colorf.h>
-#include <pta_TexCoordf.h>
+#include "computedVerticesMorph.h"
+#include "pointerToArray.h"
+#include "luse.h"
+#include "typedef.h"
+#include "eggMorphList.h"
+#include "pta_Vertexf.h"
+#include "pta_Normalf.h"
+#include "pta_Colorf.h"
+#include "pta_TexCoordf.h"
 
 #include "pset.h"
 #include "pmap.h"
 
 class ComputedVertices;
 class CharacterMaker;
+class qpCharacterMaker;
 class EggNode;
 class EggVertex;
 
@@ -81,6 +82,9 @@ public:
   ComputedVertices *make_computed_vertices(Character *character,
                                            CharacterMaker &char_maker);
 
+  ComputedVertices *make_computed_vertices(qpCharacter *character,
+                                           qpCharacterMaker &char_maker);
+
   void write(ostream &out) const;
 
 public:

+ 0 - 1
panda/src/egg2sg/computedVerticesMakerEntity.I → panda/src/egg2pg/computedVerticesMakerEntity.I

@@ -16,7 +16,6 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include <math.h>
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ComputedVerticesMakerEntity::Constructor

+ 6 - 5
panda/src/egg2sg/computedVerticesMakerEntity.h → panda/src/egg2pg/computedVerticesMakerEntity.h

@@ -19,14 +19,15 @@
 #ifndef COMPUTEDVERTICESMAKERENTITY_H
 #define COMPUTEDVERTICESMAKERENTITY_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
-#include <luse.h>
-#include <eggMorphList.h>
-#include <typedef.h>
-#include <pointerToArray.h>
+#include "luse.h"
+#include "eggMorphList.h"
+#include "typedef.h"
+#include "pointerToArray.h"
 
 #include "pmap.h"
+#include <math.h>
 
 ///////////////////////////////////////////////////////////////////
 //       Class : ComputedVerticesMakerEntity

+ 4 - 2
panda/src/egg2pg/egg2pg_composite1.cxx

@@ -1,2 +1,4 @@
-#include "config_egg2pg.cxx"
-#include "eggBinner.cxx"
+#include "animBundleMaker.cxx"
+#include "characterMaker.cxx"
+#include "qpcharacterMaker.cxx"
+#include "computedVerticesMaker.cxx"

+ 2 - 0
panda/src/egg2pg/egg2pg_composite2.cxx

@@ -1,3 +1,5 @@
+#include "config_egg2pg.cxx"
+#include "eggBinner.cxx"
 #include "qpeggLoader.cxx"
 #include "qpload_egg_file.cxx"
 

+ 9 - 11
panda/src/egg2pg/qpeggLoader.cxx

@@ -20,6 +20,7 @@
 
 #include "qpeggLoader.h"
 #include "config_egg2pg.h"
+#include "nodeChain.h"
 #include "renderState.h"
 #include "transformState.h"
 #include "textureAttrib.h"
@@ -48,7 +49,10 @@
 #include "eggBin.h"
 #include "eggTable.h"
 #include "eggBinner.h"
-#include "nodeChain.h"
+#include "qpcharacterMaker.h"
+#include "qpcharacter.h"
+#include "animBundleMaker.h"
+#include "qpanimBundleNode.h"
 
 #include <ctype.h>
 #include <algorithm>
@@ -283,7 +287,6 @@ void qpEggLoader::
 make_indexed_primitive(EggPrimitive *egg_prim, PandaNode *parent,
                        const LMatrix4d *transform,
                        ComputedVerticesMaker &_comp_verts_maker) {
-  /*
   BuilderBucket bucket;
   setup_bucket(bucket, parent, egg_prim);
 
@@ -396,7 +399,6 @@ make_indexed_primitive(EggPrimitive *egg_prim, PandaNode *parent,
   }
 
   _builder.add_prim(bucket, bprim);
-  */
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1338,11 +1340,9 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
   }
 
   if (egg_group->get_dart_type() != EggGroup::DT_none) {
-    /*
     // A group with the <Dart> flag set means to create a character.
-    CharacterMaker char_maker(egg_group, *this);
+    qpCharacterMaker char_maker(egg_group, *this);
     node = char_maker.make_node();
-    */
 
   } else if (egg_group->get_cs_type() != EggGroup::CST_none &&
              egg_group->get_cs_type() != EggGroup::CST_geode) {
@@ -1493,8 +1493,6 @@ create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
 ////////////////////////////////////////////////////////////////////
 PandaNode *qpEggLoader::
 make_node(EggTable *egg_table, PandaNode *parent) {
-  return (PandaNode *)NULL;
-  /*
   if (egg_table->get_table_type() != EggTable::TT_bundle) {
     // We only do anything with bundles.  Isolated tables are treated
     // as ordinary groups.
@@ -1504,9 +1502,9 @@ make_node(EggTable *egg_table, PandaNode *parent) {
   // It's an actual bundle, so make an AnimBundle from it and its
   // descendants.
   AnimBundleMaker bundle_maker(egg_table);
-  AnimBundleNode *node = bundle_maker.make_node();
-  return new PandaNode(parent, node);
-  */
+  qpAnimBundleNode *node = bundle_maker.make_qpnode();
+  parent->add_child(node);
+  return node;
 }
 
 

+ 3 - 1
panda/src/egg2pg/qpeggLoader.h

@@ -35,6 +35,8 @@
 #include "indirectCompareTo.h"
 #include "textureAttrib.h"
 
+#include "eggLoaderBase.h"
+
 class EggNode;
 class EggBin;
 class EggTable;
@@ -57,7 +59,7 @@ class CollisionPolygon;
 //
 //               This class isn't exported from this package.
 ////////////////////////////////////////////////////////////////////
-class qpEggLoader {
+class qpEggLoader : public EggLoaderBase {
 public:
   qpEggLoader();
   qpEggLoader(const EggData &data);

+ 1 - 3
panda/src/egg2sg/Sources.pp

@@ -11,13 +11,11 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
   #define SOURCES \
-     animBundleMaker.h characterMaker.h computedVerticesMaker.I  \
-     computedVerticesMaker.h config_egg2sg.h  \
+     config_egg2sg.h  \
      deferredArcProperty.h deferredArcTraverser.h  \
      eggLoader.h load_egg_file.h loaderFileTypeEgg.h 
 
   #define INCLUDED_SOURCES \
-     animBundleMaker.cxx characterMaker.cxx computedVerticesMaker.cxx  \
      config_egg2sg.cxx deferredArcProperty.cxx  \
      deferredArcTraverser.cxx eggLoader.cxx  \
      load_egg_file.cxx loaderFileTypeEgg.cxx 

+ 0 - 3
panda/src/egg2sg/egg2sg_composite1.cxx

@@ -1,6 +1,3 @@
 
-#include "animBundleMaker.cxx"
-#include "characterMaker.cxx"
-#include "computedVerticesMaker.cxx"
 #include "deferredArcProperty.cxx"
 #include "deferredArcTraverser.cxx"

+ 2 - 1
panda/src/egg2sg/eggLoader.h

@@ -22,6 +22,7 @@
 #include <pandabase.h>
 
 #include "deferredArcTraverser.h"
+#include "eggLoaderBase.h"
 
 #include <eggData.h>
 #include <eggTexture.h>
@@ -62,7 +63,7 @@ class CollisionPolygon;
 //
 //               This class isn't exported from this package.
 ////////////////////////////////////////////////////////////////////
-class EggLoader {
+class EggLoader : public EggLoaderBase {
 public:
   EggLoader();
   EggLoader(const EggData &data);

+ 9 - 0
panda/src/testbed/pview.cxx

@@ -34,6 +34,7 @@
 #include "texturePool.h"
 #include "dSearchPath.h"
 #include "loader.h"
+#include "auto_bind.h"
 
 // These are in support of legacy data graph operations.
 #include "namedNode.h"
@@ -294,6 +295,14 @@ main(int argc, char *argv[]) {
   // Put something in the scene graph to look at.
   get_models(render, argc, argv);
 
+  // If we happened to load up both a character file and its matching
+  // animation file, attempt to bind them together now and start the
+  // animations looping.
+  AnimControlCollection anim_controls;
+  auto_bind(render, anim_controls, ~0);
+  anim_controls.loop_all(true);
+
+
   // Tick the clock once so we won't count the time spent loading up
   // files, above, in our frame rate average.
   ClockObject::get_global_clock()->tick();