Browse Source

pgraph find

David Rose 24 years ago
parent
commit
adfa89b233

+ 7 - 5
direct/src/showbase/qpShowBase.py

@@ -314,8 +314,6 @@ class ShowBase:
         per application.
         """
 
-        print 'setup mouse'
-        
         # We create both a MouseAndKeyboard object and a MouseWatcher object
         # for the window.  The MouseAndKeyboard generates mouse events and
         # mouse button/keyboard events; the MouseWatcher passes them through
@@ -341,7 +339,12 @@ class ShowBase:
         self.drive = self.dataUnused.attachNewNode(DriveInterface('drive'))
         self.mouse2cam = self.dataUnused.attachNewNode(Transform2SG('mouse2cam'))
         self.mouse2cam.node().setNode(self.camera.node())
-        self.useDrive()
+
+        # The default is trackball mode, which is more convenient for
+        # ad-hoc development in Python using ShowBase.  Applications
+        # can expclitly call base.useDrive() if they prefer a drive
+        # interface.
+        self.useTrackball()
 
         # A ButtonThrower to generate events from the mouse and
         # keyboard buttons as they are pressed.
@@ -386,8 +389,7 @@ class ShowBase:
         # one.
         for i in range(chanConfig.getNumGroups()):
             camera = self.camera.attachNewNode(chanConfig.getGroupNode(i))
-            #cam = camera.find('**/+Camera')
-            cam = camera.getChild(0)
+            cam = camera.find('**/+Camera')
             lens = cam.node().getLens()
 
             # Enforce our expected aspect ratio, overriding whatever

+ 4 - 1
panda/src/dgraph/qpdataNode.cxx

@@ -244,6 +244,7 @@ reconnect() {
   int num_parents = get_num_parents();
   _data_connections.clear();
   // Look for each input among one of the parents.
+  int num_datanode_parents = 0;
 
   Wires::const_iterator wi;
   for (wi = _input_wires.begin(); wi != _input_wires.end(); ++wi) {
@@ -255,6 +256,7 @@ reconnect() {
       PandaNode *parent_node = get_parent(i);
       if (parent_node->is_of_type(qpDataNode::get_class_type())) {
         qpDataNode *data_node = DCAST(qpDataNode, parent_node);
+        num_datanode_parents++;
         Wires::const_iterator pi;
         pi = data_node->_output_wires.find(name);
         if (pi != data_node->_output_wires.end()) {
@@ -282,7 +284,8 @@ reconnect() {
     }
   }
             
-  if (_data_connections.empty() && get_num_inputs() != 0 && num_parents != 0) {
+  if (_data_connections.empty() && get_num_inputs() != 0 && 
+      num_datanode_parents != 0) {
     dgraph_cat.warning()
       << "No data connected to " << *this << "\n";
   }

+ 11 - 0
panda/src/pgraph/Sources.pp

@@ -27,6 +27,9 @@
     depthTestAttrib.h depthTestAttrib.I \
     depthWriteAttrib.h depthWriteAttrib.I \
     drawCullHandler.h drawCullHandler.I \
+    qpfindApproxLevel.I qpfindApproxLevel.h \
+    qpfindApproxLevelEntry.I qpfindApproxLevelEntry.h \
+    qpfindApproxPath.I qpfindApproxPath.h \
     qpgeomNode.h qpgeomNode.I \
     qplensNode.h qplensNode.I \
     qplodNode.h qplodNode.I \
@@ -67,6 +70,9 @@
     depthTestAttrib.cxx \
     depthWriteAttrib.cxx \
     drawCullHandler.cxx \
+    qpfindApproxLevel.cxx \
+    qpfindApproxLevelEntry.cxx \
+    qpfindApproxPath.cxx \
     qpgeomNode.cxx \
     qplensNode.cxx \
     qplodNode.cxx \
@@ -129,6 +135,11 @@
     transformState.h transformState.I \
     transparencyAttrib.h transparencyAttrib.I
 
+// No need to install these.
+//    qpfindApproxLevel.I qpfindApproxLevel.h \
+//    qpfindApproxLevelEntry.I qpfindApproxLevelEntry.h \
+//    qpfindApproxPath.I qpfindApproxPath.h \
+
   #define IGATESCAN all
 
 #end lib_target

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

@@ -1380,7 +1380,7 @@ fix_path_lengths(const CData *cdata) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 r_list_descendants(ostream &out, int indent_level) const {
-  write(out, indent_level);
+  indent(out, indent_level) << *this << "\n";
 
   CDReader cdata(_cycler);
   Down::const_iterator di;

+ 3 - 0
panda/src/pgraph/pgraph_composite2.cxx

@@ -2,6 +2,9 @@
 #include "depthTestAttrib.cxx"
 #include "depthWriteAttrib.cxx"
 #include "drawCullHandler.cxx"
+#include "qpfindApproxPath.cxx"
+#include "qpfindApproxLevel.cxx"
+#include "qpfindApproxLevelEntry.cxx"
 #include "qpgeomNode.cxx"
 #include "qplensNode.cxx"
 #include "qplodNode.cxx"

+ 29 - 0
panda/src/pgraph/qpfindApproxLevel.I

@@ -0,0 +1,29 @@
+// Filename: qpfindApproxLevel.I
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevel::add_entry
+//       Access: Public
+//  Description: Adds a new entry to the level.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxLevel::
+add_entry(const qpFindApproxLevelEntry &entry) {
+  _v.push_back(entry);
+}
+

+ 34 - 0
panda/src/pgraph/qpfindApproxLevel.cxx

@@ -0,0 +1,34 @@
+// Filename: qpfindApproxLevel.cxx
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpfindApproxLevel.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevel::write
+//       Access: Public
+//  Description: Shows the entire contents of the level, one entry per
+//               line.  For debugging only.
+////////////////////////////////////////////////////////////////////
+void qpFindApproxLevel::
+write(ostream &out) const {
+  Vec::const_iterator vi;
+  for (vi = _v.begin(); vi != _v.end(); ++vi) {
+    (*vi).output(out);
+    out << "\n";
+  }
+}

+ 46 - 0
panda/src/pgraph/qpfindApproxLevel.h

@@ -0,0 +1,46 @@
+// Filename: qpfindApproxLevel.h
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpFINDAPPROXLEVEL_H
+#define qpFINDAPPROXLEVEL_H
+
+#include "pandabase.h"
+
+#include "qpfindApproxLevelEntry.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpFindApproxLevel
+// Description : This class is local to this package only; it doesn't
+//               get exported.  It maintains the list of nodes
+//               find_approx() considers for each level of the scene
+//               graph it visits, in its breadth-first search.
+////////////////////////////////////////////////////////////////////
+class qpFindApproxLevel {
+public:
+  INLINE void add_entry(const qpFindApproxLevelEntry &entry);
+
+  void write(ostream &out) const;
+
+  typedef pvector<qpFindApproxLevelEntry> Vec;
+  Vec _v;
+};
+
+#include "qpfindApproxLevel.I"
+
+#endif

+ 80 - 0
panda/src/pgraph/qpfindApproxLevelEntry.I

@@ -0,0 +1,80 @@
+// Filename: qpfindApproxLevelEntry.I
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpFindApproxLevelEntry::
+qpFindApproxLevelEntry(const qpNodePath &node_path, qpFindApproxPath &approx_path) :
+  _node_path(node_path),
+  _approx_path(approx_path)
+{
+  _i = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpFindApproxLevelEntry::
+qpFindApproxLevelEntry(const qpFindApproxLevelEntry &copy) :
+  _node_path(copy._node_path),
+  _i(copy._i),
+  _approx_path(copy._approx_path)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxLevelEntry::
+operator = (const qpFindApproxLevelEntry &copy) {
+  _node_path = copy._node_path;
+  _i = copy._i;
+  nassertv(&_approx_path == &copy._approx_path);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::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 qpFindApproxLevelEntry::
+next_is_stashed() const {
+  return _approx_path.matches_stashed(_i);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::is_solution
+//       Access: Public
+//  Description: Returns true if this entry represents a solution to
+//               the search; i.e. all the components of the path have
+//               been successfully matched.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpFindApproxLevelEntry::
+is_solution() const {
+  return (_i >= _approx_path.get_num_components());
+}

+ 149 - 0
panda/src/pgraph/qpfindApproxLevelEntry.cxx

@@ -0,0 +1,149 @@
+// Filename: qpfindApproxLevelEntry.cxx
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpfindApproxLevelEntry.h"
+#include "qpnodePathCollection.h"
+#include "pandaNode.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::output
+//       Access: Public
+//  Description: Formats the entry for meaningful output.  For
+//               debugging only.
+////////////////////////////////////////////////////////////////////
+void qpFindApproxLevelEntry::
+output(ostream &out) const {
+  out << "(" << _node_path << "):";
+  if (is_solution()) {
+    out << " solution!";
+  } else {
+    out << "(";
+    _approx_path.output_component(out, _i);
+    out << ")," << _i;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::consider_node
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpFindApproxLevelEntry::
+consider_node(qpNodePathCollection &result, qpFindApproxLevel &next_level,
+              int max_matches) 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.
+    qpFindApproxLevelEntry 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);
+    }
+  }
+
+  PandaNode *this_node = _node_path.node();
+  nassertv(this_node != (PandaNode *)NULL);
+
+  bool stashed_only = next_is_stashed();
+
+  if (!stashed_only) {
+    // Check the normal list of children.
+    int num_children = this_node->get_num_children();
+    for (int i = 0; i < num_children; i++) {
+      PandaNode *child_node = this_node->get_child(i);
+      
+      consider_next_step(result, child_node, next_level, max_matches);
+      if (max_matches > 0 && result.get_num_paths() >= max_matches) {
+        return;
+      }
+    }
+  }
+
+  if (_approx_path.return_stashed() || stashed_only) {
+    // Also check the stashed list.
+    int num_stashed = this_node->get_num_stashed();
+    for (int i = 0; i < num_stashed; i++) {
+      PandaNode *stashed_node = this_node->get_stashed(i);
+      
+      consider_next_step(result, stashed_node, next_level, max_matches);
+      if (max_matches > 0 && result.get_num_paths() >= max_matches) {
+        return;
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxLevelEntry::consider_next_step
+//       Access: Public
+//  Description: Compares the indicated child node (which is assumed
+//               to be a child of _node_path) with the next component
+//               of the path.  If it matches, generates whatever
+//               additional entries are appropriate and stores them in
+//               next_level.
+//
+//               If a complete solution is found, stores it in result.
+////////////////////////////////////////////////////////////////////
+void qpFindApproxLevelEntry::
+consider_next_step(qpNodePathCollection &result, PandaNode *child_node,
+                   qpFindApproxLevel &next_level, int max_matches) const {
+  if (!_approx_path.return_hidden() &&
+      child_node->get_draw_mask().is_zero()) {
+    // If the approx path does not allow us to return hidden nodes,
+    // and this node has indeed been completely hidden, then stop
+    // here.
+    return;
+  }
+
+  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.
+
+    // And now we just add the next entry without incrementing its
+    // path entry.
+
+    qpFindApproxLevelEntry next(*this);
+    next._node_path = qpNodePath(_node_path, child_node);
+    next_level.add_entry(next);
+
+  } else {
+    if (_approx_path.matches_component(_i, child_node)) {
+      // That matched, and it consumes one path entry.
+      qpFindApproxLevelEntry next(*this);
+      ++next._i;
+      next._node_path = qpNodePath(_node_path, child_node);
+      next_level.add_entry(next);
+    }
+  }
+}

+ 75 - 0
panda/src/pgraph/qpfindApproxLevelEntry.h

@@ -0,0 +1,75 @@
+// Filename: qpfindApproxLevelEntry.h
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpFINDAPPROXLEVELENTRY_H
+#define qpFINDAPPROXLEVELENTRY_H
+
+#include "pandabase.h"
+
+#include "qpfindApproxPath.h"
+#include "qpnodePath.h"
+
+class qpFindApproxLevel;
+class qpNodePathCollection;
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpFindApproxLevelEntry
+// Description : This class is local to this package only; it doesn't
+//               get exported.  It represents a single node under
+//               consideration for matching at a single point in the
+//               breadth-first search.
+////////////////////////////////////////////////////////////////////
+class qpFindApproxLevelEntry {
+public:
+  INLINE qpFindApproxLevelEntry(const qpNodePath &node_path,
+                              qpFindApproxPath &approx_path);
+  INLINE qpFindApproxLevelEntry(const qpFindApproxLevelEntry &copy);
+  INLINE void operator = (const qpFindApproxLevelEntry &copy);
+
+  INLINE bool next_is_stashed() const;
+
+  void consider_node(qpNodePathCollection &result, qpFindApproxLevel &next_level,
+                     int max_matches) const;
+  void consider_next_step(qpNodePathCollection &result,
+                          PandaNode *child_node, qpFindApproxLevel &next_level,
+                          int max_matches) const;
+  INLINE bool is_solution() const;
+
+  void output(ostream &out) const;
+
+  // _node_path represents the most recent node that we have
+  // previously accepted as being a partial solution.
+  qpNodePath _node_path;
+
+  // _i represents the next component in the approx_path that must be
+  // matched against all of the children of _node_path, above.  If _i
+  // refers to the end of the approx_path, then _node_path is a
+  // solution.
+  int _i;
+  qpFindApproxPath &_approx_path;
+};
+
+INLINE ostream &
+operator << (ostream &out, const qpFindApproxLevelEntry &entry) {
+  entry.output(out);
+  return out;
+}
+
+#include "qpfindApproxLevelEntry.I"
+
+#endif

+ 216 - 0
panda/src/pgraph/qpfindApproxPath.I

@@ -0,0 +1,216 @@
+// Filename: qpfindApproxPath.I
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpFindApproxPath::
+qpFindApproxPath() {
+  _return_hidden = true;
+  _return_stashed = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_match_name
+//       Access: Public
+//  Description: Adds a component that must match the name of a node
+//               exactly.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+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);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_match_name_glob
+//       Access: Public
+//  Description: Adds a component that must match the name of a node
+//               using standard shell globbing rules, with wildcard
+//               characters accepted.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+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);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_match_exact_type
+//       Access: Public
+//  Description: Adds a component that must match the type of a node
+//               exactly, with no derived types matching.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+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);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_match_inexact_type
+//       Access: Public
+//  Description: Adds a component that must match the type of a node
+//               or be a base class of the node's type.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+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);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_match_one
+//       Access: Public
+//  Description: Adds a component that will match any node (but not a
+//               chain of many nodes).
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+add_match_one(int flags) {
+  Component comp;
+  comp._type = CT_match_one;
+  comp._flags = flags;
+  _path.push_back(comp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_match_many
+//       Access: Public
+//  Description: Adds a component that will match a chain of zero or
+//               more consecutive nodes.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+add_match_many(int flags) {
+  Component comp;
+  comp._type = CT_match_many;
+  comp._flags = flags;
+  _path.push_back(comp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_match_pointer
+//       Access: Public
+//  Description: Adds a component that must match a particular node
+//               exactly, by pointer.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+add_match_pointer(PandaNode *pointer, int flags) {
+  Component comp;
+  comp._type = CT_match_pointer;
+  comp._pointer = pointer;
+  comp._flags = flags;
+  _path.push_back(comp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::get_num_components
+//       Access: Public
+//  Description: Returns the number of components in the path.
+////////////////////////////////////////////////////////////////////
+INLINE int qpFindApproxPath::
+get_num_components() const {
+  return _path.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::is_component_match_many
+//       Access: Public
+//  Description: Returns true if the nth component is of type
+//               match_many, which will require special handling.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpFindApproxPath::
+is_component_match_many(int index) const {
+  nassertr(index >= 0 && index < (int)_path.size(), false);
+  return (_path[index]._type == CT_match_many);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::matches_component
+//       Access: Public
+//  Description: Returns true if the nth component of the path matches
+//               the indicated node, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpFindApproxPath::
+matches_component(int index, PandaNode *node) const {
+  nassertr(index >= 0 && index < (int)_path.size(), false);
+  return (_path[index].matches(node));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::matches_stashed
+//       Access: Public
+//  Description: Returns true if the nth component of the path matches
+//               a stashed node only, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpFindApproxPath::
+matches_stashed(int index) const {
+  if (index >= 0 && index < (int)_path.size()) {
+    return ((_path[index]._flags & CF_stashed) != 0);
+  } else {
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::return_hidden
+//       Access: Public
+//  Description: Returns true if this path allows returning of hidden
+//               nodes, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpFindApproxPath::
+return_hidden() const {
+  return _return_hidden;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::return_stashed
+//       Access: Public
+//  Description: Returns true if this path allows returning of stashed
+//               nodes, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpFindApproxPath::
+return_stashed() const {
+  return _return_stashed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::output_component
+//       Access: Public
+//  Description: Formats the nth component of the path to the
+//               indicated output stream.
+////////////////////////////////////////////////////////////////////
+INLINE void qpFindApproxPath::
+output_component(ostream &out, int index) const {
+  nassertv(index >= 0 && index < (int)_path.size());
+  out << _path[index];
+}

+ 306 - 0
panda/src/pgraph/qpfindApproxPath.cxx

@@ -0,0 +1,306 @@
+// Filename: qpfindApproxPath.cxx
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpfindApproxPath.h"
+#include "config_pgraph.h"
+
+#include "globPattern.h"
+#include "pandaNode.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::Component::matches
+//       Access: Public
+//  Description: Returns true if the indicated node matches this
+//               component, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool qpFindApproxPath::Component::
+matches(PandaNode *node) const {
+  string node_name;
+
+  switch (_type) {
+  case CT_match_name:
+    // Match the node's name exactly.
+    return (_name == node->get_name());
+
+  case CT_match_name_glob:
+    // Match the node's name according to filename globbing rules.
+    {
+      GlobPattern pattern(_name);
+      return (pattern.matches(node->get_name()));
+    }
+
+  case CT_match_exact_type:
+    // Match the node's type exactly.
+    return (node->is_exact_type(_type_handle));
+
+  case CT_match_inexact_type:
+    // Match the node's type inexactly: it's a match if the node
+    // is the type, or is derived from the type.
+    return (node->is_of_type(_type_handle));
+
+  case CT_match_one:
+  case CT_match_many:
+    // Match any node.
+    return true;
+
+  case CT_match_pointer:
+    // Match only this one particular node.
+    return (_pointer == node);
+  }
+
+  pgraph_cat.error()
+    << "Invalid component in qpFindApproxPath\n";
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::Component::output
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpFindApproxPath::Component::
+output(ostream &out) const {
+  out << _type;
+  if (_type == CT_match_name || _type == CT_match_name_glob) {
+    out << " \"" << _name << "\"";
+
+  } else if (_type == CT_match_exact_type || _type == CT_match_inexact_type) {
+    out << " " << _type_handle;
+
+  } else if (_type == CT_match_pointer) {
+    out << " (" << *_pointer << ")";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_string
+//       Access: Public
+//  Description: Adds a sequence of components separated by slashes,
+//               followed optionally by a semicolon and a sequence of
+//               control flags, to the path sequence.  Returns true if
+//               successful, false if the string contained an error.
+////////////////////////////////////////////////////////////////////
+bool qpFindApproxPath::
+add_string(const string &str_path) {
+  size_t start = 0;
+  size_t slash = str_path.find('/');
+  while (slash != string::npos) {
+    if (!add_component(str_path.substr(start, slash - start))) {
+      return false;
+    }
+    start = slash + 1;
+    slash = str_path.find('/', start);
+  }
+
+  size_t semicolon = str_path.rfind(';');
+
+  // We want to find the *last* semicolon at start or later, if there
+  // happens to be more than one.  rfind will find the rightmost
+  // semicolon in the entire string; if this is less than start, there
+  // is no semicolon right of start.
+  if (semicolon < start) {
+    semicolon = string::npos;
+  }
+
+  if (!add_component(str_path.substr(start, semicolon - start))) {
+    return false;
+  }
+
+  if (semicolon != string::npos) {
+    return add_flags(str_path.substr(semicolon + 1));
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_flags
+//       Access: Public
+//  Description: Adds a sequence of control flags.  This will be a
+//               sequence of letters preceded by either '+' or '-',
+//               with no intervening punctuation.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool qpFindApproxPath::
+add_flags(const string &str_flags) {
+  string::const_iterator pi = str_flags.begin();
+  while (pi != str_flags.end()) {
+    bool on;
+    switch (*pi) {
+    case '+':
+      on = true;
+      break;
+    case '-':
+      on = false;
+      break;
+    default:
+      pgraph_cat.error()
+        << "Invalid control flag string: " << str_flags << "\n";
+      return false;
+    }
+
+    ++pi;
+    if (pi == str_flags.end()) {
+      pgraph_cat.error()
+        << "Invalid control flag string: " << str_flags << "\n";
+      return false;
+    }
+
+    switch (*pi) {
+    case 'h':
+      _return_hidden = on;
+      break;
+
+    case 's':
+      _return_stashed = on;
+      break;
+
+    default:
+      pgraph_cat.error()
+        << "Invalid control flag string: " << str_flags << "\n";
+      return false;
+    }
+
+    ++pi;
+  }
+
+  return true;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::add_component
+//       Access: Public
+//  Description: Adds a single component to the path sequence, defined
+//               by a string as might appear between slashes in the
+//               path string.  Returns true if successful, false if
+//               the string component was in some way invalid.
+////////////////////////////////////////////////////////////////////
+bool qpFindApproxPath::
+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(flags);
+
+  } else if (str_component == "**") {
+    if ((flags & CF_stashed) != 0) {
+      pgraph_cat.error()
+        << "@@** is undefined; use @@*/** or **/@@* instead.\n";
+      return false;
+    }
+    add_match_many(flags);
+
+  } else if (!str_component.empty() && str_component[0] == '-') {
+    string type_name = str_component.substr(1);
+
+    // *** for now, as a quick hack, if a type exists with the "qp"
+    // prefix on the named type, we search for that type instead.
+    TypeHandle handle = TypeRegistry::ptr()->find_type("qp" + type_name);
+    if (handle == TypeHandle::none()) {
+      handle = TypeRegistry::ptr()->find_type(type_name);
+    }
+
+    if (handle == TypeHandle::none()) {
+      pgraph_cat.error()
+        << "Invalid type name: " + type_name;
+      return false;
+
+    } else {
+      add_match_exact_type(handle, flags);
+    }
+
+  } else if (!str_component.empty() && str_component[0] == '+') {
+    string type_name = str_component.substr(1);
+
+    // *** for now, as a quick hack, if a type exists with the "qp"
+    // prefix on the named type, we search for that type instead.
+    TypeHandle handle = TypeRegistry::ptr()->find_type("qp" + type_name);
+    if (handle == TypeHandle::none()) {
+      handle = TypeRegistry::ptr()->find_type(type_name);
+    }
+
+    if (handle == TypeHandle::none()) {
+      pgraph_cat.error()
+        << "Invalid type name: " + type_name;
+      return false;
+
+    } else {
+      add_match_inexact_type(handle, flags);
+    }
+
+  } else {
+    add_match_name_glob(str_component, flags);
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpFindApproxPath::output
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void qpFindApproxPath::
+output(ostream &out) const {
+  out << "(";
+  if (!_path.empty()) {
+    Path::const_iterator pi = _path.begin();
+    out << *pi;
+    ++pi;
+    while (pi != _path.end()) {
+      out << " / " << *pi;
+      ++pi;
+    }
+  }
+  out << ")";
+}
+
+ostream &
+operator << (ostream &out, qpFindApproxPath::ComponentType type) {
+  switch (type) {
+  case qpFindApproxPath::CT_match_name:
+    return out << "match_name";
+
+  case qpFindApproxPath::CT_match_name_glob:
+    return out << "match_name_glob";
+
+  case qpFindApproxPath::CT_match_exact_type:
+    return out << "match_exact_type";
+
+  case qpFindApproxPath::CT_match_inexact_type:
+    return out << "match_inexact_type";
+
+  case qpFindApproxPath::CT_match_one:
+    return out << "match_one";
+
+  case qpFindApproxPath::CT_match_many:
+    return out << "match_many";
+
+  case qpFindApproxPath::CT_match_pointer:
+    return out << "match_pointer";
+  };
+
+  return out << "**invalid**";
+};
+

+ 119 - 0
panda/src/pgraph/qpfindApproxPath.h

@@ -0,0 +1,119 @@
+// Filename: qpqpFindApproxPath.h
+// Created by:  drose (13Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpFINDAPPROXPATH_H
+#define qpFINDAPPROXPATH_H
+
+#include "pandabase.h"
+
+#include "pvector.h"
+
+class PandaNode;
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpFindApproxPath
+// Description : This class is local to this package only; it doesn't
+//               get exported.  It chops a string path, as supplied to
+//               find_up() or find_down(), and breaks it up into its
+//               component pieces.
+////////////////////////////////////////////////////////////////////
+class qpFindApproxPath {
+public:
+  INLINE qpFindApproxPath();
+
+  bool add_string(const string &str_path);
+  bool add_flags(const string &str_flags);
+  bool add_component(string str_component);
+
+  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(PandaNode *pointer, int flags);
+
+  INLINE int get_num_components() const;
+  INLINE bool is_component_match_many(int index) const;
+  INLINE bool matches_component(int index, PandaNode *node) const;
+  INLINE bool matches_stashed(int index) const;
+
+  INLINE bool return_hidden() const;
+  INLINE bool return_stashed() const;
+
+  void output(ostream &out) const;
+  INLINE void output_component(ostream &out, int index) const;
+
+#ifndef WIN32_VC
+// Visual C++ won't let us define the ostream operator functions for
+// these guys if they're private--even though we declare them friends.
+private:
+#endif
+  enum ComponentType {
+    CT_match_name,
+    CT_match_name_glob,
+    CT_match_exact_type,
+    CT_match_inexact_type,
+    CT_match_one,
+    CT_match_many,
+    CT_match_pointer
+  };
+  enum ComponentFlags {
+    CF_stashed        = 0x001,
+  };
+
+  class Component {
+  public:
+    bool matches(PandaNode *node) const;
+    void output(ostream &out) const;
+
+    ComponentType _type;
+    string _name;
+    TypeHandle _type_handle;
+    PandaNode *_pointer;
+    int _flags;
+  };
+
+  typedef pvector<Component> Path;
+  Path _path;
+
+  bool _return_hidden;
+  bool _return_stashed;
+
+friend ostream &operator << (ostream &, qpFindApproxPath::ComponentType);
+friend INLINE ostream &operator << (ostream &, const qpFindApproxPath::Component &);
+};
+
+ostream &
+operator << (ostream &out, qpFindApproxPath::ComponentType type);
+
+INLINE ostream &
+operator << (ostream &out, const qpFindApproxPath::Component &component) {
+  component.output(out);
+  return out;
+}
+
+INLINE ostream &
+operator << (ostream &out, const qpFindApproxPath &path) {
+  path.output(out);
+  return out;
+}
+
+#include "qpfindApproxPath.I"
+
+#endif

+ 28 - 4
panda/src/pgraph/qpnodePath.I

@@ -50,10 +50,12 @@ qpNodePath(const string &top_node_name) :
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::Constructor
 //       Access: Published
-//  Description: This constructs an empty qpNodePath with a single node.
-//
-//               If the Node pointer is NULL, this quietly creates an
-//               empty qpNodePath.
+//  Description: This constructs a NodePath for the indicated node.
+//               If the node does not have any parents, this creates a
+//               single NodePath; otherwise, it automatically finds
+//               the path from the node to the root.  If the node has
+//               multiple paths to the root, one path is chosen
+//               arbitrarily and a warning message is printed.
 ////////////////////////////////////////////////////////////////////
 INLINE qpNodePath::
 qpNodePath(PandaNode *top_node) :
@@ -64,6 +66,28 @@ qpNodePath(PandaNode *top_node) :
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::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 qpNodePath::
+qpNodePath(const qpNodePath &parent, PandaNode *child) :
+  _error_type(ET_fail)
+{
+  nassertv(!parent.is_empty());
+  nassertv(child != (PandaNode *)NULL);
+  _head = PandaNode::get_component(parent._head, child);
+  nassertv(_head != (qpNodePathComponent *)NULL);
+
+  if (_head != (qpNodePathComponent *)NULL) {
+    _error_type = ET_ok;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::Copy Constructor
 //       Access: Published

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

@@ -18,10 +18,10 @@
 
 #include "qpnodePath.h"
 #include "qpnodePathCollection.h"
-#include "node.h"
-#include "namedNode.h"
+#include "qpfindApproxPath.h"
+#include "qpfindApproxLevelEntry.h"
+#include "qpfindApproxLevel.h"
 #include "config_pgraph.h"
-#include "plist.h"
 #include "colorAttrib.h"
 #include "cullBinAttrib.h"
 #include "textureAttrib.h"
@@ -32,7 +32,11 @@
 #include "materialPool.h"
 #include "look_at.h"
 #include "compose_matrix.h"
+#include "plist.h"
 
+// stack seems to overflow on Intel C++ at 7000.  If we need more than 
+// 7000, need to increase stack size.
+int qpNodePath::_max_search_depth = 7000; 
 TypeHandle qpNodePath::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -124,6 +128,65 @@ get_children() const {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qoNodePath::find
+//       Access: Published
+//  Description: Searches for a node below the referenced node that
+//               matches the indicated string.  Returns the shortest
+//               match found, if any, or an empty NodePath if no match
+//               can be found.
+////////////////////////////////////////////////////////////////////
+qpNodePath qpNodePath::
+find(const string &path) const {
+  nassertr(!is_empty(), fail());
+
+  qpNodePathCollection col;
+  find_matches(col, path, 1);
+
+  if (col.is_empty()) {
+    return qpNodePath::not_found();
+  }
+
+  return col.get_path(0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::find_all_matches
+//       Access: Published
+//  Description: Returns the complete set of all NodePaths that begin
+//               with this NodePath and can be extended by
+//               path.  The shortest paths will be listed
+//               first.
+////////////////////////////////////////////////////////////////////
+qpNodePathCollection qpNodePath::
+find_all_matches(const string &path) const {
+  qpNodePathCollection col;
+  nassertr(!is_empty(), col);
+  nassertr(verify_complete(), col);
+  find_matches(col, path, -1);
+  return col;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::find_all_paths_to
+//       Access: Published
+//  Description: Returns the set of all NodePaths that extend from
+//               this NodePath down to the indicated node.  The
+//               shortest paths will be listed first.
+////////////////////////////////////////////////////////////////////
+qpNodePathCollection qpNodePath::
+find_all_paths_to(PandaNode *node) const {
+  qpNodePathCollection col;
+  nassertr(!is_empty(), col);
+  nassertr(verify_complete(), col);
+  nassertr(node != (PandaNode *)NULL, col);
+  qpFindApproxPath approx_path;
+  approx_path.add_match_many(0);
+  approx_path.add_match_pointer(node, 0);
+  find_matches(col, approx_path, -1);
+  return col;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::reparent_to
 //       Access: Published
@@ -258,9 +321,11 @@ attach_new_node(PandaNode *node, int sort) const {
 void qpNodePath::
 remove_node() {
   nassertv(_error_type != ET_not_found);
-  if (is_empty()) {
-    // If we have no arcs (maybe we were already removed), quietly do
-    // nothing except to ensure the qpNodePath is clear.
+  if (is_empty() || is_singleton()) {
+    // If we have no parents, remove_node() is just a do-nothing
+    // operation; if we have no nodes, maybe we were already removed.
+    // In either case, quietly do nothing except to ensure the
+    // qpNodePath is clear.
     (*this) = qpNodePath::removed();
     return;
   }
@@ -2303,7 +2368,101 @@ r_output(ostream &out, qpNodePathComponent *comp) const {
   if (node->has_name()) {
     out << node->get_name();
   } else {
-    out << "+" << node->get_type();
+    out << "-" << node->get_type();
   }
   //  out << "[" << comp->get_length() << "]";
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::find_matches
+//       Access: Private
+//  Description: Finds up to max_matches matches against the given
+//               path string from this node and deeper.  The
+//               max_matches count indicates the maximum number of
+//               matches to return, or -1 not to limit the number
+//               returned.
+////////////////////////////////////////////////////////////////////
+void qpNodePath::
+find_matches(qpNodePathCollection &result, const string &path,
+             int max_matches) const {
+  if (is_empty()) {
+    pgraph_cat.warning()
+      << "Attempt to extend an empty qpNodePath by '" << path
+      << "'.\n";
+    return;
+  }
+  qpFindApproxPath approx_path;
+  if (approx_path.add_string(path)) {
+    find_matches(result, approx_path, max_matches);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::find_matches
+//       Access: Private
+//  Description: Finds up to max_matches matches against the given
+//               approx_path from this node and deeper.  The
+//               max_matches count indicates the maximum number of
+//               matches to return, or -1 not to limit the number
+//               returned.
+////////////////////////////////////////////////////////////////////
+void qpNodePath::
+find_matches(qpNodePathCollection &result, qpFindApproxPath &approx_path,
+             int max_matches) const {
+  if (is_empty()) {
+    pgraph_cat.warning()
+      << "Attempt to extend an empty qpNodePath by: " << approx_path << ".\n";
+    return;
+  }
+  qpFindApproxLevelEntry start(*this, approx_path);
+  qpFindApproxLevel level;
+  level.add_entry(start);
+  r_find_matches(result, level, max_matches, _max_search_depth);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpNodePath::r_find_matches
+//       Access: Private
+//  Description: The recursive implementation of find_matches.
+////////////////////////////////////////////////////////////////////
+void qpNodePath::
+r_find_matches(qpNodePathCollection &result,
+               const qpFindApproxLevel &level,
+               int max_matches, int num_levels_remaining) const {
+  // Go on to the next level.  If we exceeded the requested maximum
+  // depth, stop.
+  if (num_levels_remaining <= 0) {
+    return;
+  }
+  num_levels_remaining--;
+
+  qpFindApproxLevel next_level;
+  bool okflag = true;
+
+  // For each node in the current level, build up the set of possible
+  // matches in the next level.
+  qpFindApproxLevel::Vec::const_iterator li;
+  for (li = level._v.begin(); li != level._v.end() && okflag; ++li) {
+    const qpFindApproxLevelEntry &entry = (*li);
+
+    if (entry.is_solution()) {
+      // Does this entry already represent a solution?
+      result.add_path(entry._node_path);
+    } else {
+      entry.consider_node(result, next_level, max_matches);
+    }
+
+    if (max_matches > 0 && result.get_num_paths() >= max_matches) {
+      // Really, we just want to return here.  But returning from
+      // within the conditional within the for loop seems to sometimes
+      // cause a compiler fault in GCC.  We'll use a semaphore
+      // variable instead.
+      okflag = false;
+    }
+  }
+
+  // Now recurse on the next level.
+  if (okflag) {
+    r_find_matches(result, next_level, max_matches, num_levels_remaining);
+  }
+}

+ 90 - 6
panda/src/pgraph/qpnodePath.h

@@ -32,10 +32,85 @@
 #include "typedObject.h"
 
 class qpNodePathCollection;
+class qpFindApproxLevel;
+class qpFindApproxPath;
 class Texture;
 class Material;
 class Fog;
 
+//
+// A NodePath is the fundamental unit of high-level interaction with
+// the scene graph.  It encapsulates the complete path down to a node
+// from some other node, usually the root of the scene graph.  This is
+// used to resolve ambiguities associated with instancing.
+//
+// NodePath also contains a number of handy high-level methods for
+// common scene-graph manipulations, such as reparenting, and common
+// state changes, such as repositioning.
+//
+// There are also a number of NodePath methods for finding nodes deep
+// within the tree by name or by type.  These take a path string,
+// which at its simplest consists of a series of node names separated
+// by slashes, like a directory pathname.
+//
+// Each component of the path string may optionally consist of one of
+// the following special names, instead of a node name:
+//
+//   *          -- matches exactly one node, with any name.
+//   **         -- matches any sequence of zero or more nodes.
+//   +typename  -- matches any node that is or derives from the given type.
+//   -typename  -- matches any node that is the given type exactly.
+//
+// Furthermore, a node name may itself contain standard filename
+// globbing characters, like *, ?, and [a-z], that will be accepted as
+// a partial match.  (In fact, the '*' special name may be seen as
+// just a special case of this.)  The globbing characters may not be
+// used with the typename matches.
+//
+// The special characters "@@", appearing at the beginning of a node
+// name, indicate a stashed node.  Normally, stashed nodes are not
+// returned by a find (but see the special flags, below), but a
+// stashed node may be found if it is explicitly named with its
+// leading @@ characters.  By extension, "@@*" may be used to identify
+// any stashed node.
+//
+// Examples:
+//
+// "room//graph" will look for a node named "graph", which is a child
+// of an unnamed node, which is a child of a node named "room", which
+// is a child of the starting path.
+//
+// "**/red*" will look for any node anywhere in the tree (below the
+// starting path) with a name that begins with "red".
+//
+// "**/+PartBundleNode/**/head" will look for a node named "head",
+// somewhere below a PartBundleNode anywhere in the tree.
+//
+//
+// The search is always potentially ambiguous, even if the special
+// wildcard operators are not used, because there may be multiple
+// nodes in the tree with the same name.  In general, in the case of
+// an ambiguity, the shortest path is preferred; when a method (such
+// as extend_by) must choose only only one of several possible paths,
+// it will choose the shortest available; on the other hand, when a
+// method (such as find_all_matches) is to return all of the matching
+// paths, it will sort them so that the shortest paths appear first in
+// the output.
+//
+//
+// Special flags.  The entire string may optionally be followed by the
+// ";" character, followed by one or more of the following special
+// control flags, with no intervening spaces or punctuation:
+//
+//    -h    Do not return hidden nodes.
+//    +h    Do return hidden nodes.
+//    -s    Do not return stashed nodes unless explicitly referenced with @@.
+//    +s    Return stashed nodes even without any explicit @@ characters.
+//
+// The default flags are +h-s.
+//
+
+
 ////////////////////////////////////////////////////////////////////
 //       Class : NodePath
 // Description : NodePath is the fundamental system for disambiguating
@@ -71,6 +146,7 @@ PUBLISHED:
   INLINE qpNodePath(const string &top_node_name);
   INLINE qpNodePath(PandaNode *top_node);
   INLINE qpNodePath(const qpNodePath &copy);
+  INLINE qpNodePath(const qpNodePath &parent, PandaNode *child_node);
   INLINE void operator = (const qpNodePath &copy);
 
   INLINE static qpNodePath not_found();
@@ -98,12 +174,9 @@ PUBLISHED:
   INLINE bool has_parent() const;
   INLINE qpNodePath get_parent() const;
 
-  /*
-  INLINE qpNodePath find(const string &path) const;
-
-  qpNodePathCollection
-  find_all_matches(const string &path) const;
-  */
+  qpNodePath find(const string &path) const;
+  qpNodePathCollection find_all_matches(const string &path) const;
+  qpNodePathCollection find_all_paths_to(PandaNode *node) const;
 
   // Methods that actually move nodes around in the scene graph.  The
   // optional "sort" parameter can be used to force a particular
@@ -405,8 +478,19 @@ private:
   CPT(TransformState) r_get_partial_transform(qpNodePathComponent *comp, int n) const;
   void r_output(ostream &out, qpNodePathComponent *comp) const;
 
+  void find_matches(qpNodePathCollection &result,
+                    const string &approx_path_str,
+                    int max_matches) const;
+  void find_matches(qpNodePathCollection &result,
+                    qpFindApproxPath &approx_path,
+                    int max_matches) const;
+  void r_find_matches(qpNodePathCollection &result,
+                      const qpFindApproxLevel &level,
+                      int max_matches, int num_levels_remaining) const;
+
   PT(qpNodePathComponent) _head;
   ErrorType _error_type;
+  static int _max_search_depth;
 
 public:
   static TypeHandle get_class_type() {