2
0
David Rose 24 жил өмнө
parent
commit
5306f05c77
75 өөрчлөгдсөн 1033 нэмэгдсэн , 310 устгасан
  1. 1 1
      panda/src/chan/animBundleNode.cxx
  2. 1 1
      panda/src/chan/animBundleNode.h
  3. 1 1
      panda/src/chan/animGroup.cxx
  4. 1 1
      panda/src/chan/animGroup.h
  5. 1 1
      panda/src/chan/partBundleNode.cxx
  6. 1 1
      panda/src/chan/partBundleNode.h
  7. 1 1
      panda/src/chan/partGroup.cxx
  8. 1 1
      panda/src/chan/partGroup.h
  9. 1 1
      panda/src/char/character.cxx
  10. 1 1
      panda/src/char/character.h
  11. 1 1
      panda/src/char/characterJoint.cxx
  12. 1 1
      panda/src/char/characterJoint.h
  13. 1 1
      panda/src/collide/collisionNode.cxx
  14. 1 1
      panda/src/collide/collisionNode.h
  15. 0 7
      panda/src/display/graphicsStateGuardian.h
  16. 1 1
      panda/src/effects/lensFlareNode.cxx
  17. 1 1
      panda/src/effects/lensFlareNode.h
  18. 13 1
      panda/src/egg2sg/loaderFileTypeEgg.cxx
  19. 3 2
      panda/src/egg2sg/loaderFileTypeEgg.h
  20. 1 1
      panda/src/gobj/geomSprite.cxx
  21. 1 1
      panda/src/gobj/geomSprite.h
  22. 1 1
      panda/src/graph/node.cxx
  23. 1 1
      panda/src/graph/node.h
  24. 1 1
      panda/src/graph/nodeRelation.cxx
  25. 1 1
      panda/src/graph/nodeRelation.h
  26. 1 1
      panda/src/gsgbase/Sources.pp
  27. 8 0
      panda/src/gsgbase/graphicsStateGuardianBase.h
  28. 1 1
      panda/src/loader/Sources.pp
  29. 1 7
      panda/src/loader/bamFile.cxx
  30. 13 0
      panda/src/loader/loader.I
  31. 140 10
      panda/src/loader/loader.cxx
  32. 12 11
      panda/src/loader/loader.h
  33. 13 0
      panda/src/loader/loaderFileType.cxx
  34. 8 5
      panda/src/loader/loaderFileType.h
  35. 50 0
      panda/src/loader/loaderFileTypeBam.cxx
  36. 4 3
      panda/src/loader/loaderFileTypeBam.h
  37. 1 3
      panda/src/parametrics/piecewiseCurve.cxx
  38. 1 1
      panda/src/parametrics/piecewiseCurve.h
  39. 18 1
      panda/src/pgraph/billboardAttrib.cxx
  40. 7 1
      panda/src/pgraph/colorAttrib.cxx
  41. 1 0
      panda/src/pgraph/config_pgraph.cxx
  42. 7 1
      panda/src/pgraph/cullBinAttrib.cxx
  43. 5 1
      panda/src/pgraph/cullFaceAttrib.cxx
  44. 71 1
      panda/src/pgraph/pandaNode.cxx
  45. 2 0
      panda/src/pgraph/pandaNode.h
  46. 1 1
      panda/src/pgraph/qpcullTraverser.cxx
  47. 47 0
      panda/src/pgraph/qpgeomNode.cxx
  48. 2 0
      panda/src/pgraph/qpgeomNode.h
  49. 33 26
      panda/src/pgraph/renderAttrib.cxx
  50. 1 0
      panda/src/pgraph/renderAttrib.h
  51. 13 0
      panda/src/pgraph/renderState.I
  52. 94 30
      panda/src/pgraph/renderState.cxx
  53. 3 1
      panda/src/pgraph/renderState.h
  54. 25 1
      panda/src/pgraph/textureAttrib.cxx
  55. 1 0
      panda/src/pgraph/textureAttrib.h
  56. 75 30
      panda/src/pgraph/transformState.cxx
  57. 1 1
      panda/src/pgraph/transformState.h
  58. 5 1
      panda/src/pgraph/transparencyAttrib.cxx
  59. 198 61
      panda/src/putil/bamReader.cxx
  60. 16 3
      panda/src/putil/bamReader.h
  61. 14 7
      panda/src/putil/bamWriter.cxx
  62. 7 7
      panda/src/putil/bamWriter.h
  63. 3 3
      panda/src/putil/test_bam.cxx
  64. 3 3
      panda/src/putil/test_bam.h
  65. 59 7
      panda/src/putil/typedWritable.cxx
  66. 10 32
      panda/src/putil/typedWritable.h
  67. 1 1
      panda/src/putil/writable.h
  68. 1 1
      panda/src/sgattrib/materialTransition.cxx
  69. 1 1
      panda/src/sgattrib/materialTransition.h
  70. 1 1
      panda/src/sgattrib/textureTransition.cxx
  71. 1 1
      panda/src/sgattrib/textureTransition.h
  72. 1 1
      panda/src/sgraph/geomNode.cxx
  73. 1 1
      panda/src/sgraph/geomNode.h
  74. 2 4
      panda/src/testbed/Sources.pp
  75. 11 6
      panda/src/testbed/pview.cxx

+ 1 - 1
panda/src/chan/animBundleNode.cxx

@@ -75,7 +75,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int AnimBundleNode::
-complete_pointers(vector_typedWritable &p_list, BamReader* manager)
+complete_pointers(TypedWritable **p_list, BamReader* manager)
 {
   int start = NamedNode::complete_pointers(p_list, manager);
   _bundle = DCAST(AnimBundle, p_list[start]);

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

@@ -48,7 +48,7 @@ private:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 
   static TypedWritable *make_AnimBundleNode(const FactoryParams &params);

+ 1 - 1
panda/src/chan/animGroup.cxx

@@ -221,7 +221,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int AnimGroup::
-complete_pointers(vector_typedWritable &p_list, BamReader*)
+complete_pointers(TypedWritable **p_list, BamReader*)
 {
   nassertr(p_list[0] != TypedWritable::Null, 0);
   _root = DCAST(AnimBundle, p_list[0]);

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

@@ -72,7 +72,7 @@ protected:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 
   static TypedWritable *make_AnimGroup(const FactoryParams &params);

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

@@ -76,7 +76,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int PartBundleNode::
-complete_pointers(vector_typedWritable &p_list, BamReader* manager)
+complete_pointers(TypedWritable **p_list, BamReader* manager)
 {
   int start = NamedNode::complete_pointers(p_list, manager);
   _bundle = DCAST(PartBundle, p_list[start]);

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

@@ -50,7 +50,7 @@ private:
 
 public:
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 
 protected:

+ 1 - 1
panda/src/chan/partGroup.cxx

@@ -509,7 +509,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int PartGroup::
-complete_pointers(vector_typedWritable &p_list, BamReader*)
+complete_pointers(TypedWritable **p_list, BamReader*)
 {
   int i;
   for(i = 0; i < _num_children; i++)

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

@@ -98,7 +98,7 @@ protected:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 
   static TypedWritable *make_PartGroup(const FactoryParams &params);

+ 1 - 1
panda/src/char/character.cxx

@@ -456,7 +456,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int Character::
-complete_pointers(vector_typedWritable &p_list, BamReader* manager)
+complete_pointers(TypedWritable **p_list, BamReader* manager)
 {
   int start = PartBundleNode::complete_pointers(p_list, manager);
   _computed_vertices = DCAST(ComputedVertices, p_list[start]);

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

@@ -99,7 +99,7 @@ private:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 
   static TypedWritable *make_Character(const FactoryParams &params);

+ 1 - 1
panda/src/char/characterJoint.cxx

@@ -359,7 +359,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int CharacterJoint::
-complete_pointers(vector_typedWritable &p_list, BamReader* manager)
+complete_pointers(TypedWritable **p_list, BamReader* manager)
 {
   int i;
   int start = MovingPartMatrix::complete_pointers(p_list, manager);

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

@@ -65,7 +65,7 @@ private:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 
   static TypedWritable *make_CharacterJoint(const FactoryParams &params);

+ 1 - 1
panda/src/collide/collisionNode.cxx

@@ -303,7 +303,7 @@ fillin(DatagramIterator& scan, BamReader* manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int CollisionNode::
-complete_pointers(vector_typedWritable &p_list, BamReader* manager) {
+complete_pointers(TypedWritable **p_list, BamReader* manager) {
   int num_solids = _solids.size();
   int start = NamedNode::complete_pointers(p_list, manager);
 

+ 1 - 1
panda/src/collide/collisionNode.h

@@ -80,7 +80,7 @@ private:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
   static TypedWritable *make_CollisionNode(const FactoryParams &params);

+ 0 - 7
panda/src/display/graphicsStateGuardian.h

@@ -170,13 +170,6 @@ public:
   INLINE CoordinateSystem get_coordinate_system() const;
   virtual CoordinateSystem get_internal_coordinate_system() const;
 
-  // This function may only be called during a render traversal; it
-  // will compute the distance to the indicated point, assumed to be
-  // in modelview coordinates, from the camera plane.  This is a
-  // virtual function because different GSG's may define the modelview
-  // coordinate space differently.
-  virtual float compute_distance_to(const LPoint3f &point) const=0;
-
   INLINE void clear_cached_state(void) { _state.clear(); };  
 
   virtual void issue_transform(const TransformState *transform);

+ 1 - 1
panda/src/effects/lensFlareNode.cxx

@@ -507,7 +507,7 @@ fillin(DatagramIterator &scan, BamReader *manager)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int LensFlareNode::
-complete_pointers(vector_typedWritable &p_list, BamReader *manager)
+complete_pointers(TypedWritable **p_list, BamReader *manager)
 {
   int i;
   int start = Node::complete_pointers(p_list, manager);

+ 1 - 1
panda/src/effects/lensFlareNode.h

@@ -106,7 +106,7 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
   static TypedWritable *make_LensFlareNode(const FactoryParams &params);

+ 13 - 1
panda/src/egg2sg/loaderFileTypeEgg.cxx

@@ -18,8 +18,9 @@
 
 #include "loaderFileTypeEgg.h"
 #include "load_egg_file.h"
+#include "qpload_egg_file.h"
 
-#include <eggData.h>
+#include "eggData.h"
 
 TypeHandle LoaderFileTypeEgg::_type_handle;
 
@@ -74,3 +75,14 @@ load_file(const Filename &path, bool) const {
   PT_NamedNode result = load_egg_file(path);
   return result.p();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileTypeEgg::load_file
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) LoaderFileTypeEgg::
+qpload_file(const Filename &path, bool) const {
+  PT(PandaNode) result = qpload_egg_file(path);
+  return result;
+}

+ 3 - 2
panda/src/egg2sg/loaderFileTypeEgg.h

@@ -19,9 +19,9 @@
 #ifndef LOADERFILETYPEEGG_H
 #define LOADERFILETYPEEGG_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
-#include <loaderFileType.h>
+#include "loaderFileType.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : LoaderFileTypeEgg
@@ -36,6 +36,7 @@ public:
 
   virtual void resolve_filename(Filename &path) const;
   virtual PT_Node load_file(const Filename &path, bool report_errors) const;
+  virtual PT(PandaNode) qpload_file(const Filename &path, bool report_errors) const;
 
 public:
   static TypeHandle get_class_type() {

+ 1 - 1
panda/src/gobj/geomSprite.cxx

@@ -152,7 +152,7 @@ register_with_read_factory(void) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 int GeomSprite::
-complete_pointers(vector_typedWritable &p_list, BamReader *manager) {
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int index = Geom::complete_pointers(p_list, manager);
   _texture = DCAST(Texture, p_list[index]);
 

+ 1 - 1
panda/src/gobj/geomSprite.h

@@ -88,7 +88,7 @@ public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);
 
-  int complete_pointers(vector_typedWritable &plist, BamReader *manager);
+  int complete_pointers(TypedWritable **plist, BamReader *manager);
   static TypedWritable *make_GeomSprite(const FactoryParams &params);
 
 protected:

+ 1 - 1
panda/src/graph/node.cxx

@@ -562,7 +562,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int Node::
-complete_pointers(vector_typedWritable &p_list, BamReader *manager) {
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
   if (manager->get_file_minor_ver() < 3) {
     // In bam versions before 3.3, this function does nothing (since
     // the arcs are completely responsible for adding themselves to

+ 1 - 1
panda/src/graph/node.h

@@ -125,7 +125,7 @@ protected:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
   static TypedWritable *make_Node(const FactoryParams &params);

+ 1 - 1
panda/src/graph/nodeRelation.cxx

@@ -902,7 +902,7 @@ write_datagram(BamWriter *manager, Datagram &me)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int NodeRelation::
-complete_pointers(vector_typedWritable &p_list, BamReader *manager) {
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
   nassertr(p_list[0] != TypedWritable::Null &&
            p_list[1] != TypedWritable::Null, 0);
   _parent = DCAST(Node, p_list[0]);

+ 1 - 1
panda/src/graph/nodeRelation.h

@@ -192,7 +192,7 @@ private:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
   static TypedWritable *make_NodeRelation(const FactoryParams &params);

+ 1 - 1
panda/src/gsgbase/Sources.pp

@@ -4,7 +4,7 @@
 #begin lib_target
   #define TARGET gsgbase
   #define LOCAL_LIBS \
-    putil graph
+    putil graph linmath
     
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx     
 

+ 8 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -23,6 +23,7 @@
 
 #include "typedReferenceCount.h"
 #include "nodeTransitions.h"
+#include "luse.h"
 
 // A handful of forward references.
 
@@ -148,6 +149,13 @@ public:
   virtual void set_state_and_transform(const RenderState *state,
                                        const TransformState *transform)=0;
 
+  // This function may only be called during a render traversal; it
+  // will compute the distance to the indicated point, assumed to be
+  // in modelview coordinates, from the camera plane.  This is a
+  // virtual function because different GSG's may define the modelview
+  // coordinate space differently.
+  virtual float compute_distance_to(const LPoint3f &point) const=0;
+
   // Defined here are some internal interface functions for the
   // GraphicsStateGuardian.  These are here to support
   // double-dispatching from Geoms and NodeTransitions, and are

+ 1 - 1
panda/src/loader/Sources.pp

@@ -4,7 +4,7 @@
 #begin lib_target
   #define TARGET loader
   #define LOCAL_LIBS \
-    event graph ipc putil express downloader
+    event pgraph graph ipc putil express downloader
     
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
 

+ 1 - 7
panda/src/loader/bamFile.cxx

@@ -192,13 +192,7 @@ write_object(const TypedWritable *object) {
     return false;
   }
 
-  // We'll go ahead and cast the const TypedWritable pointer to a
-  // non-const for issuing the actual write.  This allows
-  // write_datagram to be defined in such a way that it can modify
-  // itself in some minor way if it must; we assume that no class will
-  // abuse this privilege and modify its contents significantly during
-  // write_datagram().
-  if (!_writer->write_object((TypedWritable *)object)) {
+  if (!_writer->write_object(object)) {
     close();
     return false;
   }

+ 13 - 0
panda/src/loader/loader.I

@@ -28,3 +28,16 @@ load_sync(const Filename &filename) const {
   }
   return load_file(filename);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Loader::load_sync
+//       Access: Public
+//  Description: Loads the file immediately, waiting for it to complete.
+////////////////////////////////////////////////////////////////////
+INLINE PT(PandaNode) Loader::
+qpload_sync(const Filename &filename) const {
+  if (!_file_types_loaded) {
+    load_file_types();
+  }
+  return qpload_file(filename);
+}

+ 140 - 10
panda/src/loader/loader.cxx

@@ -20,22 +20,19 @@
 #include "loaderFileTypeRegistry.h"
 #include "config_loader.h"
 
-#include <event.h>
-#include <pt_Event.h>
-#include <throw_event.h>
-#include <eventParameter.h>
-#include <circBuffer.h>
-#include <filename.h>
-#include <load_dso.h>
+#include "event.h"
+#include "pt_Event.h"
+#include "throw_event.h"
+#include "eventParameter.h"
+#include "circBuffer.h"
+#include "filename.h"
+#include "load_dso.h"
 
 #include "plist.h"
 #include "pvector.h"
 #include <algorithm>
 
 
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
 bool Loader::_file_types_loaded = false;
 
 ////////////////////////////////////////////////////////////////////
@@ -337,6 +334,51 @@ load_file(const Filename &filename) const {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Loader::load_file
+//       Access: Private
+//  Description: Loads a single scene graph file, if possible.
+//               Returns the Node that is the root of the file, or
+//               NULL if the file cannot be loaded.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) Loader::
+qpload_file(const Filename &filename) const {
+  string extension = filename.get_extension();
+
+  if (extension.empty()) {
+    return qpload_unknown_file_type(filename);
+  }
+
+  LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr();
+  LoaderFileType *requested_type =
+    reg->get_type_from_extension(extension);
+
+  if (requested_type == (LoaderFileType *)NULL) {
+    loader_cat.error()
+      << "Extension of file " << filename
+      << " is unrecognized; cannot load.\n";
+    loader_cat.error(false)
+      << "Currently known scene file types are:\n";
+    reg->write_types(loader_cat.error(false), 2);
+    return NULL;
+  }
+
+  Filename requested_filename = filename;
+  if (!requested_filename.is_fully_qualified()) {
+    // Ask the loader type to look for the file along its paths.
+    requested_type->resolve_filename(requested_filename);
+  }
+
+  if (loader_cat.is_debug()) {
+    loader_cat.debug()
+      << "Loading " << requested_type->get_name() << " file: "
+      << requested_filename << "\n";
+  }
+
+  PT(PandaNode) result = requested_type->qpload_file(requested_filename, true);
+  return result;
+}
+
 class LoaderConsiderFile {
 public:
   Filename _path;
@@ -436,6 +478,94 @@ load_unknown_file_type(const Filename &filename) const {
   return (Node *)NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Loader::load_unknown_file_type
+//       Access: Private
+//  Description: Attempts to guess which file is meant when a file
+//               with no extension is given.  Looks around for a file
+//               with a suitable extension for each of our known file
+//               types, and loads the most recent file available of
+//               any file type.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) Loader::
+qpload_unknown_file_type(const Filename &filename) const {
+  typedef pvector<LoaderConsiderFile> Files;
+  Files files;
+
+  // First, build up a list of all of the possible files it could be.
+  LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr();
+  int num_types = reg->get_num_types();
+
+  if (num_types == 0) {
+    loader_cat.error()
+      << "Can't load file " << filename
+      << "; no scene file types are known.\n";
+    return (PandaNode *)NULL;
+  }
+
+  for (int i = 0; i < num_types; i++) {
+    LoaderConsiderFile consider;
+    consider._type = reg->get_type(i);
+    consider._path = filename;
+    consider._path.set_extension(consider._type->get_extension());
+
+    if (!consider._path.is_fully_qualified()) {
+      // Ask the loader type to look for the file along its paths.
+      consider._type->resolve_filename(consider._path);
+    }
+
+    if (consider._path.exists()) {
+      files.push_back(consider);
+    }
+  }
+
+  if (files.empty()) {
+    loader_cat.error()
+      << "Couldn't find file " << filename << " as:\n";
+    for (int i = 0; i < num_types; i++) {
+      Filename p = filename;
+      p.set_extension(reg->get_type(i)->get_extension());
+      loader_cat.error(false)
+        << "  " << p << "\n";
+    }
+    return (PandaNode *)NULL;
+  }
+
+  // Now sort the list into order by timestamp, from newest to oldest.
+  sort(files.begin(), files.end());
+
+  // And try to load each file one at a time.
+  Files::const_iterator fi;
+
+  if (loader_cat.is_debug()) {
+    loader_cat.debug()
+      << "Loading " << filename << ", one of " << files.size()
+      << " possible types:\n";
+    for (fi = files.begin(); fi != files.end(); ++fi) {
+      loader_cat.debug(false)
+        << "  " << (*fi)._path << "\n";
+    }
+  }
+
+  for (fi = files.begin(); fi != files.end(); ++fi) {
+    const LoaderConsiderFile &consider = (*fi);
+    PT(PandaNode) result = consider._type->qpload_file(consider._path, false);
+    if (result != (PandaNode *)NULL) {
+      return result;
+    }
+    if (loader_cat.is_debug()) {
+      loader_cat.debug()
+        << "Couldn't read " << consider._type->get_name()
+        << " file " << consider._path << "\n";
+    }
+  }
+
+  loader_cat.error()
+    << "Cannot read " << files.front()._path << "\n";
+
+  return (PandaNode *)NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Loader::resolve_unknown_file_type
 //       Access: Private

+ 12 - 11
panda/src/loader/loader.h

@@ -17,18 +17,16 @@
 ////////////////////////////////////////////////////////////////////
 #ifndef LOADER_H
 #define LOADER_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-#include <pandabase.h>
 
-#include <notify.h>
-#include <node.h>
-#include <pt_Node.h>
-#include <filename.h>
-#include <tokenBoard.h>
-#include <asyncUtility.h>
+#include "pandabase.h"
+
+#include "notify.h"
+#include "node.h"
+#include "pt_Node.h"
+#include "pandaNode.h"
+#include "filename.h"
+#include "tokenBoard.h"
+#include "asyncUtility.h"
 
 class LoaderToken;
 
@@ -45,6 +43,7 @@ PUBLISHED:
   void resolve_filename(Filename &filename) const;
 
   INLINE PT_Node load_sync(const Filename &filename) const;
+  INLINE PT(PandaNode) qpload_sync(const Filename &filename) const;
 
   uint request_load(const Filename &filename, const string &event_name);
   bool check_load(uint id);
@@ -57,6 +56,8 @@ private:
   virtual bool process_request(void);
   PT_Node load_file(const Filename &filename) const;
   PT_Node load_unknown_file_type(const Filename &filename) const;
+  PT(PandaNode) qpload_file(const Filename &filename) const;
+  PT(PandaNode) qpload_unknown_file_type(const Filename &filename) const;
   void resolve_unknown_file_type(Filename &filename) const;
 
   typedef TokenBoard<LoaderToken> LoaderTokenBoard;

+ 13 - 0
panda/src/loader/loaderFileType.cxx

@@ -51,3 +51,16 @@ LoaderFileType::
 void LoaderFileType::
 resolve_filename(Filename &) const {
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileType::load_file
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) LoaderFileType::
+qpload_file(const Filename &path, bool report_errors) const
+{
+  loader_cat.error()
+    << get_type() << " cannot read PandaNode objects.\n";
+  return NULL;
+}

+ 8 - 5
panda/src/loader/loaderFileType.h

@@ -19,12 +19,14 @@
 #ifndef LOADERFILETYPE_H
 #define LOADERFILETYPE_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
-#include <typedObject.h>
-#include <node.h>
-#include <pt_Node.h>
-#include <filename.h>
+#include "typedObject.h"
+#include "node.h"
+#include "pt_Node.h"
+#include "filename.h"
+#include "pandaNode.h"
+#include "pointerTo.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : LoaderFileType
@@ -45,6 +47,7 @@ public:
 
   virtual void resolve_filename(Filename &path) const;
   virtual PT_Node load_file(const Filename &path, bool report_errors) const=0;
+  virtual PT(PandaNode) qpload_file(const Filename &path, bool report_errors) const;
 
 public:
   static TypeHandle get_class_type() {

+ 50 - 0
panda/src/loader/loaderFileTypeBam.cxx

@@ -121,3 +121,53 @@ load_file(const Filename &path, bool report_errors) const
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileTypeBam::load_file
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) LoaderFileTypeBam::
+qpload_file(const Filename &path, bool report_errors) const {
+  BamFile bam_file;
+  if (!bam_file.open_read(path, report_errors)) {
+    return NULL;
+  }
+
+  PT(PandaNode) result;
+
+  TypedWritable *object = bam_file.read_object();
+  if (object == TypedWritable::Null) {
+    if (report_errors) {
+      loader_cat.error() << "Bam file " << path << " is empty.\n";
+    }
+
+  } else if (!object->is_of_type(PandaNode::get_class_type())) {
+    if (report_errors) {
+      loader_cat.error()
+        << "Bam file " << path
+        << " contains a " << object->get_type() << ", not a PandaNode.\n";
+    }
+
+  } else {
+    result = DCAST(PandaNode, object);
+
+    if (report_errors) {
+      bam_file.read_object();
+      if (!bam_file.is_eof()) {
+        loader_cat.warning()
+          << "Ignoring extra objects in " << path << "\n";
+      }
+    }
+  }
+
+  if (!bam_file.resolve()) {
+    if (report_errors) {
+      loader_cat.error()
+        << "Unable to resolve Bam file.\n";
+      result = (PandaNode *)NULL;
+    }
+  }
+
+  return result;
+}
+

+ 4 - 3
panda/src/loader/loaderFileTypeBam.h

@@ -19,13 +19,13 @@
 #ifndef LOADERFILETYPEBAM_H
 #define LOADERFILETYPEBAM_H
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 #include "loaderFileType.h"
 
 ////////////////////////////////////////////////////////////////////
-//       Class : LoaderFileTypeEgg
-// Description : This defines the Loader interface to read Egg files.
+//       Class : LoaderFileTypeBam
+// Description : This defines the Loader interface to read Bam files.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA LoaderFileTypeBam : public LoaderFileType {
 public:
@@ -36,6 +36,7 @@ public:
 
   virtual void resolve_filename(Filename &path) const;
   virtual PT_Node load_file(const Filename &path, bool report_errors) const;
+  virtual PT(PandaNode) qpload_file(const Filename &path, bool report_errors) const;
 
 public:
   static TypeHandle get_class_type() {

+ 1 - 3
panda/src/parametrics/piecewiseCurve.cxx

@@ -625,11 +625,9 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int PiecewiseCurve::
-complete_pointers(vector_typedWritable &p_list, BamReader *manager) {
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int used = ParametricCurve::complete_pointers(p_list, manager);
 
-  nassertr(used + _segs.size() <= p_list.size(), used);
-
   size_t i;
   for (i = 0; i < _segs.size(); i++) {
     _segs[i]._curve = DCAST(ParametricCurve, p_list[used + i]);

+ 1 - 1
panda/src/parametrics/piecewiseCurve.h

@@ -99,7 +99,7 @@ protected:
 protected:
   virtual void write_datagram(BamWriter *manager, Datagram &me);
   void fillin(DatagramIterator &scan, BamReader *manager);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
 public:

+ 18 - 1
panda/src/pgraph/billboardAttrib.cxx

@@ -220,6 +220,16 @@ register_with_read_factory() {
 void BillboardAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_bool(_off);
+  _up_vector.write_datagram(dg);
+  dg.add_bool(_eye_relative);
+  dg.add_bool(_axial_rotate);
+  dg.add_float32(_offset);
+  _look_at_point.write_datagram(dg);
+
+  // *** We don't write out the _look_at NodeChain right now.  Maybe
+  // we should.
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -239,7 +249,7 @@ make_from_bam(const FactoryParams &params) {
   parse_params(params, scan, manager);
   attrib->fillin(scan, manager);
 
-  return new_from_bam(attrib, manager);
+  return attrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -252,4 +262,11 @@ make_from_bam(const FactoryParams &params) {
 void BillboardAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
+
+  _off = scan.get_bool();
+  _up_vector.read_datagram(scan);
+  _eye_relative = scan.get_bool();
+  _axial_rotate = scan.get_bool();
+  _offset = scan.get_float32();
+  _look_at_point.read_datagram(scan);
 }

+ 7 - 1
panda/src/pgraph/colorAttrib.cxx

@@ -165,6 +165,9 @@ register_with_read_factory() {
 void ColorAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_int8(_type);
+  _color.write_datagram(dg);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -184,7 +187,7 @@ make_from_bam(const FactoryParams &params) {
   parse_params(params, scan, manager);
   attrib->fillin(scan, manager);
 
-  return new_from_bam(attrib, manager);
+  return attrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -197,4 +200,7 @@ make_from_bam(const FactoryParams &params) {
 void ColorAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
+
+  _type = (Type)scan.get_int8();
+  _color.read_datagram(scan);
 }

+ 1 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -91,6 +91,7 @@ init_libpgraph() {
 
   BillboardAttrib::register_with_read_factory();
   ColorAttrib::register_with_read_factory();
+  CullBinAttrib::register_with_read_factory();
   CullFaceAttrib::register_with_read_factory();
   qpGeomNode::register_with_read_factory();
   PandaNode::register_with_read_factory();

+ 7 - 1
panda/src/pgraph/cullBinAttrib.cxx

@@ -116,6 +116,9 @@ register_with_read_factory() {
 void CullBinAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_string(_bin_name);
+  dg.add_int32(_draw_order);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -135,7 +138,7 @@ make_from_bam(const FactoryParams &params) {
   parse_params(params, scan, manager);
   attrib->fillin(scan, manager);
 
-  return new_from_bam(attrib, manager);
+  return attrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -148,4 +151,7 @@ make_from_bam(const FactoryParams &params) {
 void CullBinAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
+
+  _bin_name = scan.get_string();
+  _draw_order = scan.get_int32();
 }

+ 5 - 1
panda/src/pgraph/cullFaceAttrib.cxx

@@ -137,6 +137,8 @@ register_with_read_factory() {
 void CullFaceAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_int8(_mode);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -156,7 +158,7 @@ make_from_bam(const FactoryParams &params) {
   parse_params(params, scan, manager);
   attrib->fillin(scan, manager);
 
-  return new_from_bam(attrib, manager);
+  return attrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -169,4 +171,6 @@ make_from_bam(const FactoryParams &params) {
 void CullFaceAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
+
+  _mode = (Mode)scan.get_int8();
 }

+ 71 - 1
panda/src/pgraph/pandaNode.cxx

@@ -141,7 +141,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 PandaNode *PandaNode::
 copy_subgraph() const {
-  //*** Do something here.
+  // *** Do something here.
   nassertr(false, (PandaNode *)NULL);
   return (PandaNode *)NULL;
 }
@@ -903,6 +903,57 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  CDReader cdata(_cycler);
+
+  dg.add_string(get_name());
+  manager->write_pointer(dg, cdata->_state);
+  manager->write_pointer(dg, cdata->_transform);
+
+  // When we write a PandaNode, we write out its list of child nodes,
+  // but not its parent nodes--so that writing out a node implicitly
+  // writes out all of the nodes in the subgraph below, but not the
+  // nodes above as well.
+
+  int num_children = cdata->_down.size();
+  nassertv(num_children == (int)(PN_uint16)num_children);
+  dg.add_uint16(num_children);
+
+  // **** We should smarten up the writing of the sort number--most of
+  // the time these will all be zero.
+  Down::const_iterator ci;
+  for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
+    PandaNode *child = (*ci).get_child();
+    int sort = (*ci).get_sort();
+    manager->write_pointer(dg, child);
+    dg.add_int32(sort);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::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 PandaNode::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  CDWriter cdata(_cycler);
+  int pi = TypedWritable::complete_pointers(p_list, manager);
+
+  // Get the state and transform pointers.
+  cdata->_state = DCAST(RenderState, p_list[pi++]);
+  cdata->_transform = DCAST(TransformState, p_list[pi++]);
+
+  // Get the child pointers.
+  Down::iterator ci;
+  for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
+    int sort = (*ci).get_sort();
+    PT(PandaNode) node = DCAST(PandaNode, p_list[pi++]);
+    (*ci) = DownConnection(node, sort);
+  }
+
+  return pi;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -934,4 +985,23 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  CDWriter cdata(_cycler);
+
+  TypedWritable::fillin(scan, manager);
+
+  string name = scan.get_string();
+  set_name(name);
+
+  // Read the state and transform pointers.
+  manager->read_pointer(scan, this);
+  manager->read_pointer(scan, this);
+
+  int num_children = scan.get_uint16();
+  // Read the list of child nodes.  Push back a NULL for each one.
+  cdata->_down.reserve(num_children);
+  for (int i = 0; i < num_children; i++) {
+    manager->read_pointer(scan, this);
+    int sort = scan.get_int32();
+    cdata->_down.push_back(DownConnection(NULL, sort));
+  }
 }

+ 2 - 0
panda/src/pgraph/pandaNode.h

@@ -235,6 +235,8 @@ public:
 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);

+ 1 - 1
panda/src/pgraph/qpcullTraverser.cxx

@@ -182,7 +182,7 @@ r_traverse(PandaNode *node,
       if (node->is_final()) {
         // The bounding volume is partially, but not completely,
         // within the viewing frustum.  Normally we'd keep testing
-        // child bounded volumes as we continue down.  But this node
+        // child bounding volumes as we continue down.  But this node
         // has the "final" flag, so the user is claiming that there is
         // some important reason we should consider everything visible
         // at this point.  So be it.

+ 47 - 0
panda/src/pgraph/qpgeomNode.cxx

@@ -212,6 +212,42 @@ register_with_read_factory() {
 void qpGeomNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
   PandaNode::write_datagram(manager, dg);
+
+  CDReader cdata(_cycler);
+
+  int num_geoms = cdata->_geoms.size();
+  nassertv(num_geoms == (int)(PN_uint16)num_geoms);
+  dg.add_uint16(num_geoms);
+  
+  Geoms::const_iterator gi;
+  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    manager->write_pointer(dg, entry._geom);
+    manager->write_pointer(dg, entry._state);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::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 qpGeomNode::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  CDWriter cdata(_cycler);
+  int pi = PandaNode::complete_pointers(p_list, manager);
+
+  // Get the geom and state pointers.
+  Geoms::iterator gi;
+  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    GeomEntry &entry = (*gi);
+    entry._geom = DCAST(Geom, p_list[pi++]);
+    entry._state = DCAST(RenderState, p_list[pi++]);
+  }
+
+  return pi;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -243,5 +279,16 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  CDWriter cdata(_cycler);
+
   PandaNode::fillin(scan, manager);
+
+  int num_geoms = scan.get_uint16();
+  // Read the list of geoms and states.  Push back a NULL for each one.
+  cdata->_geoms.reserve(num_geoms);
+  for (int i = 0; i < num_geoms; i++) {
+    manager->read_pointer(scan, this);
+    manager->read_pointer(scan, this);
+    cdata->_geoms.push_back(GeomEntry(NULL, NULL));
+  }
 }

+ 2 - 0
panda/src/pgraph/qpgeomNode.h

@@ -91,6 +91,8 @@ private:
 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);

+ 33 - 26
panda/src/pgraph/renderAttrib.cxx

@@ -218,38 +218,25 @@ make_default_impl() const {
 //               for shipping out to a Bam file.
 ////////////////////////////////////////////////////////////////////
 void RenderAttrib::
-write_datagram(BamWriter *, Datagram &) {
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RenderAttrib::finalize
-//       Access: Public, Virtual
-//  Description: Method to ensure that any necessary clean up tasks
-//               that have to be performed by this object are performed
-////////////////////////////////////////////////////////////////////
-void RenderAttrib::
-finalize() {
-  // Unref the pointer that we explicitly reffed in make_from_bam().
-  unref();
-
-  // We should never get back to zero after unreffing our own count,
-  // because we expect to have been stored in a pointer somewhere.  If
-  // we do get to zero, it's a memory leak; the way to avoid this is
-  // to call unref_delete() above instead of unref(), but this is
-  // dangerous to do from within a virtual function.
-  nassertv(get_ref_count() != 0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: RenderAttrib::new_from_bam
-//       Access: Protected, Static
-//  Description: Uniquifies the pointer for a RenderAttrib object just
-//               created from a bam file, and preserves its reference
-//               count correctly.
+//     Function: RenderAttrib::change_this
+//       Access: Public, Static
+//  Description: Called immediately after complete_pointers(), this
+//               gives the object a chance to adjust its own pointer
+//               if desired.  Most objects don't change pointers after
+//               completion, but some need to.
+//
+//               Once this function has been called, the old pointer
+//               will no longer be accessed.
 ////////////////////////////////////////////////////////////////////
 TypedWritable *RenderAttrib::
-new_from_bam(RenderAttrib *attrib, BamReader *manager) {
+change_this(TypedWritable *old_ptr, BamReader *manager) {
   // First, uniquify the pointer.
+  RenderAttrib *attrib = DCAST(RenderAttrib, old_ptr);
   CPT(RenderAttrib) pointer = return_new(attrib);
 
   // But now we have a problem, since we have to hold the reference
@@ -267,6 +254,25 @@ new_from_bam(RenderAttrib *attrib, BamReader *manager) {
   return (RenderAttrib *)pointer.p();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::finalize
+//       Access: Public, Virtual
+//  Description: Method to ensure that any necessary clean up tasks
+//               that have to be performed by this object are performed
+////////////////////////////////////////////////////////////////////
+void RenderAttrib::
+finalize() {
+  // Unref the pointer that we explicitly reffed in make_from_bam().
+  unref();
+
+  // We should never get back to zero after unreffing our own count,
+  // because we expect to have been stored in a pointer somewhere.  If
+  // we do get to zero, it's a memory leak; the way to avoid this is
+  // to call unref_delete() above instead of unref(), but this is
+  // dangerous to do from within a virtual function.
+  nassertv(get_ref_count() != 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::fillin
 //       Access: Protected
@@ -276,4 +282,5 @@ new_from_bam(RenderAttrib *attrib, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void RenderAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
 }

+ 1 - 0
panda/src/pgraph/renderAttrib.h

@@ -81,6 +81,7 @@ private:
 
 public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
   virtual void finalize();
 
 protected:

+ 13 - 0
panda/src/pgraph/renderState.I

@@ -30,6 +30,19 @@ Attribute(const RenderAttrib *attrib, int override) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::Attribute::Constructor
+//       Access: Public
+//  Description: This constructor is only used when reading the
+//               RenderState from a bam file.  At this point, the
+//               attribute pointer is unknown.
+////////////////////////////////////////////////////////////////////
+INLINE RenderState::Attribute::
+Attribute(int override) :
+  _override(override)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::Attribute::Constructor
 //       Access: Public

+ 94 - 30
panda/src/pgraph/renderState.cxx

@@ -929,13 +929,91 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void RenderState::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  int num_attribs = _attributes.size();
+  nassertv(num_attribs == (int)(PN_uint16)num_attribs);
+  dg.add_uint16(num_attribs);
+
+  // **** We should smarten up the writing of the override
+  // number--most of the time these will all be zero.
+  Attributes::const_iterator ai;
+  for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) {
+    const Attribute &attribute = (*ai);
+
+    manager->write_pointer(dg, attribute._attrib);
+    dg.add_int32(attribute._override);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::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 RenderState::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritable::complete_pointers(p_list, manager);
+
+  // Get the attribute pointers.
+  Attributes::iterator ai;
+  for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) {
+    Attribute &attribute = (*ai);
+
+    attribute._attrib = DCAST(RenderAttrib, p_list[pi++]);
+    nassertr(attribute._attrib != (RenderAttrib *)NULL, pi);
+    attribute._type = attribute._attrib->get_type();
+  }
+
+  // Now make sure the array is properly sorted.  (It won't
+  // necessarily preserve its correct sort after being read from bam,
+  // because the sort is based on TypeHandle indices, which can change
+  // from session to session.)
+  _attributes.sort();
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::change_this
+//       Access: Public, Static
+//  Description: Called immediately after complete_pointers(), this
+//               gives the object a chance to adjust its own pointer
+//               if desired.  Most objects don't change pointers after
+//               completion, but some need to.
+//
+//               Once this function has been called, the old pointer
+//               will no longer be accessed.
+////////////////////////////////////////////////////////////////////
+TypedWritable *RenderState::
+change_this(TypedWritable *old_ptr, BamReader *manager) {
+  // First, uniquify the pointer.
+  RenderState *state = DCAST(RenderState, old_ptr);
+  CPT(RenderState) pointer = return_new(state);
+
+  // But now we have a problem, since we have to hold the reference
+  // count and there's no way to return a TypedWritable while still
+  // holding the reference count!  We work around this by explicitly
+  // upping the count, and also setting a finalize() callback to down
+  // it later.
+  if (pointer == state) {
+    pointer->ref();
+    manager->register_finalize(state);
+  }
+  
+  // We have to cast the pointer back to non-const, because the bam
+  // reader expects that.
+  return (RenderState *)pointer.p();
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::finalize
 //       Access: Public, Virtual
-//  Description: Method to ensure that any necessary clean up tasks
-//               that have to be performed by this object are performed
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void RenderState::
 finalize() {
@@ -966,35 +1044,9 @@ make_from_bam(const FactoryParams &params) {
 
   parse_params(params, scan, manager);
   state->fillin(scan, manager);
+  manager->register_change_this(change_this, state);
 
-  return new_from_bam(state, manager);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: RenderState::new_from_bam
-//       Access: Protected, Static
-//  Description: Uniquifies the pointer for a RenderState object just
-//               created from a bam file, and preserves its reference
-//               count correctly.
-////////////////////////////////////////////////////////////////////
-TypedWritable *RenderState::
-new_from_bam(RenderState *state, BamReader *manager) {
-  // First, uniquify the pointer.
-  CPT(RenderState) pointer = return_new(state);
-
-  // But now we have a problem, since we have to hold the reference
-  // count and there's no way to return a TypedWritable while still
-  // holding the reference count!  We work around this by explicitly
-  // upping the count, and also setting a finalize() callback to down
-  // it later.
-  if (pointer == state) {
-    pointer->ref();
-    manager->register_finalize(state);
-  }
-  
-  // We have to cast the pointer back to non-const, because the bam
-  // reader expects that.
-  return (RenderState *)pointer.p();
+  return state;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1006,4 +1058,16 @@ new_from_bam(RenderState *state, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void RenderState::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  int num_attribs = scan.get_uint16();
+
+  // Push back a NULL pointer for each attribute for now, until we get
+  // the actual list of pointers later in complete_pointers().
+  _attributes.reserve(num_attribs);
+  for (int i = 0; i < num_attribs; i++) {
+    manager->read_pointer(scan, this);
+    int override = scan.get_int32();
+    _attributes.push_back(Attribute(override));
+  }
 }

+ 3 - 1
panda/src/pgraph/renderState.h

@@ -143,6 +143,7 @@ private:
   class Attribute {
   public:
     INLINE Attribute(const RenderAttrib *attrib, int override);
+    INLINE Attribute(int override);
     INLINE Attribute(TypeHandle type);
     INLINE Attribute(const Attribute &copy);
     INLINE void operator = (const Attribute &copy);
@@ -173,11 +174,12 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+  static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
   virtual void finalize();
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
-  static TypedWritable *new_from_bam(RenderState *state, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
   
 public:

+ 25 - 1
panda/src/pgraph/textureAttrib.cxx

@@ -137,6 +137,27 @@ register_with_read_factory() {
 void TextureAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
+
+  manager->write_pointer(dg, _texture);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::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 TextureAttrib::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = RenderAttrib::complete_pointers(p_list, manager);
+
+  TypedWritable *texture = p_list[pi++];
+  if (texture != (TypedWritable *)NULL) {
+    _texture = DCAST(Texture, texture);
+  }
+
+  return pi;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -156,7 +177,7 @@ make_from_bam(const FactoryParams &params) {
   parse_params(params, scan, manager);
   attrib->fillin(scan, manager);
 
-  return new_from_bam(attrib, manager);
+  return attrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -169,4 +190,7 @@ make_from_bam(const FactoryParams &params) {
 void TextureAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
+
+  // Read the _texture pointer.
+  manager->read_pointer(scan, this);
 }

+ 1 - 0
panda/src/pgraph/textureAttrib.h

@@ -55,6 +55,7 @@ private:
 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);

+ 75 - 30
panda/src/pgraph/transformState.cxx

@@ -577,13 +577,69 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 void TransformState::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  if ((_flags & F_is_identity) != 0) {
+    // Identity, nothing much to that.
+    int flags = F_is_identity | F_singular_known;
+    dg.add_uint16(flags);
+
+  } else if ((_flags & F_components_given) != 0) {
+    // A component-based transform.
+    int flags = F_components_given | F_components_known | F_has_components;
+    dg.add_uint16(flags);
+
+    _pos.write_datagram(dg);
+    _hpr.write_datagram(dg);
+    _scale.write_datagram(dg);
+
+  } else {
+    // A general matrix.
+    nassertv((_flags & F_mat_known) != 0);
+    int flags = F_mat_known;
+    dg.add_uint16(flags);
+    _mat.write_datagram(dg);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::change_this
+//       Access: Public, Static
+//  Description: Called immediately after complete_pointers(), this
+//               gives the object a chance to adjust its own pointer
+//               if desired.  Most objects don't change pointers after
+//               completion, but some need to.
+//
+//               Once this function has been called, the old pointer
+//               will no longer be accessed.
+////////////////////////////////////////////////////////////////////
+TypedWritable *TransformState::
+change_this(TypedWritable *old_ptr, BamReader *manager) {
+  // First, uniquify the pointer.
+  TransformState *state = DCAST(TransformState, old_ptr);
+  CPT(TransformState) pointer = return_new(state);
+
+  // But now we have a problem, since we have to hold the reference
+  // count and there's no way to return a TypedWritable while still
+  // holding the reference count!  We work around this by explicitly
+  // upping the count, and also setting a finalize() callback to down
+  // it later.
+  if (pointer == state) {
+    pointer->ref();
+    manager->register_finalize(state);
+  }
+  
+  // We have to cast the pointer back to non-const, because the bam
+  // reader expects that.
+  return (TransformState *)pointer.p();
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::finalize
 //       Access: Public, Virtual
-//  Description: Method to ensure that any necessary clean up tasks
-//               that have to be performed by this object are performed
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
 ////////////////////////////////////////////////////////////////////
 void TransformState::
 finalize() {
@@ -614,35 +670,9 @@ make_from_bam(const FactoryParams &params) {
 
   parse_params(params, scan, manager);
   state->fillin(scan, manager);
+  manager->register_change_this(change_this, state);
 
-  return new_from_bam(state, manager);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: TransformState::new_from_bam
-//       Access: Protected, Static
-//  Description: Uniquifies the pointer for a TransformState object just
-//               created from a bam file, and preserves its reference
-//               count correctly.
-////////////////////////////////////////////////////////////////////
-TypedWritable *TransformState::
-new_from_bam(TransformState *state, BamReader *manager) {
-  // First, uniquify the pointer.
-  CPT(TransformState) pointer = return_new(state);
-
-  // But now we have a problem, since we have to hold the reference
-  // count and there's no way to return a TypedWritable while still
-  // holding the reference count!  We work around this by explicitly
-  // upping the count, and also setting a finalize() callback to down
-  // it later.
-  if (pointer == state) {
-    pointer->ref();
-    manager->register_finalize(state);
-  }
-  
-  // We have to cast the pointer back to non-const, because the bam
-  // reader expects that.
-  return (TransformState *)pointer.p();
+  return state;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -654,4 +684,19 @@ new_from_bam(TransformState *state, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void TransformState::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  _flags = scan.get_uint16();
+
+  if ((_flags & F_components_known) != 0) {
+    // Componentwise transform.
+    _pos.read_datagram(scan);
+    _hpr.read_datagram(scan);
+    _scale.read_datagram(scan);
+  }
+
+  if ((_flags & F_mat_known) != 0) {
+    // General matrix.
+    _mat.read_datagram(scan);
+  }
 }

+ 1 - 1
panda/src/pgraph/transformState.h

@@ -151,11 +151,11 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
   virtual void finalize();
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
-  static TypedWritable *new_from_bam(TransformState *state, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
   
 public:

+ 5 - 1
panda/src/pgraph/transparencyAttrib.cxx

@@ -142,6 +142,8 @@ register_with_read_factory() {
 void TransparencyAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_int8(_mode);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -161,7 +163,7 @@ make_from_bam(const FactoryParams &params) {
   parse_params(params, scan, manager);
   attrib->fillin(scan, manager);
 
-  return new_from_bam(attrib, manager);
+  return attrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -174,4 +176,6 @@ make_from_bam(const FactoryParams &params) {
 void TransparencyAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
+
+  _mode = (Mode)scan.get_int8();
 }

+ 198 - 61
panda/src/putil/bamReader.cxx

@@ -42,6 +42,7 @@ BamReader(DatagramGenerator *generator)
   : _source(generator)
 {
   _num_extra_objects = 0;
+  _now_creating = _created_objs.end();
   _pta_id = -1;
 }
 
@@ -177,7 +178,8 @@ read_object() {
     return (TypedWritable *)NULL;
 
   } else {
-    TypedWritable *object = (*oi).second;
+    CreatedObj &created_obj = (*oi).second;
+    TypedWritable *object = created_obj._ptr;
 
     if (bam_cat.is_spam()) {
       if (object != (TypedWritable *)NULL) {
@@ -185,6 +187,11 @@ read_object() {
           << "Returning object of type " << object->get_type() << "\n";
       }
     }
+    if (created_obj._change_this != NULL) {
+      bam_cat.warning()
+        << "Returning pointer to " << object->get_type()
+        << " that might change.\n";
+    }
 
     return object;
   }
@@ -210,70 +217,118 @@ read_object() {
 ////////////////////////////////////////////////////////////////////
 bool BamReader::
 resolve() {
-  bool all_completed = true;
-
-  // Walk through all the objects that still have outstanding pointers.
-  Requests::iterator ri;
-  ri = _deferred_pointers.begin();
-  while (ri != _deferred_pointers.end()) {
-    TypedWritable *whom = (*ri).first;
-    const vector_ushort &pointers = (*ri).second;
-
-    // Now make sure we have all of the pointers this object is
-    // waiting for.  If any of the pointers has not yet been read in,
-    // we can't resolve this object--we can't do anything for a given
-    // object until we have *all* outstanding pointers for that
-    // object.
-
-    bool is_complete = true;
-    vector_typedWritable references;
-
-    vector_ushort::const_iterator pi;
-    for (pi = pointers.begin(); pi != pointers.end() && is_complete; ++pi) {
-      int object_id = (*pi);
-
-      if (object_id == 0) {
-        // A NULL pointer is a NULL pointer.
-        references.push_back((TypedWritable *)NULL);
-
-      } else {
-        // See if we have the pointer available now.
-        CreatedObjs::const_iterator oi = _created_objs.find(object_id);
-        if (oi == _created_objs.end()) {
-          // No, too bad.
-          is_complete = false;
-
+  bool all_completed;
+  bool any_completed_this_pass;
+
+  do {
+    all_completed = true;
+    any_completed_this_pass = false;
+
+    // Walk through all the objects that still have outstanding pointers.
+    Requests::iterator ri;
+    ri = _deferred_pointers.begin();
+    while (ri != _deferred_pointers.end()) {
+      int object_id = (*ri).first;
+      const vector_int &children = (*ri).second;
+      
+      CreatedObjs::iterator ci = _created_objs.find(object_id);
+      nassertr(ci != _created_objs.end(), false);
+      CreatedObj &created_obj = (*ci).second;
+      
+      TypedWritable *object_ptr = created_obj._ptr;
+      
+      // Now make sure we have all of the pointers this object is
+      // waiting for.  If any of the pointers has not yet been read
+      // in, we can't resolve this object--we can't do anything for a
+      // given object until we have *all* outstanding pointers for
+      // that object.
+
+      bool is_complete = true;
+      vector_typedWritable references;
+      
+      vector_int::const_iterator pi;
+      for (pi = children.begin(); pi != children.end() && is_complete; ++pi) {
+        int child_id = (*pi);
+        
+        if (child_id == 0) {
+          // A NULL pointer is a NULL pointer.
+          references.push_back((TypedWritable *)NULL);
+          
         } else {
-          // Yes, it's ready.
-          references.push_back((*oi).second);
+          // See if we have the pointer available now.
+          CreatedObjs::const_iterator oi = _created_objs.find(child_id);
+          if (oi == _created_objs.end()) {
+            // No, too bad.
+            is_complete = false;
+            
+          } else {
+            const CreatedObj &child_obj = (*oi).second;
+            if (child_obj._change_this != NULL) {
+              // It's been created, but the pointer might still change.
+              is_complete = false;
+              
+            } else {
+              // Yes, it's ready.
+              references.push_back(child_obj._ptr);
+            }
+          }
         }
       }
+      
+      if (is_complete) {
+        // Okay, here's the complete list of pointers for you!
+        int num_completed = object_ptr->complete_pointers(&references[0], this);
+        if (num_completed != (int)references.size()) {
+          bam_cat.warning()
+            << object_ptr->get_type() << " completed " << num_completed
+            << " of " << references.size() << " pointers.\n";
+        }
+        
+        // Now remove this object from the list of things that need
+        // completion.  We have to be a bit careful when deleting things
+        // from the STL container while we are traversing it.
+        Requests::iterator old = ri;
+        ++ri;
+        _deferred_pointers.erase(old);
+        
+        // Does the pointer need to change?
+        if (created_obj._change_this != NULL) {
+          created_obj._ptr = created_obj._change_this(object_ptr, this);
+          created_obj._change_this = NULL;
+        }
+        any_completed_this_pass = true;
+        
+      } else {
+        // Couldn't complete this object yet; it'll wait for next time.
+        ++ri;
+        all_completed = false;
+      }
     }
+  } while (!all_completed && any_completed_this_pass);
 
-    if (is_complete) {
-      // Okay, here's the complete list of pointers for you!
-      whom->complete_pointers(references, this);
-
-      // Now remove this object from the list of things that need
-      // completion.  We have to be a bit careful when deleting things
-      // from the STL container while we are traversing it.
-      Requests::iterator old = ri;
-      ++ri;
-      _deferred_pointers.erase(old);
+  if (all_completed) {
+    finalize();
+  } else {
+    // Report all the uncompleted objects for no good reason.  This
+    // will probably have to come out later when we have cases in
+    // which some objects might legitimately be uncompleted after
+    // calling resolve(), but for now we expect resolve() to always
+    // succeed.
+    Requests::const_iterator ri;
+    for (ri = _deferred_pointers.begin(); 
+         ri != _deferred_pointers.end();
+         ++ri) {
+      int object_id = (*ri).first;
+      CreatedObjs::iterator ci = _created_objs.find(object_id);
+      nassertr(ci != _created_objs.end(), false);
+      CreatedObj &created_obj = (*ci).second;
+      TypedWritable *object_ptr = created_obj._ptr;
 
-    } else {
-      // Couldn't complete this object yet; it'll wait for next time.
       bam_cat.warning()
-        << "Unable to complete " << whom->get_type() << "\n";
-      ++ri;
-      all_completed = false;
+        << "Unable to complete " << object_ptr->get_type() << "\n";
     }
   }
 
-  if (all_completed) {
-    finalize();
-  }
-
   return all_completed;
 }
 
@@ -381,9 +436,26 @@ read_handle(DatagramIterator &scan) {
 ////////////////////////////////////////////////////////////////////
 void BamReader::
 read_pointer(DatagramIterator &scan, TypedWritable *for_whom) {
+  nassertv(_now_creating != _created_objs.end());
+  int requestor_id = (*_now_creating).first;
+
+#ifndef NDEBUG
+  // A bit of sanity checking here: we look up the object ID, and
+  // assign the "this" pointer into the record if it's not there
+  // already.  Then we can verify the "this" pointer later.
+  CreatedObj &created_obj = (*_now_creating).second;
+  if (created_obj._ptr == (TypedWritable *)NULL) {
+    created_obj._ptr = for_whom;
+  } else {
+    // We've previously assigned this pointer, and we should have
+    // assigned it to the same this pointer we have now.
+    nassertv(created_obj._ptr == for_whom);
+  }
+#endif  // NDEBUG
+
   // Read the object ID, and associate it with the requesting object.
   int object_id = scan.get_uint16();
-  _deferred_pointers[for_whom].push_back(object_id);
+  _deferred_pointers[requestor_id].push_back(object_id);
 
   // If the object ID is zero (which indicates a NULL pointer), we
   // don't have to do anything else.
@@ -447,6 +519,44 @@ register_finalize(TypedWritable *whom) {
   _finalize_list.insert(whom);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamReader::register_change_this
+//       Access: Public
+//  Description: Called by an object reading itself from the bam file
+//               to indicate that the object pointer that will be
+//               returned is temporary, and will eventually need to be
+//               replaced with another pointer.
+//
+//               The supplied function pointer will later be called on
+//               the object, immediately after complete_pointers() is
+//               called; it should return the new and final pointer.
+//
+//               We use a static function pointer instead of a virtual
+//               function (as in finalize()), to allow the function to
+//               destruct the old pointer if necessary.  (It is
+//               invalid to destruct the this pointer within a virtual
+//               function.)
+////////////////////////////////////////////////////////////////////
+void BamReader::
+register_change_this(ChangeThisFunc func, TypedWritable *object) {
+  nassertv(_now_creating != _created_objs.end());
+  CreatedObj &created_obj = (*_now_creating).second;
+
+#ifndef NDEBUG
+  // Sanity check the pointer--it should always be the same pointer
+  // after we set it the first time.
+  if (created_obj._ptr == (TypedWritable *)NULL) {
+    created_obj._ptr = object;
+  } else {
+    // We've previously assigned this pointer, and we should have
+    // assigned it to the same this pointer we have now.
+    nassertv(created_obj._ptr == object);
+  }
+#endif  // NDEBUG
+
+  created_obj._change_this = func;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamReader::finalize_now
 //       Access: Public
@@ -597,16 +707,43 @@ p_read_object() {
     // First, we must add an entry into the map for this object ID, so
     // that in case this function is called recursively during the
     // object's factory constructor, we will have some definition for
-    // the object.  It doesn't matter yet what the pointer is.
+    // the object.  For now, we give it a NULL pointer.
+    CreatedObj new_created_obj;
+    new_created_obj._ptr = NULL;
+    new_created_obj._change_this = NULL;
     CreatedObjs::iterator oi =
-      _created_objs.insert(CreatedObjs::value_type(object_id, NULL)).first;
-
-    // Now we can call the factory to create the object.
+      _created_objs.insert(CreatedObjs::value_type(object_id, new_created_obj)).first;
+    CreatedObj &created_obj = (*oi).second;
+
+    // Now we can call the factory to create the object.  Update
+    // _now_creating during this call so if this function calls
+    // read_pointer() or register_change_this() we'll match it up
+    // properly.  This might recursively call back into this
+    // p_read_object(), so be sure to save and restore the original
+    // value of _now_creating.
+    CreatedObjs::iterator was_creating = _now_creating;
+    _now_creating = oi;
     TypedWritable *object =
       _factory->make_instance_more_general(type, fparams);
+    _now_creating = was_creating;
 
     // And now we can store the new object pointer in the map.
-    (*oi).second = object;
+    nassertr(created_obj._ptr == object || created_obj._ptr == NULL, object_id);
+    created_obj._ptr = object;
+
+    if (created_obj._change_this != NULL) {
+      // If the pointer is scheduled to change after
+      // complete_pointers(), but we have no entry in
+      // _deferred_pointers for this object (and hence no plan to call
+      // complete_pointers()), then just change the pointer
+      // immediately.
+      Requests::const_iterator ri = _deferred_pointers.find(object_id);
+      if (ri == _deferred_pointers.end()) {
+        object = created_obj._change_this(object, this);
+        created_obj._ptr = object;
+        created_obj._change_this = NULL;
+      }
+    }
 
     //Just some sanity checks
     if (object == (TypedWritable *)NULL) {

+ 16 - 3
panda/src/putil/bamReader.h

@@ -27,7 +27,7 @@
 #include "datagramIterator.h"
 #include "bamReaderParam.h"
 #include "factory.h"
-#include "vector_ushort.h"
+#include "vector_int.h"
 #include "pset.h"
 #include "dcast.h"
 
@@ -114,6 +114,9 @@ public:
 
   void register_finalize(TypedWritable *whom);
 
+  typedef TypedWritable *(*ChangeThisFunc)(TypedWritable *object, BamReader *manager);
+  void register_change_this(ChangeThisFunc func, TypedWritable *whom);
+
   void finalize_now(TypedWritable *whom);
 
   void *get_pta(DatagramIterator &scan);
@@ -143,13 +146,23 @@ private:
 
   // This maps the object ID numbers encountered within the Bam file
   // to the actual pointers of the corresponding generated objects.
-  typedef pmap<int, TypedWritable *> CreatedObjs;
+  class CreatedObj {
+  public:
+    TypedWritable *_ptr;
+    ChangeThisFunc _change_this;
+  };
+  typedef pmap<int, CreatedObj> CreatedObjs;
   CreatedObjs _created_objs;
+  // This is the iterator into the above map for the object we are
+  // currently reading in p_read_object().  It is carefully maintained
+  // during recursion.  We need this so we can associate
+  // read_pointer() calls with the proper objects.
+  CreatedObjs::iterator _now_creating;
 
   // This records all the objects that still need their pointers
   // completed, along with the object ID's of the pointers they need,
   // in the order in which read_pointer() was called.
-  typedef pmap<TypedWritable *, vector_ushort> Requests;
+  typedef pmap<int, vector_int> Requests;
   Requests _deferred_pointers;
 
   // This is the number of extra objects that must still be read (and

+ 14 - 7
panda/src/putil/bamWriter.cxx

@@ -97,7 +97,7 @@ init() {
 //               false otherwise.
 ////////////////////////////////////////////////////////////////////
 bool BamWriter::
-write_object(TypedWritable *object) {
+write_object(const TypedWritable *object) {
   nassertr(_object_queue.empty(), false);
 
   int object_id = enqueue_object(object);
@@ -132,7 +132,14 @@ write_object(TypedWritable *object) {
       write_handle(dg, type);
       dg.add_uint16(object_id);
 
-      object->write_datagram(this, dg);
+      // We cast the const pointer to non-const so that we may call
+      // write_datagram() on it.  Really, write_datagram() should be a
+      // const method anyway, but there may be times when a class
+      // object wants to update some transparent cache value during
+      // writing or something like that, so it's more convenient to
+      // cheat and define it as a non-const method.
+      ((TypedWritable *)object)->write_datagram(this, dg);
+
       (*si).second._written = true;
 
     } else {
@@ -170,10 +177,10 @@ write_object(TypedWritable *object) {
 //               automatically be written.
 ////////////////////////////////////////////////////////////////////
 void BamWriter::
-write_pointer(Datagram &packet, TypedWritable *object) {
+write_pointer(Datagram &packet, const TypedWritable *object) {
   // If the pointer is NULL, we always simply write a zero for an
   // object ID and leave it at that.
-  if (object == (TypedWritable *)NULL) {
+  if (object == (const TypedWritable *)NULL) {
     packet.add_uint16(0);
 
   } else {
@@ -211,8 +218,8 @@ write_pointer(Datagram &packet, TypedWritable *object) {
 //               the work that must be done to write a PTA.
 ////////////////////////////////////////////////////////////////////
 bool BamWriter::
-register_pta(Datagram &packet, void *ptr) {
-  if (ptr == (void *)NULL) {
+register_pta(Datagram &packet, const void *ptr) {
+  if (ptr == (const void *)NULL) {
     // A zero for the PTA ID indicates a NULL pointer.  This is a
     // special case.
     packet.add_uint16(0);
@@ -313,7 +320,7 @@ write_handle(Datagram &packet, TypeHandle type) {
 //               an error.
 ////////////////////////////////////////////////////////////////////
 int BamWriter::
-enqueue_object(TypedWritable *object) {
+enqueue_object(const TypedWritable *object) {
   Datagram dg;
 
   nassertr(object != TypedWritable::Null, 0);

+ 7 - 7
panda/src/putil/bamWriter.h

@@ -77,17 +77,17 @@ public:
   // The primary interface for a caller.
 
   bool init();
-  bool write_object(TypedWritable *obj);
+  bool write_object(const TypedWritable *obj);
 
 public:
   // Functions to support classes that write themselves to the Bam.
 
-  void write_pointer(Datagram &packet, TypedWritable *dest);
-  bool register_pta(Datagram &packet, void *ptr);
+  void write_pointer(Datagram &packet, const TypedWritable *dest);
+  bool register_pta(Datagram &packet, const void *ptr);
   void write_handle(Datagram &packet, TypeHandle type);
 
 private:
-  int enqueue_object(TypedWritable *object);
+  int enqueue_object(const TypedWritable *object);
 
 
   // This is the set of all TypeHandles already written.
@@ -103,7 +103,7 @@ private:
 
     StoreState(int object_id) : _object_id(object_id), _written(false) {}
   };
-  typedef pmap<TypedWritable *, StoreState> StateMap;
+  typedef pmap<const TypedWritable *, StoreState> StateMap;
   StateMap _state_map;
 
   // This is the next object ID that will be assigned to a new object.
@@ -111,12 +111,12 @@ private:
 
   // This is the queue of objects that need to be written when the
   // current object is finished.
-  typedef pdeque<TypedWritable *> ObjectQueue;
+  typedef pdeque<const TypedWritable *> ObjectQueue;
   ObjectQueue _object_queue;
 
   // These are used by register_pta() to unify multiple references to
   // the same PointerToArray.
-  typedef pmap<void *, int> PTAMap;
+  typedef pmap<const void *, int> PTAMap;
   PTAMap _pta_map;
   int _next_pta_id;
 

+ 3 - 3
panda/src/putil/test_bam.cxx

@@ -82,7 +82,7 @@ fillin(Person* me, DatagramIterator& scan, BamReader* manager)
 }
 
 int Person::
-complete_pointers(vector_typedWritable& p_list, BamReader *)
+complete_pointers(TypedWritable **p_list, BamReader *)
 {
   _bro = (p_list[0] == TypedWritable::Null) ? (Person*)NULL : DCAST(Person, p_list[0]);
   _sis = (p_list[1] == TypedWritable::Null) ? (Person*)NULL : DCAST(Person, p_list[1]);
@@ -127,7 +127,7 @@ fillin(Parent* me, DatagramIterator& scan, BamReader* manager)
 }
 
 int Parent::
-complete_pointers(vector_typedWritable& p_list, BamReader *manager)
+complete_pointers(TypedWritable *p_list, BamReader *manager)
 {
   int start = Person::complete_pointers(p_list, manager);
   _son = (p_list[start] == TypedWritable::Null) ? (Child*)NULL : DCAST(Child, p_list[2]);
@@ -187,7 +187,7 @@ fillin(Child* me, DatagramIterator& scan, BamReader* manager)
 }
 
 int Child::
-complete_pointers(vector_typedWritable& p_list, BamReader *manager)
+complete_pointers(TypedWritable ** p_list, BamReader *manager)
 {
   int start = Person::complete_pointers(p_list, manager);
   _dad = (p_list[start] == TypedWritable::Null) ? (Parent*)NULL : DCAST(Parent, p_list[2]);

+ 3 - 3
panda/src/putil/test_bam.h

@@ -38,7 +38,7 @@ public:
   void write_datagram(BamWriter*, Datagram&);
 
   static TypedWritable *make_person(const FactoryParams &params);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 protected:
   void fillin(Person*,DatagramIterator&,BamReader*);
@@ -93,7 +93,7 @@ public:
   void write_datagram(BamWriter*, Datagram&);
 
   static TypedWritable *make_parent(const FactoryParams &params);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable *p_list,
                                 BamReader *manager);
 protected:
   void fillin(Parent*,DatagramIterator&,BamReader*);
@@ -139,7 +139,7 @@ public:
   void write_datagram(BamWriter*, Datagram&);
 
   static TypedWritable *make_child(const FactoryParams &params);
-  virtual int complete_pointers(vector_typedWritable &p_list,
+  virtual int complete_pointers(TypedWritable **p_list,
                                 BamReader *manager);
 protected:
   void fillin(Child*,DatagramIterator&,BamReader*);

+ 59 - 7
panda/src/putil/typedWritable.cxx

@@ -26,20 +26,72 @@ TypedWritable* const TypedWritable::Null = (TypedWritable*)0L;
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-TypedWritable::~TypedWritable()
-{
+TypedWritable::~TypedWritable() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void TypedWritable::
+write_datagram(BamWriter *, Datagram &) {
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: TypedWritable::complete_pointers
 //       Access: Public, Virtual
-//  Description: Takes in a vector of pointers to TypedWritable
-//               objects that correspond to all the requests for
-//               pointers that this object made to BamReader.
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+//
+//               This is the callback function that is made by the
+//               BamReader at some later point, after all of the
+//               required pointers have been filled in.  It is
+//               necessary because there might be forward references
+//               in a bam file; when we call read_pointer() in
+//               fillin(), the object may not have been read from the
+//               file yet, so we do not have a pointer available at
+//               that time.  Thus, instead of returning a pointer,
+//               read_pointer() simply reserves a later callback.
+//               This function provides that callback.  The calling
+//               object is responsible for keeping track of the number
+//               of times it called read_pointer() and extracting the
+//               same number of pointers out of the supplied vector,
+//               and storing them appropriately within the object.
 ////////////////////////////////////////////////////////////////////
 int TypedWritable::
-complete_pointers(vector_typedWritable &, BamReader*)
-{
+complete_pointers(TypedWritable **, BamReader *) {
   return 0;
 }
+          
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::finalize
+//       Access: Public, Virtual
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
+////////////////////////////////////////////////////////////////////
+void TypedWritable::
+finalize() {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::fillin
+//       Access: Protected
+//  Description: This internal function is intended to be called by
+//               each class's make_from_bam() method to read in all of
+//               the relevant data from the BamFile for the new
+//               object.
+//
+//               It is defined at the TypedWritable level so that
+//               derived classes may call up the inheritance chain
+//               from their own fillin() method.
+////////////////////////////////////////////////////////////////////
+void TypedWritable::
+fillin(DatagramIterator &, BamReader *) {
+}
+

+ 10 - 32
panda/src/putil/typedWritable.h

@@ -27,20 +27,11 @@ class BamReader;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : TypedWritable
-// Description : Convience class to not have to derive from TypedObject
-//               and writable all the time
-//
-// Important   : Every class derived from TypedWritable that is
-//               not an abstract class, MUST define a factory method
-//               for creating and object of that type.  This method
-//               must be static.  This method must be of the form
-//               static <class type*> make_<appropriate name>(const FactoryParams &)
-//               Also, in the config file for each package, make sure
-//               to add in code to the ConfigureFN to register the
-//               creation function with BamReader's factory.
+// Description : Base class for objects that can be written to and
+//               read from Bam files.
 ////////////////////////////////////////////////////////////////////
 
-class EXPCL_PANDA TypedWritable : public TypedObject, public Writable {
+class EXPCL_PANDA TypedWritable : public TypedObject {
 public:
   static TypedWritable* const Null;
 
@@ -50,30 +41,14 @@ public:
 
   virtual ~TypedWritable();
 
-  //The essential virtual function interface to define
-  //how any writable object, writes itself to a datagram
-  virtual void write_datagram(BamWriter *, Datagram &) = 0;
+  virtual void write_datagram(BamWriter *, Datagram &);
 
-  //This function is the interface through which BamReader is
-  //able to pass the completed object references into each
-  //object that requested them.
-  //In other words, if an object (when it is creating itself
-  //from a datagram) requests BamReader to generate an object
-  //that this object points to, this is the function that is
-  //eventually called by BamReader to complete those references.
-  //Return the number of pointers read.  This is useful for when
-  //a parent reads in a variable number of pointers, so the child
-  //knows where to start reading from.
-  virtual int complete_pointers(vector_typedWritable &p_list,
-                                BamReader *manager);
+  virtual int complete_pointers(TypedWritable **p_list, BamReader *manager);
 
+  virtual void finalize();
 
 protected:
-  //This interface function is written here, as a suggestion
-  //for a function to write in any class that will have children
-  //that are also TypedWritable.  To encourage code re-use
-
-  //virtual void fillin(TypedWritable*, DatagramIterator&, BamReader *);
+  void fillin(DatagramIterator &scan, BamReader *manager);
 
 PUBLISHED:
   static TypeHandle get_class_type() {
@@ -82,6 +57,9 @@ PUBLISHED:
 
 public:
   static void init_type() {
+    // We no longer derive from Writable, but we continue to record
+    // that derivation for now, just to keep bam files consistent
+    // until we move to bam version 4.0.
     TypedObject::init_type();
     Writable::init_type();
     register_type(_type_handle, "TypedWritable",

+ 1 - 1
panda/src/putil/writable.h

@@ -58,7 +58,7 @@ public:
   //Return the number of pointers read.  This is useful for when
   //a parent reads in a variable number of pointers, so the child
   //knows where to start reading from.
-  //virtual int complete_pointers(vector_typedWritable &p_list,
+  //virtual int complete_pointers(TypedWritable **p_list,
   //                              BamReader *manager) {}
 
 

+ 1 - 1
panda/src/sgattrib/materialTransition.cxx

@@ -145,7 +145,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int MaterialTransition::
-complete_pointers(vector_typedWritable &p_list, BamReader *) {
+complete_pointers(TypedWritable **p_list, BamReader *) {
   if (p_list[0] == TypedWritable::Null) {
     if (sgattrib_cat.is_debug()) {
       sgattrib_cat->debug()

+ 1 - 1
panda/src/sgattrib/materialTransition.h

@@ -58,7 +58,7 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
 protected:

+ 1 - 1
panda/src/sgattrib/textureTransition.cxx

@@ -146,7 +146,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int TextureTransition::
-complete_pointers(vector_typedWritable &p_list, BamReader *) {
+complete_pointers(TypedWritable **p_list, BamReader *) {
   if (p_list[0] == TypedWritable::Null) {
     if (sgattrib_cat.is_debug()) {
       sgattrib_cat->debug()

+ 1 - 1
panda/src/sgattrib/textureTransition.h

@@ -70,7 +70,7 @@ private:
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
 protected:

+ 1 - 1
panda/src/sgraph/geomNode.cxx

@@ -464,7 +464,7 @@ write_datagram(BamWriter *manager, Datagram &me)
 //               pointers that this object made to BamReader.
 ////////////////////////////////////////////////////////////////////
 int GeomNode::
-complete_pointers(vector_typedWritable &p_list, BamReader* manager)
+complete_pointers(TypedWritable **p_list, BamReader* manager)
 {
   int start = NamedNode::complete_pointers(p_list, manager);
   for(int i = start; i < _num_geoms+start; i++)

+ 1 - 1
panda/src/sgraph/geomNode.h

@@ -88,7 +88,7 @@ private:
 public:
   static void register_with_read_factory(void);
   virtual void write_datagram(BamWriter* manager, Datagram &me);
-  virtual int complete_pointers(vector_typedWritable &plist,
+  virtual int complete_pointers(TypedWritable **plist,
                                 BamReader *manager);
 
   static TypedWritable *make_GeomNode(const FactoryParams &params);

+ 2 - 4
panda/src/testbed/Sources.pp

@@ -26,14 +26,12 @@
 
 #end bin_target
 
-#begin test_bin_target
+#begin bin_target
   #define TARGET pview
 
   #define SOURCES \
     pview.cxx
-
-  #define LOCAL_LIBS egg2pg pgraph $[LOCAL_LIBS]
-#end test_bin_target
+#end bin_target
 
 #begin test_bin_target
   #define TARGET open_window

+ 11 - 6
panda/src/testbed/pview.cxx

@@ -32,10 +32,8 @@
 #include "geomTri.h"
 #include "texture.h"
 #include "texturePool.h"
-
-// To load egg files directly.
-#include "qpload_egg_file.h"
-#include "eggData.h"
+#include "dSearchPath.h"
+#include "loader.h"
 
 // These are in support of legacy data graph operations.
 #include "namedNode.h"
@@ -199,13 +197,20 @@ get_models(PandaNode *parent, int argc, char *argv[]) {
     make_default_geometry(parent);
 
   } else {
+    Loader loader;
+    DSearchPath local_path(".");
 
     for (int i = 1; i < argc; i++) {
       Filename filename = argv[i];
       
       cerr << "Loading " << filename << "\n";
-      EggData::resolve_egg_filename(filename);
-      PT(PandaNode) node = qpload_egg_file(filename);
+
+      // First, we always try to resolve a filename from the current
+      // directory.  This means a local filename will always be found
+      // before the model path is searched.
+      filename.resolve_filename(local_path);
+
+      PT(PandaNode) node = loader.qpload_sync(filename);
       if (node == (PandaNode *)NULL) {
         cerr << "Unable to load " << filename << "\n";