Browse Source

pgraph copy subgraph

David Rose 24 years ago
parent
commit
0e6b7053e0

+ 201 - 64
panda/src/pgraph/pandaNode.cxx

@@ -51,6 +51,62 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
 
   dg.add_uint32(_draw_mask.get_word());
 
+  write_up_list(_up, manager, dg);
+  write_down_list(_down, manager, dg);
+  write_down_list(_stashed, manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::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::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  // Get the state and transform pointers.
+  _state = DCAST(RenderState, p_list[pi++]);
+  _transform = DCAST(TransformState, p_list[pi++]);
+
+  pi += complete_up_list(_up, p_list + pi, manager);
+  pi += complete_down_list(_down, p_list + pi, manager);
+  pi += complete_down_list(_stashed, p_list + pi, manager);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new PandaNode.
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  // Read the state and transform pointers.
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
+
+  _draw_mask.set_word(scan.get_uint32());
+
+  fillin_up_list(_up, scan, manager);
+  fillin_down_list(_down, scan, manager);
+  fillin_down_list(_stashed, scan, manager);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::write_up_list
+//       Access: Private
+//  Description: Writes the indicated list of parent node pointers to
+//               the datagram.
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+write_up_list(const PandaNode::Up &up_list,
+              BamWriter *manager, Datagram &dg) const {
   // When we write a PandaNode, we write out its complete list of
   // child node pointers, but we only write out the parent node
   // pointers that have already been added to the bam file by a
@@ -62,7 +118,7 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
 
   int num_parents = 0;
   Up::const_iterator ui;
-  for (ui = _up.begin(); ui != _up.end(); ++ui) {
+  for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
     PandaNode *parent_node = (*ui).get_parent();
     if (manager->has_object(parent_node)) {
       num_parents++;
@@ -70,57 +126,52 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
   }
   nassertv(num_parents == (int)(PN_uint16)num_parents);
   dg.add_uint16(num_parents);
-  for (ui = _up.begin(); ui != _up.end(); ++ui) {
+  for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
     PandaNode *parent_node = (*ui).get_parent();
     if (manager->has_object(parent_node)) {
       manager->write_pointer(dg, parent_node);
     }
   }
+}
 
-  int num_children = _down.size();
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::write_down_list
+//       Access: Private
+//  Description: Writes the indicated list of child node pointers to
+//               the datagram.
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+write_down_list(const PandaNode::Down &down_list,
+                BamWriter *manager, Datagram &dg) const {
+  int num_children = down_list.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.
+  // Should we smarten up the writing of the sort number?  Most of the
+  // time these will all be zero.
   Down::const_iterator di;
-  for (di = _down.begin(); di != _down.end(); ++di) {
+  for (di = down_list.begin(); di != down_list.end(); ++di) {
     PandaNode *child_node = (*di).get_child();
     int sort = (*di).get_sort();
     manager->write_pointer(dg, child_node);
     dg.add_int32(sort);
   }
-
-  int num_stashed = _stashed.size();
-  nassertv(num_stashed == (int)(PN_uint16)num_stashed);
-  dg.add_uint16(num_stashed);
-
-  for (di = _stashed.begin(); di != _stashed.end(); ++di) {
-    PandaNode *stashed_node = (*di).get_child();
-    int sort = (*di).get_sort();
-    manager->write_pointer(dg, stashed_node);
-    dg.add_int32(sort);
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CData::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.
+//     Function: PandaNode::CData::complete_up_list
+//       Access: Private
+//  Description: Calls complete_pointers() on the list of parent node
+//               pointers.
 ////////////////////////////////////////////////////////////////////
 int PandaNode::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
-
-  // Get the state and transform pointers.
-  _state = DCAST(RenderState, p_list[pi++]);
-  _transform = DCAST(TransformState, p_list[pi++]);
+complete_up_list(PandaNode::Up &up_list,
+                 TypedWritable **p_list, BamReader *manager) {
+  int pi = 0;
 
   // Get the parent pointers.
   Up::iterator ui;
-  for (ui = _up.begin(); ui != _up.end(); ++ui) {
+  for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
     PandaNode *parent_node = DCAST(PandaNode, p_list[pi++]);
 
     // For some reason, VC++ won't accept UpConnection as an inline
@@ -131,39 +182,50 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
     (*ui) = connection;
   }
 
-  // Get the child pointers.
+  // Now we should sort the list, since the sorting is based on
+  // pointer order, which might be different from one session to the
+  // next.
+  up_list.sort();
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::complete_down_list
+//       Access: Private
+//  Description: Calls complete_pointers() on the list of child node
+//               pointers.
+////////////////////////////////////////////////////////////////////
+int PandaNode::CData::
+complete_down_list(PandaNode::Down &down_list,
+                   TypedWritable **p_list, BamReader *manager) {
+  int pi = 0;
+
   Down::iterator di;
-  for (di = _down.begin(); di != _down.end(); ++di) {
+  for (di = down_list.begin(); di != down_list.end(); ++di) {
     int sort = (*di).get_sort();
     PT(PandaNode) child_node = DCAST(PandaNode, p_list[pi++]);
     (*di) = DownConnection(child_node, sort);
   }
 
-  // Get the stashed pointers.
-  for (di = _stashed.begin(); di != _stashed.end(); ++di) {
-    int sort = (*di).get_sort();
-    PT(PandaNode) stashed_node = DCAST(PandaNode, p_list[pi++]);
-    (*di) = DownConnection(stashed_node, sort);
-  }
+  // Now we should sort the list, since the sorting is based on
+  // pointer order, which might be different from one session to the
+  // next.
+  down_list.sort();
 
   return pi;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CData::fillin
-//       Access: Public, Virtual
-//  Description: This internal function is called by make_from_bam to
-//               read in all of the relevant data from the BamFile for
-//               the new PandaNode.
+//     Function: PandaNode::CData::fillin_up_list
+//       Access: Private
+//  Description: Reads the indicated list parent node pointers from
+//               the datagram (or at least calls read_pointer() for
+//               each one).
 ////////////////////////////////////////////////////////////////////
 void PandaNode::CData::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  // Read the state and transform pointers.
-  manager->read_pointer(scan);
-  manager->read_pointer(scan);
-
-  _draw_mask.set_word(scan.get_uint32());
-
+fillin_up_list(PandaNode::Up &up_list,
+               DatagramIterator &scan, BamReader *manager) {
   int num_parents = scan.get_uint16();
   // Read the list of parent nodes.  Push back a NULL for each one.
   _up.reserve(num_parents);
@@ -171,23 +233,25 @@ fillin(DatagramIterator &scan, BamReader *manager) {
     manager->read_pointer(scan);
     _up.push_back(UpConnection(NULL));
   }
+}
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::fillin_down_list
+//       Access: Private
+//  Description: Reads the indicated list child node pointers from
+//               the datagram (or at least calls read_pointer() for
+//               each one).
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+fillin_down_list(PandaNode::Down &down_list,
+                 DatagramIterator &scan, BamReader *manager) {
   int num_children = scan.get_uint16();
   // Read the list of child nodes.  Push back a NULL for each one.
-  _down.reserve(num_children);
+  down_list.reserve(num_children);
   for (int i = 0; i < num_children; i++) {
     manager->read_pointer(scan);
     int sort = scan.get_int32();
-    _down.push_back(DownConnection(NULL, sort));
-  }
-
-  int num_stashed = scan.get_uint16();
-  // Read the list of stashed nodes.  Push back a NULL for each one.
-  _stashed.reserve(num_stashed);
-  for (int i = 0; i < num_stashed; i++) {
-    manager->read_pointer(scan);
-    int sort = scan.get_int32();
-    _stashed.push_back(DownConnection(NULL, sort));
+    down_list.push_back(DownConnection(NULL, sort));
   }
 }
 
@@ -278,11 +342,10 @@ make_copy() const {
 //               original (e.g. vertex index tables), but nothing that
 //               will impede normal use of the PandaNode.
 ////////////////////////////////////////////////////////////////////
-PandaNode *PandaNode::
+PT(PandaNode) PandaNode::
 copy_subgraph() const {
-  // *** Do something here.
-  nassertr(false, (PandaNode *)NULL);
-  return (PandaNode *)NULL;
+  InstanceMap inst_map;
+  return r_copy_subgraph(inst_map);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1064,6 +1127,80 @@ void PandaNode::
 children_changed() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::r_copy_subgraph
+//       Access: Protected, Virtual
+//  Description: This is the recursive implementation of copy_subgraph().
+//               It returns a copy of the entire subgraph rooted at
+//               this node.
+//
+//               Note that it includes the parameter inst_map, which
+//               is a map type, and is not (and cannot be) exported
+//               from PANDA.DLL.  Thus, any derivative of PandaNode
+//               that is not also a member of PANDA.DLL *cannot*
+//               access this map.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) PandaNode::
+r_copy_subgraph(PandaNode::InstanceMap &inst_map) const {
+  if (!safe_to_flatten()) {
+    // If this kind of node cannot be copied, quietly return the same
+    // pointer, making an instance instead of a copy.
+    return (PandaNode *)this;
+  }
+
+  PT(PandaNode) copy = make_copy();
+  nassertr(copy != (PandaNode *)NULL, NULL);
+  if (copy->get_type() != get_type()) {
+    pgraph_cat.warning()
+      << "Don't know how to copy nodes of type " << get_type() << "\n";
+  }
+
+  copy->r_copy_children(this, inst_map);
+  copy->set_state(get_state());
+  copy->set_transform(get_transform());
+  return copy;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::r_copy_children
+//       Access: Protected, Virtual
+//  Description: This is called by r_copy_subgraph(); the copy has
+//               already been made of this particular node (and this
+//               is the copy); this function's job is to copy all of
+//               the children from the original.
+//
+//               Note that it includes the parameter inst_map, which
+//               is a map type, and is not (and cannot be) exported
+//               from PANDA.DLL.  Thus, any derivative of PandaNode
+//               that is not also a member of PANDA.DLL *cannot*
+//               access this map, and probably should not even
+//               override this function.
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map) {
+  CDReader from_cdata(from->_cycler);
+  Down::const_iterator di;
+  for (di = from_cdata->_down.begin(); di != from_cdata->_down.end(); ++di) {
+    PandaNode *source_child = (*di).get_child();
+    PT(PandaNode) dest_child;
+
+    // Check to see if we have already copied this child.  If we
+    // have, use the copy.  In this way, a subgraph that contains
+    // instances will be correctly duplicated into another subgraph
+    // that also contains its own instances.
+    InstanceMap::const_iterator ci;
+    ci = inst_map.find(source_child);
+    if (ci != inst_map.end()) {
+      dest_child = (*ci).second;
+    } else {
+      dest_child = source_child->r_copy_subgraph(inst_map);
+      inst_map[source_child] = dest_child;
+    }
+
+    add_child(dest_child);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::attach
 //       Access: Private, Static

+ 18 - 1
panda/src/pgraph/pandaNode.h

@@ -60,7 +60,7 @@ private:
 
 public:
   virtual PandaNode *make_copy() const;
-  PandaNode *copy_subgraph() const;
+  PT(PandaNode) copy_subgraph() const;
 
   virtual bool safe_to_flatten() const;
   virtual bool safe_to_transform() const;
@@ -152,6 +152,10 @@ protected:
   virtual void parents_changed();
   virtual void children_changed();
 
+  typedef pmap<PandaNode *, PandaNode *> InstanceMap;
+  virtual PT(PandaNode) r_copy_subgraph(InstanceMap &inst_map) const;
+  virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map);
+
   // This is the bounding volume around the contents of the node
   // itself (without including all of the node's children).
   // BoundedObject is itself cycled, so we don't need to protect it.
@@ -221,6 +225,19 @@ private:
     virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
+    void write_up_list(const Up &up_list,
+                       BamWriter *manager, Datagram &dg) const;
+    void write_down_list(const Down &down_list,
+                         BamWriter *manager, Datagram &dg) const;
+    int complete_up_list(Up &up_list,
+                         TypedWritable **p_list, BamReader *manager);
+    int complete_down_list(Down &down_list,
+                           TypedWritable **p_list, BamReader *manager);
+    void fillin_up_list(Up &up_list,
+                        DatagramIterator &scan, BamReader *manager);
+    void fillin_down_list(Down &down_list,
+                          DatagramIterator &scan, BamReader *manager);
+
     Down _down;
     Down _stashed;
     Up _up;

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

@@ -139,6 +139,20 @@ qpGeomNode::
 ~qpGeomNode() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomNode::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
+////////////////////////////////////////////////////////////////////
+PandaNode *qpGeomNode::
+make_copy() const {
+  return new qpGeomNode(*this);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomNode::write_geoms
 //       Access: Published

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

@@ -43,6 +43,7 @@ protected:
   qpGeomNode(const qpGeomNode &copy);
 public:
   virtual ~qpGeomNode();
+  virtual PandaNode *make_copy() const;
 
 PUBLISHED:
   INLINE int get_num_geoms() const;

+ 13 - 7
panda/src/pgraph/qpnodePath.cxx

@@ -265,16 +265,22 @@ instance_to(const qpNodePath &other, int sort) const {
 //               node.  A qpNodePath to the newly created copy is
 //               returned.
 //
-//               Certain kinds of nodes may not be copied; if one of
-//               these is encountered, the node will be "copied" as
-//               the nearest copyable base class.  For instance, a
-//               Camera node in the graph will become a simple
-//               NamedNode.
+//               Certain kinds of nodes (like a Camera, for instance,
+//               whose pointer value is important) may not be copied;
+//               if one of these is encountered, the node will be
+//               instanced, instead of actually copied.
 ////////////////////////////////////////////////////////////////////
 qpNodePath qpNodePath::
 copy_to(const qpNodePath &other, int sort) const {
-  //*****
-  return instance_to(other, sort);
+  nassertr(verify_complete(), fail());
+  nassertr(!is_empty(), fail());
+  nassertr(!other.is_empty(), fail());
+
+  PandaNode *source_node = node();
+  PT(PandaNode) copy_node = source_node->copy_subgraph();
+  nassertr(copy_node != (PandaNode *)NULL, fail());
+
+  return other.attach_new_node(copy_node, sort);
 }
 
 ////////////////////////////////////////////////////////////////////