Pārlūkot izejas kodu

*** empty log message ***

David Rose 25 gadi atpakaļ
vecāks
revīzija
259683160c

+ 3 - 3
panda/src/framework/framework.cxx

@@ -738,7 +738,7 @@ void event_C(CPT_Event) {
 void event_N(CPT_Event) {
   nout << "Reducing scene graph.\n";
   SceneGraphReducer gr(RenderRelation::get_class_type());
-  gr.apply_transitions(root);
+  gr.apply_transitions(first_arc);
   int num_reduced = gr.flatten(root, true);
   nout << "Removed " << num_reduced << " arcs.\n";
 }
@@ -1120,8 +1120,8 @@ int framework_main(int argc, char *argv[]) {
   first_arc = new RenderRelation(render, root, 100);
 
   if (files.empty() && framework.GetBool("have-omnitriangle", true)) {
-    // The user did not specify an file.  Create some default
-    // geometry.
+    // The user did not specify a model file to load.  Create some
+    // default geometry.
       
     PTA_Vertexf coords;
     PTA_TexCoordf uvs;

+ 59 - 7
panda/src/graph/nodeRelation.I

@@ -10,9 +10,9 @@
 //               attaches it.
 ////////////////////////////////////////////////////////////////////
 INLINE NodeRelation::
-NodeRelation(Node *parent, Node *to, int sort, TypeHandle type) :
+NodeRelation(Node *parent, Node *to, int sort, TypeHandle graph_type) :
   _parent(parent), _child(to), _sort(sort), 
-  _type(type), _num_transitions(0)
+  _graph_type(graph_type), _num_transitions(0)
 {
   _top_subtree = NULL;
   _attached = false;
@@ -29,8 +29,8 @@ NodeRelation(Node *parent, Node *to, int sort, TypeHandle type) :
 //               general, attempt to create an unattached arc.
 ////////////////////////////////////////////////////////////////////
 INLINE NodeRelation::
-NodeRelation(TypeHandle type) :
-  _type(type)
+NodeRelation(TypeHandle graph_type) :
+  _graph_type(graph_type)
 {
   _parent = NULL;
   _child = NULL;
@@ -49,14 +49,13 @@ NodeRelation(TypeHandle type) :
 //               unattached arc.
 ////////////////////////////////////////////////////////////////////
 INLINE NodeRelation::
-NodeRelation(void)
-{
+NodeRelation(void) {
   _parent = NULL;
   _child = NULL;
   _sort = 0;
   _top_subtree = NULL;
   _attached = false;
-  _type = get_class_type();
+  _graph_type = get_class_type();
   _num_transitions = 0;
 }
 
@@ -124,6 +123,20 @@ get_sort() const {
   return _sort;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::get_graph_type
+//       Access: Public
+//  Description: Returns the type of graph this arc represents.  This
+//               is normally the same value as get_type(), i.e. the
+//               class type of the arc, but it may be set to any type.
+//               A node may simultaneously have many arcs of different
+//               types.
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle NodeRelation::
+get_graph_type() const {
+  return _graph_type;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeRelation::change_parent
 //       Access: Public
@@ -189,6 +202,20 @@ set_sort(int sort) {
   attach();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::set_graph_type
+//       Access: Public
+//  Description: Changes the graph type of the arc.  This moves the
+//               arc into a totally different graph; it may now be
+//               detected only by a special traversal.
+////////////////////////////////////////////////////////////////////
+INLINE void NodeRelation::
+set_graph_type(TypeHandle graph_type) {
+  PT(NodeRelation) hold_arc = detach();
+  _graph_type = graph_type;
+  attach();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeRelation::set_transition
 //       Access: Public
@@ -390,6 +417,31 @@ get_factory() {
   return *_factory;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::get_class_type
+//       Access: Public, Static
+//  Description: Returns the TypeHandle associated with this type.
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle NodeRelation::
+get_class_type() {
+  return _type_handle;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::get_stashed_type
+//       Access: Public, Static
+//  Description: Returns a special TypeHandle that, by convention,
+//               arcs that wish to remove themselves from normal
+//               consideration during any traversal (including
+//               rendering and collision traversals), as well as
+//               removing themselves from the bounding volumes of
+//               their parents, should set their graph_type to.
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle NodeRelation::
+get_stashed_type() {
+  return _stashed_type_handle;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: get_transition_into
 //  Description: This external template function is handy for

+ 71 - 16
panda/src/graph/nodeRelation.cxx

@@ -12,6 +12,7 @@
 #include <algorithm>
 
 TypeHandle NodeRelation::_type_handle;
+TypeHandle NodeRelation::_stashed_type_handle;
 
 Factory<NodeRelation> *NodeRelation::_factory = NULL;
 
@@ -507,8 +508,8 @@ attach() {
 
   _attached = true;
 
-  DownRelationPointers &parent_list = _parent->_children[_type];
-  UpRelationPointers &child_list = _child->_parents[_type];
+  DownRelationPointers &parent_list = _parent->_children[_graph_type];
+  UpRelationPointers &child_list = _child->_parents[_graph_type];
   
   bool inserted_one = internal_insert_arc(parent_list, this);
   bool inserted_two = internal_insert_arc(child_list, this);
@@ -516,7 +517,7 @@ attach() {
 
   // Blow out the cache and increment the current update sequence.
   _net_transitions.clear();
-  _last_update = ++last_graph_update(_type);
+  _last_update = ++last_graph_update(_graph_type);
 
   // If we have just added a new parent arc to a node that previously
   // had exactly one parent, we also need to set the update sequence
@@ -557,8 +558,8 @@ detach() {
 
   force_bound_stale();
 
-  DownRelationPointers &parent_list = _parent->_children[_type];
-  UpRelationPointers &child_list = _child->_parents[_type];
+  DownRelationPointers &parent_list = _parent->_children[_graph_type];
+  UpRelationPointers &child_list = _child->_parents[_graph_type];
 
   bool removed_one = internal_remove_arc(parent_list, this);
   bool removed_two = internal_remove_arc(child_list, this);
@@ -570,7 +571,7 @@ detach() {
 
   // Blow out the cache and increment the current update sequence.
   _net_transitions.clear();
-  _last_update = ++last_graph_update(_type);
+  _last_update = ++last_graph_update(_graph_type);
 
   // If we have just removed a parent arc from a node, leaving exactly
   // one parent, we also need to set the update sequence
@@ -580,6 +581,16 @@ detach() {
     child_list[0]->_last_update = _last_update;
   }
 
+  // If we have completely emptied the parent or child lists, remove
+  // the entry for this graph type from the node, as a small render
+  // optimization.
+  if (parent_list.empty()) {
+    _parent->_children.erase(_graph_type);
+  }
+  if (child_list.empty()) {
+    _child->_parents.erase(_graph_type);
+  }
+
   return result;
 }
 
@@ -601,7 +612,7 @@ detach_below() {
 
   force_bound_stale();
 
-  bool removed = internal_remove_arc(_child->_parents[_type], this);
+  bool removed = internal_remove_arc(_child->_parents[_graph_type], this);
 
   nassertr(removed, result);
 
@@ -609,7 +620,7 @@ detach_below() {
 
   // Blow out the cache and increment the current update sequence.
   _net_transitions.clear();
-  _last_update = ++last_graph_update(_type);
+  _last_update = ++last_graph_update(_graph_type);
 
   return result;
 }
@@ -631,7 +642,7 @@ changed_transition(TypeHandle trans_type) {
   if (_net_transitions != (NodeTransitionCache *)NULL) {
     _net_transitions->clear_transition(trans_type);
   }
-  _last_update = ++last_graph_update(get_type());
+  _last_update = ++last_graph_update(_graph_type);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -648,7 +659,7 @@ propagate_stale_bound() {
   nassertv(node != (Node*)NULL);
 
   UpRelations::const_iterator uri;
-  uri = node->_parents.find(get_type());
+  uri = node->_parents.find(_graph_type);
   if (uri != node->_parents.end()) {
     const UpRelationPointers &urp = (*uri).second;
     
@@ -681,7 +692,7 @@ recompute_bound() {
   child_volumes.push_back(&node->get_bound());
 
   DownRelations::const_iterator dri;
-  dri = node->_children.find(get_type());
+  dri = node->_children.find(_graph_type);
   if (dri != node->_children.end()) {
     const DownRelationPointers &drp = (*dri).second;
     
@@ -717,7 +728,7 @@ void NodeRelation::
 write_datagram(BamWriter *manager, Datagram &me)
 {
   //Write out the "dynamic" type
-  manager->write_handle(me, _type);
+  manager->write_handle(me, _graph_type);
 
   //We should always be attached if we are trying to write out
   nassertv(_attached);
@@ -769,7 +780,7 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager)
 
     // We must explicitly add the arc to its child node, but not to
     // its parent node.
-    UpRelationPointers &child_list = _child->_parents[_type];
+    UpRelationPointers &child_list = _child->_parents[_graph_type];
     bool inserted = internal_insert_arc(child_list, this);
     nassertr(inserted, _num_transitions + 2);
     _attached = true;
@@ -785,8 +796,9 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager)
     //at old code
     if (plist[i] == TypedWriteable::Null)
     {
-      graph_cat->warning() << get_type().get_name() 
-			   << "Ignoring null Transition" << endl;
+      graph_cat->warning() 
+	<< get_type().get_name() 
+	<< ": Ignoring null Transition" << endl;
     }
     else
     {
@@ -829,7 +841,7 @@ make_NodeRelation(const FactoryParams &params)
 void NodeRelation::
 fillin(DatagramIterator& scan, BamReader* manager)
 {
-  _type = manager->read_handle(scan);
+  _graph_type = manager->read_handle(scan);
   //Read in my parent
   manager->read_pointer(scan, this);
   //Read in my child
@@ -855,3 +867,46 @@ register_with_read_factory(void)
 {
   BamReader::get_factory()->register_factory(get_class_type(), make_NodeRelation);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::init_type
+//       Access: Public, Static
+//  Description: Initializes the TypeHandles associated with this
+//               class.
+////////////////////////////////////////////////////////////////////
+void NodeRelation::
+init_type() {
+  TypedWriteableReferenceCount::init_type();
+  BoundedObject::init_type();
+  register_type(_type_handle, "NodeRelation",
+		TypedWriteableReferenceCount::get_class_type(),
+		BoundedObject::get_class_type());
+  register_type(_stashed_type_handle, "StashedNodeRelation",
+		get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::get_type
+//       Access: Public, Virtual
+//  Description: Returns the particular type represented by this
+//               instance of the class.
+////////////////////////////////////////////////////////////////////
+TypeHandle NodeRelation::
+get_type() const {
+  return get_class_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRelation::force_init_type
+//       Access: Public, Virtual
+//  Description: Called by the TypeHandle system when it is detected
+//               that init_type() was not called for some reason.
+//               This is only called in an error situation, and it
+//               attempts to remedy the problem so we can recover and
+//               continue.
+////////////////////////////////////////////////////////////////////
+TypeHandle NodeRelation::
+force_init_type() {
+  init_type();
+  return get_class_type(); 
+}

+ 13 - 17
panda/src/graph/nodeRelation.h

@@ -47,13 +47,13 @@ extern EXPCL_PANDA UpdateSeq &last_graph_update(TypeHandle graph_type);
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA NodeRelation : public TypedWriteableReferenceCount, public BoundedObject {
 public:
-  INLINE NodeRelation(Node *from, Node *to, int sort, TypeHandle type);
+  INLINE NodeRelation(Node *from, Node *to, int sort, TypeHandle graph_type);
 
 protected:
   // Normally, this should only be used from derived classes for
   // passing to the factory.  Don't attempt to create an unattached
   // arc directly.
-  INLINE NodeRelation(TypeHandle type);
+  INLINE NodeRelation(TypeHandle graph_type);
 
   //make_NodeRelation needs to have a construct that takes nothing
   //since it creates the object before it has any information
@@ -80,12 +80,14 @@ PUBLISHED:
   INLINE Node *get_parent() const;
   INLINE Node *get_child() const;
   INLINE int get_sort() const;
+  INLINE TypeHandle get_graph_type() const;
 
   INLINE void change_parent(Node *parent);
   INLINE void change_parent(Node *parent, int sort);
   INLINE void change_child(Node *child);
   INLINE void change_parent_and_child(Node *parent, Node *child);
   INLINE void set_sort(int sort);
+  INLINE void set_graph_type(TypeHandle graph_type);
 
   INLINE PT(NodeTransition) set_transition(TypeHandle handle, 
 					   NodeTransition *trans);
@@ -141,7 +143,7 @@ private:
   Node *_parent;
   PT_Node _child;
   int _sort;
-  TypeHandle _type;
+  TypeHandle _graph_type;
   bool _attached;
 
 private:
@@ -193,24 +195,18 @@ protected:
   virtual void propagate_stale_bound();
   virtual void recompute_bound();
 
+PUBLISHED:
+  INLINE static TypeHandle get_class_type();
+  INLINE static TypeHandle get_stashed_type();
+
 public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    TypedWriteableReferenceCount::init_type();
-    BoundedObject::init_type();
-    register_type(_type_handle, "NodeRelation",
-		  TypedWriteableReferenceCount::get_class_type(),
-		  BoundedObject::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static void init_type();
+  virtual TypeHandle get_type() const;
+  virtual TypeHandle force_init_type();
 
 private:
   static TypeHandle _type_handle;
+  static TypeHandle _stashed_type_handle;
 
   friend INLINE void remove_arc(NodeRelation *arc);
   friend class Node;

+ 10 - 0
panda/src/sgattrib/renderRelation.I

@@ -39,3 +39,13 @@ INLINE void RenderRelation::
 register_with_factory() {
   get_factory().register_factory(get_class_type(), make_arc);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderRelation::get_class_type
+//       Access: Public, Static
+//  Description: Returns the TypeHandle associated with this type.
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle RenderRelation::
+get_class_type() {
+  return _type_handle;
+}

+ 40 - 0
panda/src/sgattrib/renderRelation.cxx

@@ -98,3 +98,43 @@ register_with_read_factory(void)
 {
   BamReader::get_factory()->register_factory(get_class_type(), make_RenderRelation);
 }
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderRelation::init_type
+//       Access: Public, Static
+//  Description: Initializes the TypeHandles associated with this
+//               class.
+////////////////////////////////////////////////////////////////////
+void RenderRelation::
+init_type() {
+  NodeRelation::init_type();
+  register_type(_type_handle, "RenderRelation",
+		NodeRelation::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderRelation::get_type
+//       Access: Public, Virtual
+//  Description: Returns the particular type represented by this
+//               instance of the class.
+////////////////////////////////////////////////////////////////////
+TypeHandle RenderRelation::
+get_type() const {
+  return get_class_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderRelation::force_init_type
+//       Access: Public, Virtual
+//  Description: Called by the TypeHandle system when it is detected
+//               that init_type() was not called for some reason.
+//               This is only called in an error situation, and it
+//               attempts to remedy the problem so we can recover and
+//               continue.
+////////////////////////////////////////////////////////////////////
+TypeHandle RenderRelation::
+force_init_type() {
+  init_type();
+  return get_class_type(); 
+}

+ 4 - 12
panda/src/sgattrib/renderRelation.h

@@ -43,20 +43,12 @@ public:
   static TypedWriteable *make_RenderRelation(const FactoryParams &params);
 
 PUBLISHED:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
+  INLINE static TypeHandle get_class_type();
 
 public:
-  static void init_type() {
-    NodeRelation::init_type();
-    register_type(_type_handle, "RenderRelation",
-		 NodeRelation::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static void init_type();
+  virtual TypeHandle get_type() const;
+  virtual TypeHandle force_init_type();
 
 private:
   static TypeHandle _type_handle;

+ 11 - 0
panda/src/sgmanip/findApproxLevel.I

@@ -44,6 +44,17 @@ operator = (const FindApproxLevelEntry &copy) {
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxLevelEntry::next_is_stashed
+//       Access: Public
+//  Description: Returns true if the next node matched by this entry
+//               must be a stashed node, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool FindApproxLevelEntry::
+next_is_stashed() const {
+  return _approx_path.matches_stashed(_i);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FindApproxLevelEntry::is_solution
 //       Access: Public

+ 58 - 22
panda/src/sgmanip/findApproxLevel.cxx

@@ -30,6 +30,61 @@ output(ostream &out) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxLevelEntry::consider_node
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FindApproxLevelEntry::
+consider_node(NodePathCollection &result, FindApproxLevel &next_level, 
+	      int max_matches, TypeHandle graph_type) const {
+  nassertv(_i < _approx_path.get_num_components());
+
+  if (_approx_path.is_component_match_many(_i)) {
+    // Match any number, zero or more, levels of nodes.  This is the
+    // tricky case that requires this whole nutty breadth-first thing.
+    
+    // This means we must reconsider our own entry with the next path
+    // entry, before we consider the next entry--this supports
+    // matching zero levels of nodes.
+    FindApproxLevelEntry reconsider(*this);
+    ++reconsider._i;
+
+    if (reconsider.is_solution()) {
+      // Does this now represent a solution?
+      result.add_path(reconsider._node_path);
+      if (max_matches > 0 && result.get_num_paths() >= max_matches) {
+	return;
+      }
+    } else {
+      reconsider.consider_node(result, next_level, max_matches, graph_type);
+    }
+  }
+
+  Node *bottom_node = _node_path.node();
+  nassertv(bottom_node != (Node *)NULL);
+
+  TypeHandle next_graph_type = graph_type;
+  if (next_is_stashed()) {
+    next_graph_type = NodeRelation::get_stashed_type();
+  }
+  
+  DownRelations::const_iterator dri;
+  dri = bottom_node->_children.find(next_graph_type);
+  if (dri != bottom_node->_children.end()) {
+    const DownRelationPointers &drp = (*dri).second;
+    
+    DownRelationPointers::const_iterator drpi;
+    for (drpi = drp.begin(); drpi != drp.end(); ++drpi) {
+      NodeRelation *arc = (*drpi);
+      consider_next_step(result, arc, next_level, max_matches, graph_type);
+      if (max_matches > 0 && result.get_num_paths() >= max_matches) {
+	return;
+      }
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FindApproxLevelEntry::consider_next_step
 //       Access: Public
@@ -40,47 +95,28 @@ output(ostream &out) const {
 //               whatever additional entries are appropriate and
 //               stores them in next_level.
 //
-//               If a complete solution is found, returns the solution
-//               node; otherwise, returns NULL.
+//               If a complete solution is found, stores it in result.
 ////////////////////////////////////////////////////////////////////
 void FindApproxLevelEntry::
 consider_next_step(NodePathCollection &result,
 		   NodeRelation *arc, FindApproxLevel &next_level, 
-		   int max_matches) const {
+		   int max_matches, TypeHandle graph_type) const {
   nassertv(_i < _approx_path.get_num_components());
 
   FindApproxLevelEntry next(*this);
   bool eb = next._node_path.extend_by(arc);
   nassertv(eb);
 
-  Node *child = arc->get_child();
   if (_approx_path.is_component_match_many(_i)) {
     // Match any number, zero or more, levels of nodes.  This is the
     // tricky case that requires this whole nutty breadth-first thing.
     
-    // This means we must reconsider our own entry with the next
-    // path entry, before we consider the next entry.
-    FindApproxLevelEntry reconsider(*this);
-    ++reconsider._i;
-    if (reconsider.is_solution()) {
-      // Does this now represent a solution?
-      bool eb = reconsider._node_path.extend_by(arc);
-      nassertv(eb);
-      result.add_path(reconsider._node_path);
-    } else {
-      reconsider.consider_next_step(result, arc, next_level, max_matches);
-    }
-    
-    if (max_matches > 0 && result.get_num_paths() >= max_matches) {
-      return;
-    }
-    
     // And now we just add the next entry without incrementing its
     // path entry.
     next_level.add_entry(next);
 
   } else {
-    if (_approx_path.matches_component(_i, child)) {
+    if (_approx_path.matches_component(_i, arc)) {
       // That matched, and it consumes one path entry.
       ++next._i;
       next_level.add_entry(next);

+ 5 - 1
panda/src/sgmanip/findApproxLevel.h

@@ -32,9 +32,13 @@ public:
   INLINE FindApproxLevelEntry(const FindApproxLevelEntry &copy);
   INLINE void operator = (const FindApproxLevelEntry &copy);
 
+  INLINE bool next_is_stashed() const;
+
+  void consider_node(NodePathCollection &result, FindApproxLevel &next_level, 
+		     int max_matches, TypeHandle graph_type) const;
   void consider_next_step(NodePathCollection &result,
 			  NodeRelation *arc, FindApproxLevel &next_level, 
-			  int max_matches) const;
+			  int max_matches, TypeHandle graph_type) const;
   INLINE bool is_solution() const;
 
   void output(ostream &out) const;

+ 31 - 9
panda/src/sgmanip/findApproxPath.I

@@ -20,10 +20,11 @@ FindApproxPath() {
 //               exactly.
 ////////////////////////////////////////////////////////////////////
 INLINE void FindApproxPath::
-add_match_name(const string &name) {
+add_match_name(const string &name, int flags) {
   Component comp;
   comp._type = CT_match_name;
   comp._name = name;
+  comp._flags = flags;
   _path.push_back(comp);
 }
 
@@ -35,10 +36,11 @@ add_match_name(const string &name) {
 //               characters accepted.
 ////////////////////////////////////////////////////////////////////
 INLINE void FindApproxPath::
-add_match_name_glob(const string &name) {
+add_match_name_glob(const string &name, int flags) {
   Component comp;
   comp._type = CT_match_name_glob;
   comp._name = name;
+  comp._flags = flags;
   _path.push_back(comp);
 }
 
@@ -49,10 +51,11 @@ add_match_name_glob(const string &name) {
 //               exactly, with no derived types matching.
 ////////////////////////////////////////////////////////////////////
 INLINE void FindApproxPath::
-add_match_exact_type(TypeHandle type) {
+add_match_exact_type(TypeHandle type, int flags) {
   Component comp;
   comp._type = CT_match_exact_type;
   comp._type_handle = type;
+  comp._flags = flags;
   _path.push_back(comp);
 }
 
@@ -63,10 +66,11 @@ add_match_exact_type(TypeHandle type) {
 //               or be a base class of the node's type.
 ////////////////////////////////////////////////////////////////////
 INLINE void FindApproxPath::
-add_match_inexact_type(TypeHandle type) {
+add_match_inexact_type(TypeHandle type, int flags) {
   Component comp;
   comp._type = CT_match_inexact_type;
   comp._type_handle = type;
+  comp._flags = flags;
   _path.push_back(comp);
 }
 
@@ -77,9 +81,10 @@ add_match_inexact_type(TypeHandle type) {
 //               chain of many nodes).
 ////////////////////////////////////////////////////////////////////
 INLINE void FindApproxPath::
-add_match_one() {
+add_match_one(int flags) {
   Component comp;
   comp._type = CT_match_one;
+  comp._flags = flags;
   _path.push_back(comp);
 }
 
@@ -90,9 +95,10 @@ add_match_one() {
 //               more consecutive nodes.
 ////////////////////////////////////////////////////////////////////
 INLINE void FindApproxPath::
-add_match_many() {
+add_match_many(int flags) {
   Component comp;
   comp._type = CT_match_many;
+  comp._flags = flags;
   _path.push_back(comp);
 }
 
@@ -103,10 +109,11 @@ add_match_many() {
 //               exactly, by pointer.
 ////////////////////////////////////////////////////////////////////
 INLINE void FindApproxPath::
-add_match_pointer(Node *pointer) {
+add_match_pointer(Node *pointer, int flags) {
   Component comp;
   comp._type = CT_match_pointer;
   comp._pointer = pointer;
+  comp._flags = flags;
   _path.push_back(comp);
 }
 
@@ -139,9 +146,24 @@ is_component_match_many(int index) const {
 //               the indicated node, false otherwise.
 ////////////////////////////////////////////////////////////////////
 INLINE bool FindApproxPath::
-matches_component(int index, Node *node) const {
+matches_component(int index, NodeRelation *arc) const {
   nassertr(index >= 0 && index < (int)_path.size(), false);
-  return (_path[index].matches(node));
+  return (_path[index].matches(arc));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxPath::matches_stashed
+//       Access: Public
+//  Description: Returns true if the nth component of the path matches
+//               a stashed node only, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool FindApproxPath::
+matches_stashed(int index) const {
+  if (index >= 0 && index < (int)_path.size()) {
+    return ((_path[index]._flags & CF_stashed) != 0);
+  } else {
+    return false;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 19 - 7
panda/src/sgmanip/findApproxPath.cxx

@@ -9,6 +9,7 @@
 #include <globPattern.h>
 #include <node.h>
 #include <namedNode.h>
+#include <nodeRelation.h>
 
 
 ////////////////////////////////////////////////////////////////////
@@ -18,7 +19,8 @@
 //               component, false otherwise.
 ////////////////////////////////////////////////////////////////////
 bool FindApproxPath::Component::
-matches(Node *node) const {
+matches(NodeRelation *arc) const {
+  Node *node = arc->get_child();
   string node_name;
 
   switch (_type) {
@@ -113,12 +115,22 @@ add_string(const string &str_path) {
 //               the string component was in some way invalid.
 ////////////////////////////////////////////////////////////////////
 bool FindApproxPath::
-add_component(const string &str_component) {
+add_component(string str_component) {
+  int flags = 0;
+  if (str_component.size() >= 2 && str_component.substr(0, 2) == "@@") {
+    flags |= CF_stashed;
+    str_component = str_component.substr(2);
+  }
+
   if (str_component == "*") {
-    add_match_one();
+    add_match_one(flags);
 
   } else if (str_component == "**") {
-    add_match_many();
+    if ((flags & CF_stashed) != 0) {
+      sgmanip_cat.warning()
+	<< "@@** is ambiguous; use @@*/** or **/@@* instead.\n";
+    }
+    add_match_many(flags);
 
   } else if (!str_component.empty() && str_component[0] == '-') {
     string type_name = str_component.substr(1);
@@ -130,7 +142,7 @@ add_component(const string &str_component) {
       return false;
 
     } else {
-      add_match_exact_type(handle);
+      add_match_exact_type(handle, flags);
     }
 
   } else if (!str_component.empty() && str_component[0] == '+') {
@@ -143,11 +155,11 @@ add_component(const string &str_component) {
       return false;
 
     } else {
-      add_match_inexact_type(handle);
+      add_match_inexact_type(handle, flags);
     }
 
   } else {
-    add_match_name_glob(str_component);
+    add_match_name_glob(str_component, flags);
   }
 
   return true;

+ 16 - 10
panda/src/sgmanip/findApproxPath.h

@@ -14,6 +14,7 @@
 #include <vector>
 
 class Node;
+class NodeRelation;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : FindApproxPath
@@ -27,19 +28,20 @@ public:
   INLINE FindApproxPath();
 
   bool add_string(const string &str_path);
-  bool add_component(const string &str_component);
+  bool add_component(string str_component);
 
-  INLINE void add_match_name(const string &name);
-  INLINE void add_match_name_glob(const string &glob);
-  INLINE void add_match_exact_type(TypeHandle type);
-  INLINE void add_match_inexact_type(TypeHandle type);
-  INLINE void add_match_one();
-  INLINE void add_match_many();
-  INLINE void add_match_pointer(Node *pointer);
+  INLINE void add_match_name(const string &name, int flags);
+  INLINE void add_match_name_glob(const string &glob, int flags);
+  INLINE void add_match_exact_type(TypeHandle type, int flags);
+  INLINE void add_match_inexact_type(TypeHandle type, int flags);
+  INLINE void add_match_one(int flags);
+  INLINE void add_match_many(int flags);
+  INLINE void add_match_pointer(Node *pointer, int flags);
 
   INLINE int get_num_components() const;
   INLINE bool is_component_match_many(int index) const;
-  INLINE bool matches_component(int index, Node *node) const;
+  INLINE bool matches_component(int index, NodeRelation *arc) const;
+  INLINE bool matches_stashed(int index) const;
 
   void output(ostream &out) const;
   INLINE void output_component(ostream &out, int index) const;
@@ -58,16 +60,20 @@ private:
     CT_match_many,
     CT_match_pointer
   };
+  enum ComponentFlags {
+    CF_stashed        = 0x001,
+  };
 
   class Component {
   public:
-    bool matches(Node *node) const;
+    bool matches(NodeRelation *arc) const;
     void output(ostream &out) const;
 
     ComponentType _type;
     string _name;
     TypeHandle _type_handle;
     Node *_pointer;
+    int _flags;
   };
 
   typedef vector<Component> Path;

+ 78 - 0
panda/src/sgmanip/nodePath.I

@@ -98,6 +98,7 @@ INLINE NodePath::
 NodePath(const ArcChain &chain, TypeHandle graph_type) :
   NodePathBase(chain, graph_type)
 {
+  reset_top_node();
   nassertv(verify_connectivity());
 }
 
@@ -262,6 +263,18 @@ get_num_nodes() const {
   return get_num_arcs() + 1;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::get_top_node
+//       Access: Public
+//  Description: Returns the top node of the path, or NULL if the path
+//               is empty.
+////////////////////////////////////////////////////////////////////
+INLINE Node *NodePath::
+get_top_node() {
+  reset_top_node();
+  return _top_node;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::node
 //       Access: Public
@@ -1409,6 +1422,13 @@ show() {
 //       Access: Public
 //  Description: Puts a PruneTransition on this bottom arc so that the
 //               geometry at this level and below will be invisible.
+//
+//               However, it will remain part of the scene graph, and
+//               will still be counted in its parent's bounding
+//               volume; furthermore, traversals like the collision
+//               traversal will still visit the node.
+//
+//               See stash() for a more thorough way to hide the node.
 ////////////////////////////////////////////////////////////////////
 INLINE void NodePath::
 hide() {
@@ -1439,3 +1459,61 @@ INLINE void NodePath::
 hide_collision_solids() {
   find_all_matches("**/+CollisionNode").hide();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::is_hidden
+//       Access: Public
+//  Description: Returns true if some arc above this bottom node has
+//               been set to 'hide', false if it should be visible.
+////////////////////////////////////////////////////////////////////
+INLINE bool NodePath::
+is_hidden() const {
+  return !get_hidden_ancestor().is_empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::stash
+//       Access: Public
+//  Description: A more thorough version of hide(), this effectively
+//               removes the bottom node from the scene graph--it does
+//               not appear in any traversals, rendering or otherwise,
+//               and cannot be located again via NodePath::find().
+//               The node is also removed from its parents' bounding
+//               volume.
+//
+//               However, the node is still associated with its
+//               parent, and will be removed if the parent is removed,
+//               and it can be revealed again by a future call to
+//               unstash().
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+stash() {
+  nassertv(has_arcs());
+  nassertv(_head != (ArcComponent *)NULL);
+
+  _head->_arc->set_graph_type(NodeRelation::get_stashed_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::unstash
+//       Access: Public
+//  Description: Reveals a node that was previously hidden via stash().
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+unstash() {
+  nassertv(has_arcs());
+  nassertv(_head != (ArcComponent *)NULL);
+
+  _head->_arc->set_graph_type(_head->_arc->get_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::is_stashed
+//       Access: Public
+//  Description: Returns true if some arc above this bottom node has
+//               been set to 'stash', false if it should be visible.
+////////////////////////////////////////////////////////////////////
+INLINE bool NodePath::
+is_stashed() const {
+  return !get_stashed_ancestor().is_empty();
+}

+ 112 - 93
panda/src/sgmanip/nodePath.cxx

@@ -122,8 +122,6 @@ bool NodePath::
 extend_by(NodeRelation *arc) {
   nassertr(verify_connectivity(), false);
   nassertr(arc != (NodeRelation *)NULL, false);
-  // Make sure the graph types are consistent.
-  nassertr(arc->get_type() == _graph_type, false);
 
   if (is_empty()) {
     nassertr(_head == (ArcComponent *)NULL, false);
@@ -212,8 +210,8 @@ extend_down_to(Node *dnode) {
   nassertr(verify_connectivity(), false);
   NodePathCollection col;
   FindApproxPath approx_path;
-  approx_path.add_match_many();
-  approx_path.add_match_pointer(dnode);
+  approx_path.add_match_many(0);
+  approx_path.add_match_pointer(dnode, 0);
   find_matches(col, approx_path, -1);
 
   if (col.is_empty()) {
@@ -352,8 +350,8 @@ find_all_paths_down_to(Node *dnode) const {
   nassertr(verify_connectivity(), col);
   nassertr(dnode != (Node *)NULL, col);
   FindApproxPath approx_path;
-  approx_path.add_match_many();
-  approx_path.add_match_pointer(dnode);
+  approx_path.add_match_many(0);
+  approx_path.add_match_pointer(dnode, 0);
   find_matches(col, approx_path, -1);
   return col;
 }
@@ -485,30 +483,6 @@ get_arc(int index) const {
   return comp->_arc;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: NodePath::get_top_node
-//       Access: Public
-//  Description: Returns the top node of the path, or NULL if the path
-//               is empty.  This requires iterating through the path.
-////////////////////////////////////////////////////////////////////
-Node *NodePath::
-get_top_node() const {
-  if (_head == (ArcComponent *)NULL) {
-    // A singleton or empty list.
-    return _top_node;
-  }
-
-  ArcComponent *comp = _head;
-  while (comp->_next != (ArcComponent *)NULL) {
-    comp = comp->_next;
-  }
-
-  // This assertion should not fail unless there is a logic error in
-  // the above.
-  nassertr(comp != (ArcComponent *)NULL, NULL);
-  return comp->_arc->get_parent();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::share_with
 //       Access: Public
@@ -604,12 +578,6 @@ verify_connectivity() const {
     return false;
   }
 
-  // We also want to verify that all of the arcs are of the proper
-  // type.
-  if (comp->_arc->get_type() != _graph_type) {
-    return false;
-  }
-
   comp = comp->_next;
   while (comp != (const ArcComponent *)NULL) {
     Node *next_parent = comp->_arc->get_parent();
@@ -620,15 +588,21 @@ verify_connectivity() const {
     if (next_child != parent) {
       return false;
     }
-    if (comp->_arc->get_type() != _graph_type) {
-      return false;
-    }
 
     parent = next_parent;
     child = next_child;
     comp = comp->_next;
   }
 
+  // We cannot insist that _top_node be correct, since it might have
+  // wandered, particularly if our parent path has changed without our
+  // knowledge.
+  /*
+    if (parent != _top_node) {
+      return false;
+    }
+  */
+
   return true;
 }
 
@@ -666,15 +640,6 @@ amputate_badness() {
     return false;
   }
 
-  // We also want to verify that all of the arcs are of the proper
-  // type.
-  if (comp->_arc->get_type() != _graph_type) {
-    // Eek!  The bottom arc is broken!
-    _top_node = child;
-    _head = (ArcComponent *)NULL;
-    return false;
-  }
-
   ArcComponent *prev = comp;
   comp = comp->_next;
   while (comp != (const ArcComponent *)NULL) {
@@ -682,14 +647,12 @@ amputate_badness() {
     Node *next_child = comp->_arc->get_child();
     if (next_parent == (Node *)NULL || next_child == (Node *)NULL) {
       prev->_next = (ArcComponent *)NULL;
+      _top_node = parent;
       return false;
     }
     if (next_child != parent) {
       prev->_next = (ArcComponent *)NULL;
-      return false;
-    }
-    if (comp->_arc->get_type() != _graph_type) {
-      prev->_next = (ArcComponent *)NULL;
+      _top_node = parent;
       return false;
     }
 
@@ -714,6 +677,14 @@ amputate_badness() {
 ////////////////////////////////////////////////////////////////////
 bool NodePath::
 repair_connectivity(const NodePath &top) {
+  // If the only problem is the top node, we can fix that first.
+  reset_top_node();
+  if (verify_connectivity()) {
+    return true;
+  }
+
+  nassertr(top.verify_connectivity(), false);
+  
   NodePath new_path(*this);
   new_path.amputate_badness();
   if (new_path.is_empty()) {
@@ -731,6 +702,7 @@ repair_connectivity(const NodePath &top) {
   }
 
   (*this) = full_path;
+
   return true;
 }
 
@@ -767,6 +739,7 @@ reparent_to(const NodePath &other, int sort) {
   // update our own path, as well as all paths that share the same
   // head pointer (i.e. all paths derived from this one).
   _head->_next = other._head;
+  _top_node = other._top_node;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -832,6 +805,7 @@ instance_to(const NodePath &other, int sort) const {
 
   NodePath instance(*this);
   instance._head = new ArcComponent(darc, other._head);
+  instance._top_node = other._top_node;
   return instance;
 }
 
@@ -874,6 +848,7 @@ copy_to(const NodePath &other, int sort) const {
 
   NodePath instance(*this);
   instance._head = new ArcComponent(darc, other._head);
+  instance._top_node = other._top_node;
   return instance;
 }
 
@@ -1080,7 +1055,7 @@ int NodePath::
 flatten_medium() {
   nassertr(!is_empty(), 0);
   SceneGraphReducer gr(_graph_type);
-  gr.apply_transitions(node());
+  gr.apply_transitions(arc());
   int num_removed = gr.flatten(node(), false);
 
   if (sgmanip_cat.is_debug()) {
@@ -1112,7 +1087,7 @@ int NodePath::
 flatten_strong() {
   nassertr(!is_empty(), 0);
   SceneGraphReducer gr(_graph_type);
-  gr.apply_transitions(node());
+  gr.apply_transitions(arc());
   int num_removed = gr.flatten(node(), true);
 
   if (sgmanip_cat.is_debug()) {
@@ -2312,17 +2287,6 @@ get_transparency() const {
   return false;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: NodePath::is_hidden
-//       Access: Public
-//  Description: Returns true if some arc above this bottom node has
-//               been set to 'hide', false if it should be visible.
-////////////////////////////////////////////////////////////////////
-bool NodePath::
-is_hidden() const {
-  return !get_hidden_ancestor().is_empty();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::get_hidden_ancestor
 //       Access: Public
@@ -2351,6 +2315,33 @@ get_hidden_ancestor() const {
   return next.get_hidden_ancestor();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::get_stashed_ancestor
+//       Access: Public
+//  Description: Returns a NodePath indicating the lowest arc above
+//               this node which has been set to 'stash'.  Calling
+//               unstash() on the NodePath returned by this function
+//               should make the node visible again (unless there is
+//               another arc further up that also has been 'stashed'.
+//
+//               This function returns an empty NodePath if no
+//               ancestors have been 'stashed'.
+////////////////////////////////////////////////////////////////////
+NodePath NodePath::
+get_stashed_ancestor() const {
+  if (!has_arcs()) {
+    return NodePath();
+  }
+  nassertr(_head != (ArcComponent *)NULL, NodePath());
+  if (_head->_arc->get_graph_type() != _graph_type) {
+    return *this;
+  }
+
+  NodePath next(*this);
+  next.shorten();
+  return next.get_stashed_ancestor();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::prepare_scene
 //       Access: Public
@@ -2450,6 +2441,27 @@ write_bounds(ostream &out) const {
   get_bounds()->write(out);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::reset_top_node
+//       Access: Private
+//  Description: Recomputes the _top_node member to accurately reflect
+//               the top node of the chain.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+reset_top_node() {
+  if (_head != (ArcComponent *)NULL) {
+    ArcComponent *comp = _head;
+    while (comp->_next != (ArcComponent *)NULL) {
+      comp = comp->_next;
+    }
+    
+    // This assertion should not fail unless there is a logic error in
+    // the above.
+    nassertv(comp != (ArcComponent *)NULL);
+    _top_node = comp->_arc->get_parent();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::r_compare_to
 //       Access: Private, Static
@@ -2513,8 +2525,9 @@ r_as_string(const ArcComponent *comp, string &result, int skip_nodes) const {
     // Here's the end of the chain.
     if (skip_nodes == 0) {
       // Skip no nodes; return the full name.
-      result = format_node_name(comp->_arc->get_parent()) + "/" +
-	format_node_name(comp->_arc->get_child());
+      result = format_node_name(comp->_arc->get_parent());
+      result += format_arc_name(comp->_arc);
+      result += format_node_name(comp->_arc->get_child());
 
     } else if (skip_nodes == 1) {
       // Skip the first node.
@@ -2530,14 +2543,13 @@ r_as_string(const ArcComponent *comp, string &result, int skip_nodes) const {
 	// This is not the first node, so format a slash between the
 	// previous node and this node.
 
-	if (comp->_arc->get_child() == (Node *)NULL ||
-	    comp->_arc->get_parent() == comp->_next->_arc->get_child()) {
-	  result += "/";
-	} else {
+	if (!(comp->_arc->get_child() == (Node *)NULL ||
+	      comp->_arc->get_parent() == comp->_next->_arc->get_child())) {
 	  // Unless the path is broken here.  In this case, insert a
 	  // visual indication of the break.
-	  result += "/.../" + format_node_name(comp->_arc->get_parent()) + "/";
+	  result += "/.../" + format_node_name(comp->_arc->get_parent());
 	}
+	result += format_arc_name(comp->_arc);
 	result += format_node_name(comp->_arc->get_child());
       }
     }
@@ -2615,6 +2627,28 @@ format_node_name(Node *dnode) const {
   return name;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::format_arc_name
+//       Access: Private
+//  Description: Formats the "name" for the arc in as_string().
+//               Normally, this is simply "/", but for certain arcs
+//               (as for stashed nodes, for instance), it might
+//               contain other characters.
+////////////////////////////////////////////////////////////////////
+string NodePath::
+format_arc_name(NodeRelation *arc) const {
+  string result = "/";
+
+  if (arc->get_graph_type() == NodeRelation::get_stashed_type()) {
+    result += "@@";
+    
+  } else if (arc->get_graph_type() != _graph_type) {
+    result += "@@(" + arc->get_graph_type().get_name() + ")";
+  }
+
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::find_matches
 //       Access: Private
@@ -2685,31 +2719,16 @@ r_find_matches(NodePathCollection &result,
   FindApproxLevel::Vec::const_iterator li;
   for (li = level._v.begin(); li != level._v.end(); ++li) {
     const FindApproxLevelEntry &entry = (*li);
+
     if (entry.is_solution()) {
       // Does this entry already represent a solution?
       result.add_path(entry._node_path);
-      if (max_matches > 0 && result.get_num_paths() >= max_matches) {
-	return;
-      }
-
     } else {
-      Node *bottom_node = entry._node_path.node();
-      nassertv(bottom_node != (Node *)NULL);
-
-      DownRelations::const_iterator dri;
-      dri = bottom_node->_children.find(_graph_type);
-      if (dri != bottom_node->_children.end()) {
-	const DownRelationPointers &drp = (*dri).second;
-	
-	DownRelationPointers::const_iterator drpi;
-	for (drpi = drp.begin(); drpi != drp.end(); ++drpi) {
-	  NodeRelation *arc = (*drpi);
-	  entry.consider_next_step(result, arc, next_level, max_matches);
-	  if (max_matches > 0 && result.get_num_paths() >= max_matches) {
-	    return;
-	  }
-	}
-      }
+      entry.consider_node(result, next_level, max_matches, _graph_type);
+    }
+
+    if (max_matches > 0 && result.get_num_paths() >= max_matches) {
+      return;
     }
   }
 

+ 9 - 2
panda/src/sgmanip/nodePath.h

@@ -154,7 +154,7 @@ PUBLISHED:
   int get_num_arcs() const;
   NodeRelation *get_arc(int index) const;
 
-  Node *get_top_node() const;
+  INLINE Node *get_top_node();
   INLINE Node *node() const;
   INLINE NodeRelation *arc() const;
 
@@ -442,9 +442,14 @@ PUBLISHED:
   INLINE void hide();
   INLINE void show_collision_solids();
   INLINE void hide_collision_solids();
-  bool is_hidden() const;
+  INLINE bool is_hidden() const;
   NodePath get_hidden_ancestor() const;
 
+  INLINE void stash();
+  INLINE void unstash();
+  INLINE bool is_stashed() const;
+  NodePath get_stashed_ancestor() const;
+
   void prepare_scene(GraphicsStateGuardianBase *gsg);
 
   void show_bounds();
@@ -466,6 +471,7 @@ public:
   };
 
 private:
+  void reset_top_node();
   static int r_compare_to(const ArcComponent *a, const ArcComponent *v);
   bool r_extend_by(const ArcComponent *other);
   int r_as_string(const ArcComponent *comp, string &result, 
@@ -475,6 +481,7 @@ private:
   void r_get_net_transitions(const ArcComponent *comp, 
 			     AllTransitionsWrapper &trans) const;
   string format_node_name(Node *dnode) const;
+  string format_arc_name(NodeRelation *arc) const;
 
   void find_matches(NodePathCollection &result, 
 		    const string &approx_path_str,

+ 8 - 2
panda/src/sgmanip/nodePathBase.h

@@ -32,8 +32,14 @@ protected:
   // Most of the interesting part of NodePathBase is inherited from
   // ArcChain.  This gives us a sharable linked list of arcs.
 
-  // The _top_node pointer is only used when the NodePath is a
-  // singleton.  If there is at least one arc, this is ignored.
+  // We also add an explicit pointer to the top node in the chain,
+  // mainly to allow us to define a NodePath containing a single node,
+  // even if the chain of arcs is empty.
+
+  // If the chain is nonempty, this might still be useful (int that it
+  // keeps a reference count to the top node), but this is problematic
+  // since it will not automatically update if our parent is changed
+  // without our knowledge.
   PT_Node _top_node;
 
   TypeHandle _graph_type;

+ 24 - 0
panda/src/sgmanip/nodePathCollection.cxx

@@ -348,6 +348,30 @@ hide() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePathCollection::stash
+//       Access: Published
+//  Description: Stashes all NodePaths in the collection.
+////////////////////////////////////////////////////////////////////
+void NodePathCollection::
+stash() {
+  for (int i = 0; i < get_num_paths(); i++) {
+    get_path(i).stash();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePathCollection::unstash
+//       Access: Published
+//  Description: Unstashes all NodePaths in the collection.
+////////////////////////////////////////////////////////////////////
+void NodePathCollection::
+unstash() {
+  for (int i = 0; i < get_num_paths(); i++) {
+    get_path(i).unstash();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePathCollection::output
 //       Access: Published

+ 2 - 0
panda/src/sgmanip/nodePathCollection.h

@@ -54,6 +54,8 @@ PUBLISHED:
 
   void show();
   void hide();
+  void stash();
+  void unstash();
 
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;

+ 2 - 15
panda/src/sgraphutil/sceneGraphReducer.cxx

@@ -89,7 +89,7 @@ SceneGraphReducer(TypeHandle graph_type) :
 //               operations impossible.
 ////////////////////////////////////////////////////////////////////
 void SceneGraphReducer::
-apply_transitions(Node *root, int transition_types) {
+apply_transitions(NodeRelation *arc, int transition_types) {
   AccumulatedTransitions trans;
   if ((transition_types & TT_transform) != 0) {
     trans._transform = new TransformTransition;
@@ -101,20 +101,7 @@ apply_transitions(Node *root, int transition_types) {
     trans._texture_matrix = new TexMatrixTransition;
   }
 
-  DownRelations::const_iterator dri;
-  dri = root->_children.find(_graph_type);
-  if (dri != root->_children.end()) {
-    // We must make a temporary copy of the DownRelationPointers,
-    // because we'll be modifying this list as we go.
-    DownRelationPointers drp = (*dri).second;
-
-    DownRelationPointers::const_iterator drpi;
-    for (drpi = drp.begin(); drpi != drp.end(); ++drpi) {
-      NodeRelation *child_arc = (*drpi);
-
-      r_apply_transitions(child_arc, transition_types, trans, false);
-    }
-  }
+  r_apply_transitions(arc, transition_types, trans, false);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/sgraphutil/sceneGraphReducer.h

@@ -36,7 +36,7 @@ public:
     TT_texture_matrix  = 0x004,
   };
 
-  void apply_transitions(Node *root, int transition_types = ~0);
+  void apply_transitions(NodeRelation *arc, int transition_types = ~0);
 
 protected:
   class AccumulatedTransitions {

+ 1 - 1
panda/src/text/textNode.cxx

@@ -510,7 +510,7 @@ do_rebuild() {
 
   if (flatten_text) {
     SceneGraphReducer gr(RenderRelation::get_class_type());
-    gr.apply_transitions(_root);
+    gr.apply_transitions(_root_arc);
     gr.flatten(_root, true);
   }
 }