Browse Source

bamify NodePath's parents etc too, to better support common nodes

David Rose 16 years ago
parent
commit
981890183b

+ 31 - 25
panda/src/pgraph/nodePath.I

@@ -84,31 +84,6 @@ any_path(PandaNode *node, Thread *current_thread) {
   return result;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: NodePath::Constructor
-//       Access: Published
-//  Description: Constructs a NodePath with the indicated parent
-//               NodePath and child node; the child node must be a
-//               stashed or unstashed child of the parent.
-////////////////////////////////////////////////////////////////////
-INLINE NodePath::
-NodePath(const NodePath &parent, PandaNode *child_node, 
-         Thread *current_thread) :
-  _error_type(ET_fail)
-{
-  nassertv(!parent.is_empty());
-  nassertv(child_node != (PandaNode *)NULL);
-  int pipeline_stage = current_thread->get_pipeline_stage();
-  _head = PandaNode::get_component(parent._head, child_node, pipeline_stage,
-                                   current_thread);
-  nassertv(_head != (NodePathComponent *)NULL);
-
-  if (_head != (NodePathComponent *)NULL) {
-    _error_type = ET_ok;
-  }
-  _backup_key = 0;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::Copy Constructor
 //       Access: Published
@@ -2387,6 +2362,37 @@ get_name() const {
   return node()->get_name();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::encode_full_path_to_bam_stream
+//       Access: Published
+//  Description: Converts the NodePath object into a single
+//               stream of data using a BamWriter, and returns that
+//               data as a string string.  Returns empty string on
+//               failure.
+//
+//               This is different from NodePath::write_bam_stream()
+//               and PandaNode::encode_to_bam_stream(), in that it
+//               encodes the *entire graph* of all nodes connected to
+//               the NodePath, including all parent nodes and
+//               siblings.  (The other methods only encode this node
+//               and the nodes below it.)  This may be necessary for
+//               correct streaming of related NodePaths and
+//               restoration of instances, etc., but it does mean you
+//               must detach() a node before writing it if you want to
+//               limit the nodes that get written.
+//
+//               This method is used by __reduce__ to handle streaming
+//               of NodePaths to a pickle file.
+////////////////////////////////////////////////////////////////////
+INLINE string NodePath::
+encode_full_path_to_bam_stream() const {
+  string data;
+  if (!encode_full_path_to_bam_stream(data)) {
+    return string();
+  }
+  return data;
+}
+
 
 INLINE ostream &operator << (ostream &out, const NodePath &node_path) {
   node_path.output(out);

+ 182 - 26
panda/src/pgraph/nodePath.cxx

@@ -67,6 +67,7 @@
 #include "pStatTimer.h"
 #include "modelNode.h"
 #include "py_panda.h"
+#include "bam.h"
 
 // stack seems to overflow on Intel C++ at 7000.  If we need more than 
 // 7000, need to increase stack size.
@@ -136,6 +137,36 @@ static ConfigVariableEnum<EmptyNodePathType> empty_node_path
 // ***End temporary transition code for operator bool
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::Constructor
+//       Access: Published
+//  Description: Constructs a NodePath with the indicated parent
+//               NodePath and child node; the child node must be a
+//               stashed or unstashed child of the parent.
+////////////////////////////////////////////////////////////////////
+NodePath::
+NodePath(const NodePath &parent, PandaNode *child_node, 
+         Thread *current_thread) :
+  _error_type(ET_fail)
+{
+  nassertv(child_node != (PandaNode *)NULL);
+  int pipeline_stage = current_thread->get_pipeline_stage();
+
+  if (parent.is_empty()) {
+    // Special case: constructing a NodePath at the root.
+    _head = PandaNode::attach(NULL, child_node, 0, pipeline_stage, current_thread);
+  } else {
+    _head = PandaNode::get_component(parent._head, child_node, pipeline_stage,
+                                     current_thread);
+  }
+  nassertv(_head != (NodePathComponent *)NULL);
+
+  if (_head != (NodePathComponent *)NULL) {
+    _error_type = ET_ok;
+  }
+  _backup_key = 0;
+}
+
 #ifdef HAVE_PYTHON
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::__copy__
@@ -236,20 +267,6 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const {
   // (e.g. this), and the arguments necessary to reconstruct this
   // object.
 
-  if (is_empty()) {
-    // Reconstruct an empty NodePath.  Not a 100% reconstruction,
-    // because we lose the specific error status, but I don't think
-    // that matters much.
-    PyObject *this_class = PyObject_Type(self);
-    if (this_class == NULL) {
-      return NULL;
-    }
-    
-    PyObject *result = Py_BuildValue("(O())", this_class);
-    Py_DECREF(this_class);
-    return result;
-  }
-
   BamWriter *writer = NULL;
   if (pickler != NULL) {
     PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
@@ -262,13 +279,12 @@ __reduce_persist__(PyObject *self, PyObject *pickler) const {
     }
   }
 
-  // We have a non-empty NodePath.  We need to streamify the
-  // underlying node.
+  // We have a non-empty NodePath.
 
   string bam_stream;
-  if (!node()->encode_to_bam_stream(bam_stream, writer)) {
+  if (!encode_full_path_to_bam_stream(bam_stream, writer)) {
     ostringstream stream;
-    stream << "Could not bamify object of type " << node()->get_type() << "\n";
+    stream << "Could not bamify " << this;
     string message = stream.str();
     PyErr_SetString(PyExc_TypeError, message.c_str());
     return NULL;
@@ -6606,6 +6622,153 @@ write_bam_stream(ostream &out) const {
   return okflag;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::encode_full_path_to_bam_stream
+//       Access: Published
+//  Description: Converts the NodePath object into a single
+//               stream of data using a BamWriter, and stores that
+//               data in the indicated string.  Returns true on
+//               success, false on failure.
+//
+//               This is different from NodePath::write_bam_stream()
+//               and PandaNode::encode_to_bam_stream(), in that it
+//               encodes the *entire graph* of all nodes connected to
+//               the NodePath, including all parent nodes and
+//               siblings.  (The other methods only encode this node
+//               and the nodes below it.)  This may be necessary for
+//               correct streaming of related NodePaths and
+//               restoration of instances, etc., but it does mean you
+//               must detach() a node before writing it if you want to
+//               limit the nodes that get written.
+//
+//               This method is used by __reduce__ to handle streaming
+//               of NodePaths to a pickle file.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+encode_full_path_to_bam_stream(string &data, BamWriter *writer) const {
+  data.clear();
+  ostringstream stream;
+
+  DatagramOutputFile dout;
+  if (!dout.open(stream)) {
+    return false;
+  }
+  
+  BamWriter local_writer;
+  if (writer == NULL) {
+    // Create our own writer.
+    
+    if (!dout.write_header(_bam_header)) {
+      return false;
+    }
+    writer = &local_writer;
+  }
+  
+  writer->set_target(&dout);
+  
+  // Write an initial Datagram to represent the error type and
+  // number of nodes.
+  int num_nodes = get_num_nodes();
+  Datagram dg;
+  dg.add_uint8(_error_type);
+  dg.add_int32(num_nodes);
+  
+  if (!dout.put_datagram(dg)) {
+    writer->set_target(NULL);
+    return false;
+  }
+  
+  // Now write the nodes, one at a time.
+  for (int i = 0; i < num_nodes; ++i) {
+    PandaNode *node = get_node(num_nodes - i - 1);
+    nassertr(node != NULL, false);
+    if (!writer->write_object(node)) {
+      writer->set_target(NULL);
+      return false;
+    }
+  }
+  writer->set_target(NULL);
+  
+  data = stream.str();
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::decode_full_path_from_bam_stream
+//       Access: Published, Static
+//  Description: Reads the string created by a previous call to
+//               encode_full_path_to_bam_stream(), and extracts and
+//               returns the NodePath on that string.  Returns NULL on
+//               error.
+////////////////////////////////////////////////////////////////////
+NodePath NodePath::
+decode_full_path_from_bam_stream(const string &data, BamReader *reader) {
+  NodePath result;
+
+  istringstream stream(data);
+
+  DatagramInputFile din;
+  if (!din.open(stream)) {
+    return NodePath::fail();
+  }
+
+  BamReader local_reader;
+  if (reader == NULL) {
+    // Create a local reader.
+
+    string head;
+    if (!din.read_header(head, _bam_header.size())) {
+      return NodePath::fail();
+    }
+    
+    if (head != _bam_header) {
+      return NodePath::fail();
+    }
+
+    reader = &local_reader;
+  }
+  
+  reader->set_source(&din);
+
+  // One initial datagram to encode the error type, and the number of nodes.
+  Datagram dg;
+  if (!din.get_datagram(dg)) {
+    return NodePath::fail();
+  }
+
+  DatagramIterator dgi(dg);
+  ErrorType error_type = (ErrorType)dgi.get_uint8();
+  int num_nodes = dgi.get_int32();
+  if (num_nodes == 0) {
+    // An empty NodePath.
+    result._error_type = error_type;
+
+  } else {
+    // A real NodePath.  Ignore error_type.
+    for (int i = 0; i < num_nodes; ++i) {
+      TypedWritable *object = reader->read_object();
+
+      if (object == (TypedWritable *)NULL ||
+          !object->is_of_type(PandaNode::get_class_type())) {
+        reader->set_source(NULL);
+        return NodePath::fail();
+      }
+  
+      if (!reader->resolve()) {
+        reader->set_source(NULL);
+        return NodePath::fail();
+      }
+
+      PandaNode *node = DCAST(PandaNode, object);
+      result = NodePath(result, node);
+    }
+  }
+  
+  reader->set_source(NULL);
+
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::find_common_ancestor
 //       Access: Private, Static
@@ -7542,7 +7705,6 @@ py_decode_NodePath_from_bam_stream(const string &data) {
 ////////////////////////////////////////////////////////////////////
 NodePath
 py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data) {
-
   BamReader *reader = NULL;
   if (unpickler != NULL) {
     PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader");
@@ -7555,13 +7717,7 @@ py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &da
     }
   }
 
-  PT(PandaNode) node = PandaNode::decode_from_bam_stream(data, reader);
-  if (node == (PandaNode *)NULL) {
-    PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream");
-    return NodePath();
-  }    
-
-  return NodePath(node);
+  return NodePath::decode_full_path_from_bam_stream(data, reader);
 }
 #endif  // HAVE_PYTHON
 

+ 6 - 2
panda/src/pgraph/nodePath.h

@@ -162,8 +162,8 @@ PUBLISHED:
   INLINE NodePath(const string &top_node_name, Thread *current_thread = Thread::get_current_thread());
   INLINE NodePath(PandaNode *node, Thread *current_thread = Thread::get_current_thread());
   INLINE static NodePath any_path(PandaNode *node, Thread *current_thread = Thread::get_current_thread());
-  INLINE NodePath(const NodePath &parent, PandaNode *child_node,
-                  Thread *current_thread = Thread::get_current_thread());
+  NodePath(const NodePath &parent, PandaNode *child_node,
+           Thread *current_thread = Thread::get_current_thread());
 
   INLINE NodePath(const NodePath &copy);
   INLINE void operator = (const NodePath &copy);
@@ -869,6 +869,10 @@ PUBLISHED:
   BLOCKING bool write_bam_file(const string &filename) const;
   BLOCKING bool write_bam_stream(ostream &out) const;
 
+  INLINE string encode_full_path_to_bam_stream() const;
+  bool encode_full_path_to_bam_stream(string &data, BamWriter *writer = NULL) const;
+  static NodePath decode_full_path_from_bam_stream(const string &data, BamReader *reader = NULL);
+
 private:
   static NodePathComponent *
   find_common_ancestor(const NodePath &a, const NodePath &b,

+ 1 - 1
panda/src/putil/typedWritable.I

@@ -82,7 +82,7 @@ get_bam_modified() const {
 //               efficient to use the same BamWriter to serialize all
 //               of them together.
 ////////////////////////////////////////////////////////////////////
-string TypedWritable::
+INLINE string TypedWritable::
 encode_to_bam_stream() const {
   string data;
   if (!encode_to_bam_stream(data)) {