Panda3D

nodePath.cxx

00001 // Filename: nodePath.cxx
00002 // Created by:  drose (25Feb02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "nodePath.h"
00016 #include "nodePathCollection.h"
00017 #include "findApproxPath.h"
00018 #include "findApproxLevelEntry.h"
00019 #include "internalNameCollection.h"
00020 #include "config_pgraph.h"
00021 #include "colorAttrib.h"
00022 #include "colorScaleAttrib.h"
00023 #include "cullBinAttrib.h"
00024 #include "textureAttrib.h"
00025 #include "texMatrixAttrib.h"
00026 #include "texGenAttrib.h"
00027 #include "materialAttrib.h"
00028 #include "materialCollection.h"
00029 #include "lightAttrib.h"
00030 #include "clipPlaneAttrib.h"
00031 #include "polylightEffect.h"
00032 #include "fogAttrib.h"
00033 #include "renderModeAttrib.h"
00034 #include "cullFaceAttrib.h"
00035 #include "alphaTestAttrib.h"
00036 #include "depthTestAttrib.h"
00037 #include "depthWriteAttrib.h"
00038 #include "depthOffsetAttrib.h"
00039 #include "shaderAttrib.h"
00040 #include "billboardEffect.h"
00041 #include "compassEffect.h"
00042 #include "showBoundsEffect.h"
00043 #include "transparencyAttrib.h"
00044 #include "antialiasAttrib.h"
00045 #include "audioVolumeAttrib.h"
00046 #include "texProjectorEffect.h"
00047 #include "scissorEffect.h"
00048 #include "texturePool.h"
00049 #include "planeNode.h"
00050 #include "lensNode.h"
00051 #include "materialPool.h"
00052 #include "look_at.h"
00053 #include "plist.h"
00054 #include "boundingSphere.h"
00055 #include "geomNode.h"
00056 #include "sceneGraphReducer.h"
00057 #include "textureCollection.h"
00058 #include "textureStageCollection.h"
00059 #include "globPattern.h"
00060 #include "shader.h"
00061 #include "shaderInput.h"
00062 #include "config_gobj.h"
00063 #include "bamFile.h"
00064 #include "preparedGraphicsObjects.h"
00065 #include "dcast.h"
00066 #include "pStatCollector.h"
00067 #include "pStatTimer.h"
00068 #include "modelNode.h"
00069 #include "py_panda.h"
00070 #include "bam.h"
00071 #include "bamWriter.h"
00072 
00073 // stack seems to overflow on Intel C++ at 7000.  If we need more than 
00074 // 7000, need to increase stack size.
00075 int NodePath::_max_search_depth = 7000; 
00076 TypeHandle NodePath::_type_handle;
00077 
00078 
00079 #ifdef HAVE_PYTHON
00080 #include "py_panda.h"  
00081 #ifndef CPPPARSER
00082 extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter;
00083 extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamReader;
00084 #endif  // CPPPARSER
00085 #endif  // HAVE_PYTHON
00086 
00087 // ***Begin temporary transition code for operator bool
00088 enum EmptyNodePathType {
00089   ENP_future,
00090   ENP_transition,
00091   ENP_deprecated,
00092   ENP_notify,
00093 };
00094 
00095 ostream &operator << (ostream &out, EmptyNodePathType enp) {
00096   switch (enp) {
00097   case ENP_future:
00098     return out << "future";
00099   case ENP_transition:
00100     return out << "transition";
00101   case ENP_deprecated:
00102     return out << "deprecated";
00103   case ENP_notify:
00104     return out << "notify";
00105   }
00106   return out << "**invalid EmptyNodePathType value (" << (int)enp << ")**";
00107 }
00108 
00109 istream &operator >> (istream &in, EmptyNodePathType &enp) {
00110   string word;
00111   in >> word;
00112   if (word == "future") {
00113     enp = ENP_future;
00114   } else if (word == "transition") {
00115     enp = ENP_transition;
00116   } else if (word == "deprecated") {
00117     enp = ENP_deprecated;
00118   } else if (word == "notify") {
00119     enp = ENP_notify;
00120   } else {
00121     pgraph_cat.warning()
00122       << "Invalid EmptyNodePathType value (\"" << word << "\")\n";
00123     enp = ENP_transition;
00124   }
00125   return in;
00126 }
00127 
00128 static ConfigVariableEnum<EmptyNodePathType> empty_node_path
00129 ("empty-node-path", ENP_future,
00130  PRC_DESC("This is a temporary transition variable to control the behavior "
00131           "of a NodePath when it is used as a boolean false.  Set this to "
00132           "'deprecated' to preserve the original behavior: every NodePath "
00133           "evaluates true, even an empty NodePath.  Set it to 'future' to "
00134           "support the new behavior: non-empty NodePaths evaluate true, "
00135           "and empty NodePaths evaluate false.  Set it to 'transition' to "
00136           "raise an exception if an empty NodePath is used as a boolean."));
00137 
00138 // ***End temporary transition code for operator bool
00139 
00140 
00141 ////////////////////////////////////////////////////////////////////
00142 //     Function: NodePath::Constructor
00143 //       Access: Published
00144 //  Description: Constructs a NodePath with the indicated parent
00145 //               NodePath and child node; the child node must be a
00146 //               stashed or unstashed child of the parent.
00147 ////////////////////////////////////////////////////////////////////
00148 NodePath::
00149 NodePath(const NodePath &parent, PandaNode *child_node, 
00150          Thread *current_thread) :
00151   _error_type(ET_fail)
00152 {
00153   nassertv(child_node != (PandaNode *)NULL);
00154   int pipeline_stage = current_thread->get_pipeline_stage();
00155 
00156   if (parent.is_empty()) {
00157     // Special case: constructing a NodePath at the root.
00158     _head = PandaNode::get_top_component(child_node, true, 
00159                                          pipeline_stage, current_thread);
00160 
00161   } else {
00162     _head = PandaNode::get_component(parent._head, child_node, pipeline_stage,
00163                                      current_thread);
00164   }
00165   nassertv(_head != (NodePathComponent *)NULL);
00166 
00167   if (_head != (NodePathComponent *)NULL) {
00168     _error_type = ET_ok;
00169   }
00170   _backup_key = 0;
00171 }
00172 
00173 #ifdef HAVE_PYTHON
00174 ////////////////////////////////////////////////////////////////////
00175 //     Function: NodePath::__copy__
00176 //       Access: Published
00177 //  Description: A special Python method that is invoked by
00178 //               copy.copy(node).  Unlike the NodePath copy
00179 //               constructor, this makes a duplicate copy of the
00180 //               underlying PandaNode (but shares children, instead of
00181 //               copying them or omitting them).
00182 ////////////////////////////////////////////////////////////////////
00183 NodePath NodePath::
00184 __copy__() const {
00185   if (is_empty()) {
00186     // Invoke the copy constructor if we have no node.
00187     return *this;
00188   }
00189 
00190   // If we do have a node, duplicate it, and wrap it in a new
00191   // NodePath.
00192   return NodePath(node()->__copy__());
00193 }
00194 #endif  // HAVE_PYTHON
00195 
00196 #ifdef HAVE_PYTHON
00197 ////////////////////////////////////////////////////////////////////
00198 //     Function: NodePath::__deepcopy__
00199 //       Access: Published
00200 //  Description: A special Python method that is invoked by
00201 //               copy.deepcopy(np).  This calls copy_to() unless the
00202 //               NodePath is already present in the provided
00203 //               dictionary.
00204 ////////////////////////////////////////////////////////////////////
00205 PyObject *NodePath::
00206 __deepcopy__(PyObject *self, PyObject *memo) const {
00207   IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath;
00208 
00209   // Borrowed reference.
00210   PyObject *dupe = PyDict_GetItem(memo, self);
00211   if (dupe != NULL) {
00212     // Already in the memo dictionary.
00213     Py_INCREF(dupe);
00214     return dupe;
00215   }
00216 
00217   NodePath *np_dupe;
00218   if (is_empty()) {
00219     np_dupe = new NodePath(*this);
00220   } else {
00221     np_dupe = new NodePath(copy_to(NodePath()));
00222   }
00223 
00224   dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath,
00225                                 true, false);
00226   if (PyDict_SetItem(memo, self, dupe) != 0) {
00227     Py_DECREF(dupe);
00228     return NULL;
00229   }
00230 
00231   return dupe;
00232 }
00233 #endif  // HAVE_PYTHON
00234 
00235 #ifdef HAVE_PYTHON
00236 ////////////////////////////////////////////////////////////////////
00237 //     Function: NodePath::__reduce__
00238 //       Access: Published
00239 //  Description: This special Python method is implement to provide
00240 //               support for the pickle module.
00241 //
00242 //               This hooks into the native pickle and cPickle
00243 //               modules, but it cannot properly handle
00244 //               self-referential BAM objects.
00245 ////////////////////////////////////////////////////////////////////
00246 PyObject *NodePath::
00247 __reduce__(PyObject *self) const {
00248   return __reduce_persist__(self, NULL);
00249 }
00250 #endif  // HAVE_PYTHON
00251 
00252 #ifdef HAVE_PYTHON
00253 ////////////////////////////////////////////////////////////////////
00254 //     Function: NodePath::__reduce_persist__
00255 //       Access: Published
00256 //  Description: This special Python method is implement to provide
00257 //               support for the pickle module.
00258 //
00259 //               This is similar to __reduce__, but it provides
00260 //               additional support for the missing persistent-state
00261 //               object needed to properly support self-referential
00262 //               BAM objects written to the pickle stream.  This hooks
00263 //               into the pickle and cPickle modules implemented in
00264 //               direct/src/stdpy.
00265 ////////////////////////////////////////////////////////////////////
00266 PyObject *NodePath::
00267 __reduce_persist__(PyObject *self, PyObject *pickler) const {
00268   // We should return at least a 2-tuple, (Class, (args)): the
00269   // necessary class object whose constructor we should call
00270   // (e.g. this), and the arguments necessary to reconstruct this
00271   // object.
00272 
00273   BamWriter *writer = NULL;
00274   if (pickler != NULL) {
00275     PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
00276     if (py_writer == NULL) {
00277       // It's OK if there's no bamWriter.
00278       PyErr_Clear();
00279     } else {
00280       DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer);
00281       Py_DECREF(py_writer);
00282     }
00283   }
00284 
00285   // We have a non-empty NodePath.
00286 
00287   string bam_stream;
00288   if (!encode_to_bam_stream(bam_stream, writer)) {
00289     ostringstream stream;
00290     stream << "Could not bamify " << this;
00291     string message = stream.str();
00292     PyErr_SetString(PyExc_TypeError, message.c_str());
00293     return NULL;
00294   }
00295 
00296   // Start by getting this class object.
00297   PyObject *this_class = PyObject_Type(self);
00298   if (this_class == NULL) {
00299     return NULL;
00300   }
00301 
00302   PyObject *func;
00303   if (writer != NULL) {
00304     // The modified pickle support: call the "persistent" version of
00305     // this function, which receives the unpickler itself as an
00306     // additional parameter.
00307     func = TypedWritable::find_global_decode(this_class, "pyDecodeNodePathFromBamStreamPersist");
00308     if (func == NULL) {
00309       PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeNodePathFromBamStreamPersist()");
00310       Py_DECREF(this_class);
00311       return NULL;
00312     }
00313 
00314   } else {
00315     // The traditional pickle support: call the non-persistent version
00316     // of this function.
00317 
00318     func = TypedWritable::find_global_decode(this_class, "pyDecodeNodePathFromBamStream");
00319     if (func == NULL) {
00320       PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeNodePathFromBamStream()");
00321       Py_DECREF(this_class);
00322       return NULL;
00323     }
00324   }
00325 
00326   PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size());
00327   Py_DECREF(func);
00328   Py_DECREF(this_class);
00329   return result;
00330 }
00331 #endif  // HAVE_PYTHON
00332 
00333 ////////////////////////////////////////////////////////////////////
00334 //     Function: NodePath::operator bool
00335 //       Access: Published
00336 //  Description: Returns true if the NodePath is valid (not empty),
00337 //               or false if it contains no nodes.
00338 ////////////////////////////////////////////////////////////////////
00339 NodePath::
00340 operator bool () const {
00341   switch (empty_node_path) {
00342   case ENP_future:
00343     return !is_empty();
00344 
00345   case ENP_deprecated:
00346     return true;
00347 
00348   case ENP_notify:
00349     {
00350       const char *msg = "NodePath being used as a Boolean (talk to Zac)";
00351 #ifdef HAVE_PYTHON
00352       PyErr_Warn(PyExc_FutureWarning, (char *)msg);
00353 #endif
00354       return !is_empty();
00355     }
00356 
00357 
00358   case ENP_transition:
00359     if (!is_empty()) {
00360       return true;
00361     }
00362     
00363     {
00364       const char *message = "Using an empty NodePath as a boolean value.  Because the meaning of this operation is changing, you should avoid doing this to avoid ambiguity, or set the config variable empty-node-path to 'future' or 'deprecated' to specify the desired behavior.";
00365       pgraph_cat.warning()
00366         << message << "\n";
00367 #ifdef HAVE_PYTHON
00368       PyErr_Warn(PyExc_FutureWarning, (char *)message);
00369 #endif
00370     }
00371     return true;
00372   }
00373 
00374   nassertr(false, true);
00375   return true;
00376 }
00377 
00378 ////////////////////////////////////////////////////////////////////
00379 //     Function: NodePath::get_num_nodes
00380 //       Access: Published
00381 //  Description: Returns the number of nodes in the path.
00382 ////////////////////////////////////////////////////////////////////
00383 int NodePath::
00384 get_num_nodes(Thread *current_thread) const {
00385   if (is_empty()) {
00386     return 0;
00387   }
00388   int pipeline_stage = current_thread->get_pipeline_stage();
00389   return _head->get_length(pipeline_stage, current_thread);
00390 }
00391 
00392 ////////////////////////////////////////////////////////////////////
00393 //     Function: NodePath::get_node
00394 //       Access: Published
00395 //  Description: Returns the nth node of the path, where 0 is the
00396 //               referenced (bottom) node and get_num_nodes() - 1 is
00397 //               the top node.  This requires iterating through the
00398 //               path.
00399 //
00400 //               Also see node(), which is a convenience function to
00401 //               return the same thing as get_node(0) (since the
00402 //               bottom node is the most important node in the
00403 //               NodePath, and is the one most frequently referenced).
00404 //
00405 //               Note that this function returns the same thing as
00406 //               get_ancestor(index).node().
00407 ////////////////////////////////////////////////////////////////////
00408 PandaNode *NodePath::
00409 get_node(int index, Thread *current_thread) const {
00410   nassertr(index >= 0 && index < get_num_nodes(), NULL);
00411 
00412   int pipeline_stage = current_thread->get_pipeline_stage();
00413 
00414   NodePathComponent *comp = _head;
00415   while (index > 0) {
00416     // If this assertion fails, the index was out of range; the
00417     // component's length must have been invalid.
00418     nassertr(comp != (NodePathComponent *)NULL, NULL);
00419     comp = comp->get_next(pipeline_stage, current_thread);
00420     index--;
00421   }
00422 
00423   // If this assertion fails, the index was out of range; the
00424   // component's length must have been invalid.
00425   nassertr(comp != (NodePathComponent *)NULL, NULL);
00426   return comp->get_node();
00427 }
00428 
00429 ////////////////////////////////////////////////////////////////////
00430 //     Function: NodePath::get_ancestor
00431 //       Access: Published
00432 //  Description: Returns the nth ancestor of the path, where 0 is the
00433 //               NodePath itself and get_num_nodes() - 1 is get_top().
00434 //               This requires iterating through the path.
00435 //
00436 //               Also see get_node(), which returns the same thing as
00437 //               a PandaNode pointer, not a NodePath.
00438 ////////////////////////////////////////////////////////////////////
00439 NodePath NodePath::
00440 get_ancestor(int index, Thread *current_thread) const {
00441   nassertr(index >= 0 && index < get_num_nodes(), NodePath::fail());
00442 
00443   int pipeline_stage = current_thread->get_pipeline_stage();
00444 
00445   NodePathComponent *comp = _head;
00446   while (index > 0) {
00447     // If this assertion fails, the index was out of range; the
00448     // component's length must have been invalid.
00449     nassertr(comp != (NodePathComponent *)NULL, NodePath::fail());
00450     comp = comp->get_next(pipeline_stage, current_thread);
00451     index--;
00452   }
00453 
00454   // If this assertion fails, the index was out of range; the
00455   // component's length must have been invalid.
00456   nassertr(comp != (NodePathComponent *)NULL, NodePath::fail());
00457 
00458   NodePath result;
00459   result._head = comp;
00460   return result;
00461 }
00462 
00463 ////////////////////////////////////////////////////////////////////
00464 //     Function: NodePath::get_top
00465 //       Access: Published
00466 //  Description: Returns a singleton NodePath that represents the top
00467 //               of the path, or empty NodePath if this path is empty.
00468 ////////////////////////////////////////////////////////////////////
00469 NodePath NodePath::
00470 get_top(Thread *current_thread) const {
00471   if (is_empty()) {
00472     return *this;
00473   }
00474 
00475   int pipeline_stage = current_thread->get_pipeline_stage();
00476 
00477   NodePathComponent *comp = _head;
00478   while (!comp->is_top_node(pipeline_stage, current_thread)) {
00479     comp = comp->get_next(pipeline_stage, current_thread);
00480     nassertr(comp != (NodePathComponent *)NULL, NodePath::fail());
00481   }
00482 
00483   NodePath top;
00484   top._head = comp;
00485   return top;
00486 }
00487 
00488 
00489 ////////////////////////////////////////////////////////////////////
00490 //     Function: NodePath::get_children
00491 //       Access: Published
00492 //  Description: Returns the set of all child nodes of the referenced
00493 //               node.
00494 ////////////////////////////////////////////////////////////////////
00495 NodePathCollection NodePath::
00496 get_children(Thread *current_thread) const {
00497   NodePathCollection result;
00498   nassertr_always(!is_empty(), result);
00499 
00500   PandaNode *bottom_node = node();
00501 
00502   int pipeline_stage = current_thread->get_pipeline_stage();
00503 
00504   PandaNode::Children cr = bottom_node->get_children();
00505   int num_children = cr.get_num_children();
00506   for (int i = 0; i < num_children; i++) {
00507     NodePath child;
00508     child._head = PandaNode::get_component(_head, cr.get_child(i),
00509                                            pipeline_stage, current_thread);
00510     result.add_path(child);
00511   }
00512 
00513   return result;
00514 }
00515 
00516 ////////////////////////////////////////////////////////////////////
00517 //     Function: NodePath::get_stashed_children
00518 //       Access: Published
00519 //  Description: Returns the set of all child nodes of the referenced
00520 //               node that have been stashed.  These children are not
00521 //               normally visible on the node, and do not appear in
00522 //               the list returned by get_children().
00523 ////////////////////////////////////////////////////////////////////
00524 NodePathCollection NodePath::
00525 get_stashed_children(Thread *current_thread) const {
00526   NodePathCollection result;
00527   nassertr_always(!is_empty(), result);
00528 
00529   PandaNode *bottom_node = node();
00530 
00531   int pipeline_stage = current_thread->get_pipeline_stage();
00532 
00533   int num_stashed = bottom_node->get_num_stashed();
00534   for (int i = 0; i < num_stashed; i++) {
00535     NodePath stashed;
00536     stashed._head = PandaNode::get_component(_head, bottom_node->get_stashed(i),
00537                                              pipeline_stage, current_thread);
00538     result.add_path(stashed);
00539   }
00540 
00541   return result;
00542 }
00543 
00544 ////////////////////////////////////////////////////////////////////
00545 //     Function: NodePath::get_sort
00546 //       Access: Published
00547 //  Description: Returns the sort value of the referenced node within
00548 //               its parent; that is, the sort number passed on the
00549 //               last reparenting operation for this node.  This will
00550 //               control the position of the node within its parent's
00551 //               list of children.
00552 ////////////////////////////////////////////////////////////////////
00553 int NodePath::
00554 get_sort(Thread *current_thread) const {
00555   if (!has_parent()) {
00556     return 0;
00557   }
00558 
00559   int pipeline_stage = current_thread->get_pipeline_stage();
00560 
00561   PandaNode *parent = _head->get_next(pipeline_stage, current_thread)->get_node();
00562   PandaNode *child = node();
00563   nassertr(parent != (PandaNode *)NULL && child != (PandaNode *)NULL, 0);
00564   int child_index = parent->find_child(child);
00565   if (child_index != -1) {
00566     return parent->get_child_sort(child_index);
00567   }
00568 
00569   child_index = parent->find_stashed(child);
00570   if (child_index != -1) {
00571     return parent->get_stashed_sort(child_index);
00572   }
00573 
00574   nassertr(false, 0);
00575   return 0;
00576 }
00577 
00578 ////////////////////////////////////////////////////////////////////
00579 //     Function: NodePath::find
00580 //       Access: Published
00581 //  Description: Searches for a node below the referenced node that
00582 //               matches the indicated string.  Returns the shortest
00583 //               match found, if any, or an empty NodePath if no match
00584 //               can be found.
00585 ////////////////////////////////////////////////////////////////////
00586 NodePath NodePath::
00587 find(const string &path) const {
00588   nassertr_always(!is_empty(), fail());
00589 
00590   NodePathCollection col;
00591   find_matches(col, path, 1);
00592 
00593   if (col.is_empty()) {
00594     return NodePath::not_found();
00595   }
00596 
00597   return col.get_path(0);
00598 }
00599 
00600 ////////////////////////////////////////////////////////////////////
00601 //     Function: NodePath::find_path_to
00602 //       Access: Published
00603 //  Description: Searches for the indicated node below this node and
00604 //               returns the shortest NodePath that connects them.
00605 ////////////////////////////////////////////////////////////////////
00606 NodePath NodePath::
00607 find_path_to(PandaNode *node) const {
00608   nassertr_always(!is_empty(), fail());
00609   nassertr(node != (PandaNode *)NULL, fail());
00610 
00611   NodePathCollection col;
00612   FindApproxPath approx_path;
00613   approx_path.add_match_many(0);
00614   approx_path.add_match_pointer(node, 0);
00615   find_matches(col, approx_path, 1);
00616 
00617   if (col.is_empty()) {
00618     return NodePath::not_found();
00619   }
00620 
00621   return col.get_path(0);
00622 }
00623 
00624 ////////////////////////////////////////////////////////////////////
00625 //     Function: NodePath::find_all_matches
00626 //       Access: Published
00627 //  Description: Returns the complete set of all NodePaths that begin
00628 //               with this NodePath and can be extended by
00629 //               path.  The shortest paths will be listed
00630 //               first.
00631 ////////////////////////////////////////////////////////////////////
00632 NodePathCollection NodePath::
00633 find_all_matches(const string &path) const {
00634   NodePathCollection col;
00635   nassertr_always(!is_empty(), col);
00636   nassertr(verify_complete(), col);
00637   find_matches(col, path, -1);
00638   return col;
00639 }
00640 
00641 ////////////////////////////////////////////////////////////////////
00642 //     Function: NodePath::find_all_paths_to
00643 //       Access: Published
00644 //  Description: Returns the set of all NodePaths that extend from
00645 //               this NodePath down to the indicated node.  The
00646 //               shortest paths will be listed first.
00647 ////////////////////////////////////////////////////////////////////
00648 NodePathCollection NodePath::
00649 find_all_paths_to(PandaNode *node) const {
00650   NodePathCollection col;
00651   nassertr_always(!is_empty(), col);
00652   nassertr(verify_complete(), col);
00653   nassertr(node != (PandaNode *)NULL, col);
00654   FindApproxPath approx_path;
00655   approx_path.add_match_many(0);
00656   approx_path.add_match_pointer(node, 0);
00657   find_matches(col, approx_path, -1);
00658   return col;
00659 }
00660 
00661 ////////////////////////////////////////////////////////////////////
00662 //     Function: NodePath::reparent_to
00663 //       Access: Published
00664 //  Description: Removes the referenced node of the NodePath from its
00665 //               current parent and attaches it to the referenced node
00666 //               of the indicated NodePath.  
00667 //
00668 //               If the destination NodePath is empty, this is the
00669 //               same thing as detach_node().
00670 //
00671 //               If the referenced node is already a child of the
00672 //               indicated NodePath (via some other instance), this
00673 //               operation fails and leaves the NodePath detached.
00674 ////////////////////////////////////////////////////////////////////
00675 void NodePath::
00676 reparent_to(const NodePath &other, int sort, Thread *current_thread) {
00677   nassertv(verify_complete());
00678   nassertv(other.verify_complete());
00679   nassertv_always(!is_empty());
00680   nassertv(other._error_type == ET_ok);
00681 
00682   // Reparenting implicitly resets the delta vector.
00683   node()->reset_prev_transform();
00684 
00685   int pipeline_stage = current_thread->get_pipeline_stage();
00686   bool reparented = PandaNode::reparent(other._head, _head, sort, false,
00687                                         pipeline_stage, current_thread);
00688   nassertv(reparented);
00689 }
00690 
00691 ////////////////////////////////////////////////////////////////////
00692 //     Function: NodePath::stash_to
00693 //       Access: Published
00694 //  Description: Similar to reparent_to(), but the node is added to
00695 //               its new parent's stashed list, so that the result is
00696 //               equivalent to calling reparent_to() immediately
00697 //               followed by stash().
00698 ////////////////////////////////////////////////////////////////////
00699 void NodePath::
00700 stash_to(const NodePath &other, int sort, Thread *current_thread) {
00701   nassertv(verify_complete());
00702   nassertv(other.verify_complete());
00703   nassertv_always(!is_empty());
00704   nassertv(other._error_type == ET_ok);
00705 
00706   // Reparenting implicitly resets the delta vector.
00707   node()->reset_prev_transform();
00708 
00709   int pipeline_stage = current_thread->get_pipeline_stage();
00710   bool reparented = PandaNode::reparent(other._head, _head, sort, true,
00711                                         pipeline_stage, current_thread);
00712   nassertv(reparented);
00713 }
00714 
00715 ////////////////////////////////////////////////////////////////////
00716 //     Function: NodePath::wrt_reparent_to
00717 //       Access: Published
00718 //  Description: This functions identically to reparent_to(), except
00719 //               the transform on this node is also adjusted so that
00720 //               the node remains in the same place in world
00721 //               coordinates, even if it is reparented into a
00722 //               different coordinate system.
00723 ////////////////////////////////////////////////////////////////////
00724 void NodePath::
00725 wrt_reparent_to(const NodePath &other, int sort, Thread *current_thread) {
00726   nassertv(verify_complete(current_thread));
00727   nassertv(other.verify_complete(current_thread));
00728   nassertv_always(!is_empty());
00729   nassertv(other._error_type == ET_ok);
00730 
00731   if (get_transform(current_thread) == get_prev_transform(current_thread)) {
00732     set_transform(get_transform(other, current_thread), current_thread);
00733     node()->reset_prev_transform(current_thread);
00734   } else {
00735     set_transform(get_transform(other, current_thread), current_thread);
00736     set_prev_transform(get_prev_transform(other, current_thread), current_thread);
00737   }
00738 
00739   reparent_to(other, sort, current_thread);
00740 }
00741 
00742 ////////////////////////////////////////////////////////////////////
00743 //     Function: NodePath::instance_to
00744 //       Access: Published
00745 //  Description: Adds the referenced node of the NodePath as a child
00746 //               of the referenced node of the indicated other
00747 //               NodePath.  Any other parent-child relations of the
00748 //               node are unchanged; in particular, the node is not
00749 //               removed from its existing parent, if any.
00750 //
00751 //               If the node already had an existing parent, this
00752 //               method will create a new instance of the node within
00753 //               the scene graph.
00754 //
00755 //               This does not change the NodePath itself, but does
00756 //               return a new NodePath that reflects the new instance
00757 //               node.
00758 //
00759 //               If the destination NodePath is empty, this creates a
00760 //               new instance which is not yet parented to any node.
00761 //               A new instance of this sort cannot easily be
00762 //               differentiated from other similar instances, but it
00763 //               is nevertheless a different instance and it will
00764 //               return a different get_id() value.
00765 //
00766 //               If the referenced node is already a child of the
00767 //               indicated NodePath, returns that already-existing
00768 //               instance, unstashing it first if necessary.
00769 ////////////////////////////////////////////////////////////////////
00770 NodePath NodePath::
00771 instance_to(const NodePath &other, int sort, Thread *current_thread) const {
00772   nassertr(verify_complete(), NodePath::fail());
00773   nassertr(other.verify_complete(), NodePath::fail());
00774   nassertr_always(!is_empty(), NodePath::fail());
00775   nassertr(other._error_type == ET_ok, NodePath::fail());
00776 
00777   NodePath new_instance;
00778 
00779   // First, we'll attach to NULL, to guarantee we get a brand new
00780   // instance.
00781   int pipeline_stage = current_thread->get_pipeline_stage();
00782   new_instance._head = PandaNode::attach(NULL, node(), sort, pipeline_stage,
00783                                          current_thread);
00784 
00785   // Now, we'll reparent the new instance to the target node.
00786   bool reparented = PandaNode::reparent(other._head, new_instance._head,
00787                                         sort, false, pipeline_stage,
00788                                         current_thread);
00789   if (!reparented) {
00790     // Hmm, couldn't reparent.  Either making this instance would
00791     // create a cycle, or it was already a child of that node.  If it
00792     // was already a child, return that existing NodePath instead.
00793     NodePath orig(other, node(), current_thread);
00794     if (!orig.is_empty()) {
00795       if (orig.is_stashed()) {
00796         orig.unstash();
00797       }
00798       return orig;
00799     }
00800     
00801     // Nope, it must be a cycle.
00802     nassertr(reparented, new_instance);
00803   }
00804 
00805   // instance_to() doesn't reset the velocity delta, unlike most of
00806   // the other reparenting operations.  The reasoning is that
00807   // instance_to() is not necessarily a reparenting operation, since
00808   // it doesn't change the original instance.
00809 
00810   return new_instance;
00811 }
00812 
00813 ////////////////////////////////////////////////////////////////////
00814 //     Function: NodePath::instance_under_node
00815 //       Access: Published
00816 //  Description: Behaves like instance_to(), but implicitly creates a
00817 //               new node to instance the geometry under, and returns a
00818 //               NodePath to that new node.  This allows the
00819 //               programmer to set a unique state and/or transform on
00820 //               this instance.
00821 ////////////////////////////////////////////////////////////////////
00822 NodePath NodePath::
00823 instance_under_node(const NodePath &other, const string &name, int sort,
00824                     Thread *current_thread) const {
00825   NodePath new_node = other.attach_new_node(name, sort, current_thread);
00826   NodePath instance = instance_to(new_node, 0, current_thread);
00827   if (instance.is_empty()) {
00828     new_node.remove_node(current_thread);
00829     return instance;
00830   }
00831   return new_node;
00832 }
00833 
00834 ////////////////////////////////////////////////////////////////////
00835 //     Function: NodePath::copy_to
00836 //       Access: Published
00837 //  Description: Functions like instance_to(), except a deep
00838 //               copy is made of the referenced node and all of its
00839 //               descendents, which is then parented to the indicated
00840 //               node.  A NodePath to the newly created copy is
00841 //               returned.
00842 ////////////////////////////////////////////////////////////////////
00843 NodePath NodePath::
00844 copy_to(const NodePath &other, int sort, Thread *current_thread) const {
00845   nassertr(verify_complete(current_thread), fail());
00846   nassertr(other.verify_complete(current_thread), fail());
00847   nassertr_always(!is_empty(), fail());
00848   nassertr(other._error_type == ET_ok, fail());
00849 
00850   PandaNode *source_node = node();
00851   PT(PandaNode) copy_node = source_node->copy_subgraph(current_thread);
00852   nassertr(copy_node != (PandaNode *)NULL, fail());
00853 
00854   copy_node->reset_prev_transform(current_thread);
00855 
00856   return other.attach_new_node(copy_node, sort, current_thread);
00857 }
00858 
00859 ////////////////////////////////////////////////////////////////////
00860 //     Function: NodePath::attach_new_node
00861 //       Access: Published
00862 //  Description: Attaches a new node, with or without existing
00863 //               parents, to the scene graph below the referenced node
00864 //               of this NodePath.  This is the preferred way to add
00865 //               nodes to the graph.
00866 //
00867 //               If the node was already a child of the parent, this
00868 //               returns a NodePath to the existing child.
00869 //
00870 //               This does *not* automatically extend the current
00871 //               NodePath to reflect the attachment; however, a
00872 //               NodePath that does reflect this extension is
00873 //               returned.
00874 ////////////////////////////////////////////////////////////////////
00875 NodePath NodePath::
00876 attach_new_node(PandaNode *node, int sort, Thread *current_thread) const {
00877   nassertr(verify_complete(current_thread), NodePath::fail());
00878   nassertr(_error_type == ET_ok, NodePath::fail());
00879   nassertr(node != (PandaNode *)NULL, NodePath::fail());
00880 
00881   NodePath new_path(*this);
00882   int pipeline_stage = current_thread->get_pipeline_stage();
00883   new_path._head = PandaNode::attach(_head, node, sort, pipeline_stage,
00884                                      current_thread);
00885   return new_path;
00886 }
00887 
00888 ////////////////////////////////////////////////////////////////////
00889 //     Function: NodePath::remove_node
00890 //       Access: Published
00891 //  Description: Disconnects the referenced node from the scene graph.
00892 //               This will also delete the node if there are no other
00893 //               pointers to it.
00894 //
00895 //               Normally, this should be called only when you are
00896 //               really done with the node.  If you want to remove a
00897 //               node from the scene graph but keep it around for
00898 //               later, you should probably use detach_node() instead.
00899 //
00900 //               In practice, the only difference between
00901 //               remove_node() and detach_node() is that remove_node()
00902 //               also resets the NodePath to empty, which will cause
00903 //               the node to be deleted immediately if there are no
00904 //               other references.  On the other hand, detach_node()
00905 //               leaves the NodePath referencing the node, which will
00906 //               keep at least one reference to the node for as long
00907 //               as the NodePath exists.
00908 ////////////////////////////////////////////////////////////////////
00909 void NodePath::
00910 remove_node(Thread *current_thread) {
00911   nassertv(_error_type != ET_not_found);
00912 
00913   // If we have no parents, remove_node() is just a do-nothing
00914   // operation; if we have no nodes, maybe we were already removed.
00915   // In either case, quietly do nothing except to ensure the
00916   // NodePath is clear.
00917   if (!is_empty() && !is_singleton(current_thread)) {
00918     node()->reset_prev_transform(current_thread);
00919     int pipeline_stage = current_thread->get_pipeline_stage();
00920     PandaNode::detach(_head, pipeline_stage, current_thread);
00921   }
00922 
00923   if (is_empty() || _head->has_key()) {
00924     // Preserve the key we had on the node before we removed it.
00925     int key = get_key();
00926     (*this) = NodePath::removed();
00927     _backup_key = key;
00928 
00929   } else {
00930     // We didn't have a key; just clear the NodePath.
00931     (*this) = NodePath::removed();
00932   }
00933 }
00934 
00935 ////////////////////////////////////////////////////////////////////
00936 //     Function: NodePath::detach_node
00937 //       Access: Published
00938 //  Description: Disconnects the referenced node from its parent, but
00939 //               does not immediately delete it.  The NodePath retains
00940 //               a pointer to the node, and becomes a singleton
00941 //               NodePath.
00942 //
00943 //               This should be called to detach a node from the scene
00944 //               graph, with the option of reattaching it later to the
00945 //               same parent or to a different parent.
00946 //
00947 //               In practice, the only difference between
00948 //               remove_node() and detach_node() is that remove_node()
00949 //               also resets the NodePath to empty, which will cause
00950 //               the node to be deleted immediately if there are no
00951 //               other references.  On the other hand, detach_node()
00952 //               leaves the NodePath referencing the node, which will
00953 //               keep at least one reference to the node for as long
00954 //               as the NodePath exists.
00955 ////////////////////////////////////////////////////////////////////
00956 void NodePath::
00957 detach_node(Thread *current_thread) {
00958   nassertv(_error_type != ET_not_found);
00959   if (!is_empty() && !is_singleton()) {
00960     node()->reset_prev_transform();
00961     int pipeline_stage = current_thread->get_pipeline_stage();
00962     PandaNode::detach(_head, pipeline_stage, current_thread);
00963   }
00964 }
00965 
00966 ////////////////////////////////////////////////////////////////////
00967 //     Function: NodePath::output
00968 //       Access: Published
00969 //  Description: Writes a sensible description of the NodePath to the
00970 //               indicated output stream.
00971 ////////////////////////////////////////////////////////////////////
00972 void NodePath::
00973 output(ostream &out) const {
00974   switch (_error_type) {
00975   case ET_not_found:
00976     out << "**not found**";
00977     return;
00978   case ET_removed:
00979     out << "**removed**";
00980     return;
00981   case ET_fail:
00982     out << "**error**";
00983     return;
00984   default:
00985     break;
00986   }
00987 
00988   if (_head == (NodePathComponent *)NULL) {
00989     out << "(empty)";
00990   } else {
00991     _head->output(out);
00992   }
00993 }
00994 
00995 ////////////////////////////////////////////////////////////////////
00996 //     Function: NodePath::get_state
00997 //       Access: Published
00998 //  Description: Returns the complete state object set on this node.
00999 ////////////////////////////////////////////////////////////////////
01000 const RenderState *NodePath::
01001 get_state(Thread *current_thread) const {
01002   // This method is declared non-inline to avoid a compiler bug in
01003   // gcc-3.4 and gcc-4.0.
01004   nassertr_always(!is_empty(), RenderState::make_empty());
01005   return node()->get_state(current_thread);
01006 }
01007 
01008 ////////////////////////////////////////////////////////////////////
01009 //     Function: NodePath::get_state
01010 //       Access: Published
01011 //  Description: Returns the state changes that must be made to
01012 //               transition to the render state of this node from the
01013 //               render state of the other node.
01014 ////////////////////////////////////////////////////////////////////
01015 CPT(RenderState) NodePath::
01016 get_state(const NodePath &other, Thread *current_thread) const {
01017   nassertr(_error_type == ET_ok && other._error_type == ET_ok, RenderState::make_empty());
01018 
01019   if (other.is_empty()) {
01020     return get_net_state(current_thread);
01021   }
01022   if (is_empty()) {
01023     return other.get_net_state(current_thread)->invert_compose(RenderState::make_empty());
01024   }
01025     
01026   nassertr(verify_complete(current_thread), RenderState::make_empty());
01027   nassertr(other.verify_complete(current_thread), RenderState::make_empty());
01028 
01029   int a_count, b_count;
01030   if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) {
01031     if (allow_unrelated_wrt) {
01032       pgraph_cat.debug()
01033         << *this << " is not related to " << other << "\n";
01034     } else {
01035       pgraph_cat.error()
01036         << *this << " is not related to " << other << "\n";
01037       nassertr(false, RenderState::make_empty());
01038     }
01039   }
01040 
01041   CPT(RenderState) a_state = r_get_partial_state(_head, a_count, current_thread);
01042   CPT(RenderState) b_state = r_get_partial_state(other._head, b_count, current_thread);
01043   return b_state->invert_compose(a_state);
01044 }
01045 
01046 ////////////////////////////////////////////////////////////////////
01047 //     Function: NodePath::set_state
01048 //       Access: Published
01049 //  Description: Sets the state object on this node, relative to
01050 //               the other node.  This computes a new state object
01051 //               that will have the indicated value when seen from the
01052 //               other node.
01053 ////////////////////////////////////////////////////////////////////
01054 void NodePath::
01055 set_state(const NodePath &other, const RenderState *state,
01056           Thread *current_thread) {
01057   nassertv(_error_type == ET_ok && other._error_type == ET_ok);
01058   nassertv_always(!is_empty());
01059 
01060   // First, we perform a wrt to the parent, to get the conversion.
01061   CPT(RenderState) rel_state;
01062   if (has_parent()) {
01063     rel_state = other.get_state(get_parent(current_thread), current_thread);
01064   } else {
01065     rel_state = other.get_state(NodePath(), current_thread);
01066   }
01067 
01068   CPT(RenderState) new_state = rel_state->compose(state);
01069   set_state(new_state, current_thread);
01070 }
01071 
01072 ////////////////////////////////////////////////////////////////////
01073 //     Function: NodePath::get_transform
01074 //       Access: Published
01075 //  Description: Returns the complete transform object set on this node.
01076 ////////////////////////////////////////////////////////////////////
01077 const TransformState *NodePath::
01078 get_transform(Thread *current_thread) const {
01079   // This method is declared non-inline to avoid a compiler bug in
01080   // gcc-3.4 and gcc-4.0.
01081   nassertr_always(!is_empty(), TransformState::make_identity());
01082   return node()->get_transform(current_thread);
01083 }
01084 
01085 ////////////////////////////////////////////////////////////////////
01086 //     Function: NodePath::get_transform
01087 //       Access: Published
01088 //  Description: Returns the relative transform to this node from the
01089 //               other node; i.e. the transformation of this node
01090 //               as seen from the other node.
01091 ////////////////////////////////////////////////////////////////////
01092 CPT(TransformState) NodePath::
01093 get_transform(const NodePath &other, Thread *current_thread) const {
01094   nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity());
01095   static PStatCollector _get_transform_pcollector("*:NodePath:get_transform");
01096   PStatTimer timer(_get_transform_pcollector);
01097 
01098   if (other.is_empty()) {
01099     return get_net_transform(current_thread);
01100   }
01101   if (is_empty()) {
01102     return other.get_net_transform(current_thread)->invert_compose(TransformState::make_identity());
01103   }
01104 
01105   nassertr(verify_complete(current_thread), TransformState::make_identity());
01106   nassertr(other.verify_complete(current_thread), TransformState::make_identity());
01107 
01108   int a_count, b_count;
01109   if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) {
01110     if (allow_unrelated_wrt) {
01111       if (pgraph_cat.is_debug()) {
01112         pgraph_cat.debug()
01113           << *this << " is not related to " << other << "\n";
01114       }
01115     } else {
01116       pgraph_cat.error()
01117         << *this << " is not related to " << other << "\n";
01118       nassertr(false, TransformState::make_identity());
01119     }
01120   }
01121 
01122   CPT(TransformState) a_transform, b_transform;
01123 
01124   a_transform = r_get_partial_transform(_head, a_count, current_thread);
01125   if (a_transform != (TransformState *)NULL) {
01126     b_transform = r_get_partial_transform(other._head, b_count, current_thread);
01127   }
01128   if (b_transform == (TransformState *)NULL) {
01129     // If either path involved a node with a net_transform
01130     // RenderEffect applied, we have to go all the way up to the root
01131     // to get the right answer.
01132     a_transform = r_get_net_transform(_head, current_thread);
01133     b_transform = r_get_net_transform(other._head, current_thread);
01134   }
01135 
01136   return b_transform->invert_compose(a_transform);
01137 }
01138 
01139 ////////////////////////////////////////////////////////////////////
01140 //     Function: NodePath::set_transform
01141 //       Access: Published
01142 //  Description: Sets the transform object on this node, relative to
01143 //               the other node.  This computes a new transform object
01144 //               that will have the indicated value when seen from the
01145 //               other node.
01146 ////////////////////////////////////////////////////////////////////
01147 void NodePath::
01148 set_transform(const NodePath &other, const TransformState *transform,
01149               Thread *current_thread) {
01150   nassertv(_error_type == ET_ok && other._error_type == ET_ok);
01151   nassertv_always(!is_empty());
01152 
01153   // First, we perform a wrt to the parent, to get the conversion.
01154   CPT(TransformState) rel_trans;
01155   if (has_parent()) {
01156     rel_trans = other.get_transform(get_parent(current_thread), current_thread);
01157   } else {
01158     rel_trans = other.get_transform(NodePath(), current_thread);
01159   }
01160 
01161   CPT(TransformState) new_trans = rel_trans->compose(transform);
01162   set_transform(new_trans, current_thread);
01163 }
01164 
01165 ////////////////////////////////////////////////////////////////////
01166 //     Function: NodePath::get_prev_transform
01167 //       Access: Published
01168 //  Description: Returns the transform that has been set as this
01169 //               node's "previous" position.  See
01170 //               set_prev_transform().
01171 ////////////////////////////////////////////////////////////////////
01172 const TransformState *NodePath::
01173 get_prev_transform(Thread *current_thread) const {
01174   // This method is declared non-inline to avoid a compiler bug in
01175   // gcc-3.4 and gcc-4.0.
01176   nassertr_always(!is_empty(), TransformState::make_identity());
01177   return node()->get_prev_transform(current_thread);
01178 }
01179 
01180 ////////////////////////////////////////////////////////////////////
01181 //     Function: NodePath::get_prev_transform
01182 //       Access: Published
01183 //  Description: Returns the relative "previous" transform to this
01184 //               node from the other node; i.e. the position of this
01185 //               node in the previous frame, as seen by the other node
01186 //               in the previous frame.
01187 ////////////////////////////////////////////////////////////////////
01188 CPT(TransformState) NodePath::
01189 get_prev_transform(const NodePath &other, Thread *current_thread) const {
01190   nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity());
01191 
01192   if (other.is_empty()) {
01193     return get_net_prev_transform(current_thread);
01194   }
01195   if (is_empty()) {
01196     return other.get_net_prev_transform(current_thread)->invert_compose(TransformState::make_identity());
01197   }
01198     
01199   nassertr(verify_complete(current_thread), TransformState::make_identity());
01200   nassertr(other.verify_complete(current_thread), TransformState::make_identity());
01201 
01202   int a_count, b_count;
01203   if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) {
01204     if (allow_unrelated_wrt) {
01205       pgraph_cat.debug()
01206         << *this << " is not related to " << other << "\n";
01207     } else {
01208       pgraph_cat.error()
01209         << *this << " is not related to " << other << "\n";
01210       nassertr(false, TransformState::make_identity());
01211     }
01212   }
01213 
01214   CPT(TransformState) a_prev_transform = r_get_partial_prev_transform(_head, a_count, current_thread);
01215   CPT(TransformState) b_prev_transform = r_get_partial_prev_transform(other._head, b_count, current_thread);
01216   return b_prev_transform->invert_compose(a_prev_transform);
01217 }
01218 
01219 ////////////////////////////////////////////////////////////////////
01220 //     Function: NodePath::set_prev_transform
01221 //       Access: Published
01222 //  Description: Sets the "previous" transform object on this node,
01223 //               relative to the other node.  This computes a new
01224 //               transform object that will have the indicated value
01225 //               when seen from the other node.
01226 ////////////////////////////////////////////////////////////////////
01227 void NodePath::
01228 set_prev_transform(const NodePath &other, const TransformState *transform,
01229                    Thread *current_thread) {
01230   nassertv(_error_type == ET_ok && other._error_type == ET_ok);
01231   nassertv_always(!is_empty());
01232 
01233   // First, we perform a wrt to the parent, to get the conversion.
01234   CPT(TransformState) rel_trans;
01235   if (has_parent(current_thread)) {
01236     rel_trans = other.get_prev_transform(get_parent(current_thread), current_thread);
01237   } else {
01238     rel_trans = other.get_prev_transform(NodePath(), current_thread);
01239   }
01240 
01241   CPT(TransformState) new_trans = rel_trans->compose(transform);
01242   set_prev_transform(new_trans, current_thread);
01243 }
01244 
01245 ////////////////////////////////////////////////////////////////////
01246 //     Function: NodePath::set_pos
01247 //       Access: Published
01248 //  Description: Sets the translation component of the transform,
01249 //               leaving rotation and scale untouched.  This also
01250 //               resets the node's "previous" position, so that the
01251 //               collision system will see the node as having suddenly
01252 //               appeared in the new position, without passing any
01253 //               points in between.
01254 //     See Also: NodePath::set_fluid_pos
01255 ////////////////////////////////////////////////////////////////////
01256 void NodePath::
01257 set_pos(const LVecBase3f &pos) {
01258   nassertv_always(!is_empty());
01259   set_transform(get_transform()->set_pos(pos));
01260   node()->reset_prev_transform();
01261 }
01262 
01263 void NodePath::
01264 set_x(float x) {
01265   nassertv_always(!is_empty());
01266   LPoint3f pos = get_pos();
01267   pos[0] = x;
01268   set_pos(pos);
01269 }
01270 
01271 void NodePath::
01272 set_y(float y) {
01273   nassertv_always(!is_empty());
01274   LPoint3f pos = get_pos();
01275   pos[1] = y;
01276   set_pos(pos);
01277 }
01278 
01279 void NodePath::
01280 set_z(float z) {
01281   nassertv_always(!is_empty());
01282   LPoint3f pos = get_pos();
01283   pos[2] = z;
01284   set_pos(pos);
01285 }
01286 
01287 ////////////////////////////////////////////////////////////////////
01288 //     Function: NodePath::set_fluid_pos
01289 //       Access: Published
01290 //  Description: Sets the translation component, without changing the
01291 //               "previous" position, so that the collision system
01292 //               will see the node as moving fluidly from its previous
01293 //               position to its new position.
01294 //     See Also: NodePath::set_pos
01295 ////////////////////////////////////////////////////////////////////
01296 void NodePath::
01297 set_fluid_pos(const LVecBase3f &pos) {
01298   nassertv_always(!is_empty());
01299   set_transform(get_transform()->set_pos(pos));
01300 }
01301 
01302 void NodePath::
01303 set_fluid_x(float x) {
01304   nassertv_always(!is_empty());
01305   LPoint3f pos = get_pos();
01306   pos[0] = x;
01307   set_fluid_pos(pos);
01308 }
01309 
01310 void NodePath::
01311 set_fluid_y(float y) {
01312   nassertv_always(!is_empty());
01313   LPoint3f pos = get_pos();
01314   pos[1] = y;
01315   set_fluid_pos(pos);
01316 }
01317 
01318 void NodePath::
01319 set_fluid_z(float z) {
01320   nassertv_always(!is_empty());
01321   LPoint3f pos = get_pos();
01322   pos[2] = z;
01323   set_fluid_pos(pos);
01324 }
01325 
01326 ////////////////////////////////////////////////////////////////////
01327 //     Function: NodePath::get_pos
01328 //       Access: Published
01329 //  Description: Retrieves the translation component of the transform.
01330 ////////////////////////////////////////////////////////////////////
01331 LPoint3f NodePath::
01332 get_pos() const {
01333   nassertr_always(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
01334   return get_transform()->get_pos();
01335 }
01336 
01337 ////////////////////////////////////////////////////////////////////
01338 //     Function: NodePath::get_pos_delta
01339 //       Access: Published
01340 //  Description: Returns the delta vector from this node's position in
01341 //               the previous frame (according to
01342 //               set_prev_transform(), typically set via the use of
01343 //               set_fluid_pos()) and its position in the current
01344 //               frame.  This is the vector used to determine
01345 //               collisions.  Generally, if the node was last
01346 //               repositioned via set_pos(), the delta will be zero;
01347 //               if it was adjusted via set_fluid_pos(), the delta
01348 //               will represent the change from the previous frame's
01349 //               position.
01350 ////////////////////////////////////////////////////////////////////
01351 LVector3f NodePath::
01352 get_pos_delta() const {
01353   nassertr_always(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
01354   return get_transform()->get_pos() - get_prev_transform()->get_pos();
01355 }
01356 
01357 ////////////////////////////////////////////////////////////////////
01358 //     Function: NodePath::set_hpr
01359 //       Access: Published
01360 //  Description: Sets the rotation component of the transform,
01361 //               leaving translation and scale untouched.
01362 ////////////////////////////////////////////////////////////////////
01363 void NodePath::
01364 set_hpr(const LVecBase3f &hpr) {
01365   nassertv_always(!is_empty());
01366   CPT(TransformState) transform = get_transform();
01367   nassertv(transform->has_hpr());
01368   set_transform(transform->set_hpr(hpr));
01369 }
01370 
01371 void NodePath::
01372 set_h(float h) {
01373   nassertv_always(!is_empty());
01374   CPT(TransformState) transform = get_transform();
01375   nassertv(transform->has_hpr());
01376   LVecBase3f hpr = transform->get_hpr();
01377   hpr[0] = h;
01378   set_transform(transform->set_hpr(hpr));
01379 }
01380 
01381 void NodePath::
01382 set_p(float p) {
01383   nassertv_always(!is_empty());
01384   CPT(TransformState) transform = get_transform();
01385   nassertv(transform->has_hpr());
01386   LVecBase3f hpr = transform->get_hpr();
01387   hpr[1] = p;
01388   set_transform(transform->set_hpr(hpr));
01389 }
01390 
01391 void NodePath::
01392 set_r(float r) {
01393   nassertv_always(!is_empty());
01394   CPT(TransformState) transform = get_transform();
01395   nassertv(transform->has_hpr());
01396   LVecBase3f hpr = transform->get_hpr();
01397   hpr[2] = r;
01398   set_transform(transform->set_hpr(hpr));
01399 }
01400 
01401 ////////////////////////////////////////////////////////////////////
01402 //     Function: NodePath::get_hpr
01403 //       Access: Published
01404 //  Description: Retrieves the rotation component of the transform.
01405 ////////////////////////////////////////////////////////////////////
01406 LVecBase3f NodePath::
01407 get_hpr() const {
01408   nassertr_always(!is_empty(), LVecBase3f(0.0f, 0.0f, 0.0f));
01409   CPT(TransformState) transform = get_transform();
01410   nassertr(transform->has_hpr(), LVecBase3f(0.0f, 0.0f, 0.0f));
01411   return transform->get_hpr();
01412 }
01413 
01414 ////////////////////////////////////////////////////////////////////
01415 //     Function: NodePath::set_quat
01416 //       Access: Published
01417 //  Description: Sets the rotation component of the transform,
01418 //               leaving translation and scale untouched.
01419 ////////////////////////////////////////////////////////////////////
01420 void NodePath::
01421 set_quat(const LQuaternionf &quat) {
01422   nassertv_always(!is_empty());
01423   CPT(TransformState) transform = get_transform();
01424   set_transform(transform->set_quat(quat));
01425 }
01426 
01427 ////////////////////////////////////////////////////////////////////
01428 //     Function: NodePath::get_quat
01429 //       Access: Published
01430 //  Description: Retrieves the rotation component of the transform.
01431 ////////////////////////////////////////////////////////////////////
01432 LQuaternionf NodePath::
01433 get_quat() const {
01434   nassertr_always(!is_empty(), LQuaternionf::ident_quat());
01435   CPT(TransformState) transform = get_transform();
01436   return transform->get_quat();
01437 }
01438 
01439 ////////////////////////////////////////////////////////////////////
01440 //     Function: NodePath::set_scale
01441 //       Access: Published
01442 //  Description: Sets the scale component of the transform,
01443 //               leaving translation and rotation untouched.
01444 ////////////////////////////////////////////////////////////////////
01445 void NodePath::
01446 set_scale(const LVecBase3f &scale) {
01447   nassertv_always(!is_empty());
01448   CPT(TransformState) transform = get_transform();
01449   set_transform(transform->set_scale(scale));
01450 }
01451 
01452 void NodePath::
01453 set_sx(float sx) {
01454   nassertv_always(!is_empty());
01455   CPT(TransformState) transform = get_transform();
01456   LVecBase3f scale = transform->get_scale();
01457   scale[0] = sx;
01458   set_transform(transform->set_scale(scale));
01459 }
01460 
01461 void NodePath::
01462 set_sy(float sy) {
01463   nassertv_always(!is_empty());
01464   CPT(TransformState) transform = get_transform();
01465   LVecBase3f scale = transform->get_scale();
01466   scale[1] = sy;
01467   set_transform(transform->set_scale(scale));
01468 }
01469 
01470 void NodePath::
01471 set_sz(float sz) {
01472   nassertv_always(!is_empty());
01473   CPT(TransformState) transform = get_transform();
01474   LVecBase3f scale = transform->get_scale();
01475   scale[2] = sz;
01476   set_transform(transform->set_scale(scale));
01477 }
01478 
01479 ////////////////////////////////////////////////////////////////////
01480 //     Function: NodePath::get_scale
01481 //       Access: Published
01482 //  Description: Retrieves the scale component of the transform.
01483 ////////////////////////////////////////////////////////////////////
01484 LVecBase3f NodePath::
01485 get_scale() const {
01486   nassertr_always(!is_empty(), LVecBase3f(0.0f, 0.0f, 0.0f));
01487   CPT(TransformState) transform = get_transform();
01488   return transform->get_scale();
01489 }
01490 
01491 ////////////////////////////////////////////////////////////////////
01492 //     Function: NodePath::set_shear
01493 //       Access: Published
01494 //  Description: Sets the shear component of the transform,
01495 //               leaving translation and rotation untouched.
01496 ////////////////////////////////////////////////////////////////////
01497 void NodePath::
01498 set_shear(const LVecBase3f &shear) {
01499   nassertv_always(!is_empty());
01500   CPT(TransformState) transform = get_transform();
01501   set_transform(transform->set_shear(shear));
01502 }
01503 
01504 void NodePath::
01505 set_shxy(float shxy) {
01506   nassertv_always(!is_empty());
01507   CPT(TransformState) transform = get_transform();
01508   LVecBase3f shear = transform->get_shear();
01509   shear[0] = shxy;
01510   set_transform(transform->set_shear(shear));
01511 }
01512 
01513 void NodePath::
01514 set_shxz(float shxz) {
01515   nassertv_always(!is_empty());
01516   CPT(TransformState) transform = get_transform();
01517   LVecBase3f shear = transform->get_shear();
01518   shear[1] = shxz;
01519   set_transform(transform->set_shear(shear));
01520 }
01521 
01522 void NodePath::
01523 set_shyz(float shyz) {
01524   nassertv_always(!is_empty());
01525   CPT(TransformState) transform = get_transform();
01526   LVecBase3f shear = transform->get_shear();
01527   shear[2] = shyz;
01528   set_transform(transform->set_shear(shear));
01529 }
01530 
01531 ////////////////////////////////////////////////////////////////////
01532 //     Function: NodePath::get_shear
01533 //       Access: Published
01534 //  Description: Retrieves the shear component of the transform.
01535 ////////////////////////////////////////////////////////////////////
01536 LVecBase3f NodePath::
01537 get_shear() const {
01538   nassertr_always(!is_empty(), LVecBase3f(0.0f, 0.0f, 0.0f));
01539   CPT(TransformState) transform = get_transform();
01540   return transform->get_shear();
01541 }
01542 
01543 ////////////////////////////////////////////////////////////////////
01544 //     Function: NodePath::set_pos_hpr
01545 //       Access: Published
01546 //  Description: Sets the translation and rotation component of the
01547 //               transform, leaving scale untouched.
01548 ////////////////////////////////////////////////////////////////////
01549 void NodePath::
01550 set_pos_hpr(const LVecBase3f &pos, const LVecBase3f &hpr) {
01551   nassertv_always(!is_empty());
01552   CPT(TransformState) transform = get_transform();
01553   transform = TransformState::make_pos_hpr_scale_shear
01554     (pos, hpr, transform->get_scale(), transform->get_shear());
01555   set_transform(transform);
01556   node()->reset_prev_transform();
01557 }
01558 
01559 ////////////////////////////////////////////////////////////////////
01560 //     Function: NodePath::set_pos_quat
01561 //       Access: Published
01562 //  Description: Sets the translation and rotation component of the
01563 //               transform, leaving scale untouched.
01564 ////////////////////////////////////////////////////////////////////
01565 void NodePath::
01566 set_pos_quat(const LVecBase3f &pos, const LQuaternionf &quat) {
01567   nassertv_always(!is_empty());
01568   CPT(TransformState) transform = get_transform();
01569   transform = TransformState::make_pos_quat_scale_shear
01570     (pos, quat, transform->get_scale(), transform->get_shear());
01571   set_transform(transform);
01572   node()->reset_prev_transform();
01573 }
01574 
01575 ////////////////////////////////////////////////////////////////////
01576 //     Function: NodePath::set_hpr_scale
01577 //       Access: Published
01578 //  Description: Sets the rotation and scale components of the
01579 //               transform, leaving translation untouched.
01580 ////////////////////////////////////////////////////////////////////
01581 void NodePath::
01582 set_hpr_scale(const LVecBase3f &hpr, const LVecBase3f &scale) {
01583   nassertv_always(!is_empty());
01584   CPT(TransformState) transform = get_transform();
01585   transform = TransformState::make_pos_hpr_scale_shear
01586     (transform->get_pos(), hpr, scale, transform->get_shear());
01587   set_transform(transform);
01588 }
01589 
01590 ////////////////////////////////////////////////////////////////////
01591 //     Function: NodePath::set_quat_scale
01592 //       Access: Published
01593 //  Description: Sets the rotation and scale components of the
01594 //               transform, leaving translation untouched.
01595 ////////////////////////////////////////////////////////////////////
01596 void NodePath::
01597 set_quat_scale(const LQuaternionf &quat, const LVecBase3f &scale) {
01598   nassertv_always(!is_empty());
01599   CPT(TransformState) transform = get_transform();
01600   transform = TransformState::make_pos_quat_scale_shear
01601     (transform->get_pos(), quat, scale, transform->get_shear());
01602   set_transform(transform);
01603 }
01604 
01605 ////////////////////////////////////////////////////////////////////
01606 //     Function: NodePath::set_pos_hpr_scale
01607 //       Access: Published
01608 //  Description: Replaces the translation, rotation, and scale
01609 //               components, implicitly setting shear to 0.
01610 ////////////////////////////////////////////////////////////////////
01611 void NodePath::
01612 set_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr,
01613                   const LVecBase3f &scale) {
01614   nassertv_always(!is_empty());
01615   set_transform(TransformState::make_pos_hpr_scale
01616                 (pos, hpr, scale));
01617   node()->reset_prev_transform();
01618 }
01619 
01620 ////////////////////////////////////////////////////////////////////
01621 //     Function: NodePath::set_pos_quat_scale
01622 //       Access: Published
01623 //  Description: Replaces the translation, rotation, and scale
01624 //               components, implicitly setting shear to 0.
01625 ////////////////////////////////////////////////////////////////////
01626 void NodePath::
01627 set_pos_quat_scale(const LVecBase3f &pos, const LQuaternionf &quat,
01628                    const LVecBase3f &scale) {
01629   nassertv_always(!is_empty());
01630   set_transform(TransformState::make_pos_quat_scale
01631                 (pos, quat, scale));
01632   node()->reset_prev_transform();
01633 }
01634 
01635 ////////////////////////////////////////////////////////////////////
01636 //     Function: NodePath::set_pos_hpr_scale_shear
01637 //       Access: Published
01638 //  Description: Completely replaces the transform with new
01639 //               translation, rotation, scale, and shear components.
01640 ////////////////////////////////////////////////////////////////////
01641 void NodePath::
01642 set_pos_hpr_scale_shear(const LVecBase3f &pos, const LVecBase3f &hpr,
01643                         const LVecBase3f &scale, const LVecBase3f &shear) {
01644   nassertv_always(!is_empty());
01645   set_transform(TransformState::make_pos_hpr_scale_shear
01646                 (pos, hpr, scale, shear));
01647   node()->reset_prev_transform();
01648 }
01649 
01650 ////////////////////////////////////////////////////////////////////
01651 //     Function: NodePath::set_pos_quat_scale_shear
01652 //       Access: Published
01653 //  Description: Completely replaces the transform with new
01654 //               translation, rotation, scale, and shear components.
01655 ////////////////////////////////////////////////////////////////////
01656 void NodePath::
01657 set_pos_quat_scale_shear(const LVecBase3f &pos, const LQuaternionf &quat,
01658                          const LVecBase3f &scale, const LVecBase3f &shear) {
01659   nassertv_always(!is_empty());
01660   set_transform(TransformState::make_pos_quat_scale_shear
01661                 (pos, quat, scale, shear));
01662   node()->reset_prev_transform();
01663 }
01664 
01665 ////////////////////////////////////////////////////////////////////
01666 //     Function: NodePath::set_mat
01667 //       Access: Published
01668 //  Description: Directly sets an arbitrary 4x4 transform matrix.
01669 ////////////////////////////////////////////////////////////////////
01670 void NodePath::
01671 set_mat(const LMatrix4f &mat) {
01672   nassertv_always(!is_empty());
01673   set_transform(TransformState::make_mat(mat));
01674   node()->reset_prev_transform();
01675 }
01676 
01677 ////////////////////////////////////////////////////////////////////
01678 //     Function: NodePath::look_at
01679 //       Access: Published
01680 //  Description: Sets the hpr on this NodePath so that it
01681 //               rotates to face the indicated point in space.
01682 ////////////////////////////////////////////////////////////////////
01683 void NodePath::
01684 look_at(const LPoint3f &point, const LVector3f &up) {
01685   nassertv_always(!is_empty());
01686 
01687   LPoint3f pos = get_pos();
01688 
01689   LQuaternionf quat;
01690   ::look_at(quat, point - pos, up);
01691   set_quat(quat);
01692 }
01693 
01694 ////////////////////////////////////////////////////////////////////
01695 //     Function: NodePath::heads_up
01696 //       Access: Published
01697 //  Description: Behaves like look_at(), but with a strong preference
01698 //               to keeping the up vector oriented in the indicated
01699 //               "up" direction.
01700 ////////////////////////////////////////////////////////////////////
01701 void NodePath::
01702 heads_up(const LPoint3f &point, const LVector3f &up) {
01703   nassertv_always(!is_empty());
01704 
01705   LPoint3f pos = get_pos();
01706 
01707   LQuaternionf quat;
01708   ::heads_up(quat, point - pos, up);
01709   set_quat(quat);
01710 }
01711 
01712 ////////////////////////////////////////////////////////////////////
01713 //     Function: NodePath::set_pos
01714 //       Access: Published
01715 //  Description: Sets the translation component of the transform,
01716 //               relative to the other node.
01717 ////////////////////////////////////////////////////////////////////
01718 void NodePath::
01719 set_pos(const NodePath &other, const LVecBase3f &pos) {
01720   nassertv_always(!is_empty());
01721   CPT(TransformState) rel_transform = get_transform(other);
01722 
01723   CPT(TransformState) orig_transform = get_transform();
01724   if (orig_transform->has_components()) {
01725     // If we had a componentwise transform before we started, we
01726     // should be careful to preserve the other three components.  We
01727     // wouldn't need to do this, except for the possibility of
01728     // numerical error or decompose ambiguity.
01729     const LVecBase3f &orig_hpr = orig_transform->get_hpr();
01730     const LVecBase3f &orig_scale = orig_transform->get_scale();
01731     const LVecBase3f &orig_shear = orig_transform->get_shear();
01732 
01733     set_transform(other, rel_transform->set_pos(pos));
01734     set_pos_hpr_scale_shear(get_transform()->get_pos(), orig_hpr, orig_scale, orig_shear);
01735 
01736   } else {
01737     // If we didn't have a componentwise transform already, never
01738     // mind.
01739     set_transform(other, rel_transform->set_pos(pos));
01740   }
01741   node()->reset_prev_transform();
01742 }
01743 
01744 void NodePath::
01745 set_x(const NodePath &other, float x) {
01746   nassertv_always(!is_empty());
01747   LPoint3f pos = get_pos(other);
01748   pos[0] = x;
01749   set_pos(other, pos);
01750 }
01751 
01752 void NodePath::
01753 set_y(const NodePath &other, float y) {
01754   nassertv_always(!is_empty());
01755   LPoint3f pos = get_pos(other);
01756   pos[1] = y;
01757   set_pos(other, pos);
01758 }
01759 
01760 void NodePath::
01761 set_z(const NodePath &other, float z) {
01762   nassertv_always(!is_empty());
01763   LPoint3f pos = get_pos(other);
01764   pos[2] = z;
01765   set_pos(other, pos);
01766 }
01767 
01768 ////////////////////////////////////////////////////////////////////
01769 //     Function: NodePath::set_fluid_pos
01770 //       Access: Published
01771 //  Description: Sets the translation component of the transform,
01772 //               relative to the other node.
01773 ////////////////////////////////////////////////////////////////////
01774 void NodePath::
01775 set_fluid_pos(const NodePath &other, const LVecBase3f &pos) {
01776   nassertv_always(!is_empty());
01777   CPT(TransformState) rel_transform = get_transform(other);
01778 
01779   CPT(TransformState) orig_transform = get_transform();
01780   if (orig_transform->has_components()) {
01781     // If we had a componentwise transform before we started, we
01782     // should be careful to preserve the other three components.  We
01783     // wouldn't need to do this, except for the possibility of
01784     // numerical error or decompose ambiguity.
01785     const LVecBase3f &orig_hpr = orig_transform->get_hpr();
01786     const LVecBase3f &orig_scale = orig_transform->get_scale();
01787     const LVecBase3f &orig_shear = orig_transform->get_shear();
01788 
01789     // Use the relative set_transform() to compute the relative pos, and
01790     // then reset all of the other components back to the way they were.
01791     set_transform(other, rel_transform->set_pos(pos));
01792     set_transform(TransformState::make_pos_hpr_scale_shear
01793                   (get_transform()->get_pos(), orig_hpr, orig_scale, orig_shear));
01794 
01795   } else {
01796     // If we didn't have a componentwise transform already, never
01797     // mind.
01798     set_transform(other, rel_transform->set_pos(pos));
01799   }
01800 }
01801 
01802 void NodePath::
01803 set_fluid_x(const NodePath &other, float x) {
01804   nassertv_always(!is_empty());
01805   LPoint3f pos = get_pos(other);
01806   pos[0] = x;
01807   set_fluid_pos(other, pos);
01808 }
01809 
01810 void NodePath::
01811 set_fluid_y(const NodePath &other, float y) {
01812   nassertv_always(!is_empty());
01813   LPoint3f pos = get_pos(other);
01814   pos[1] = y;
01815   set_fluid_pos(other, pos);
01816 }
01817 
01818 void NodePath::
01819 set_fluid_z(const NodePath &other, float z) {
01820   nassertv_always(!is_empty());
01821   LPoint3f pos = get_pos(other);
01822   pos[2] = z;
01823   set_fluid_pos(other, pos);
01824 }
01825 
01826 ////////////////////////////////////////////////////////////////////
01827 //     Function: NodePath::get_pos
01828 //       Access: Published
01829 //  Description: Returns the relative position of the referenced node
01830 //               as seen from the other node.
01831 ////////////////////////////////////////////////////////////////////
01832 LPoint3f NodePath::
01833 get_pos(const NodePath &other) const {
01834   nassertr_always(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
01835   return get_transform(other)->get_pos();
01836 }
01837 
01838 ////////////////////////////////////////////////////////////////////
01839 //     Function: NodePath::get_pos_delta
01840 //       Access: Published
01841 //  Description: Returns the delta vector from this node's position in
01842 //               the previous frame (according to
01843 //               set_prev_transform(), typically set via the use of
01844 //               set_fluid_pos()) and its position in the current
01845 //               frame, as seen in the indicated node's coordinate
01846 //               space.  This is the vector used to determine
01847 //               collisions.  Generally, if the node was last
01848 //               repositioned via set_pos(), the delta will be zero;
01849 //               if it was adjusted via set_fluid_pos(), the delta
01850 //               will represent the change from the previous frame's
01851 //               position.
01852 ////////////////////////////////////////////////////////////////////
01853 LVector3f NodePath::
01854 get_pos_delta(const NodePath &other) const {
01855   nassertr_always(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
01856   return get_transform(other)->get_pos() - get_prev_transform(other)->get_pos();
01857 }
01858 
01859 ////////////////////////////////////////////////////////////////////
01860 //     Function: NodePath::set_hpr
01861 //       Access: Published
01862 //  Description: Sets the rotation component of the transform,
01863 //               relative to the other node.
01864 ////////////////////////////////////////////////////////////////////
01865 void NodePath::
01866 set_hpr(const NodePath &other, const LVecBase3f &hpr) {
01867   nassertv_always(!is_empty());
01868   CPT(TransformState) rel_transform = get_transform(other);
01869   nassertv(rel_transform->has_hpr());
01870 
01871   CPT(TransformState) orig_transform = get_transform();
01872   if (orig_transform->has_components()) {
01873     // If we had a componentwise transform before we started, we
01874     // should be careful to preserve the other three components.  We
01875     // wouldn't need to do this, except for the possibility of
01876     // numerical error or decompose ambiguity.
01877     const LVecBase3f &orig_pos = orig_transform->get_pos();
01878     const LVecBase3f &orig_scale = orig_transform->get_scale();
01879     const LVecBase3f &orig_shear = orig_transform->get_shear();
01880 
01881     set_transform(other, rel_transform->set_hpr(hpr));
01882     const TransformState *new_transform = get_transform();
01883     if (new_transform->has_components()) {
01884       set_transform(TransformState::make_pos_hpr_scale_shear
01885                     (orig_pos, new_transform->get_hpr(), orig_scale, orig_shear));
01886     }
01887 
01888   } else {
01889     // If we didn't have a componentwise transform already, never
01890     // mind.
01891     set_transform(other, rel_transform->set_hpr(hpr));
01892   }
01893 }
01894 
01895 void NodePath::
01896 set_h(const NodePath &other, float h) {
01897   nassertv_always(!is_empty());
01898   LVecBase3f hpr = get_hpr(other);
01899   hpr[0] = h;
01900   set_hpr(other, hpr);
01901 }
01902 
01903 void NodePath::
01904 set_p(const NodePath &other, float p) {
01905   nassertv_always(!is_empty());
01906   LVecBase3f hpr = get_hpr(other);
01907   hpr[1] = p;
01908   set_hpr(other, hpr);
01909 }
01910 
01911 void NodePath::
01912 set_r(const NodePath &other, float r) {
01913   nassertv_always(!is_empty());
01914   LVecBase3f hpr = get_hpr(other);
01915   hpr[2] = r;
01916   set_hpr(other, hpr);
01917 }
01918 
01919 ////////////////////////////////////////////////////////////////////
01920 //     Function: NodePath::get_hpr
01921 //       Access: Published
01922 //  Description: Returns the relative orientation of the bottom node
01923 //               as seen from the other node.
01924 ////////////////////////////////////////////////////////////////////
01925 LVecBase3f NodePath::
01926 get_hpr(const NodePath &other) const {
01927   nassertr_always(!is_empty(), LVecBase3f(0.0f, 0.0f, 0.0f));
01928   CPT(TransformState) transform = get_transform(other);
01929   nassertr(transform->has_hpr(), LVecBase3f(0.0f, 0.0f, 0.0f));
01930   return transform->get_hpr();
01931 }
01932 
01933 ////////////////////////////////////////////////////////////////////
01934 //     Function: NodePath::set_quat
01935 //       Access: Published
01936 //  Description: Sets the rotation component of the transform,
01937 //               relative to the other node.
01938 ////////////////////////////////////////////////////////////////////
01939 void NodePath::
01940 set_quat(const NodePath &other, const LQuaternionf &quat) {
01941   nassertv_always(!is_empty());
01942   CPT(TransformState) rel_transform = get_transform(other);
01943 
01944   CPT(TransformState) orig_transform = get_transform();
01945   if (orig_transform->has_components()) {
01946     // If we had a componentwise transform before we started, we
01947     // should be careful to preserve the other three components.  We
01948     // wouldn't need to do this, except for the possibility of
01949     // numerical error or decompose ambiguity.
01950     const LVecBase3f &orig_pos = orig_transform->get_pos();
01951     const LVecBase3f &orig_scale = orig_transform->get_scale();
01952     const LVecBase3f &orig_shear = orig_transform->get_shear();
01953 
01954     set_transform(other, rel_transform->set_quat(quat));
01955     const TransformState *new_transform = get_transform();
01956     if (new_transform->has_components()) {
01957       set_transform(TransformState::make_pos_quat_scale_shear
01958                     (orig_pos, new_transform->get_quat(), orig_scale, orig_shear));
01959     }
01960 
01961   } else {
01962     // If we didn't have a componentwise transform already, never
01963     // mind.
01964     set_transform(other, rel_transform->set_quat(quat));
01965   }
01966 }
01967 
01968 ////////////////////////////////////////////////////////////////////
01969 //     Function: NodePath::get_quat
01970 //       Access: Published
01971 //  Description: Returns the relative orientation of the bottom node
01972 //               as seen from the other node.
01973 ////////////////////////////////////////////////////////////////////
01974 LQuaternionf NodePath::
01975 get_quat(const NodePath &other) const {
01976   nassertr_always(!is_empty(), LQuaternionf::ident_quat());
01977   CPT(TransformState) transform = get_transform(other);
01978   return transform->get_quat();
01979 }
01980 
01981 ////////////////////////////////////////////////////////////////////
01982 //     Function: NodePath::set_scale
01983 //       Access: Published
01984 //  Description: Sets the scale component of the transform,
01985 //               relative to the other node.
01986 ////////////////////////////////////////////////////////////////////
01987 void NodePath::
01988 set_scale(const NodePath &other, const LVecBase3f &scale) {
01989   nassertv_always(!is_empty());
01990   CPT(TransformState) rel_transform = get_transform(other);
01991 
01992   CPT(TransformState) orig_transform = get_transform();
01993   if (orig_transform->has_components()) {
01994     // If we had a componentwise transform before we started, we
01995     // should be careful to preserve the other three components.  We
01996     // wouldn't need to do this, except for the possibility of
01997     // numerical error or decompose ambiguity.
01998     const LVecBase3f &orig_pos = orig_transform->get_pos();
01999     const LVecBase3f &orig_hpr = orig_transform->get_hpr();
02000     const LVecBase3f &orig_shear = orig_transform->get_shear();
02001 
02002     set_transform(other, rel_transform->set_scale(scale));
02003     const TransformState *new_transform = get_transform();
02004     if (new_transform->has_components()) {
02005       set_transform(TransformState::make_pos_hpr_scale_shear
02006                     (orig_pos, orig_hpr, new_transform->get_scale(), orig_shear));
02007     }
02008 
02009   } else {
02010     // If we didn't have a componentwise transform already, never
02011     // mind.
02012     set_transform(other, rel_transform->set_scale(scale));
02013   }
02014 }
02015 
02016 void NodePath::
02017 set_sx(const NodePath &other, float sx) {
02018   nassertv_always(!is_empty());
02019   LVecBase3f scale = get_scale(other);
02020   scale[0] = sx;
02021   set_scale(other, scale);
02022 }
02023 
02024 void NodePath::
02025 set_sy(const NodePath &other, float sy) {
02026   nassertv_always(!is_empty());
02027   LVecBase3f scale = get_scale(other);
02028   scale[1] = sy;
02029   set_scale(other, scale);
02030 }
02031 
02032 void NodePath::
02033 set_sz(const NodePath &other, float sz) {
02034   nassertv_always(!is_empty());
02035   LVecBase3f scale = get_scale(other);
02036   scale[2] = sz;
02037   set_scale(other, scale);
02038 }
02039 
02040 ////////////////////////////////////////////////////////////////////
02041 //     Function: NodePath::get_scale
02042 //       Access: Published
02043 //  Description: Returns the relative scale of the bottom node
02044 //               as seen from the other node.
02045 ////////////////////////////////////////////////////////////////////
02046 LVecBase3f NodePath::
02047 get_scale(const NodePath &other) const {
02048   nassertr_always(!is_empty(), LVecBase3f(0.0f, 0.0f, 0.0f));
02049   CPT(TransformState) transform = get_transform(other);
02050   return transform->get_scale();
02051 }
02052 
02053 ////////////////////////////////////////////////////////////////////
02054 //     Function: NodePath::set_shear
02055 //       Access: Published
02056 //  Description: Sets the shear component of the transform,
02057 //               relative to the other node.
02058 ////////////////////////////////////////////////////////////////////
02059 void NodePath::
02060 set_shear(const NodePath &other, const LVecBase3f &shear) {
02061   nassertv_always(!is_empty());
02062   CPT(TransformState) rel_transform = get_transform(other);
02063 
02064   CPT(TransformState) orig_transform = get_transform();
02065   if (orig_transform->has_components()) {
02066     // If we had a componentwise transform before we started, we
02067     // should be careful to preserve the other three components.  We
02068     // wouldn't need to do this, except for the possibility of
02069     // numerical error or decompose ambiguity.
02070     const LVecBase3f &orig_pos = orig_transform->get_pos();
02071     const LVecBase3f &orig_hpr = orig_transform->get_hpr();
02072     const LVecBase3f &orig_scale = orig_transform->get_scale();
02073 
02074     set_transform(other, rel_transform->set_shear(shear));
02075     const TransformState *new_transform = get_transform();
02076     if (new_transform->has_components()) {
02077       set_transform(TransformState::make_pos_hpr_scale_shear
02078                     (orig_pos, orig_hpr, orig_scale, new_transform->get_shear()));
02079     }
02080 
02081   } else {
02082     // If we didn't have a componentwise transform already, never
02083     // mind.
02084     set_transform(other, rel_transform->set_shear(shear));
02085   }
02086 }
02087 
02088 void NodePath::
02089 set_shxy(const NodePath &other, float shxy) {
02090   nassertv_always(!is_empty());
02091   LVecBase3f shear = get_shear(other);
02092   shear[0] = shxy;
02093   set_shear(other, shear);
02094 }
02095 
02096 void NodePath::
02097 set_shxz(const NodePath &other, float shxz) {
02098   nassertv_always(!is_empty());
02099   LVecBase3f shear = get_shear(other);
02100   shear[1] = shxz;
02101   set_shear(other, shear);
02102 }
02103 
02104 void NodePath::
02105 set_shyz(const NodePath &other, float shyz) {
02106   nassertv_always(!is_empty());
02107   LVecBase3f shear = get_shear(other);
02108   shear[2] = shyz;
02109   set_shear(other, shear);
02110 }
02111 
02112 ////////////////////////////////////////////////////////////////////
02113 //     Function: NodePath::get_shear
02114 //       Access: Published
02115 //  Description: Returns the relative shear of the bottom node
02116 //               as seen from the other node.
02117 ////////////////////////////////////////////////////////////////////
02118 LVecBase3f NodePath::
02119 get_shear(const NodePath &other) const {
02120   nassertr_always(!is_empty(), LVecBase3f(0.0f, 0.0f, 0.0f));
02121   CPT(TransformState) transform = get_transform(other);
02122   return transform->get_shear();
02123 }
02124 
02125 ////////////////////////////////////////////////////////////////////
02126 //     Function: NodePath::set_pos_hpr
02127 //       Access: Published
02128 //  Description: Sets the translation and rotation component of the
02129 //               transform, relative to the other node.
02130 ////////////////////////////////////////////////////////////////////
02131 void NodePath::
02132 set_pos_hpr(const NodePath &other, const LVecBase3f &pos,
02133             const LVecBase3f &hpr) {
02134   nassertv_always(!is_empty());
02135   CPT(TransformState) rel_transform = get_transform(other);
02136 
02137   CPT(TransformState) orig_transform = get_transform();
02138   if (orig_transform->has_components()) {
02139     // If we had a componentwise transform before we started, we
02140     // should be careful to preserve the other two components.  We
02141     // wouldn't need to do this, except for the possibility of
02142     // numerical error or decompose ambiguity.
02143     const LVecBase3f &orig_scale = orig_transform->get_scale();
02144     const LVecBase3f &orig_shear = orig_transform->get_shear();
02145 
02146     set_transform(other, TransformState::make_pos_hpr_scale_shear
02147                   (pos, hpr, rel_transform->get_scale(), rel_transform->get_shear()));
02148     const TransformState *new_transform = get_transform();
02149     if (new_transform->has_components()) {
02150       set_pos_hpr_scale_shear(new_transform->get_pos(), new_transform->get_hpr(),
02151                               orig_scale, orig_shear);
02152     }
02153 
02154   } else {
02155     // If we didn't have a componentwise transform already, never
02156     // mind.
02157     set_transform(other, TransformState::make_pos_hpr_scale_shear
02158                   (pos, hpr, rel_transform->get_scale(), rel_transform->get_shear()));
02159     node()->reset_prev_transform();
02160   }
02161 }
02162 
02163 ////////////////////////////////////////////////////////////////////
02164 //     Function: NodePath::set_pos_quat
02165 //       Access: Published
02166 //  Description: Sets the translation and rotation component of the
02167 //               transform, relative to the other node.
02168 ////////////////////////////////////////////////////////////////////
02169 void NodePath::
02170 set_pos_quat(const NodePath &other, const LVecBase3f &pos,
02171              const LQuaternionf &quat) {
02172   nassertv_always(!is_empty());
02173   CPT(TransformState) rel_transform = get_transform(other);
02174   
02175   CPT(TransformState) orig_transform = get_transform();
02176   if (orig_transform->has_components()) {
02177     // If we had a componentwise transform before we started, we
02178     // should be careful to preserve the other two components.  We
02179     // wouldn't need to do this, except for the possibility of
02180     // numerical error or decompose ambiguity.
02181     const LVecBase3f &orig_scale = orig_transform->get_scale();
02182     const LVecBase3f &orig_shear = orig_transform->get_shear();
02183 
02184     set_transform(other, TransformState::make_pos_quat_scale_shear
02185                   (pos, quat, rel_transform->get_scale(), rel_transform->get_shear()));
02186     const TransformState *new_transform = get_transform();
02187     if (new_transform->has_components()) {
02188       set_pos_quat_scale_shear(new_transform->get_pos(), new_transform->get_quat(),
02189                                orig_scale, orig_shear);
02190     }
02191 
02192   } else {
02193     // If we didn't have a componentwise transform already, never
02194     // mind.
02195     set_transform(other, TransformState::make_pos_quat_scale_shear
02196                   (pos, quat, rel_transform->get_scale(), rel_transform->get_shear()));
02197     node()->reset_prev_transform();
02198   }
02199 }
02200 
02201 ////////////////////////////////////////////////////////////////////
02202 //     Function: NodePath::set_hpr_scale
02203 //       Access: Published
02204 //  Description: Sets the rotation and scale components of the
02205 //               transform, leaving translation untouched.  This, or
02206 //               set_pos_hpr_scale, is the preferred way to update a
02207 //               transform when both hpr and scale are to be changed.
02208 ////////////////////////////////////////////////////////////////////
02209 void NodePath::
02210 set_hpr_scale(const NodePath &other, const LVecBase3f &hpr, const LVecBase3f &scale) {
02211   // We don't bother trying very hard to preserve pos across this
02212   // operation, unlike the work we do above to preserve hpr or scale,
02213   // since it generally doesn't matter that much if pos is off by a
02214   // few thousandths.
02215   nassertv_always(!is_empty());
02216   CPT(TransformState) transform = get_transform(other);
02217   transform = TransformState::make_pos_hpr_scale_shear
02218     (transform->get_pos(), hpr, scale, transform->get_shear());
02219   set_transform(other, transform);
02220 }
02221 
02222 ////////////////////////////////////////////////////////////////////
02223 //     Function: NodePath::set_quat_scale
02224 //       Access: Published
02225 //  Description: Sets the rotation and scale components of the
02226 //               transform, leaving translation untouched.  This, or
02227 //               set_pos_quat_scale, is the preferred way to update a
02228 //               transform when both quat and scale are to be changed.
02229 ////////////////////////////////////////////////////////////////////
02230 void NodePath::
02231 set_quat_scale(const NodePath &other, const LQuaternionf &quat, 
02232                const LVecBase3f &scale) {
02233   // We don't bother trying very hard to preserve pos across this
02234   // operation, unlike the work we do above to preserve quat or scale,
02235   // since it generally doesn't matter that much if pos is off by a
02236   // few thousandths.
02237   nassertv_always(!is_empty());
02238   CPT(TransformState) transform = get_transform(other);
02239   transform = TransformState::make_pos_quat_scale_shear
02240     (transform->get_pos(), quat, scale, transform->get_shear());
02241   set_transform(other, transform);
02242 }
02243 
02244 ////////////////////////////////////////////////////////////////////
02245 //     Function: NodePath::set_pos_hpr_scale
02246 //       Access: Published
02247 //  Description: Completely replaces the transform with new
02248 //               translation, rotation, and scale components, relative
02249 //               to the other node, implicitly setting shear to 0.
02250 ////////////////////////////////////////////////////////////////////
02251 void NodePath::
02252 set_pos_hpr_scale(const NodePath &other,
02253                   const LVecBase3f &pos, const LVecBase3f &hpr,
02254                   const LVecBase3f &scale) {
02255   nassertv_always(!is_empty());
02256   set_transform(other, TransformState::make_pos_hpr_scale
02257                 (pos, hpr, scale));
02258   node()->reset_prev_transform();
02259 }
02260 
02261 ////////////////////////////////////////////////////////////////////
02262 //     Function: NodePath::set_pos_quat_scale
02263 //       Access: Published
02264 //  Description: Completely replaces the transform with new
02265 //               translation, rotation, and scale components, relative
02266 //               to the other node, implicitly setting shear to 0.
02267 ////////////////////////////////////////////////////////////////////
02268 void NodePath::
02269 set_pos_quat_scale(const NodePath &other,
02270                    const LVecBase3f &pos, const LQuaternionf &quat,
02271                    const LVecBase3f &scale) {
02272   nassertv_always(!is_empty());
02273   set_transform(other, TransformState::make_pos_quat_scale
02274                 (pos, quat, scale));
02275   node()->reset_prev_transform();
02276 }
02277 
02278 ////////////////////////////////////////////////////////////////////
02279 //     Function: NodePath::set_pos_hpr_scale_shear
02280 //       Access: Published
02281 //  Description: Completely replaces the transform with new
02282 //               translation, rotation, scale, and shear components,
02283 //               relative to the other node.
02284 ////////////////////////////////////////////////////////////////////
02285 void NodePath::
02286 set_pos_hpr_scale_shear(const NodePath &other,
02287                         const LVecBase3f &pos, const LVecBase3f &hpr,
02288                         const LVecBase3f &scale, const LVecBase3f &shear) {
02289   nassertv_always(!is_empty());
02290   set_transform(other, TransformState::make_pos_hpr_scale_shear
02291                 (pos, hpr, scale, shear));
02292   node()->reset_prev_transform();
02293 }
02294 
02295 ////////////////////////////////////////////////////////////////////
02296 //     Function: NodePath::set_pos_quat_scale_shear
02297 //       Access: Published
02298 //  Description: Completely replaces the transform with new
02299 //               translation, rotation, scale, and shear components,
02300 //               relative to the other node.
02301 ////////////////////////////////////////////////////////////////////
02302 void NodePath::
02303 set_pos_quat_scale_shear(const NodePath &other,
02304                          const LVecBase3f &pos, const LQuaternionf &quat,
02305                          const LVecBase3f &scale, const LVecBase3f &shear) {
02306   nassertv_always(!is_empty());
02307   set_transform(other, TransformState::make_pos_quat_scale_shear
02308                 (pos, quat, scale, shear));
02309   node()->reset_prev_transform();
02310 }
02311 
02312 ////////////////////////////////////////////////////////////////////
02313 //     Function: NodePath::get_mat
02314 //       Access: Published
02315 //  Description: Returns the matrix that describes the coordinate
02316 //               space of the bottom node, relative to the other
02317 //               path's bottom node's coordinate space.
02318 ////////////////////////////////////////////////////////////////////
02319 LMatrix4f NodePath::
02320 get_mat(const NodePath &other) const {
02321   CPT(TransformState) transform = get_transform(other);
02322   // We can't safely return a reference to the matrix, because we
02323   // can't assume the transform won't go away when the function
02324   // returns.  If the transform was partially modified by, say, a
02325   // CompassEffect, it won't be stored in the cache, and thus we might
02326   // have the only reference to it.
02327   return transform->get_mat();
02328 }
02329 
02330 ////////////////////////////////////////////////////////////////////
02331 //     Function: NodePath::set_mat
02332 //       Access: Published
02333 //  Description: Converts the indicated matrix from the other's
02334 //               coordinate space to the local coordinate space, and
02335 //               applies it to the node.
02336 ////////////////////////////////////////////////////////////////////
02337 void NodePath::
02338 set_mat(const NodePath &other, const LMatrix4f &mat) {
02339   nassertv_always(!is_empty());
02340   set_transform(other, TransformState::make_mat(mat));
02341   node()->reset_prev_transform();
02342 }
02343 
02344 ////////////////////////////////////////////////////////////////////
02345 //     Function: NodePath::get_relative_point
02346 //       Access: Published
02347 //  Description: Given that the indicated point is in the coordinate
02348 //               system of the other node, returns the same point in
02349 //               this node's coordinate system.
02350 ////////////////////////////////////////////////////////////////////
02351 LPoint3f NodePath::
02352 get_relative_point(const NodePath &other, const LVecBase3f &point) const {
02353   CPT(TransformState) transform = other.get_transform(*this);
02354   LPoint3f rel_point = LPoint3f(point) * transform->get_mat();
02355   return rel_point;
02356 }
02357 
02358 ////////////////////////////////////////////////////////////////////
02359 //     Function: NodePath::get_relative_vector
02360 //       Access: Published
02361 //  Description: Given that the indicated vector is in the coordinate
02362 //               system of the other node, returns the same vector in
02363 //               this node's coordinate system.
02364 ////////////////////////////////////////////////////////////////////
02365 LVector3f NodePath::
02366 get_relative_vector(const NodePath &other, const LVecBase3f &vec) const {
02367   CPT(TransformState) transform = other.get_transform(*this);
02368   LVector3f rel_vector = LVector3f(vec) * transform->get_mat();
02369   return rel_vector;
02370 }
02371 
02372 ////////////////////////////////////////////////////////////////////
02373 //     Function: NodePath::look_at
02374 //       Access: Published
02375 //  Description: Sets the transform on this NodePath so that it
02376 //               rotates to face the indicated point in space, which
02377 //               is relative to the other NodePath.
02378 ////////////////////////////////////////////////////////////////////
02379 void NodePath::
02380 look_at(const NodePath &other, const LPoint3f &point, const LVector3f &up) {
02381   nassertv_always(!is_empty());
02382 
02383   CPT(TransformState) transform = other.get_transform(get_parent());
02384   LPoint3f rel_point = point * transform->get_mat();
02385 
02386   LPoint3f pos = get_pos();
02387 
02388   LQuaternionf quat;
02389   ::look_at(quat, rel_point - pos, up);
02390   set_quat(quat);
02391 }
02392 
02393 ////////////////////////////////////////////////////////////////////
02394 //     Function: NodePath::heads_up
02395 //       Access: Published
02396 //  Description: Behaves like look_at(), but with a strong preference
02397 //               to keeping the up vector oriented in the indicated
02398 //               "up" direction.
02399 ////////////////////////////////////////////////////////////////////
02400 void NodePath::
02401 heads_up(const NodePath &other, const LPoint3f &point, const LVector3f &up) {
02402   nassertv_always(!is_empty());
02403 
02404   CPT(TransformState) transform = other.get_transform(get_parent());
02405   LPoint3f rel_point = point * transform->get_mat();
02406 
02407   LPoint3f pos = get_pos();
02408 
02409   LQuaternionf quat;
02410   ::heads_up(quat, rel_point - pos, up);
02411   set_quat(quat);
02412 }
02413 
02414 
02415 ////////////////////////////////////////////////////////////////////
02416 //     Function: NodePath::set_color
02417 //       Access: Published
02418 //  Description: Applies a scene-graph color to the referenced node.
02419 //               This color will apply to all geometry at this level
02420 //               and below (that does not specify a new color or a
02421 //               set_color_off()).
02422 ////////////////////////////////////////////////////////////////////
02423 void NodePath::
02424 set_color(float r, float g, float b, float a,
02425           int priority) {
02426   set_color(Colorf(r, g, b, a), priority);
02427 }
02428 
02429 ////////////////////////////////////////////////////////////////////
02430 //     Function: NodePath::set_color
02431 //       Access: Published
02432 //  Description: Applies a scene-graph color to the referenced node.
02433 //               This color will apply to all geometry at this level
02434 //               and below (that does not specify a new color or a
02435 //               set_color_off()).
02436 ////////////////////////////////////////////////////////////////////
02437 void NodePath::
02438 set_color(const Colorf &color, int priority) {
02439   nassertv_always(!is_empty());
02440   node()->set_attrib(ColorAttrib::make_flat(color), priority);
02441 }
02442 
02443 ////////////////////////////////////////////////////////////////////
02444 //     Function: NodePath::set_color_off
02445 //       Access: Published
02446 //  Description: Sets the geometry at this level and below to render
02447 //               using the geometry color.  This is normally the
02448 //               default, but it may be useful to use this to
02449 //               contradict set_color() at a higher node level (or,
02450 //               with a priority, to override a set_color() at a lower
02451 //               level).
02452 ////////////////////////////////////////////////////////////////////
02453 void NodePath::
02454 set_color_off(int priority) {
02455   nassertv_always(!is_empty());
02456   node()->set_attrib(ColorAttrib::make_vertex(), priority);
02457 }
02458 
02459 ////////////////////////////////////////////////////////////////////
02460 //     Function: NodePath::clear_color
02461 //       Access: Published
02462 //  Description: Completely removes any color adjustment from the node.
02463 //               This allows the natural color of the geometry, or
02464 //               whatever color transitions might be otherwise
02465 //               affecting the geometry, to show instead.
02466 ////////////////////////////////////////////////////////////////////
02467 void NodePath::
02468 clear_color() {
02469   nassertv_always(!is_empty());
02470   node()->clear_attrib(ColorAttrib::get_class_slot());
02471 }
02472 
02473 ////////////////////////////////////////////////////////////////////
02474 //     Function: NodePath::has_color
02475 //       Access: Published
02476 //  Description: Returns true if a color has been applied to the given
02477 //               node, false otherwise.
02478 ////////////////////////////////////////////////////////////////////
02479 bool NodePath::
02480 has_color() const {
02481   nassertr_always(!is_empty(), false);
02482   return node()->has_attrib(ColorAttrib::get_class_slot());
02483 }
02484 
02485 ////////////////////////////////////////////////////////////////////
02486 //     Function: NodePath::get_color
02487 //       Access: Published
02488 //  Description: Returns the color that has been assigned to the node,
02489 //               or black if no color has been assigned.
02490 ////////////////////////////////////////////////////////////////////
02491 Colorf NodePath::
02492 get_color() const {
02493   nassertr_always(!is_empty(), false);
02494   const RenderAttrib *attrib =
02495     node()->get_attrib(ColorAttrib::get_class_slot());
02496   if (attrib != (const RenderAttrib *)NULL) {
02497     const ColorAttrib *ca = DCAST(ColorAttrib, attrib);
02498     if (ca->get_color_type() == ColorAttrib::T_flat) {
02499       return ca->get_color();
02500     }
02501   }
02502 
02503   pgraph_cat.warning()
02504     << "get_color() called on " << *this << " which has no color set.\n";
02505 
02506   return Colorf(1.0f, 1.0f, 1.0f, 1.0f);
02507 }
02508 
02509 ////////////////////////////////////////////////////////////////////
02510 //     Function: NodePath::has_color_scale
02511 //       Access: Published
02512 //  Description: Returns true if a color scale has been applied
02513 //               to the referenced node, false otherwise.  It is still
02514 //               possible that color at this node might have been
02515 //               scaled by an ancestor node.
02516 ////////////////////////////////////////////////////////////////////
02517 bool NodePath::
02518 has_color_scale() const {
02519   nassertr_always(!is_empty(), false);
02520   return node()->has_attrib(ColorScaleAttrib::get_class_slot());
02521 }
02522 
02523 ////////////////////////////////////////////////////////////////////
02524 //     Function: NodePath::clear_color_scale
02525 //       Access: Published
02526 //  Description: Completely removes any color scale from the
02527 //               referenced node.  This is preferable to simply
02528 //               setting the color scale to identity, as it also
02529 //               removes the overhead associated with having a color
02530 //               scale at all.
02531 ////////////////////////////////////////////////////////////////////
02532 void NodePath::
02533 clear_color_scale() {
02534   nassertv_always(!is_empty());
02535   node()->clear_attrib(ColorScaleAttrib::get_class_slot());
02536 }
02537 
02538 ////////////////////////////////////////////////////////////////////
02539 //     Function: NodePath::compose_color_scale
02540 //       Access: Published
02541 //  Description: multiplies the color scale component of the transform,
02542 //               with previous color scale leaving translation and 
02543 //               rotation untouched.
02544 ////////////////////////////////////////////////////////////////////
02545 void NodePath::
02546 compose_color_scale(const LVecBase4f &scale, int priority) {
02547   nassertv_always(!is_empty());
02548 
02549   const RenderAttrib *attrib =
02550     node()->get_attrib(ColorScaleAttrib::get_class_slot());
02551   if (attrib != (const RenderAttrib *)NULL) {
02552     priority = max(priority,
02553                    node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
02554     const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
02555 
02556     // Modify the existing ColorScaleAttrib by multiplying with the 
02557     // indicated colorScale.
02558     LVecBase4f prev_color_scale = csa->get_scale();
02559     LVecBase4f new_color_scale(prev_color_scale[0]*scale[0],
02560                                prev_color_scale[1]*scale[1],
02561                                prev_color_scale[2]*scale[2],
02562                                prev_color_scale[3]*scale[3]);
02563     node()->set_attrib(csa->set_scale(new_color_scale), priority);
02564 
02565   } else {
02566     // Create a new ColorScaleAttrib for this node.
02567     node()->set_attrib(ColorScaleAttrib::make(scale), priority);
02568   }
02569 }
02570 
02571 ////////////////////////////////////////////////////////////////////
02572 //     Function: NodePath::set_color_scale
02573 //       Access: Published
02574 //  Description: Sets the color scale component of the transform,
02575 //               leaving translation and rotation untouched.
02576 ////////////////////////////////////////////////////////////////////
02577 void NodePath::
02578 set_color_scale(const LVecBase4f &scale, int priority) {
02579   nassertv_always(!is_empty());
02580 
02581   const RenderAttrib *attrib =
02582     node()->get_attrib(ColorScaleAttrib::get_class_slot());
02583   if (attrib != (const RenderAttrib *)NULL) {
02584     priority = max(priority,
02585                    node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
02586     const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
02587 
02588     // Modify the existing ColorScaleAttrib to add the indicated
02589     // colorScale.
02590     node()->set_attrib(csa->set_scale(scale), priority);
02591 
02592   } else {
02593     // Create a new ColorScaleAttrib for this node.
02594     node()->set_attrib(ColorScaleAttrib::make(scale), priority);
02595   }
02596 }
02597 
02598 ////////////////////////////////////////////////////////////////////
02599 //     Function: NodePath::set_color_scale_off
02600 //       Access: Published
02601 //  Description: Disables any color scale attribute inherited from
02602 //               above.  This is not the same thing as
02603 //               clear_color_scale(), which undoes any previous
02604 //               set_color_scale() operation on this node; rather,
02605 //               this actively disables any set_color_scale() that
02606 //               might be inherited from a parent node.  This also
02607 //               disables set_alpha_scale() at the same time.
02608 //
02609 //               It is legal to specify a new color scale on the same
02610 //               node with a subsequent call to set_color_scale() or
02611 //               set_alpha_scale(); this new scale will apply to lower
02612 //               geometry.
02613 ////////////////////////////////////////////////////////////////////
02614 void NodePath::
02615 set_color_scale_off(int priority) {
02616   nassertv_always(!is_empty());
02617   node()->set_attrib(ColorScaleAttrib::make_off(), priority);
02618 }
02619 
02620 ////////////////////////////////////////////////////////////////////
02621 //     Function: NodePath::set_alpha_scale
02622 //       Access: Published
02623 //  Description: Sets the alpha scale component of the transform
02624 //               without (much) affecting the color scale.  Note that
02625 //               any priority specified will also apply to the color
02626 //               scale.
02627 ////////////////////////////////////////////////////////////////////
02628 void NodePath::
02629 set_alpha_scale(float scale, int priority) {
02630   nassertv_always(!is_empty());
02631 
02632   const RenderAttrib *attrib =
02633     node()->get_attrib(ColorScaleAttrib::get_class_slot());
02634   if (attrib != (const RenderAttrib *)NULL) {
02635     priority = max(priority,
02636                    node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
02637     const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
02638 
02639     // Modify the existing ColorScaleAttrib to add the indicated
02640     // colorScale.
02641     const LVecBase4f &sc = csa->get_scale();
02642     node()->set_attrib(csa->set_scale(LVecBase4f(sc[0], sc[1], sc[2], scale)), priority);
02643 
02644   } else {
02645     // Create a new ColorScaleAttrib for this node.
02646     node()->set_attrib(ColorScaleAttrib::make(LVecBase4f(1.0f, 1.0f, 1.0f, scale)), priority);
02647   }
02648 }
02649 
02650 ////////////////////////////////////////////////////////////////////
02651 //     Function: NodePath::set_all_color_scale
02652 //       Access: Published
02653 //  Description: Scales all the color components of the object by the
02654 //               same amount, darkening the object, without (much)
02655 //               affecting alpha.  Note that any priority specified
02656 //               will also apply to the alpha scale.
02657 ////////////////////////////////////////////////////////////////////
02658 void NodePath::
02659 set_all_color_scale(float scale, int priority) {
02660   nassertv_always(!is_empty());
02661 
02662   const RenderAttrib *attrib =
02663     node()->get_attrib(ColorScaleAttrib::get_class_slot());
02664   if (attrib != (const RenderAttrib *)NULL) {
02665     priority = max(priority,
02666                    node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
02667     const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
02668 
02669     // Modify the existing ColorScaleAttrib to add the indicated
02670     // colorScale.
02671     const LVecBase4f &sc = csa->get_scale();
02672     node()->set_attrib(csa->set_scale(LVecBase4f(scale, scale, scale, sc[3])), priority);
02673 
02674   } else {
02675     // Create a new ColorScaleAttrib for this node.
02676     node()->set_attrib(ColorScaleAttrib::make(LVecBase4f(scale, scale, scale, 1.0f)), priority);
02677   }
02678 }
02679 
02680 ////////////////////////////////////////////////////////////////////
02681 //     Function: NodePath::get_color_scale
02682 //       Access: Published
02683 //  Description: Returns the complete color scale vector that has been
02684 //               applied to this node via a previous call to
02685 //               set_color_scale() and/or set_alpha_scale(), or all
02686 //               1's (identity) if no scale has been applied to this
02687 //               particular node.
02688 ////////////////////////////////////////////////////////////////////
02689 const LVecBase4f &NodePath::
02690 get_color_scale() const {
02691   static const LVecBase4f ident_scale(1.0f, 1.0f, 1.0f, 1.0f);
02692   nassertr_always(!is_empty(), ident_scale);
02693   const RenderAttrib *attrib =
02694     node()->get_attrib(ColorScaleAttrib::get_class_slot());
02695   if (attrib != (const RenderAttrib *)NULL) {
02696     const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
02697     return csa->get_scale();
02698   }
02699 
02700   return ident_scale;
02701 }
02702 
02703 ////////////////////////////////////////////////////////////////////
02704 //     Function: NodePath::set_light
02705 //       Access: Published
02706 //  Description: Adds the indicated Light or PolylightNode to the list
02707 //               of lights that illuminate geometry at this node and
02708 //               below.  The light itself should be parented into the
02709 //               scene graph elsewhere, to represent the light's
02710 //               position in space; but until set_light() is called it
02711 //               will illuminate no geometry.
02712 ////////////////////////////////////////////////////////////////////
02713 void NodePath::
02714 set_light(const NodePath &light, int priority) {
02715   nassertv_always(!is_empty());
02716   if (!light.is_empty()) {
02717     Light *light_obj = light.node()->as_light();
02718     if (light_obj != (Light *)NULL) {
02719       // It's an actual Light object.
02720       const RenderAttrib *attrib =
02721         node()->get_attrib(LightAttrib::get_class_slot());
02722       if (attrib != (const RenderAttrib *)NULL) {
02723         priority = max(priority,
02724                        node()->get_state()->get_override(LightAttrib::get_class_slot()));
02725         const LightAttrib *la = DCAST(LightAttrib, attrib);
02726         
02727         // Modify the existing LightAttrib to add the indicated
02728         // light.
02729         node()->set_attrib(la->add_on_light(light), priority);
02730         
02731       } else {
02732         // Create a new LightAttrib for this node.
02733         CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make());
02734         node()->set_attrib(la->add_on_light(light), priority);
02735       }
02736       return;
02737 
02738     } else if (light.node()->is_of_type(PolylightNode::get_class_type())) {
02739       // It's a Polylight object.
02740       if (priority != 0) {
02741         // PolylightEffects can't have a priority, since they're just
02742         // an effect to be applied immediately.
02743         pgraph_cat.warning()
02744           << "Ignoring priority on set_light(" << light << ")\n";
02745       }
02746 
02747       const RenderEffect *effect =
02748         node()->get_effect(PolylightEffect::get_class_type());
02749       if (effect != (const RenderEffect *)NULL) {
02750         const PolylightEffect *ple = DCAST(PolylightEffect, effect);
02751         
02752         // Modify the existing PolylightEffect to add the indicated
02753         // light.
02754         node()->set_effect(ple->add_light(light));
02755         
02756       } else {
02757         // Create a new PolylightEffect for this node.
02758         CPT(PolylightEffect) ple = DCAST(PolylightEffect, PolylightEffect::make());
02759         node()->set_effect(ple->add_light(light));
02760       }
02761       return;
02762     }
02763   }
02764   nassert_raise("Not a Light object.");
02765 }
02766 
02767 ////////////////////////////////////////////////////////////////////
02768 //     Function: NodePath::set_light_off
02769 //       Access: Published
02770 //  Description: Sets the geometry at this level and below to render
02771 //               using no lights at all.  This is different
02772 //               from not specifying a light; rather, this
02773 //               specifically contradicts set_light() at a higher
02774 //               node level (or, with a priority, overrides a
02775 //               set_light() at a lower level).
02776 //
02777 //               If no lights are in effect on a particular piece of
02778 //               geometry, that geometry is rendered with lighting
02779 //               disabled.
02780 ////////////////////////////////////////////////////////////////////
02781 void NodePath::
02782 set_light_off(int priority) {
02783   nassertv_always(!is_empty());
02784   node()->set_attrib(LightAttrib::make_all_off(), priority);
02785   node()->clear_effect(PolylightEffect::get_class_type());
02786 }
02787 
02788 ////////////////////////////////////////////////////////////////////
02789 //     Function: NodePath::set_light_off
02790 //       Access: Published
02791 //  Description: Sets the geometry at this level and below to render
02792 //               without using the indicated Light.  This is different
02793 //               from not specifying the Light; rather, this
02794 //               specifically contradicts set_light() at a higher node
02795 //               level (or, with a priority, overrides a set_light()
02796 //               at a lower level).
02797 //
02798 //               This interface does not support PolylightNodes, which
02799 //               cannot be turned off at a lower level.
02800 ////////////////////////////////////////////////////////////////////
02801 void NodePath::
02802 set_light_off(const NodePath &light, int priority) {
02803   nassertv_always(!is_empty());
02804 
02805   if (!light.is_empty()) {
02806     Light *light_obj = light.node()->as_light();
02807     if (light_obj != (Light *)NULL) {
02808       const RenderAttrib *attrib =
02809         node()->get_attrib(LightAttrib::get_class_slot());
02810       if (attrib != (const RenderAttrib *)NULL) {
02811         priority = max(priority,
02812                        node()->get_state()->get_override(LightAttrib::get_class_slot()));
02813         const LightAttrib *la = DCAST(LightAttrib, attrib);
02814         
02815         // Modify the existing LightAttrib to add the indicated light
02816         // to the "off" list.  This also, incidentally, removes it from
02817         // the "on" list if it is there.
02818         node()->set_attrib(la->add_off_light(light), priority);
02819         
02820       } else {
02821         // Create a new LightAttrib for this node that turns off the
02822         // indicated light.
02823         CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make());
02824         node()->set_attrib(la->add_off_light(light), priority);
02825       }
02826       return;
02827     }
02828   }
02829   nassert_raise("Not a Light object.");
02830 }
02831 
02832 ////////////////////////////////////////////////////////////////////
02833 //     Function: NodePath::clear_light
02834 //       Access: Published
02835 //  Description: Completely removes any lighting operations that may
02836 //               have been set via set_light() or set_light_off()
02837 //               from this particular node.
02838 ////////////////////////////////////////////////////////////////////
02839 void NodePath::
02840 clear_light() {
02841   nassertv_always(!is_empty());
02842   node()->clear_attrib(LightAttrib::get_class_slot());
02843   node()->clear_effect(PolylightEffect::get_class_type());
02844 }
02845 
02846 ////////////////////////////////////////////////////////////////////
02847 //     Function: NodePath::clear_light
02848 //       Access: Published
02849 //  Description: Removes any reference to the indicated Light or
02850 //               PolylightNode from the NodePath.
02851 ////////////////////////////////////////////////////////////////////
02852 void NodePath::
02853 clear_light(const NodePath &light) {
02854   nassertv_always(!is_empty());
02855 
02856   if (!light.is_empty()) {
02857     Light *light_obj = light.node()->as_light();
02858     if (light_obj != (Light *)NULL) {
02859       const RenderAttrib *attrib =
02860         node()->get_attrib(LightAttrib::get_class_slot());
02861       if (attrib != (const RenderAttrib *)NULL) {
02862         CPT(LightAttrib) la = DCAST(LightAttrib, attrib);
02863         la = DCAST(LightAttrib, la->remove_on_light(light));
02864         la = DCAST(LightAttrib, la->remove_off_light(light));
02865         
02866         if (la->is_identity()) {
02867           node()->clear_attrib(LightAttrib::get_class_slot());
02868           
02869         } else {
02870           int priority = node()->get_state()->get_override(LightAttrib::get_class_slot());
02871           node()->set_attrib(la, priority);
02872         }
02873       }
02874       return;
02875 
02876     } else if (light.node()->is_of_type(PolylightNode::get_class_type())) {
02877       const RenderEffect *effect =
02878         node()->get_effect(PolylightEffect::get_class_type());
02879       if (effect != (const RenderEffect *)NULL) {
02880         CPT(PolylightEffect) ple = DCAST(PolylightEffect, effect);
02881         ple = DCAST(PolylightEffect, ple->remove_light(light));
02882         node()->set_effect(ple);
02883       }
02884       return;
02885     }
02886   }
02887   nassert_raise("Not a Light object.");
02888 }
02889 
02890 ////////////////////////////////////////////////////////////////////
02891 //     Function: NodePath::has_light
02892 //       Access: Published
02893 //  Description: Returns true if the indicated Light or PolylightNode
02894 //               has been specifically enabled on this particular
02895 //               node.  This means that someone called set_light() on
02896 //               this node with the indicated light.
02897 ////////////////////////////////////////////////////////////////////
02898 bool NodePath::
02899 has_light(const NodePath &light) const {
02900   nassertr_always(!is_empty(), false);
02901 
02902   if (!light.is_empty()) {
02903     Light *light_obj = light.node()->as_light();
02904     if (light_obj != (Light *)NULL) {
02905       const RenderAttrib *attrib =
02906         node()->get_attrib(LightAttrib::get_class_slot());
02907       if (attrib != (const RenderAttrib *)NULL) {
02908         const LightAttrib *la = DCAST(LightAttrib, attrib);
02909         return la->has_on_light(light);
02910       }
02911       return false;
02912 
02913     } else if (light.node()->is_of_type(PolylightNode::get_class_type())) {
02914       const RenderEffect *effect =
02915         node()->get_effect(PolylightEffect::get_class_type());
02916       if (effect != (const RenderEffect *)NULL) {
02917         const PolylightEffect *ple = DCAST(PolylightEffect, effect);
02918         return ple->has_light(light);
02919       }
02920       return false;
02921     }
02922   }
02923   nassert_raise("Not a Light object.");
02924   return false;
02925 }
02926 
02927 ////////////////////////////////////////////////////////////////////
02928 //     Function: NodePath::has_light_off
02929 //       Access: Published
02930 //  Description: Returns true if all Lights have been specifically
02931 //               disabled on this particular node.  This means that
02932 //               someone called set_light_off() on this node with no
02933 //               parameters.
02934 ////////////////////////////////////////////////////////////////////
02935 bool NodePath::
02936 has_light_off() const {
02937   nassertr_always(!is_empty(), false);
02938 
02939   const RenderAttrib *attrib =
02940     node()->get_attrib(LightAttrib::get_class_slot());
02941   if (attrib != (const RenderAttrib *)NULL) {
02942     const LightAttrib *la = DCAST(LightAttrib, attrib);
02943     return la->has_all_off();
02944   }
02945 
02946   return false;
02947 }
02948 
02949 ////////////////////////////////////////////////////////////////////
02950 //     Function: NodePath::has_light_off
02951 //       Access: Published
02952 //  Description: Returns true if the indicated Light has been
02953 //               specifically disabled on this particular node.  This
02954 //               means that someone called set_light_off() on this
02955 //               node with the indicated light.
02956 //
02957 //               This interface does not support PolylightNodes, which
02958 //               cannot be turned off at a lower level.
02959 ////////////////////////////////////////////////////////////////////
02960 bool NodePath::
02961 has_light_off(const NodePath &light) const {
02962   nassertr_always(!is_empty(), false);
02963   if (!light.is_empty()) {
02964     Light *light_obj = light.node()->as_light();
02965     if (light_obj != (Light *)NULL) {
02966       const RenderAttrib *attrib =
02967         node()->get_attrib(LightAttrib::get_class_slot());
02968       if (attrib != (const RenderAttrib *)NULL) {
02969         const LightAttrib *la = DCAST(LightAttrib, attrib);
02970         return la->has_off_light(light);
02971       }
02972     }
02973   }
02974   nassert_raise("Not a Light object.");
02975   return false;
02976 }
02977 
02978 ////////////////////////////////////////////////////////////////////
02979 //     Function: NodePath::set_clip_plane
02980 //       Access: Published
02981 //  Description: Adds the indicated clipping plane to the list of
02982 //               planes that apply to geometry at this node and below.
02983 //               The clipping plane itself, a PlaneNode, should be
02984 //               parented into the scene graph elsewhere, to represent
02985 //               the plane's position in space; but until
02986 //               set_clip_plane() is called it will clip no geometry.
02987 ////////////////////////////////////////////////////////////////////
02988 void NodePath::
02989 set_clip_plane(const NodePath &clip_plane, int priority) {
02990   nassertv_always(!is_empty());
02991   if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
02992     const RenderAttrib *attrib =
02993       node()->get_attrib(ClipPlaneAttrib::get_class_slot());
02994     if (attrib != (const RenderAttrib *)NULL) {
02995       priority = max(priority,
02996                      node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot()));
02997       const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
02998       
02999       // Modify the existing ClipPlaneAttrib to add the indicated
03000       // clip_plane.
03001       node()->set_attrib(la->add_on_plane(clip_plane), priority);
03002       
03003     } else {
03004       // Create a new ClipPlaneAttrib for this node.
03005       CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make());
03006       node()->set_attrib(la->add_on_plane(clip_plane), priority);
03007     }
03008     return;
03009   }
03010   nassert_raise("Not a PlaneNode object.");
03011 }
03012 
03013 ////////////////////////////////////////////////////////////////////
03014 //     Function: NodePath::set_clip_plane_off
03015 //       Access: Published
03016 //  Description: Sets the geometry at this level and below to render
03017 //               using no clip_planes at all.  This is different
03018 //               from not specifying a clip_plane; rather, this
03019 //               specifically contradicts set_clip_plane() at a higher
03020 //               node level (or, with a priority, overrides a
03021 //               set_clip_plane() at a lower level).
03022 //
03023 //               If no clip_planes are in effect on a particular piece
03024 //               of geometry, that geometry is rendered without being
03025 //               clipped (other than by the viewing frustum).
03026 ////////////////////////////////////////////////////////////////////
03027 void NodePath::
03028 set_clip_plane_off(int priority) {
03029   nassertv_always(!is_empty());
03030   node()->set_attrib(ClipPlaneAttrib::make_all_off(), priority);
03031 }
03032 
03033 ////////////////////////////////////////////////////////////////////
03034 //     Function: NodePath::set_clip_plane_off
03035 //       Access: Published
03036 //  Description: Sets the geometry at this level and below to render
03037 //               without being clipped by the indicated PlaneNode.
03038 //               This is different from not specifying the PlaneNode;
03039 //               rather, this specifically contradicts
03040 //               set_clip_plane() at a higher node level (or, with a
03041 //               priority, overrides a set_clip_plane() at a lower
03042 //               level).
03043 ////////////////////////////////////////////////////////////////////
03044 void NodePath::
03045 set_clip_plane_off(const NodePath &clip_plane, int priority) {
03046   nassertv_always(!is_empty());
03047 
03048   if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
03049     const RenderAttrib *attrib =
03050       node()->get_attrib(ClipPlaneAttrib::get_class_slot());
03051     if (attrib != (const RenderAttrib *)NULL) {
03052       priority = max(priority,
03053                      node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot()));
03054       const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
03055       
03056       // Modify the existing ClipPlaneAttrib to add the indicated clip_plane
03057       // to the "off" list.  This also, incidentally, removes it from
03058       // the "on" list if it is there.
03059       node()->set_attrib(la->add_off_plane(clip_plane), priority);
03060       
03061     } else {
03062       // Create a new ClipPlaneAttrib for this node that turns off the
03063       // indicated clip_plane.
03064       CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make());
03065       node()->set_attrib(la->add_off_plane(clip_plane), priority);
03066     }
03067     return;
03068   }
03069   nassert_raise("Not a PlaneNode object.");
03070 }
03071 
03072 ////////////////////////////////////////////////////////////////////
03073 //     Function: NodePath::clear_clip_plane
03074 //       Access: Published
03075 //  Description: Completely removes any clip planes that may have been
03076 //               set via set_clip_plane() or set_clip_plane_off() from
03077 //               this particular node.
03078 ////////////////////////////////////////////////////////////////////
03079 void NodePath::
03080 clear_clip_plane() {
03081   nassertv_always(!is_empty());
03082   node()->clear_attrib(ClipPlaneAttrib::get_class_slot());
03083 }
03084 
03085 ////////////////////////////////////////////////////////////////////
03086 //     Function: NodePath::clear_clip_plane
03087 //       Access: Published
03088 //  Description: Removes any reference to the indicated clipping plane
03089 //               from the NodePath.
03090 ////////////////////////////////////////////////////////////////////
03091 void NodePath::
03092 clear_clip_plane(const NodePath &clip_plane) {
03093   nassertv_always(!is_empty());
03094 
03095   if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
03096     const RenderAttrib *attrib =
03097       node()->get_attrib(ClipPlaneAttrib::get_class_slot());
03098     if (attrib != (const RenderAttrib *)NULL) {
03099       CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, attrib);
03100       la = DCAST(ClipPlaneAttrib, la->remove_on_plane(clip_plane));
03101       la = DCAST(ClipPlaneAttrib, la->remove_off_plane(clip_plane));
03102         
03103       if (la->is_identity()) {
03104         node()->clear_attrib(ClipPlaneAttrib::get_class_slot());
03105           
03106       } else {
03107         int priority = node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot());
03108         node()->set_attrib(la, priority);
03109       }
03110     }
03111     return;
03112   }
03113   nassert_raise("Not a PlaneNode object.");
03114 }
03115 
03116 ////////////////////////////////////////////////////////////////////
03117 //     Function: NodePath::has_clip_plane
03118 //       Access: Published
03119 //  Description: Returns true if the indicated clipping plane has been
03120 //               specifically applied to this particular node.  This
03121 //               means that someone called set_clip_plane() on this
03122 //               node with the indicated clip_plane.
03123 ////////////////////////////////////////////////////////////////////
03124 bool NodePath::
03125 has_clip_plane(const NodePath &clip_plane) const {
03126   nassertr_always(!is_empty(), false);
03127 
03128   if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
03129     const RenderAttrib *attrib =
03130       node()->get_attrib(ClipPlaneAttrib::get_class_slot());
03131     if (attrib != (const RenderAttrib *)NULL) {
03132       const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
03133       return la->has_on_plane(clip_plane);
03134     }
03135     return false;
03136   }
03137   nassert_raise("Not a PlaneNode object.");
03138   return false;
03139 }
03140 
03141 ////////////////////////////////////////////////////////////////////
03142 //     Function: NodePath::has_clip_plane_off
03143 //       Access: Published
03144 //  Description: Returns true if all clipping planes have been
03145 //               specifically disabled on this particular node.  This
03146 //               means that someone called set_clip_plane_off() on
03147 //               this node with no parameters.
03148 ////////////////////////////////////////////////////////////////////
03149 bool NodePath::
03150 has_clip_plane_off() const {
03151   nassertr_always(!is_empty(), false);
03152 
03153   const RenderAttrib *attrib =
03154     node()->get_attrib(ClipPlaneAttrib::get_class_slot());
03155   if (attrib != (const RenderAttrib *)NULL) {
03156     const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
03157     return la->has_all_off();
03158   }
03159 
03160   return false;
03161 }
03162 
03163 ////////////////////////////////////////////////////////////////////
03164 //     Function: NodePath::has_clip_plane_off
03165 //       Access: Published
03166 //  Description: Returns true if the indicated clipping plane has been
03167 //               specifically disabled on this particular node.  This
03168 //               means that someone called set_clip_plane_off() on
03169 //               this node with the indicated clip_plane.
03170 ////////////////////////////////////////////////////////////////////
03171 bool NodePath::
03172 has_clip_plane_off(const NodePath &clip_plane) const {
03173   nassertr_always(!is_empty(), false);
03174   if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
03175     const RenderAttrib *attrib =
03176       node()->get_attrib(ClipPlaneAttrib::get_class_slot());
03177     if (attrib != (const RenderAttrib *)NULL) {
03178       const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
03179       return la->has_off_plane(clip_plane);
03180     }
03181   }
03182   nassert_raise("Not a PlaneNode object.");
03183   return false;
03184 }
03185 
03186 ////////////////////////////////////////////////////////////////////
03187 //     Function: NodePath::set_scissor
03188 //       Access: Published
03189 //  Description: Sets up a scissor region on the nodes rendered at
03190 //               this level and below.  The four coordinates are
03191 //               understood to define a rectangle in screen space.
03192 //               These numbers are relative to the current
03193 //               DisplayRegion, where (0,0) is the lower-left corner
03194 //               of the DisplayRegion, and (1,1) is the upper-right
03195 //               corner.
03196 ////////////////////////////////////////////////////////////////////
03197 void NodePath::
03198 set_scissor(float left, float right, float bottom, float top) {
03199   set_effect(ScissorEffect::make_screen(LVecBase4f(left, right, bottom, top)));
03200 }
03201 
03202 ////////////////////////////////////////////////////////////////////
03203 //     Function: NodePath::set_scissor
03204 //       Access: Published
03205 //  Description: Sets up a scissor region on the nodes rendered at
03206 //               this level and below.  The two points are understood
03207 //               to be relative to this node.  When these points are
03208 //               projected into screen space, they define the
03209 //               diagonally-opposite points that determine the scissor
03210 //               region.
03211 ////////////////////////////////////////////////////////////////////
03212 void NodePath::
03213 set_scissor(const LPoint3f &a, const LPoint3f &b) {
03214   set_effect(ScissorEffect::make_node(a, b));
03215 }
03216 
03217 ////////////////////////////////////////////////////////////////////
03218 //     Function: NodePath::set_scissor
03219 //       Access: Published
03220 //  Description: Sets up a scissor region on the nodes rendered at
03221 //               this level and below.  The four points are understood
03222 //               to be relative to this node.  When these points are
03223 //               projected into screen space, they define the
03224 //               bounding volume of the scissor region (the scissor
03225 //               region is the smallest onscreen rectangle that
03226 //               encloses all four points).
03227 ////////////////////////////////////////////////////////////////////
03228 void NodePath::
03229 set_scissor(const LPoint3f &a, const LPoint3f &b,
03230             const LPoint3f &c, const LPoint3f &d) {
03231   set_effect(ScissorEffect::make_node(a, b, c, d));
03232 }
03233 
03234 ////////////////////////////////////////////////////////////////////
03235 //     Function: NodePath::set_scissor
03236 //       Access: Published
03237 //  Description: Sets up a scissor region on the nodes rendered at
03238 //               this level and below.  The two points are understood
03239 //               to be relative to the indicated other node.  When
03240 //               these points are projected into screen space, they
03241 //               define the diagonally-opposite points that determine
03242 //               the scissor region.
03243 ////////////////////////////////////////////////////////////////////
03244 void NodePath::
03245 set_scissor(const NodePath &other, const LPoint3f &a, const LPoint3f &b) {
03246   set_effect(ScissorEffect::make_node(a, b, other));
03247 }
03248 
03249 ////////////////////////////////////////////////////////////////////
03250 //     Function: NodePath::set_scissor
03251 //       Access: Published
03252 //  Description: Sets up a scissor region on the nodes rendered at
03253 //               this level and below.  The four points are understood
03254 //               to be relative to the indicated other node.  When
03255 //               these points are projected into screen space, they
03256 //               define the bounding volume of the scissor region (the
03257 //               scissor region is the smallest onscreen rectangle
03258 //               that encloses all four points).
03259 ////////////////////////////////////////////////////////////////////
03260 void NodePath::
03261 set_scissor(const NodePath &other,
03262             const LPoint3f &a, const LPoint3f &b,
03263             const LPoint3f &c, const LPoint3f &d) {
03264   set_effect(ScissorEffect::make_node(a, b, c, d, other));
03265 }
03266 
03267 ////////////////////////////////////////////////////////////////////
03268 //     Function: NodePath::clear_scissor
03269 //       Access: Published
03270 //  Description: Removes the scissor region that was defined at this
03271 //               node level by a previous call to set_scissor().
03272 ////////////////////////////////////////////////////////////////////
03273 void NodePath::
03274 clear_scissor() {
03275   clear_effect(ScissorEffect::get_class_type());
03276 }
03277 
03278 ////////////////////////////////////////////////////////////////////
03279 //     Function: NodePath::has_scissor
03280 //       Access: Published
03281 //  Description: Returns true if a scissor region was defined at this
03282 //               node by a previous call to set_scissor().  This does
03283 //               not check for scissor regions inherited from a parent
03284 //               class.  It also does not check for the presence of a
03285 //               low-level ScissorAttrib, which is different from the
03286 //               ScissorEffect added by set_scissor.
03287 ////////////////////////////////////////////////////////////////////
03288 bool NodePath::
03289 has_scissor() const {
03290   return has_effect(ScissorEffect::get_class_type());
03291 }
03292 
03293 ////////////////////////////////////////////////////////////////////
03294 //     Function: NodePath::set_bin
03295 //       Access: Published
03296 //  Description: Assigns the geometry at this level and below to the
03297 //               named rendering bin.  It is the user's responsibility
03298 //               to ensure that such a bin already exists, either via
03299 //               the cull-bin Configrc variable, or by explicitly
03300 //               creating a GeomBin of the appropriate type at
03301 //               runtime.
03302 //
03303 //               There are two default bins created when Panda is
03304 //               started: "default" and "fixed".  Normally, all
03305 //               geometry is assigned to "default" unless specified
03306 //               otherwise.  This bin renders opaque geometry in
03307 //               state-sorted order, followed by transparent geometry
03308 //               sorted back-to-front.  If any geometry is assigned to
03309 //               "fixed", this will be rendered following all the
03310 //               geometry in "default", in the order specified by
03311 //               draw_order for each piece of geometry so assigned.
03312 //
03313 //               The draw_order parameter is meaningful only for
03314 //               GeomBinFixed type bins, e.g. "fixed".  Other kinds of
03315 //               bins ignore it.
03316 ////////////////////////////////////////////////////////////////////
03317 void NodePath::
03318 set_bin(const string &bin_name, int draw_order, int priority) {
03319   nassertv_always(!is_empty());
03320   node()->set_attrib(CullBinAttrib::make(bin_name, draw_order), priority);
03321 }
03322 
03323 ////////////////////////////////////////////////////////////////////
03324 //     Function: NodePath::clear_bin
03325 //       Access: Published
03326 //  Description: Completely removes any bin adjustment that may have
03327 //               been set via set_bin() from this particular node.
03328 ////////////////////////////////////////////////////////////////////
03329 void NodePath::
03330 clear_bin() {
03331   nassertv_always(!is_empty());
03332   node()->clear_attrib(CullBinAttrib::get_class_slot());
03333 }
03334 
03335 ////////////////////////////////////////////////////////////////////
03336 //     Function: NodePath::has_bin
03337 //       Access: Published
03338 //  Description: Returns true if the node has been assigned to the a
03339 //               particular rendering bin via set_bin(), false
03340 //               otherwise.
03341 ////////////////////////////////////////////////////////////////////
03342 bool NodePath::
03343 has_bin() const {
03344   nassertr_always(!is_empty(), false);
03345   return node()->has_attrib(CullBinAttrib::get_class_slot());
03346 }
03347 
03348 ////////////////////////////////////////////////////////////////////
03349 //     Function: NodePath::get_bin_name
03350 //       Access: Published
03351 //  Description: Returns the name of the bin that this particular node
03352 //               was assigned to via set_bin(), or the empty string if
03353 //               no bin was assigned.  See set_bin() and has_bin().
03354 ////////////////////////////////////////////////////////////////////
03355 string NodePath::
03356 get_bin_name() const {
03357   nassertr_always(!is_empty(), string());
03358   const RenderAttrib *attrib =
03359     node()->get_attrib(CullBinAttrib::get_class_slot());
03360   if (attrib != (const RenderAttrib *)NULL) {
03361     const CullBinAttrib *ba = DCAST(CullBinAttrib, attrib);
03362     return ba->get_bin_name();
03363   }
03364 
03365   return string();
03366 }
03367 
03368 ////////////////////////////////////////////////////////////////////
03369 //     Function: NodePath::get_bin_draw_order
03370 //       Access: Published
03371 //  Description: Returns the drawing order associated with the bin
03372 //               that this particular node was assigned to via
03373 //               set_bin(), or 0 if no bin was assigned.  See
03374 //               set_bin() and has_bin().
03375 ////////////////////////////////////////////////////////////////////
03376 int NodePath::
03377 get_bin_draw_order() const {
03378   nassertr_always(!is_empty(), false);
03379   const RenderAttrib *attrib =
03380     node()->get_attrib(CullBinAttrib::get_class_slot());
03381   if (attrib != (const RenderAttrib *)NULL) {
03382     const CullBinAttrib *ba = DCAST(CullBinAttrib, attrib);
03383     return ba->get_draw_order();
03384   }
03385 
03386   return 0;
03387 }
03388 
03389 ////////////////////////////////////////////////////////////////////
03390 //     Function: NodePath::set_texture
03391 //       Access: Published
03392 //  Description: Adds the indicated texture to the list of textures
03393 //               that will be rendered on the default texture stage.
03394 //
03395 //               This is the convenience single-texture variant of
03396 //               this method; it is now superceded by set_texture()
03397 //               that accepts a stage and texture.  You may use this
03398 //               method if you just want to adjust the default stage.
03399 ////////////////////////////////////////////////////////////////////
03400 void NodePath::
03401 set_texture(Texture *tex, int priority) {
03402   nassertv_always(!is_empty());
03403   PT(TextureStage) stage = TextureStage::get_default();
03404   set_texture(stage, tex, priority);
03405 }
03406 
03407 ////////////////////////////////////////////////////////////////////
03408 //     Function: NodePath::set_texture
03409 //       Access: Published
03410 //  Description: Adds the indicated texture to the list of textures
03411 //               that will be rendered on the indicated multitexture
03412 //               stage.  If there are multiple texture stages
03413 //               specified (possibly on multiple different nodes at
03414 //               different levels), they will all be applied to
03415 //               geometry together, according to the stage
03416 //               specification set up in the TextureStage object.
03417 ////////////////////////////////////////////////////////////////////
03418 void NodePath::
03419 set_texture(TextureStage *stage, Texture *tex, int priority) {
03420   nassertv_always(!is_empty());
03421 
03422   const RenderAttrib *attrib =
03423     node()->get_attrib(TextureAttrib::get_class_slot());
03424   if (attrib != (const RenderAttrib *)NULL) {
03425     const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
03426     int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
03427 
03428     // Modify the existing TextureAttrib to add the indicated
03429     // texture.
03430     node()->set_attrib(tsa->add_on_stage(stage, tex, priority), sg_priority);
03431 
03432   } else {
03433     // Create a new TextureAttrib for this node.
03434     CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
03435     node()->set_attrib(tsa->add_on_stage(stage, tex, priority));
03436   }
03437 }
03438 
03439 ////////////////////////////////////////////////////////////////////
03440 //     Function: NodePath::set_texture_off
03441 //       Access: Published
03442 //  Description: Sets the geometry at this level and below to render
03443 //               using no texture, on any stage.  This is different
03444 //               from not specifying a texture; rather, this
03445 //               specifically contradicts set_texture() at a higher
03446 //               node level (or, with a priority, overrides a
03447 //               set_texture() at a lower level).
03448 ////////////////////////////////////////////////////////////////////
03449 void NodePath::
03450 set_texture_off(int priority) {
03451   nassertv_always(!is_empty());
03452   node()->set_attrib(TextureAttrib::make_all_off(), priority);
03453 }
03454 
03455 ////////////////////////////////////////////////////////////////////
03456 //     Function: NodePath::set_texture_off
03457 //       Access: Published
03458 //  Description: Sets the geometry at this level and below to render
03459 //               using no texture, on the indicated stage.  This is
03460 //               different from not specifying a texture; rather, this
03461 //               specifically contradicts set_texture() at a higher
03462 //               node level (or, with a priority, overrides a
03463 //               set_texture() at a lower level).
03464 ////////////////////////////////////////////////////////////////////
03465 void NodePath::
03466 set_texture_off(TextureStage *stage, int priority) {
03467   nassertv_always(!is_empty());
03468 
03469   const RenderAttrib *attrib =
03470     node()->get_attrib(TextureAttrib::get_class_slot());
03471   if (attrib != (const RenderAttrib *)NULL) {
03472     const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
03473     int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
03474 
03475     // Modify the existing TextureAttrib to add the indicated texture
03476     // to the "off" list.  This also, incidentally, removes it from
03477     // the "on" list if it is there.
03478     node()->set_attrib(tsa->add_off_stage(stage, priority), sg_priority);
03479 
03480   } else {
03481     // Create a new TextureAttrib for this node that turns off the
03482     // indicated stage.
03483     CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
03484     node()->set_attrib(tsa->add_off_stage(stage, priority));
03485   }
03486 }
03487 
03488 ////////////////////////////////////////////////////////////////////
03489 //     Function: NodePath::clear_texture
03490 //       Access: Published
03491 //  Description: Completely removes any texture adjustment that may
03492 //               have been set via set_texture() or set_texture_off()
03493 //               from this particular node.  This allows whatever
03494 //               textures might be otherwise affecting the geometry to
03495 //               show instead.
03496 ////////////////////////////////////////////////////////////////////
03497 void NodePath::
03498 clear_texture() {
03499   nassertv_always(!is_empty());
03500   node()->clear_attrib(TextureAttrib::get_class_slot());
03501 }
03502 
03503 ////////////////////////////////////////////////////////////////////
03504 //     Function: NodePath::clear_texture
03505 //       Access: Published
03506 //  Description: Removes any reference to the indicated texture stage
03507 //               from the NodePath.
03508 ////////////////////////////////////////////////////////////////////
03509 void NodePath::
03510 clear_texture(TextureStage *stage) {
03511   nassertv_always(!is_empty());
03512 
03513   const RenderAttrib *attrib =
03514     node()->get_attrib(TextureAttrib::get_class_slot());
03515   if (attrib != (const RenderAttrib *)NULL) {
03516     CPT(TextureAttrib) tsa = DCAST(TextureAttrib, attrib);
03517     tsa = DCAST(TextureAttrib, tsa->remove_on_stage(stage));
03518     tsa = DCAST(TextureAttrib, tsa->remove_off_stage(stage));
03519 
03520     if (tsa->is_identity()) {
03521       node()->clear_attrib(TextureAttrib::get_class_slot());
03522 
03523     } else {
03524       int priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
03525       node()->set_attrib(tsa, priority);
03526     }
03527   }
03528 }
03529 
03530 ////////////////////////////////////////////////////////////////////
03531 //     Function: NodePath::has_texture
03532 //       Access: Published
03533 //  Description: Returns true if a texture has been applied to this
03534 //               particular node via set_texture(), false otherwise.
03535 //               This is not the same thing as asking whether the
03536 //               geometry at this node will be rendered with
03537 //               texturing, as there may be a texture in effect from a
03538 //               higher or lower level.
03539 ////////////////////////////////////////////////////////////////////
03540 bool NodePath::
03541 has_texture() const {
03542   return get_texture() != (Texture *)NULL;
03543 }
03544 
03545 ////////////////////////////////////////////////////////////////////
03546 //     Function: NodePath::has_texture
03547 //       Access: Published
03548 //  Description: Returns true if texturing has been specifically
03549 //               enabled on this particular node for the indicated
03550 //               stage.  This means that someone called
03551 //               set_texture() on this node with the indicated stage
03552 //               name, or the stage_name is the default stage_name,
03553 //               and someone called set_texture() on this node.
03554 ////////////////////////////////////////////////////////////////////
03555 bool NodePath::
03556 has_texture(TextureStage *stage) const {
03557   nassertr_always(!is_empty(), false);
03558 
03559   const RenderAttrib *attrib =
03560     node()->get_attrib(TextureAttrib::get_class_slot());
03561   if (attrib != (const RenderAttrib *)NULL) {
03562     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
03563     return ta->has_on_stage(stage);
03564   }
03565 
03566   return false;
03567 }
03568 
03569 ////////////////////////////////////////////////////////////////////
03570 //     Function: NodePath::has_texture_off
03571 //       Access: Published
03572 //  Description: Returns true if texturing has been specifically
03573 //               disabled on this particular node via
03574 //               set_texture_off(), false otherwise.  This is not the
03575 //               same thing as asking whether the geometry at this
03576 //               node will be rendered untextured, as there may be a
03577 //               texture in effect from a higher or lower level.
03578 ////////////////////////////////////////////////////////////////////
03579 bool NodePath::
03580 has_texture_off() const {
03581   nassertr_always(!is_empty(), false);
03582   const RenderAttrib *attrib =
03583     node()->get_attrib(TextureAttrib::get_class_slot());
03584   if (attrib != (const RenderAttrib *)NULL) {
03585     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
03586     return ta->has_all_off();
03587   }
03588 
03589   return false;
03590 }
03591 
03592 ////////////////////////////////////////////////////////////////////
03593 //     Function: NodePath::has_texture_off
03594 //       Access: Published
03595 //  Description: Returns true if texturing has been specifically
03596 //               disabled on this particular node for the indicated
03597 //               stage.  This means that someone called
03598 //               set_texture_off() on this node with the indicated
03599 //               stage name, or that someone called set_texture_off()
03600 //               on this node to remove all stages.
03601 ////////////////////////////////////////////////////////////////////
03602 bool NodePath::
03603 has_texture_off(TextureStage *stage) const {
03604   nassertr_always(!is_empty(), false);
03605 
03606   const RenderAttrib *attrib =
03607     node()->get_attrib(TextureAttrib::get_class_slot());
03608   if (attrib != (const RenderAttrib *)NULL) {
03609     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
03610     return ta->has_off_stage(stage);
03611   }
03612 
03613   return false;
03614 }
03615 
03616 ////////////////////////////////////////////////////////////////////
03617 //     Function: NodePath::get_texture
03618 //       Access: Published
03619 //  Description: Returns the base-level texture that has been set on
03620 //               this particular node, or NULL if no texture has been
03621 //               set.  This is not necessarily the texture that will
03622 //               be applied to the geometry at or below this level, as
03623 //               another texture at a higher or lower level may
03624 //               override.
03625 //
03626 //               See also find_texture().
03627 ////////////////////////////////////////////////////////////////////
03628 Texture *NodePath::
03629 get_texture() const {
03630   nassertr_always(!is_empty(), NULL);
03631   const RenderAttrib *attrib =
03632     node()->get_attrib(TextureAttrib::get_class_slot());
03633   if (attrib != (const RenderAttrib *)NULL) {
03634     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
03635     return ta->get_texture();
03636   }
03637 
03638   return NULL;
03639 }
03640 
03641 ////////////////////////////////////////////////////////////////////
03642 //     Function: NodePath::get_texture
03643 //       Access: Published
03644 //  Description: Returns the texture that has been set on the
03645 //               indicated stage for this particular node, or NULL if
03646 //               no texture has been set for this stage.
03647 ////////////////////////////////////////////////////////////////////
03648 Texture *NodePath::
03649 get_texture(TextureStage *stage) const {
03650   nassertr_always(!is_empty(), NULL);
03651   const RenderAttrib *attrib =
03652     node()->get_attrib(TextureAttrib::get_class_slot());
03653   if (attrib != (const RenderAttrib *)NULL) {
03654     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
03655     return ta->get_on_texture(stage);
03656   }
03657 
03658   return NULL;
03659 }
03660 
03661 ////////////////////////////////////////////////////////////////////
03662 //     Function: NodePath::set_shader
03663 //       Access: Published
03664 //  Description: 
03665 ////////////////////////////////////////////////////////////////////
03666 void NodePath::
03667 set_shader(const Shader *sha, int priority) {
03668   nassertv_always(!is_empty());
03669 
03670   const RenderAttrib *attrib =
03671     node()->get_attrib(ShaderAttrib::get_class_slot());
03672   if (attrib != (const RenderAttrib *)NULL) {
03673     priority = max(priority,
03674                    node()->get_state()->get_override(ShaderAttrib::get_class_slot()));
03675     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03676     node()->set_attrib(sa->set_shader(sha, priority));
03677   } else {
03678     // Create a new ShaderAttrib for this node.
03679     CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
03680     node()->set_attrib(sa->set_shader(sha, priority));
03681   }
03682 }
03683 
03684 ////////////////////////////////////////////////////////////////////
03685 //     Function: NodePath::set_shader_off
03686 //       Access: Published
03687 //  Description: 
03688 ////////////////////////////////////////////////////////////////////
03689 void NodePath::
03690 set_shader_off(int priority) {
03691   set_shader(NULL, priority);
03692 }
03693 
03694 ////////////////////////////////////////////////////////////////////
03695 //     Function: NodePath::set_shader_auto
03696 //       Access: Published
03697 //  Description: 
03698 ////////////////////////////////////////////////////////////////////
03699 void NodePath::
03700 set_shader_auto(int priority) {
03701   nassertv_always(!is_empty());
03702 
03703   const RenderAttrib *attrib =
03704     node()->get_attrib(ShaderAttrib::get_class_slot());
03705   if (attrib != (const RenderAttrib *)NULL) {
03706     priority = max(priority,
03707                    node()->get_state()->get_override(ShaderAttrib::get_class_slot()));
03708     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03709     node()->set_attrib(sa->set_shader_auto(priority));
03710   } else {
03711     // Create a new ShaderAttrib for this node.
03712     CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
03713     node()->set_attrib(sa->set_shader_auto(priority));
03714   }
03715 }
03716 
03717 ////////////////////////////////////////////////////////////////////
03718 //     Function: NodePath::clear_shader
03719 //       Access: Published
03720 //  Description: 
03721 ////////////////////////////////////////////////////////////////////
03722 void NodePath::
03723 clear_shader() {
03724   nassertv_always(!is_empty());
03725 
03726   const RenderAttrib *attrib =
03727     node()->get_attrib(ShaderAttrib::get_class_slot());
03728   if (attrib != (const RenderAttrib *)NULL) {
03729     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03730     node()->set_attrib(sa->clear_shader());
03731   }
03732 }
03733 
03734 ////////////////////////////////////////////////////////////////////
03735 //     Function: NodePath::get_shader
03736 //       Access: Published
03737 //  Description: 
03738 ////////////////////////////////////////////////////////////////////
03739 const Shader *NodePath::
03740 get_shader() const {
03741   nassertr_always(!is_empty(), NULL);
03742   const RenderAttrib *attrib =
03743     node()->get_attrib(ShaderAttrib::get_class_slot());
03744   if (attrib != (const RenderAttrib *)NULL) {
03745     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03746     return sa->get_shader();
03747   }
03748   return NULL;
03749 }
03750 
03751 ////////////////////////////////////////////////////////////////////
03752 //     Function: NodePath::set_shader_input
03753 //       Access: Published
03754 //  Description: 
03755 ////////////////////////////////////////////////////////////////////
03756 void NodePath::
03757 set_shader_input(const ShaderInput *inp) {
03758   nassertv_always(!is_empty());
03759 
03760   const RenderAttrib *attrib =
03761     node()->get_attrib(ShaderAttrib::get_class_slot());
03762   if (attrib != (const RenderAttrib *)NULL) {
03763     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03764     node()->set_attrib(sa->set_shader_input(inp));
03765   } else {
03766     // Create a new ShaderAttrib for this node.
03767     CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
03768     node()->set_attrib(sa->set_shader_input(inp));
03769   }
03770 }
03771 
03772 ////////////////////////////////////////////////////////////////////
03773 //     Function: NodePath::get_shader_input
03774 //       Access: Published
03775 //  Description: 
03776 ////////////////////////////////////////////////////////////////////
03777 const ShaderInput *NodePath::
03778 get_shader_input(InternalName *id) const {
03779   nassertr_always(!is_empty(), NULL);
03780   
03781   const RenderAttrib *attrib =
03782     node()->get_attrib(ShaderAttrib::get_class_slot());
03783   if (attrib != (const RenderAttrib *)NULL) {
03784     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03785     return sa->get_shader_input(id);
03786   }
03787   return NULL;
03788 }
03789 
03790 ////////////////////////////////////////////////////////////////////
03791 //     Function: NodePath::get_instance_count
03792 //       Access: Published
03793 //  Description: Returns the geometry instance count, or 0 if
03794 //               disabled. See set_instance_count.
03795 ////////////////////////////////////////////////////////////////////
03796 const int NodePath::
03797 get_instance_count() const {
03798   nassertr_always(!is_empty(), NULL);
03799   const RenderAttrib *attrib =
03800     node()->get_attrib(ShaderAttrib::get_class_slot());
03801   if (attrib != (const RenderAttrib *)NULL) {
03802     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03803     return sa->get_instance_count();
03804   }
03805   return 0;
03806 }
03807 
03808 ////////////////////////////////////////////////////////////////////
03809 //     Function: NodePath::clear_shader_input
03810 //       Access: Published
03811 //  Description: 
03812 ////////////////////////////////////////////////////////////////////
03813 void NodePath::
03814 clear_shader_input(InternalName *id) {
03815   nassertv_always(!is_empty());
03816 
03817   const RenderAttrib *attrib =
03818     node()->get_attrib(ShaderAttrib::get_class_slot());
03819   if (attrib != (const RenderAttrib *)NULL) {
03820     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03821     node()->set_attrib(sa->clear_shader_input(id));
03822   }
03823 }
03824 
03825 ////////////////////////////////////////////////////////////////////
03826 //     Function: NodePath::set_shader_input
03827 //       Access: Published
03828 //  Description: 
03829 ////////////////////////////////////////////////////////////////////
03830 void NodePath::
03831 set_shader_input(InternalName *id, Texture *tex, int priority) {
03832   set_shader_input(new ShaderInput(id,tex,priority));
03833 }
03834 
03835 ////////////////////////////////////////////////////////////////////
03836 //     Function: NodePath::set_shader_input
03837 //       Access: Published
03838 //  Description: 
03839 ////////////////////////////////////////////////////////////////////
03840 void NodePath::
03841 set_shader_input(InternalName *id, const NodePath &np, int priority) {
03842   set_shader_input(new ShaderInput(id,np,priority));
03843 }
03844 
03845 ////////////////////////////////////////////////////////////////////
03846 //     Function: NodePath::set_shader_input
03847 //       Access: Published
03848 //  Description: 
03849 ////////////////////////////////////////////////////////////////////
03850 void NodePath::
03851 set_shader_input(InternalName *id, const LVector4f &v, int priority) {
03852   set_shader_input(new ShaderInput(id,v,priority));
03853 }
03854 
03855 ////////////////////////////////////////////////////////////////////
03856 //     Function: NodePath::set_shader_input
03857 //       Access: Published
03858 //  Description: 
03859 ////////////////////////////////////////////////////////////////////
03860 void NodePath::
03861 set_shader_input(InternalName *id, double n1, double n2, double n3, double n4, int priority) {
03862   set_shader_input(new ShaderInput(id,LVector4f(n1,n2,n3,n4),priority));
03863 }
03864 
03865 ////////////////////////////////////////////////////////////////////
03866 //     Function: NodePath::set_shader_input
03867 //       Access: Published
03868 //  Description: 
03869 ////////////////////////////////////////////////////////////////////
03870 void NodePath::
03871 set_shader_input(const string &id, Texture *tex, int priority) {
03872   set_shader_input(new ShaderInput(InternalName::make(id),tex,priority));
03873 }
03874 
03875 ////////////////////////////////////////////////////////////////////
03876 //     Function: NodePath::set_shader_input
03877 //       Access: Published
03878 //  Description: 
03879 ////////////////////////////////////////////////////////////////////
03880 void NodePath::
03881 set_shader_input(const string &id, const NodePath &np, int priority) {
03882   set_shader_input(new ShaderInput(InternalName::make(id),np,priority));
03883 }
03884 
03885 ////////////////////////////////////////////////////////////////////
03886 //     Function: NodePath::set_shader_input
03887 //       Access: Published
03888 //  Description: 
03889 ////////////////////////////////////////////////////////////////////
03890 void NodePath::
03891 set_shader_input(const string &id, const LVector4f &v, int priority) {
03892   set_shader_input(new ShaderInput(InternalName::make(id),v,priority));
03893 }
03894 
03895 ////////////////////////////////////////////////////////////////////
03896 //     Function: NodePath::set_shader_input
03897 //       Access: Published
03898 //  Description: 
03899 ////////////////////////////////////////////////////////////////////
03900 void NodePath::
03901 set_shader_input(const string &id, double n1, double n2, double n3, double n4, int priority) {
03902   set_shader_input(new ShaderInput(InternalName::make(id),LVector4f(n1,n2,n3,n4),priority));
03903 }
03904 
03905 ////////////////////////////////////////////////////////////////////
03906 //     Function: NodePath::get_shader_input
03907 //       Access: Published
03908 //  Description: 
03909 ////////////////////////////////////////////////////////////////////
03910 const ShaderInput *NodePath::
03911 get_shader_input(const string &id) const {
03912   return get_shader_input(InternalName::make(id));
03913 }
03914 
03915 ////////////////////////////////////////////////////////////////////
03916 //     Function: NodePath::clear_shader_input
03917 //       Access: Published
03918 //  Description: 
03919 ////////////////////////////////////////////////////////////////////
03920 void NodePath::
03921 clear_shader_input(const string &id) {
03922   clear_shader_input(InternalName::make(id));
03923 }
03924 
03925 ////////////////////////////////////////////////////////////////////
03926 //     Function: NodePath::set_instance_count
03927 //       Access: Published
03928 //  Description: Sets the geometry instance count, or 0 if
03929 //               geometry instancing should be disabled. Do not
03930 //               confuse with instanceTo which only applies to
03931 //               animation instancing.
03932 ////////////////////////////////////////////////////////////////////
03933 void NodePath::
03934 set_instance_count(int instance_count) {
03935   nassertv_always(!is_empty());
03936 
03937   const RenderAttrib *attrib =
03938     node()->get_attrib(ShaderAttrib::get_class_slot());
03939   if (attrib != (const RenderAttrib *)NULL) {
03940     const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
03941     node()->set_attrib(sa->set_instance_count(instance_count));
03942   } else {
03943     // Create a new ShaderAttrib for this node.
03944     CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
03945     node()->set_attrib(sa->set_instance_count(instance_count));
03946   }
03947 }
03948 
03949 ////////////////////////////////////////////////////////////////////
03950 //     Function: NodePath::set_tex_transform
03951 //       Access: Published
03952 //  Description: Sets the texture matrix on the current node to the
03953 //               indicated transform for the given stage.
03954 ////////////////////////////////////////////////////////////////////
03955 void NodePath::
03956 set_tex_transform(TextureStage *stage, const TransformState *transform) {
03957   nassertv_always(!is_empty());
03958 
03959   const RenderAttrib *attrib =
03960     node()->get_attrib(TexMatrixAttrib::get_class_slot());
03961   if (attrib != (const RenderAttrib *)NULL) {
03962     const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
03963 
03964     // Modify the existing TexMatrixAttrib to add the indicated
03965     // stage.
03966     node()->set_attrib(tma->add_stage(stage, transform));
03967 
03968   } else {
03969     // Create a new TexMatrixAttrib for this node.
03970     node()->set_attrib(TexMatrixAttrib::make(stage, transform));
03971   }
03972 }
03973 
03974 ////////////////////////////////////////////////////////////////////
03975 //     Function: NodePath::clear_tex_transform
03976 //       Access: Published
03977 //  Description: Removes all texture matrices from the current node.
03978 ////////////////////////////////////////////////////////////////////
03979 void NodePath::
03980 clear_tex_transform() {
03981   nassertv_always(!is_empty());
03982   node()->clear_attrib(TexMatrixAttrib::get_class_slot());
03983 }
03984 
03985 ////////////////////////////////////////////////////////////////////
03986 //     Function: NodePath::clear_tex_transform
03987 //       Access: Published
03988 //  Description: Removes the texture matrix on the current node for
03989 //               the given stage.
03990 ////////////////////////////////////////////////////////////////////
03991 void NodePath::
03992 clear_tex_transform(TextureStage *stage) {
03993   nassertv_always(!is_empty());
03994 
03995   const RenderAttrib *attrib =
03996     node()->get_attrib(TexMatrixAttrib::get_class_slot());
03997   if (attrib != (const RenderAttrib *)NULL) {
03998     CPT(TexMatrixAttrib) tma = DCAST(TexMatrixAttrib, attrib);
03999     tma = DCAST(TexMatrixAttrib, tma->remove_stage(stage));
04000 
04001     if (tma->is_empty()) {
04002       node()->clear_attrib(TexMatrixAttrib::get_class_slot());
04003 
04004     } else {
04005       node()->set_attrib(tma);
04006     }
04007   }
04008 }
04009 
04010 ////////////////////////////////////////////////////////////////////
04011 //     Function: NodePath::has_tex_transform
04012 //       Access: Published
04013 //  Description: Returns true if there is an explicit texture matrix
04014 //               on the current node for the given stage.
04015 ////////////////////////////////////////////////////////////////////
04016 bool NodePath::
04017 has_tex_transform(TextureStage *stage) const {
04018   nassertr_always(!is_empty(), false);
04019 
04020   const RenderAttrib *attrib =
04021     node()->get_attrib(TexMatrixAttrib::get_class_slot());
04022   if (attrib != (const RenderAttrib *)NULL) {
04023     const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
04024     return tma->has_stage(stage);
04025   }
04026 
04027   return false;
04028 }
04029 
04030 ////////////////////////////////////////////////////////////////////
04031 //     Function: NodePath::get_tex_transform
04032 //       Access: Published
04033 //  Description: Returns the texture matrix on the current node for the
04034 //               given stage, or identity transform if there is no
04035 //               explicit transform set for the given stage.
04036 ////////////////////////////////////////////////////////////////////
04037 CPT(TransformState) NodePath::
04038 get_tex_transform(TextureStage *stage) const {
04039   nassertr_always(!is_empty(), NULL);
04040 
04041   const RenderAttrib *attrib =
04042     node()->get_attrib(TexMatrixAttrib::get_class_slot());
04043   if (attrib != (const RenderAttrib *)NULL) {
04044     const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
04045     return tma->get_transform(stage);
04046   }
04047 
04048   return TransformState::make_identity();
04049 }
04050 
04051 ////////////////////////////////////////////////////////////////////
04052 //     Function: NodePath::set_tex_transform
04053 //       Access: Published
04054 //  Description: Sets the texture matrix on the current node to the
04055 //               indicated transform for the given stage.
04056 ////////////////////////////////////////////////////////////////////
04057 void NodePath::
04058 set_tex_transform(const NodePath &other, TextureStage *stage, const TransformState *transform) {
04059   nassertv(_error_type == ET_ok && other._error_type == ET_ok);
04060   nassertv_always(!is_empty());
04061 
04062   CPT(RenderState) state = get_state(other);
04063   const RenderAttrib *attrib =
04064     state->get_attrib(TexMatrixAttrib::get_class_slot());
04065   if (attrib != (const RenderAttrib *)NULL) {
04066     const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
04067 
04068     // Modify the existing TexMatrixAttrib to add the indicated
04069     // stage.
04070     state = state->add_attrib(tma->add_stage(stage, transform));
04071 
04072   } else {
04073     // Create a new TexMatrixAttrib for this node.
04074     state = state->add_attrib(TexMatrixAttrib::make(stage, transform));
04075   }
04076 
04077   // Now compose that with our parent's state.
04078   CPT(RenderState) rel_state;
04079   if (has_parent()) {
04080     rel_state = other.get_state(get_parent());
04081   } else {
04082     rel_state = other.get_state(NodePath());
04083   }
04084   CPT(RenderState) new_state = rel_state->compose(state);
04085 
04086   // And apply only the TexMatrixAttrib to the current node, leaving
04087   // the others unchanged.
04088   node()->set_attrib(new_state->get_attrib(TexMatrixAttrib::get_class_slot()));
04089 }
04090 
04091 ////////////////////////////////////////////////////////////////////
04092 //     Function: NodePath::get_tex_transform
04093 //       Access: Published
04094 //  Description: Returns the texture matrix on the current node for the
04095 //               given stage, relative to the other node.
04096 ////////////////////////////////////////////////////////////////////
04097 CPT(TransformState) NodePath::
04098 get_tex_transform(const NodePath &other, TextureStage *stage) const {
04099   nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity());
04100 
04101   CPT(RenderState) state = get_state(other);
04102   const RenderAttrib *attrib =
04103     state->get_attrib(TexMatrixAttrib::get_class_slot());
04104   if (attrib != (const RenderAttrib *)NULL) {
04105     const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
04106     return tma->get_transform(stage);
04107   }
04108 
04109   return TransformState::make_identity();
04110 }
04111 
04112 ////////////////////////////////////////////////////////////////////
04113 //     Function: NodePath::set_tex_gen
04114 //       Access: Published
04115 //  Description: Enables automatic texture coordinate generation for
04116 //               the indicated texture stage.
04117 ////////////////////////////////////////////////////////////////////
04118 void NodePath::
04119 set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, int priority) {
04120   nassertv_always(!is_empty());
04121 
04122   const RenderAttrib *attrib =
04123     node()->get_attrib(TexGenAttrib::get_class_slot());
04124 
04125   CPT(TexGenAttrib) tga;
04126 
04127   if (attrib != (const RenderAttrib *)NULL) {
04128     priority = max(priority,
04129                    node()->get_state()->get_override(TextureAttrib::get_class_slot()));
04130     tga = DCAST(TexGenAttrib, attrib);
04131 
04132   } else {
04133     tga = DCAST(TexGenAttrib, TexGenAttrib::make());
04134   }
04135 
04136   node()->set_attrib(tga->add_stage(stage, mode), priority);
04137 }
04138 
04139 ////////////////////////////////////////////////////////////////////
04140 //     Function: NodePath::set_tex_gen
04141 //       Access: Published
04142 //  Description: Enables automatic texture coordinate generation for
04143 //               the indicated texture stage.  This version of this
04144 //               method is useful when setting M_light_vector, which
04145 //               requires the name of the texture coordinate set that
04146 //               supplies the tangent and binormal, as well as the
04147 //               specific light to generate coordinates for.
04148 ////////////////////////////////////////////////////////////////////
04149 void NodePath::
04150 set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, 
04151             const string &source_name, const NodePath &light, int priority) {
04152   nassertv_always(!is_empty());
04153 
04154   const RenderAttrib *attrib =
04155     node()->get_attrib(TexGenAttrib::get_class_slot());
04156 
04157   CPT(TexGenAttrib) tga;
04158 
04159   if (attrib != (const RenderAttrib *)NULL) {
04160     priority = max(priority,
04161                    node()->get_state()->get_override(TextureAttrib::get_class_slot()));
04162     tga = DCAST(TexGenAttrib, attrib);
04163 
04164   } else {
04165     tga = DCAST(TexGenAttrib, TexGenAttrib::make());
04166   }
04167 
04168   node()->set_attrib(tga->add_stage(stage, mode, source_name, light), priority);
04169 }
04170 
04171 ////////////////////////////////////////////////////////////////////
04172 //     Function: NodePath::set_tex_gen
04173 //       Access: Published
04174 //  Description: Enables automatic texture coordinate generation for
04175 //               the indicated texture stage.  This version of this
04176 //               method is useful when setting M_constant, which
04177 //               requires a constant texture coordinate value.
04178 ////////////////////////////////////////////////////////////////////
04179 void NodePath::
04180 set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, 
04181             const TexCoord3f &constant_value, int priority) {
04182   nassertv_always(!is_empty());
04183 
04184   const RenderAttrib *attrib =
04185     node()->get_attrib(TexGenAttrib::get_class_slot());
04186 
04187   CPT(TexGenAttrib) tga;
04188 
04189   if (attrib != (const RenderAttrib *)NULL) {
04190     priority = max(priority,
04191                    node()->get_state()->get_override(TextureAttrib::get_class_slot()));
04192     tga = DCAST(TexGenAttrib, attrib);
04193 
04194   } else {
04195     tga = DCAST(TexGenAttrib, TexGenAttrib::make());
04196   }
04197 
04198   node()->set_attrib(tga->add_stage(stage, mode, constant_value), priority);
04199 }
04200 
04201 ////////////////////////////////////////////////////////////////////
04202 //     Function: NodePath::clear_tex_gen
04203 //       Access: Published
04204 //  Description: Removes the texture coordinate generation mode from
04205 //               all texture stages on this node.
04206 ////////////////////////////////////////////////////////////////////
04207 void NodePath::
04208 clear_tex_gen() {
04209   nassertv_always(!is_empty());
04210   node()->clear_attrib(TexGenAttrib::get_class_slot());
04211 }
04212 
04213 ////////////////////////////////////////////////////////////////////
04214 //     Function: NodePath::clear_tex_gen
04215 //       Access: Published
04216 //  Description: Disables automatic texture coordinate generation for
04217 //               the indicated texture stage.
04218 ////////////////////////////////////////////////////////////////////
04219 void NodePath::
04220 clear_tex_gen(TextureStage *stage) {
04221   nassertv_always(!is_empty());
04222 
04223   const RenderAttrib *attrib =
04224     node()->get_attrib(TexGenAttrib::get_class_slot());
04225   if (attrib != (const RenderAttrib *)NULL) {
04226     CPT(TexGenAttrib) tga = DCAST(TexGenAttrib, attrib);
04227     tga = DCAST(TexGenAttrib, tga->remove_stage(stage));
04228 
04229     if (tga->is_empty()) {
04230       node()->clear_attrib(TexGenAttrib::get_class_slot());
04231 
04232     } else {
04233       node()->set_attrib(tga);
04234     }
04235   }
04236 }
04237 
04238 ////////////////////////////////////////////////////////////////////
04239 //     Function: NodePath::has_tex_gen
04240 //       Access: Published
04241 //  Description: Returns true if there is a mode for automatic texture
04242 //               coordinate generation on the current node for the
04243 //               given stage.
04244 ////////////////////////////////////////////////////////////////////
04245 bool NodePath::
04246 has_tex_gen(TextureStage *stage) const {
04247   nassertr_always(!is_empty(), false);
04248 
04249   const RenderAttrib *attrib =
04250     node()->get_attrib(TexGenAttrib::get_class_slot());
04251   if (attrib != (const RenderAttrib *)NULL) {
04252     const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib);
04253     return tga->has_stage(stage);
04254   }
04255 
04256   return false;
04257 }
04258 
04259 ////////////////////////////////////////////////////////////////////
04260 //     Function: NodePath::get_tex_gen
04261 //       Access: Published
04262 //  Description: Returns the texture coordinate generation mode for
04263 //               the given stage, or M_off if there is no explicit
04264 //               mode set for the given stage.
04265 ////////////////////////////////////////////////////////////////////
04266 RenderAttrib::TexGenMode NodePath::
04267 get_tex_gen(TextureStage *stage) const {
04268   nassertr_always(!is_empty(), TexGenAttrib::M_off);
04269 
04270   const RenderAttrib *attrib =
04271     node()->get_attrib(TexGenAttrib::get_class_slot());
04272   if (attrib != (const RenderAttrib *)NULL) {
04273     const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib);
04274     return tga->get_mode(stage);
04275   }
04276 
04277   return TexGenAttrib::M_off;
04278 }
04279 
04280 ////////////////////////////////////////////////////////////////////
04281 //     Function: NodePath::get_tex_gen_light
04282 //       Access: Published
04283 //  Description: Returns the particular Light set for the indicated
04284 //               texgen mode's texture stage, or empty NodePath if no
04285 //               light is set.  This is only meaningful if the texgen
04286 //               mode (returned by get_tex_gen()) is M_light_vector.
04287 ////////////////////////////////////////////////////////////////////
04288 NodePath NodePath::
04289 get_tex_gen_light(TextureStage *stage) const {
04290   nassertr_always(!is_empty(), NodePath::fail());
04291 
04292   const RenderAttrib *attrib =
04293     node()->get_attrib(TexGenAttrib::get_class_slot());
04294   if (attrib != (const RenderAttrib *)NULL) {
04295     const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib);
04296     return tga->get_light(stage);
04297   }
04298 
04299   return NodePath();
04300 }
04301 
04302 ////////////////////////////////////////////////////////////////////
04303 //     Function: NodePath::set_tex_projector
04304 //       Access: Published
04305 //  Description: Establishes a TexProjectorEffect on this node, which
04306 //               can be used to establish projective texturing (but
04307 //               see also the NodePath::project_texture() convenience
04308 //               function), or it can be used to bind this node's
04309 //               texture transform to particular node's position in
04310 //               space, allowing a LerpInterval (for instance) to
04311 //               adjust this node's texture coordinates.
04312 ////////////////////////////////////////////////////////////////////
04313 void NodePath::
04314 set_tex_projector(TextureStage *stage, const NodePath &from, const NodePath &to) {
04315   nassertv_always(!is_empty());
04316 
04317   const RenderEffect *effect =
04318     node()->get_effect(TexProjectorEffect::get_class_type());
04319 
04320   CPT(TexProjectorEffect) tpe;
04321 
04322   if (effect != (const RenderEffect *)NULL) {
04323     tpe = DCAST(TexProjectorEffect, effect);
04324 
04325   } else {
04326     tpe = DCAST(TexProjectorEffect, TexProjectorEffect::make());
04327   }
04328 
04329   node()->set_effect(tpe->add_stage(stage, from, to));
04330 }
04331 
04332 ////////////////////////////////////////////////////////////////////
04333 //     Function: NodePath::clear_tex_projector
04334 //       Access: Published
04335 //  Description: Removes the TexProjectorEffect for the indicated
04336 //               stage from this node.
04337 ////////////////////////////////////////////////////////////////////
04338 void NodePath::
04339 clear_tex_projector(TextureStage *stage) {
04340   nassertv_always(!is_empty());
04341 
04342   const RenderEffect *effect =
04343     node()->get_effect(TexProjectorEffect::get_class_type());
04344   if (effect != (const RenderEffect *)NULL) {
04345     CPT(TexProjectorEffect) tpe = DCAST(TexProjectorEffect, effect);
04346     tpe = DCAST(TexProjectorEffect, tpe->remove_stage(stage));
04347 
04348     if (tpe->is_empty()) {
04349       node()->clear_effect(TexProjectorEffect::get_class_type());
04350 
04351     } else {
04352       node()->set_effect(tpe);
04353     }
04354   }
04355 }
04356 
04357 ////////////////////////////////////////////////////////////////////
04358 //     Function: NodePath::clear_tex_projector
04359 //       Access: Published
04360 //  Description: Removes the TexProjectorEffect for all stages from
04361 //               this node.
04362 ////////////////////////////////////////////////////////////////////
04363 void NodePath::
04364 clear_tex_projector() {
04365   nassertv_always(!is_empty());
04366   node()->clear_effect(TexProjectorEffect::get_class_type());
04367 }
04368 
04369 ////////////////////////////////////////////////////////////////////
04370 //     Function: NodePath::has_tex_projector
04371 //       Access: Published
04372 //  Description: Returns true if this node has a TexProjectorEffect
04373 //               for the indicated stage, false otherwise.
04374 ////////////////////////////////////////////////////////////////////
04375 bool NodePath::
04376 has_tex_projector(TextureStage *stage) const {
04377   nassertr_always(!is_empty(), false);
04378 
04379   const RenderEffect *effect =
04380     node()->get_effect(TexProjectorEffect::get_class_type());
04381   if (effect != (const RenderEffect *)NULL) {
04382     const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect);
04383     return tpe->has_stage(stage);
04384   }
04385 
04386   return false;
04387 }
04388 
04389 ////////////////////////////////////////////////////////////////////
04390 //     Function: NodePath::get_tex_projector_from
04391 //       Access: Published
04392 //  Description: Returns the "from" node associated with the
04393 //               TexProjectorEffect on the indicated stage.  The
04394 //               relative transform between the "from" and the "to"
04395 //               nodes is automatically applied to the texture
04396 //               transform each frame.
04397 ////////////////////////////////////////////////////////////////////
04398 NodePath NodePath::
04399 get_tex_projector_from(TextureStage *stage) const {
04400   nassertr_always(!is_empty(), NodePath::fail());
04401 
04402   const RenderEffect *effect =
04403     node()->get_effect(TexProjectorEffect::get_class_type());
04404   if (effect != (const RenderEffect *)NULL) {
04405     const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect);
04406     return tpe->get_from(stage);
04407   }
04408 
04409   return NodePath::not_found();
04410 }
04411 
04412 ////////////////////////////////////////////////////////////////////
04413 //     Function: NodePath::get_tex_projector_to
04414 //       Access: Published
04415 //  Description: Returns the "to" node associated with the
04416 //               TexProjectorEffect on the indicated stage.  The
04417 //               relative transform between the "from" and the "to"
04418 //               nodes is automatically applied to the texture
04419 //               transform each frame.
04420 ////////////////////////////////////////////////////////////////////
04421 NodePath NodePath::
04422 get_tex_projector_to(TextureStage *stage) const {
04423   nassertr_always(!is_empty(), NodePath::fail());
04424 
04425   const RenderEffect *effect =
04426     node()->get_effect(TexProjectorEffect::get_class_type());
04427   if (effect != (const RenderEffect *)NULL) {
04428     const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect);
04429     return tpe->get_to(stage);
04430   }
04431 
04432   return NodePath::not_found();
04433 }
04434 
04435 ////////////////////////////////////////////////////////////////////
04436 //     Function: NodePath::project_texture
04437 //       Access: Published
04438 //  Description: A convenience function to enable projective texturing
04439 //               at this node level and below, using the indicated
04440 //               NodePath (which should contain a LensNode) as the
04441 //               projector.
04442 ////////////////////////////////////////////////////////////////////
04443 void NodePath::
04444 project_texture(TextureStage *stage, Texture *tex, const NodePath &projector) {
04445   nassertv(!projector.is_empty() && projector.node()->is_of_type(LensNode::get_class_type()));
04446   set_texture(stage, tex);
04447   set_tex_gen(stage, TexGenAttrib::M_world_position);
04448   set_tex_projector(stage, NodePath(), projector);
04449 }
04450 
04451 
04452 ////////////////////////////////////////////////////////////////////
04453 //     Function: NodePath::set_normal_map
04454 //       Access: Published
04455 //  Description: A convenience function to set up a normal map on this
04456 //               geometry.  This uses the single highest-priority
04457 //               light on the object only.  It also requires
04458 //               multitexture, and consumes at least two texture
04459 //               stages, in addition to what may already be in use.
04460 //
04461 //               The normal_map parameter is the texture that contains
04462 //               the normal map information (with a 3-d delta vector
04463 //               encoded into the r,g,b of each texel).  texcoord_name is
04464 //               the name of the texture coordinate set that contains
04465 //               the tangent and binormal we wish to use.  If
04466 //               preserve_color is true, then one additional texture
04467 //               stage is consumed to blend in the geometry's original
04468 //               vertex color.
04469 //
04470 //               Only one normal map may be in effect through this
04471 //               interface at any given time.
04472 ////////////////////////////////////////////////////////////////////
04473 void NodePath::
04474 set_normal_map(Texture *normal_map, const string &texcoord_name,
04475                bool preserve_color) {
04476   clear_normal_map();
04477 
04478   // First, we apply the normal map itself, to the bottom layer.
04479   PT(TextureStage) normal_map_ts = new TextureStage("__normal_map");
04480   normal_map_ts->set_texcoord_name(texcoord_name);
04481   normal_map_ts->set_sort(-20);
04482   normal_map_ts->set_mode(TextureStage::M_replace);
04483   set_texture(normal_map_ts, normal_map);
04484 
04485   // Then, we apply a normalization map, to normalize, per-pixel, the
04486   // vector to the light.
04487   PT(Texture) normalization_map = TexturePool::get_normalization_cube_map(32);
04488   PT(TextureStage) normalization_map_ts = new TextureStage("__normalization_map");
04489   normalization_map_ts->set_combine_rgb
04490     (TextureStage::CM_dot3_rgb, 
04491      TextureStage::CS_texture, TextureStage::CO_src_color,
04492      TextureStage::CS_previous, TextureStage::CO_src_color);
04493   normalization_map_ts->set_texcoord_name("light_vector");
04494   normalization_map_ts->set_sort(-15);
04495   set_texture(normalization_map_ts, normalization_map);
04496 
04497   // Finally, we enable M_light_vector texture coordinate generation.
04498   set_tex_gen(normalization_map_ts, TexGenAttrib::M_light_vector, 
04499               texcoord_name, NodePath());
04500 
04501   if (preserve_color) {
04502     // One more stage to get back the original color.
04503     PT(TextureStage) orig_color_ts = new TextureStage("__orig_color");
04504     orig_color_ts->set_combine_rgb
04505       (TextureStage::CM_modulate,
04506        TextureStage::CS_primary_color, TextureStage::CO_src_color,
04507        TextureStage::CS_previous, TextureStage::CO_src_color);
04508     set_texture(orig_color_ts, normal_map);
04509   }
04510 }
04511 
04512 ////////////////////////////////////////////////////////////////////
04513 //     Function: NodePath::clear_normal_map
04514 //       Access: Published
04515 //  Description: Undoes the effect of a previous call to
04516 //               set_normal_map().
04517 ////////////////////////////////////////////////////////////////////
04518 void NodePath::
04519 clear_normal_map() {
04520   // Scan through the TextureStages, and if we find any whose name
04521   // matches one of the stages that would have been left by
04522   // set_normal_map(), remove it from the state.
04523 
04524   CPT(RenderAttrib) attrib =
04525     get_state()->get_attrib(TextureAttrib::get_class_slot());
04526   if (attrib != (const RenderAttrib *)NULL) {
04527     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
04528     for (int i = 0; i < ta->get_num_on_stages(); i++) {
04529       TextureStage *stage = ta->get_on_stage(i);
04530       if (stage->get_name() == "__normal_map") {
04531         clear_texture(stage);
04532 
04533       } else if (stage->get_name() == "__normalization_map") {
04534         clear_texture(stage);
04535         clear_tex_gen(stage);
04536 
04537       } else if (stage->get_name() == "__orig_color") {
04538         clear_texture(stage);
04539       }
04540     }
04541   }
04542 }
04543 
04544 ////////////////////////////////////////////////////////////////////
04545 //     Function: NodePath::has_vertex_column
04546 //       Access: Published
04547 //  Description: Returns true if there are at least some vertices at
04548 //               this node and below that contain a reference to the
04549 //               indicated vertex data column name, false otherwise.
04550 //
04551 //               This is particularly useful for testing whether a
04552 //               particular model has a given texture coordinate set
04553 //               (but see has_texcoord()).
04554 ////////////////////////////////////////////////////////////////////
04555 bool NodePath::
04556 has_vertex_column(const InternalName *name) const {
04557   nassertr_always(!is_empty(), false);
04558   return r_has_vertex_column(node(), name);
04559 }
04560 
04561 ////////////////////////////////////////////////////////////////////
04562 //     Function: NodePath::find_all_vertex_columns
04563 //       Access: Published
04564 //  Description: Returns a list of all vertex array columns stored on
04565 //               some geometry found at this node level and below.
04566 ////////////////////////////////////////////////////////////////////
04567 InternalNameCollection NodePath::
04568 find_all_vertex_columns() const {
04569   nassertr_always(!is_empty(), InternalNameCollection());
04570   InternalNames vertex_columns;
04571   r_find_all_vertex_columns(node(), vertex_columns);
04572 
04573   InternalNameCollection tc;
04574   InternalNames::iterator ti;
04575   for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
04576     tc.add_name(*ti);
04577   }
04578   return tc;
04579 }
04580 
04581 ////////////////////////////////////////////////////////////////////
04582 //     Function: NodePath::find_all_vertex_columns
04583 //       Access: Published
04584 //  Description: Returns a list of all vertex array columns stored on
04585 //               some geometry found at this node level and below that
04586 //               match the indicated name (which may contain wildcard
04587 //               characters).
04588 ////////////////////////////////////////////////////////////////////
04589 InternalNameCollection NodePath::
04590 find_all_vertex_columns(const string &name) const {
04591   nassertr_always(!is_empty(), InternalNameCollection());
04592   InternalNames vertex_columns;
04593   r_find_all_vertex_columns(node(), vertex_columns);
04594 
04595   GlobPattern glob(name);
04596 
04597   InternalNameCollection tc;
04598   InternalNames::iterator ti;
04599   for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
04600     InternalName *name = (*ti);
04601     if (glob.matches(name->get_name())) {
04602       tc.add_name(name);
04603     }
04604   }
04605   return tc;
04606 }
04607 
04608 ////////////////////////////////////////////////////////////////////
04609 //     Function: NodePath::find_all_texcoords
04610 //       Access: Published
04611 //  Description: Returns a list of all texture coordinate sets used by
04612 //               any geometry at this node level and below.
04613 ////////////////////////////////////////////////////////////////////
04614 InternalNameCollection NodePath::
04615 find_all_texcoords() const {
04616   nassertr_always(!is_empty(), InternalNameCollection());
04617   InternalNames vertex_columns;
04618   r_find_all_vertex_columns(node(), vertex_columns);
04619 
04620   CPT(InternalName) texcoord_name = InternalName::get_texcoord();
04621   
04622   InternalNameCollection tc;
04623   InternalNames::iterator ti;
04624   for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
04625     if ((*ti)->get_top() == texcoord_name) {
04626       tc.add_name(*ti);
04627     }
04628   }
04629   return tc;
04630 }
04631 
04632 ////////////////////////////////////////////////////////////////////
04633 //     Function: NodePath::find_all_texcoords
04634 //       Access: Published
04635 //  Description: Returns a list of all texture coordinate sets used by
04636 //               any geometry at this node level and below that match
04637 //               the indicated name (which may contain wildcard
04638 //               characters).
04639 ////////////////////////////////////////////////////////////////////
04640 InternalNameCollection NodePath::
04641 find_all_texcoords(const string &name) const {
04642   nassertr_always(!is_empty(), InternalNameCollection());
04643   InternalNames vertex_columns;
04644   r_find_all_vertex_columns(node(), vertex_columns);
04645 
04646   GlobPattern glob(name);
04647   CPT(InternalName) texcoord_name = InternalName::get_texcoord();
04648 
04649   InternalNameCollection tc;
04650   InternalNames::iterator ti;
04651   for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
04652     InternalName *name = (*ti);
04653     if (name->get_top() == texcoord_name) {
04654       // This is a texture coordinate name.  Figure out the basename
04655       // of the texture coordinates.
04656       int index = name->find_ancestor("texcoord");
04657       nassertr(index != -1, InternalNameCollection());
04658       string net_basename = name->get_net_basename(index - 1);
04659 
04660       if (glob.matches(net_basename)) {
04661         tc.add_name(name);
04662       }
04663     }
04664   }
04665   return tc;
04666 }
04667 
04668 ////////////////////////////////////////////////////////////////////
04669 //     Function: NodePath::find_texture
04670 //       Access: Published
04671 //  Description: Returns the first texture found applied to geometry
04672 //               at this node or below that matches the indicated name
04673 //               (which may contain wildcards).  Returns the texture
04674 //               if it is found, or NULL if it is not.
04675 ////////////////////////////////////////////////////////////////////
04676 Texture *NodePath::
04677 find_texture(const string &name) const {
04678   nassertr_always(!is_empty(), NULL);
04679   GlobPattern glob(name);
04680   return r_find_texture(node(), get_net_state(), glob);
04681 }
04682 
04683 ////////////////////////////////////////////////////////////////////
04684 //     Function: NodePath::find_texture
04685 //       Access: Published
04686 //  Description: Returns the first texture found applied to geometry
04687 //               at this node or below that is assigned to the
04688 //               indicated texture stage.  Returns the texture if it
04689 //               is found, or NULL if it is not.
04690 ////////////////////////////////////////////////////////////////////
04691 Texture *NodePath::
04692 find_texture(TextureStage *stage) const {
04693   nassertr_always(!is_empty(), NULL);
04694   return r_find_texture(node(), stage);
04695 }
04696 
04697 ////////////////////////////////////////////////////////////////////
04698 //     Function: NodePath::find_all_textures
04699 //       Access: Published
04700 //  Description: Returns a list of a textures applied to geometry at
04701 //               this node and below.
04702 ////////////////////////////////////////////////////////////////////
04703 TextureCollection NodePath::
04704 find_all_textures() const {
04705   nassertr_always(!is_empty(), TextureCollection());
04706   Textures textures;
04707   r_find_all_textures(node(), get_net_state(), textures);
04708 
04709   TextureCollection tc;
04710   Textures::iterator ti;
04711   for (ti = textures.begin(); ti != textures.end(); ++ti) {
04712     tc.add_texture(*ti);
04713   }
04714   return tc;
04715 }
04716 
04717 ////////////////////////////////////////////////////////////////////
04718 //     Function: NodePath::find_all_textures
04719 //       Access: Published
04720 //  Description: Returns a list of a textures applied to geometry at
04721 //               this node and below that match the indicated name
04722 //               (which may contain wildcard characters).
04723 ////////////////////////////////////////////////////////////////////
04724 TextureCollection NodePath::
04725 find_all_textures(const string &name) const {
04726   nassertr_always(!is_empty(), TextureCollection());
04727   Textures textures;
04728   r_find_all_textures(node(), get_net_state(), textures);
04729 
04730   GlobPattern glob(name);
04731 
04732   TextureCollection tc;
04733   Textures::iterator ti;
04734   for (ti = textures.begin(); ti != textures.end(); ++ti) {
04735     Texture *texture = (*ti);
04736     if (glob.matches(texture->get_name())) {
04737       tc.add_texture(texture);
04738     }
04739   }
04740   return tc;
04741 }
04742 
04743 ////////////////////////////////////////////////////////////////////
04744 //     Function: NodePath::find_all_textures
04745 //       Access: Published
04746 //  Description: Returns a list of a textures on geometry at
04747 //               this node and below that are assigned to the
04748 //               indicated texture stage.
04749 ////////////////////////////////////////////////////////////////////
04750 TextureCollection NodePath::
04751 find_all_textures(TextureStage *stage) const {
04752   nassertr_always(!is_empty(), TextureCollection());
04753   Textures textures;
04754   r_find_all_textures(node(), stage, textures);
04755 
04756   TextureCollection tc;
04757   Textures::iterator ti;
04758   for (ti = textures.begin(); ti != textures.end(); ++ti) {
04759     Texture *texture = (*ti);
04760     tc.add_texture(texture);
04761   }
04762   return tc;
04763 }
04764 
04765 ////////////////////////////////////////////////////////////////////
04766 //     Function: NodePath::find_texture_stage
04767 //       Access: Published
04768 //  Description: Returns the first TextureStage found applied to
04769 //               geometry at this node or below that matches the
04770 //               indicated name (which may contain wildcards).
04771 //               Returns the TextureStage if it is found, or NULL if
04772 //               it is not.
04773 ////////////////////////////////////////////////////////////////////
04774 TextureStage *NodePath::
04775 find_texture_stage(const string &name) const {
04776   nassertr_always(!is_empty(), NULL);
04777   GlobPattern glob(name);
04778   return r_find_texture_stage(node(), get_net_state(), glob);
04779 }
04780 
04781 ////////////////////////////////////////////////////////////////////
04782 //     Function: NodePath::find_all_texture_stages
04783 //       Access: Published
04784 //  Description: Returns a list of a TextureStages applied to geometry
04785 //               at this node and below.
04786 ////////////////////////////////////////////////////////////////////
04787 TextureStageCollection NodePath::
04788 find_all_texture_stages() const {
04789   nassertr_always(!is_empty(), TextureStageCollection());
04790   TextureStages texture_stages;
04791   r_find_all_texture_stages(node(), get_net_state(), texture_stages);
04792 
04793   TextureStageCollection tc;
04794   TextureStages::iterator ti;
04795   for (ti = texture_stages.begin(); ti != texture_stages.end(); ++ti) {
04796     tc.add_texture_stage(*ti);
04797   }
04798   return tc;
04799 }
04800 
04801 ////////////////////////////////////////////////////////////////////
04802 //     Function: NodePath::unify_texture_stages
04803 //       Access: Published
04804 //  Description: Searches through all TextureStages at this node and
04805 //               below.  Any TextureStages that share the same name as
04806 //               the indicated TextureStage object are replaced with
04807 //               this object, thus ensuring that all geometry at this
04808 //               node and below with a particular TextureStage name is
04809 //               using the same TextureStage object.
04810 ////////////////////////////////////////////////////////////////////
04811 void NodePath::
04812 unify_texture_stages(TextureStage *stage) {
04813   nassertv_always(!is_empty());
04814   r_unify_texture_stages(node(), stage);
04815 }
04816 
04817 ////////////////////////////////////////////////////////////////////
04818 //     Function: NodePath::find_all_texture_stages
04819 //       Access: Published
04820 //  Description: Returns a list of a TextureStages applied to geometry
04821 //               at this node and below that match the indicated name
04822 //               (which may contain wildcard characters).
04823 ////////////////////////////////////////////////////////////////////
04824 TextureStageCollection NodePath::
04825 find_all_texture_stages(const string &name) const {
04826   nassertr_always(!is_empty(), TextureStageCollection());
04827   TextureStages texture_stages;
04828   r_find_all_texture_stages(node(), get_net_state(), texture_stages);
04829 
04830   GlobPattern glob(name);
04831 
04832   TextureStageCollection tc;
04833   TextureStages::iterator ti;
04834   for (ti = texture_stages.begin(); ti != texture_stages.end(); ++ti) {
04835     TextureStage *texture_stage = (*ti);
04836     if (glob.matches(texture_stage->get_name())) {
04837       tc.add_texture_stage(texture_stage);
04838     }
04839   }
04840   return tc;
04841 }
04842 
04843 ////////////////////////////////////////////////////////////////////
04844 //     Function: NodePath::find_material
04845 //       Access: Published
04846 //  Description: Returns the first material found applied to geometry
04847 //               at this node or below that matches the indicated name
04848 //               (which may contain wildcards).  Returns the material
04849 //               if it is found, or NULL if it is not.
04850 ////////////////////////////////////////////////////////////////////
04851 Material *NodePath::
04852 find_material(const string &name) const {
04853   nassertr_always(!is_empty(), NULL);
04854   GlobPattern glob(name);
04855   return r_find_material(node(), get_net_state(), glob);
04856 }
04857 
04858 ////////////////////////////////////////////////////////////////////
04859 //     Function: NodePath::find_all_materials
04860 //       Access: Published
04861 //  Description: Returns a list of a materials applied to geometry at
04862 //               this node and below.
04863 ////////////////////////////////////////////////////////////////////
04864 MaterialCollection NodePath::
04865 find_all_materials() const {
04866   nassertr_always(!is_empty(), MaterialCollection());
04867   Materials materials;
04868   r_find_all_materials(node(), get_net_state(), materials);
04869 
04870   MaterialCollection tc;
04871   Materials::iterator ti;
04872   for (ti = materials.begin(); ti != materials.end(); ++ti) {
04873     tc.add_material(*ti);
04874   }
04875   return tc;
04876 }
04877 
04878 ////////////////////////////////////////////////////////////////////
04879 //     Function: NodePath::find_all_materials
04880 //       Access: Published
04881 //  Description: Returns a list of a materials applied to geometry at
04882 //               this node and below that match the indicated name
04883 //               (which may contain wildcard characters).
04884 ////////////////////////////////////////////////////////////////////
04885 MaterialCollection NodePath::
04886 find_all_materials(const string &name) const {
04887   nassertr_always(!is_empty(), MaterialCollection());
04888   Materials materials;
04889   r_find_all_materials(node(), get_net_state(), materials);
04890 
04891   GlobPattern glob(name);
04892 
04893   MaterialCollection tc;
04894   Materials::iterator ti;
04895   for (ti = materials.begin(); ti != materials.end(); ++ti) {
04896     Material *material = (*ti);
04897     if (glob.matches(material->get_name())) {
04898       tc.add_material(material);
04899     }
04900   }
04901   return tc;
04902 }
04903 
04904 ////////////////////////////////////////////////////////////////////
04905 //     Function: NodePath::set_material
04906 //       Access: Published
04907 //  Description: Sets the geometry at this level and below to render
04908 //               using the indicated material.
04909 //
04910 //               Previously, this operation made a copy of the
04911 //               material structure, but nowadays it assigns the
04912 //               pointer directly.
04913 ////////////////////////////////////////////////////////////////////
04914 void NodePath::
04915 set_material(Material *mat, int priority) {
04916   nassertv_always(!is_empty());
04917   nassertv(mat != NULL);
04918   node()->set_attrib(MaterialAttrib::make(mat), priority);
04919 }
04920 
04921 ////////////////////////////////////////////////////////////////////
04922 //     Function: NodePath::set_material_off
04923 //       Access: Published
04924 //  Description: Sets the geometry at this level and below to render
04925 //               using no material.  This is normally the default, but
04926 //               it may be useful to use this to contradict
04927 //               set_material() at a higher node level (or, with a
04928 //               priority, to override a set_material() at a lower
04929 //               level).
04930 ////////////////////////////////////////////////////////////////////
04931 void NodePath::
04932 set_material_off(int priority) {
04933   nassertv_always(!is_empty());
04934   node()->set_attrib(MaterialAttrib::make_off(), priority);
04935 }
04936 
04937 ////////////////////////////////////////////////////////////////////
04938 //     Function: NodePath::clear_material
04939 //       Access: Published
04940 //  Description: Completely removes any material adjustment that may
04941 //               have been set via set_material() from this particular
04942 //               node.
04943 ////////////////////////////////////////////////////////////////////
04944 void NodePath::
04945 clear_material() {
04946   nassertv_always(!is_empty());
04947   node()->clear_attrib(MaterialAttrib::get_class_slot());
04948 }
04949 
04950 ////////////////////////////////////////////////////////////////////
04951 //     Function: NodePath::has_material
04952 //       Access: Published
04953 //  Description: Returns true if a material has been applied to this
04954 //               particular node via set_material(), false otherwise.
04955 ////////////////////////////////////////////////////////////////////
04956 bool NodePath::
04957 has_material() const {
04958   nassertr_always(!is_empty(), false);
04959   const RenderAttrib *attrib =
04960     node()->get_attrib(MaterialAttrib::get_class_slot());
04961   if (attrib != (const RenderAttrib *)NULL) {
04962     const MaterialAttrib *ma = DCAST(MaterialAttrib, attrib);
04963     return !ma->is_off();
04964   }
04965 
04966   return false;
04967 }
04968 
04969 ////////////////////////////////////////////////////////////////////
04970 //     Function: NodePath::get_material
04971 //       Access: Published
04972 //  Description: Returns the material that has been set on this
04973 //               particular node, or NULL if no material has been set.
04974 //               This is not necessarily the material that will be
04975 //               applied to the geometry at or below this level, as
04976 //               another material at a higher or lower level may
04977 //               override.
04978 
04979 //               See also find_material().
04980 ////////////////////////////////////////////////////////////////////
04981 PT(Material) NodePath::
04982 get_material() const {
04983   nassertr_always(!is_empty(), NULL);
04984   const RenderAttrib *attrib =
04985     node()->get_attrib(MaterialAttrib::get_class_slot());
04986   if (attrib != (const RenderAttrib *)NULL) {
04987     const MaterialAttrib *ma = DCAST(MaterialAttrib, attrib);
04988     return ma->get_material();
04989   }
04990 
04991   return NULL;
04992 }
04993 
04994 ////////////////////////////////////////////////////////////////////
04995 //     Function: NodePath::set_fog
04996 //       Access: Published
04997 //  Description: Sets the geometry at this level and below to render
04998 //               using the indicated fog.
04999 ////////////////////////////////////////////////////////////////////
05000 void NodePath::
05001 set_fog(Fog *fog, int priority) {
05002   nassertv_always(!is_empty());
05003   node()->set_attrib(FogAttrib::make(fog), priority);
05004 }
05005 
05006 ////////////////////////////////////////////////////////////////////
05007 //     Function: NodePath::set_fog_off
05008 //       Access: Published
05009 //  Description: Sets the geometry at this level and below to render
05010 //               using no fog.  This is normally the default, but
05011 //               it may be useful to use this to contradict
05012 //               set_fog() at a higher node level (or, with a
05013 //               priority, to override a set_fog() at a lower
05014 //               level).
05015 ////////////////////////////////////////////////////////////////////
05016 void NodePath::
05017 set_fog_off(int priority) {
05018   nassertv_always(!is_empty());
05019   node()->set_attrib(FogAttrib::make_off(), priority);
05020 }
05021 
05022 ////////////////////////////////////////////////////////////////////
05023 //     Function: NodePath::clear_fog
05024 //       Access: Published
05025 //  Description: Completely removes any fog adjustment that may
05026 //               have been set via set_fog() or set_fog_off()
05027 //               from this particular node.  This allows whatever
05028 //               fogs might be otherwise affecting the geometry to
05029 //               show instead.
05030 ////////////////////////////////////////////////////////////////////
05031 void NodePath::
05032 clear_fog() {
05033   nassertv_always(!is_empty());
05034   node()->clear_attrib(FogAttrib::get_class_slot());
05035 }
05036 
05037 ////////////////////////////////////////////////////////////////////
05038 //     Function: NodePath::has_fog
05039 //       Access: Published
05040 //  Description: Returns true if a fog has been applied to this
05041 //               particular node via set_fog(), false otherwise.
05042 //               This is not the same thing as asking whether the
05043 //               geometry at this node will be rendered with
05044 //               fog, as there may be a fog in effect from a higher or
05045 //               lower level.
05046 ////////////////////////////////////////////////////////////////////
05047 bool NodePath::
05048 has_fog() const {
05049   nassertr_always(!is_empty(), false);
05050   const RenderAttrib *attrib =
05051     node()->get_attrib(FogAttrib::get_class_slot());
05052   if (attrib != (const RenderAttrib *)NULL) {
05053     const FogAttrib *fa = DCAST(FogAttrib, attrib);
05054     return !fa->is_off();
05055   }
05056 
05057   return false;
05058 }
05059 
05060 ////////////////////////////////////////////////////////////////////
05061 //     Function: NodePath::has_fog_off
05062 //       Access: Published
05063 //  Description: Returns true if a fog has been specifically
05064 //               disabled on this particular node via
05065 //               set_fog_off(), false otherwise.  This is not the
05066 //               same thing as asking whether the geometry at this
05067 //               node will be rendered unfogged, as there may be a
05068 //               fog in effect from a higher or lower level.
05069 ////////////////////////////////////////////////////////////////////
05070 bool NodePath::
05071 has_fog_off() const {
05072   nassertr_always(!is_empty(), false);
05073   const RenderAttrib *attrib =
05074     node()->get_attrib(FogAttrib::get_class_slot());
05075   if (attrib != (const RenderAttrib *)NULL) {
05076     const FogAttrib *fa = DCAST(FogAttrib, attrib);
05077     return fa->is_off();
05078   }
05079 
05080   return false;
05081 }
05082 
05083 ////////////////////////////////////////////////////////////////////
05084 //     Function: NodePath::get_fog
05085 //       Access: Published
05086 //  Description: Returns the fog that has been set on this
05087 //               particular node, or NULL if no fog has been set.
05088 //               This is not necessarily the fog that will be
05089 //               applied to the geometry at or below this level, as
05090 //               another fog at a higher or lower level may
05091 //               override.
05092 ////////////////////////////////////////////////////////////////////
05093 Fog *NodePath::
05094 get_fog() const {
05095   nassertr_always(!is_empty(), NULL);
05096   const RenderAttrib *attrib =
05097     node()->get_attrib(FogAttrib::get_class_slot());
05098   if (attrib != (const RenderAttrib *)NULL) {
05099     const FogAttrib *fa = DCAST(FogAttrib, attrib);
05100     return fa->get_fog();
05101   }
05102 
05103   return NULL;
05104 }
05105 
05106 ////////////////////////////////////////////////////////////////////
05107 //     Function: NodePath::set_render_mode_wireframe
05108 //       Access: Published
05109 //  Description: Sets up the geometry at this level and below (unless
05110 //               overridden) to render in wireframe mode.
05111 ////////////////////////////////////////////////////////////////////
05112 void NodePath::
05113 set_render_mode_wireframe(int priority) {
05114   nassertv_always(!is_empty());
05115   float thickness = get_render_mode_thickness();
05116   bool perspective = get_render_mode_perspective();
05117   node()->set_attrib(RenderModeAttrib::make(RenderModeAttrib::M_wireframe, thickness, perspective), priority);
05118 }
05119 
05120 ////////////////////////////////////////////////////////////////////
05121 //     Function: NodePath::set_render_mode_filled
05122 //       Access: Published
05123 //  Description: Sets up the geometry at this level and below (unless
05124 //               overridden) to render in filled (i.e. not wireframe)
05125 //               mode.
05126 ////////////////////////////////////////////////////////////////////
05127 void NodePath::
05128 set_render_mode_filled(int priority) {
05129   nassertv_always(!is_empty());
05130   float thickness = get_render_mode_thickness();
05131   bool perspective = get_render_mode_perspective();
05132   node()->set_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled, thickness, perspective), priority);
05133 }
05134 
05135 ////////////////////////////////////////////////////////////////////
05136 //     Function: NodePath::set_render_mode_perspective
05137 //       Access: Published
05138 //  Description: Sets up the point geometry at this level and below to
05139 //               render as perspective sprites (that is, billboarded
05140 //               quads).  The thickness, as specified with
05141 //               set_render_mode_thickness(), is the width of each
05142 //               point in 3-D units, unless it is overridden on a
05143 //               per-vertex basis.  This does not affect geometry
05144 //               other than points.
05145 //
05146 //               If you want the quads to be individually textured,
05147 //               you should also set a TexGenAttrib::M_point_sprite on
05148 //               the node.
05149 ////////////////////////////////////////////////////////////////////
05150 void NodePath::
05151 set_render_mode_perspective(bool perspective, int priority) {
05152   nassertv_always(!is_empty());
05153   RenderModeAttrib::Mode mode = get_render_mode();
05154   float thickness = get_render_mode_thickness();
05155   node()->set_attrib(RenderModeAttrib::make(mode, thickness, perspective), priority);
05156 }
05157 
05158 ////////////////////////////////////////////////////////////////////
05159 //     Function: NodePath::set_render_mode_thickness
05160 //       Access: Published
05161 //  Description: Sets up the point geometry at this level and below to
05162 //               render as thick points (that is, billboarded
05163 //               quads).  The thickness is in pixels, unless
05164 //               set_render_mode_perspective is also true, in which
05165 //               case it is in 3-D units.
05166 //
05167 //               If you want the quads to be individually textured,
05168 //               you should also set a TexGenAttrib::M_point_sprite on
05169 //               the node.
05170 ////////////////////////////////////////////////////////////////////
05171 void NodePath::
05172 set_render_mode_thickness(float thickness, int priority) {
05173   nassertv_always(!is_empty());
05174   RenderModeAttrib::Mode mode = get_render_mode();
05175   bool perspective = get_render_mode_perspective();
05176   node()->set_attrib(RenderModeAttrib::make(mode, thickness, perspective), priority);
05177 }
05178 
05179 ////////////////////////////////////////////////////////////////////
05180 //     Function: NodePath::set_render_mode
05181 //       Access: Published
05182 //  Description: Sets up the geometry at this level and below (unless
05183 //               overridden) to render in the specified mode and with
05184 //               the indicated line and/or point thickness.
05185 ////////////////////////////////////////////////////////////////////
05186 void NodePath::
05187 set_render_mode(RenderModeAttrib::Mode mode, float thickness, int priority) {
05188   nassertv_always(!is_empty());
05189 
05190   node()->set_attrib(RenderModeAttrib::make(mode, thickness), priority);
05191 }
05192 
05193 ////////////////////////////////////////////////////////////////////
05194 //     Function: NodePath::clear_render_mode
05195 //       Access: Published
05196 //  Description: Completely removes any render mode adjustment that
05197 //               may have been set on this node via
05198 //               set_render_mode_wireframe() or
05199 //               set_render_mode_filled().
05200 ////////////////////////////////////////////////////////////////////
05201 void NodePath::
05202 clear_render_mode() {
05203   nassertv_always(!is_empty());
05204   node()->clear_attrib(RenderModeAttrib::get_class_slot());
05205 }
05206 
05207 ////////////////////////////////////////////////////////////////////
05208 //     Function: NodePath::has_render_mode
05209 //       Access: Published
05210 //  Description: Returns true if a render mode has been explicitly set
05211 //               on this particular node via set_render_mode() (or
05212 //               set_render_mode_wireframe() or
05213 //               set_render_mode_filled()), false otherwise.
05214 ////////////////////////////////////////////////////////////////////
05215 bool NodePath::
05216 has_render_mode() const {
05217   nassertr_always(!is_empty(), false);
05218   return node()->has_attrib(RenderModeAttrib::get_class_slot());
05219 }
05220 
05221 ////////////////////////////////////////////////////////////////////
05222 //     Function: NodePath::get_render_mode
05223 //       Access: Published
05224 //  Description: Returns the render mode that has been specifically
05225 //               set on this node via set_render_mode(), or
05226 //               M_unchanged if nothing has been set.
05227 ////////////////////////////////////////////////////////////////////
05228 RenderModeAttrib::Mode NodePath::
05229 get_render_mode() const {
05230   nassertr_always(!is_empty(), RenderModeAttrib::M_unchanged);
05231   const RenderAttrib *attrib =
05232     node()->get_attrib(RenderModeAttrib::get_class_slot());
05233   if (attrib != (const RenderAttrib *)NULL) {
05234     const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib);
05235     return ta->get_mode();
05236   }
05237 
05238   return RenderModeAttrib::M_unchanged;
05239 }
05240 
05241 ////////////////////////////////////////////////////////////////////
05242 //     Function: NodePath::get_render_mode_thickness
05243 //       Access: Published
05244 //  Description: Returns the render mode thickness that has been
05245 //               specifically set on this node via set_render_mode(),
05246 //               or 1.0 if nothing has been set.
05247 ////////////////////////////////////////////////////////////////////
05248 float NodePath::
05249 get_render_mode_thickness() const {
05250   nassertr_always(!is_empty(), 0.0f);
05251   const RenderAttrib *attrib =
05252     node()->get_attrib(RenderModeAttrib::get_class_slot());
05253   if (attrib != (const RenderAttrib *)NULL) {
05254     const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib);
05255     return ta->get_thickness();
05256   }
05257 
05258   return 1.0f;
05259 }
05260 
05261 ////////////////////////////////////////////////////////////////////
05262 //     Function: NodePath::get_render_mode_perspective
05263 //       Access: Published
05264 //  Description: Returns the flag that has been set on this node via
05265 //               set_render_mode_perspective(), or false if no flag
05266 //               has been set.
05267 ////////////////////////////////////////////////////////////////////
05268 bool NodePath::
05269 get_render_mode_perspective() const {
05270   nassertr_always(!is_empty(), 0.0f);
05271   const RenderAttrib *attrib =
05272     node()->get_attrib(RenderModeAttrib::get_class_slot());
05273   if (attrib != (const RenderAttrib *)NULL) {
05274     const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib);
05275     return ta->get_perspective();
05276   }
05277 
05278   return false;
05279 }
05280 
05281 ////////////////////////////////////////////////////////////////////
05282 //     Function: NodePath::set_two_sided
05283 //       Access: Published
05284 //  Description: Specifically sets or disables two-sided rendering
05285 //               mode on this particular node.  If no other nodes
05286 //               override, this will cause backfacing polygons to be
05287 //               drawn (in two-sided mode, true) or culled (in
05288 //               one-sided mode, false).
05289 ////////////////////////////////////////////////////////////////////
05290 void NodePath::
05291 set_two_sided(bool two_sided, int priority) {
05292   nassertv_always(!is_empty());
05293 
05294   CullFaceAttrib::Mode mode =
05295     two_sided ?
05296     CullFaceAttrib::M_cull_none :
05297     CullFaceAttrib::M_cull_clockwise;
05298 
05299   node()->set_attrib(CullFaceAttrib::make(mode), priority);
05300 }
05301 
05302 ////////////////////////////////////////////////////////////////////
05303 //     Function: NodePath::clear_two_sided
05304 //       Access: Published
05305 //  Description: Completely removes any two-sided adjustment that
05306 //               may have been set on this node via set_two_sided().
05307 //               The geometry at this level and below will
05308 //               subsequently be rendered either two-sided or
05309 //               one-sided, according to whatever other nodes may have
05310 //               had set_two_sided() on it, or according to the
05311 //               initial state otherwise.
05312 ////////////////////////////////////////////////////////////////////
05313 void NodePath::
05314 clear_two_sided() {
05315   nassertv_always(!is_empty());
05316   node()->clear_attrib(CullFaceAttrib::get_class_slot());
05317 }
05318 
05319 ////////////////////////////////////////////////////////////////////
05320 //     Function: NodePath::has_two_sided
05321 //       Access: Published
05322 //  Description: Returns true if a two-sided adjustment has been
05323 //               explicitly set on this particular node via
05324 //               set_two_sided().  If this returns true, then
05325 //               get_two_sided() may be called to determine which has
05326 //               been set.
05327 ////////////////////////////////////////////////////////////////////
05328 bool NodePath::
05329 has_two_sided() const {
05330   nassertr_always(!is_empty(), false);
05331   return node()->has_attrib(CullFaceAttrib::get_class_slot());
05332 }
05333 
05334 ////////////////////////////////////////////////////////////////////
05335 //     Function: NodePath::get_two_sided
05336 //       Access: Published
05337 //  Description: Returns true if two-sided rendering has been
05338 //               specifically set on this node via set_two_sided(), or
05339 //               false if one-sided rendering has been specifically
05340 //               set, or if nothing has been specifically set.  See
05341 //               also has_two_sided().  This does not necessarily
05342 //               imply that the geometry will or will not be rendered
05343 //               two-sided, as there may be other nodes that override.
05344 ////////////////////////////////////////////////////////////////////
05345 bool NodePath::
05346 get_two_sided() const {
05347   nassertr_always(!is_empty(), false);
05348   const RenderAttrib *attrib =
05349     node()->get_attrib(CullFaceAttrib::get_class_slot());
05350   if (attrib != (const RenderAttrib *)NULL) {
05351     const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, attrib);
05352     return (cfa->get_actual_mode() == CullFaceAttrib::M_cull_none);
05353   }
05354 
05355   return false;
05356 }
05357 
05358 ////////////////////////////////////////////////////////////////////
05359 //     Function: NodePath::set_depth_test
05360 //       Access: Published
05361 //  Description: Specifically sets or disables the testing of the
05362 //               depth buffer on this particular node.  This is
05363 //               normally on in the 3-d scene graph and off in the 2-d
05364 //               scene graph; it should be on for rendering most 3-d
05365 //               objects properly.
05366 ////////////////////////////////////////////////////////////////////
05367 void NodePath::
05368 set_depth_test(bool depth_test, int priority) {
05369   nassertv_always(!is_empty());
05370 
05371   DepthTestAttrib::PandaCompareFunc mode =
05372     depth_test ?
05373     DepthTestAttrib::M_less :
05374     DepthTestAttrib::M_none;
05375 
05376   node()->set_attrib(DepthTestAttrib::make(mode), priority);
05377 }
05378 
05379 ////////////////////////////////////////////////////////////////////
05380 //     Function: NodePath::clear_depth_test
05381 //       Access: Published
05382 //  Description: Completely removes any depth-test adjustment that
05383 //               may have been set on this node via set_depth_test().
05384 ////////////////////////////////////////////////////////////////////
05385 void NodePath::
05386 clear_depth_test() {
05387   nassertv_always(!is_empty());
05388   node()->clear_attrib(DepthTestAttrib::get_class_slot());
05389 }
05390 
05391 ////////////////////////////////////////////////////////////////////
05392 //     Function: NodePath::has_depth_test
05393 //       Access: Published
05394 //  Description: Returns true if a depth-test adjustment has been
05395 //               explicitly set on this particular node via
05396 //               set_depth_test().  If this returns true, then
05397 //               get_depth_test() may be called to determine which has
05398 //               been set.
05399 ////////////////////////////////////////////////////////////////////
05400 bool NodePath::
05401 has_depth_test() const {
05402   nassertr_always(!is_empty(), false);
05403   return node()->has_attrib(DepthTestAttrib::get_class_slot());
05404 }
05405 
05406 ////////////////////////////////////////////////////////////////////
05407 //     Function: NodePath::get_depth_test
05408 //       Access: Published
05409 //  Description: Returns true if depth-test rendering has been
05410 //               specifically set on this node via set_depth_test(), or
05411 //               false if depth-test rendering has been specifically
05412 //               disabled.  If nothing has been specifically set,
05413 //               returns true.  See also has_depth_test().
05414 ////////////////////////////////////////////////////////////////////
05415 bool NodePath::
05416 get_depth_test() const {
05417   nassertr_always(!is_empty(), false);
05418   const RenderAttrib *attrib =
05419     node()->get_attrib(DepthTestAttrib::get_class_slot());
05420   if (attrib != (const RenderAttrib *)NULL) {
05421     const DepthTestAttrib *dta = DCAST(DepthTestAttrib, attrib);
05422     return (dta->get_mode() != DepthTestAttrib::M_none);
05423   }
05424 
05425   return true;
05426 }
05427 
05428 ////////////////////////////////////////////////////////////////////
05429 //     Function: NodePath::set_depth_write
05430 //       Access: Published
05431 //  Description: Specifically sets or disables the writing to the
05432 //               depth buffer on this particular node.  This is
05433 //               normally on in the 3-d scene graph and off in the 2-d
05434 //               scene graph; it should be on for rendering most 3-d
05435 //               objects properly.
05436 ////////////////////////////////////////////////////////////////////
05437 void NodePath::
05438 set_depth_write(bool depth_write, int priority) {
05439   nassertv_always(!is_empty());
05440 
05441   DepthWriteAttrib::Mode mode =
05442     depth_write ?
05443     DepthWriteAttrib::M_on :
05444     DepthWriteAttrib::M_off;
05445 
05446   node()->set_attrib(DepthWriteAttrib::make(mode), priority);
05447 }
05448 
05449 ////////////////////////////////////////////////////////////////////
05450 //     Function: NodePath::clear_depth_write
05451 //       Access: Published
05452 //  Description: Completely removes any depth-write adjustment that
05453 //               may have been set on this node via set_depth_write().
05454 ////////////////////////////////////////////////////////////////////
05455 void NodePath::
05456 clear_depth_write() {
05457   nassertv_always(!is_empty());
05458   node()->clear_attrib(DepthWriteAttrib::get_class_slot());
05459 }
05460 
05461 ////////////////////////////////////////////////////////////////////
05462 //     Function: NodePath::has_depth_write
05463 //       Access: Published
05464 //  Description: Returns true if a depth-write adjustment has been
05465 //               explicitly set on this particular node via
05466 //               set_depth_write().  If this returns true, then
05467 //               get_depth_write() may be called to determine which has
05468 //               been set.
05469 ////////////////////////////////////////////////////////////////////
05470 bool NodePath::
05471 has_depth_write() const {
05472   nassertr_always(!is_empty(), false);
05473   return node()->has_attrib(DepthWriteAttrib::get_class_slot());
05474 }
05475 
05476 ////////////////////////////////////////////////////////////////////
05477 //     Function: NodePath::get_depth_write
05478 //       Access: Published
05479 //  Description: Returns true if depth-write rendering has been
05480 //               specifically set on this node via set_depth_write(), or
05481 //               false if depth-write rendering has been specifically
05482 //               disabled.  If nothing has been specifically set,
05483 //               returns true.  See also has_depth_write().
05484 ////////////////////////////////////////////////////////////////////
05485 bool NodePath::
05486 get_depth_write() const {
05487   nassertr_always(!is_empty(), false);
05488   const RenderAttrib *attrib =
05489     node()->get_attrib(DepthWriteAttrib::get_class_slot());
05490   if (attrib != (const RenderAttrib *)NULL) {
05491     const DepthWriteAttrib *dta = DCAST(DepthWriteAttrib, attrib);
05492     return (dta->get_mode() != DepthWriteAttrib::M_off);
05493   }
05494 
05495   return true;
05496 }
05497 
05498 ////////////////////////////////////////////////////////////////////
05499 //     Function: NodePath::set_depth_offset
05500 //       Access: Published
05501 //  Description: This instructs the graphics driver to apply an
05502 //               offset or bias to the generated depth values for
05503 //               rendered polygons, before they are written to the
05504 //               depth buffer. This can be used to shift polygons
05505 //               forward slightly, to resolve depth conflicts, or
05506 //               self-shadowing artifacts on thin objects.
05507 //               The bias is always an integer number, and each
05508 //               integer increment represents the smallest possible
05509 //               increment in Z that is sufficient to completely
05510 //               resolve two coplanar polygons. Positive numbers
05511 //               are closer towards the camera.
05512 ////////////////////////////////////////////////////////////////////
05513 void NodePath::
05514 set_depth_offset(int bias, int priority) {
05515   nassertv_always(!is_empty());
05516 
05517   node()->set_attrib(DepthOffsetAttrib::make(bias), priority);
05518 }
05519 
05520 ////////////////////////////////////////////////////////////////////
05521 //     Function: NodePath::clear_depth_offset
05522 //       Access: Published
05523 //  Description: Completely removes any depth-offset adjustment that
05524 //               may have been set on this node via set_depth_offset().
05525 ////////////////////////////////////////////////////////////////////
05526 void NodePath::
05527 clear_depth_offset() {
05528   nassertv_always(!is_empty());
05529   node()->clear_attrib(DepthOffsetAttrib::get_class_slot());
05530 }
05531 
05532 ////////////////////////////////////////////////////////////////////
05533 //     Function: NodePath::has_depth_offset
05534 //       Access: Published
05535 //  Description: Returns true if a depth-offset adjustment has been
05536 //               explicitly set on this particular node via
05537 //               set_depth_offset().  If this returns true, then
05538 //               get_depth_offset() may be called to determine which has
05539 //               been set.
05540 ////////////////////////////////////////////////////////////////////
05541 bool NodePath::
05542 has_depth_offset() const {
05543   nassertr_always(!is_empty(), false);
05544   return node()->has_attrib(DepthOffsetAttrib::get_class_slot());
05545 }
05546 
05547 ////////////////////////////////////////////////////////////////////
05548 //     Function: NodePath::get_depth_offset
05549 //       Access: Published
05550 //  Description: Returns the depth offset value if it has been
05551 //               specified using set_depth_offset, or 0 if not.
05552 ////////////////////////////////////////////////////////////////////
05553 int NodePath::
05554 get_depth_offset() const {
05555   nassertr_always(!is_empty(), 0);
05556   const RenderAttrib *attrib =
05557     node()->get_attrib(DepthOffsetAttrib::get_class_slot());
05558   if (attrib != (const RenderAttrib *)NULL) {
05559     const DepthOffsetAttrib *doa = DCAST(DepthOffsetAttrib, attrib);
05560     return doa->get_offset();
05561   }
05562 
05563   return 0;
05564 }
05565 
05566 ////////////////////////////////////////////////////////////////////
05567 //     Function: NodePath::do_billboard_axis
05568 //       Access: Published
05569 //  Description: Performs a billboard-type rotate to the indicated
05570 //               camera node, one time only, and leaves the object
05571 //               rotated.  This is similar in principle to heads_up().
05572 ////////////////////////////////////////////////////////////////////
05573 void NodePath::
05574 do_billboard_axis(const NodePath &camera, float offset) {
05575   nassertv_always(!is_empty());
05576 
05577   CPT(TransformState) transform = camera.get_transform(get_parent());
05578   const LMatrix4f &rel_mat = transform->get_mat();
05579 
05580   LVector3f up = LVector3f::up();
05581   LVector3f rel_pos = -rel_mat.get_row3(3);
05582 
05583   LQuaternionf quat;
05584   ::heads_up(quat, rel_pos, up);
05585   set_quat(quat);
05586 
05587   // Also slide the geometry towards the camera according to the
05588   // offset factor.
05589   if (offset != 0.0f) {
05590     LVector3f translate = rel_mat.get_row3(3);
05591     translate.normalize();
05592     translate *= offset;
05593     set_pos(translate);
05594   }
05595 }
05596 
05597 ////////////////////////////////////////////////////////////////////
05598 //     Function: NodePath::do_billboard_point_eye
05599 //       Access: Published
05600 //  Description: Performs a billboard-type rotate to the indicated
05601 //               camera node, one time only, and leaves the object
05602 //               rotated.  This is similar in principle to look_at(),
05603 //               although the point_eye billboard effect cannot be
05604 //               achieved using the ordinary look_at() call.
05605 ////////////////////////////////////////////////////////////////////
05606 void NodePath::
05607 do_billboard_point_eye(const NodePath &camera, float offset) {
05608   nassertv_always(!is_empty());
05609 
05610   CPT(TransformState) transform = camera.get_transform(get_parent());
05611   const LMatrix4f &rel_mat = transform->get_mat();
05612 
05613   LVector3f up = LVector3f::up() * rel_mat;
05614   LVector3f rel_pos = LVector3f::forward() * rel_mat;
05615 
05616   LQuaternionf quat;
05617   ::look_at(quat, rel_pos, up);
05618   set_quat(quat);
05619 
05620   // Also slide the geometry towards the camera according to the
05621   // offset factor.
05622   if (offset != 0.0f) {
05623     LVector3f translate = rel_mat.get_row3(3);
05624     translate.normalize();
05625     translate *= offset;
05626     set_pos(translate);
05627   }
05628 }
05629 
05630 ////////////////////////////////////////////////////////////////////
05631 //     Function: NodePath::do_billboard_point_world
05632 //       Access: Published
05633 //  Description: Performs a billboard-type rotate to the indicated
05634 //               camera node, one time only, and leaves the object
05635 //               rotated.  This is similar in principle to look_at().
05636 ////////////////////////////////////////////////////////////////////
05637 void NodePath::
05638 do_billboard_point_world(const NodePath &camera, float offset) {
05639   nassertv_always(!is_empty());
05640 
05641   CPT(TransformState) transform = camera.get_transform(get_parent());
05642   const LMatrix4f &rel_mat = transform->get_mat();
05643 
05644   LVector3f up = LVector3f::up();
05645   LVector3f rel_pos = -rel_mat.get_row3(3);
05646 
05647   LQuaternionf quat;
05648   ::look_at(quat, rel_pos, up);
05649   set_quat(quat);
05650 
05651   // Also slide the geometry towards the camera according to the
05652   // offset factor.
05653   if (offset != 0.0f) {
05654     LVector3f translate = rel_mat.get_row3(3);
05655     translate.normalize();
05656     translate *= offset;
05657     set_pos(translate);
05658   }
05659 }
05660 
05661 ////////////////////////////////////////////////////////////////////
05662 //     Function: NodePath::set_billboard_axis
05663 //       Access: Published
05664 //  Description: Puts a billboard transition on the node such that it
05665 //               will rotate in two dimensions around the up axis,
05666 //               towards a specified "camera" instead of to the
05667 //               viewing camera.
05668 ////////////////////////////////////////////////////////////////////
05669 void NodePath::
05670 set_billboard_axis(const NodePath &camera, float offset) {
05671   nassertv_always(!is_empty());
05672   CPT(RenderEffect) billboard = BillboardEffect::make
05673     (LVector3f::up(), false, true, 
05674      offset, camera, LPoint3f(0.0f, 0.0f, 0.0f));
05675   node()->set_effect(billboard);
05676 }
05677 
05678 ////////////////////////////////////////////////////////////////////
05679 //     Function: NodePath::set_billboard_point_eye
05680 //       Access: Published
05681 //  Description: Puts a billboard transition on the node such that it
05682 //               will rotate in three dimensions about the origin,
05683 //               keeping its up vector oriented to the top of the
05684 //               camera, towards a specified "camera" instead of to
05685 //               the viewing camera.
05686 ////////////////////////////////////////////////////////////////////
05687 void NodePath::
05688 set_billboard_point_eye(const NodePath &camera, float offset) {
05689   nassertv_always(!is_empty());
05690   CPT(RenderEffect) billboard = BillboardEffect::make
05691     (LVector3f::up(), true, false,
05692      offset, camera, LPoint3f(0.0f, 0.0f, 0.0f));
05693   node()->set_effect(billboard);
05694 }
05695 
05696 ////////////////////////////////////////////////////////////////////
05697 //     Function: NodePath::set_billboard_point_world
05698 //       Access: Published
05699 //  Description: Puts a billboard transition on the node such that it
05700 //               will rotate in three dimensions about the origin,
05701 //               keeping its up vector oriented to the sky, towards a
05702 //               specified "camera" instead of to the viewing camera.
05703 ////////////////////////////////////////////////////////////////////
05704 void NodePath::
05705 set_billboard_point_world(const NodePath &camera, float offset) {
05706   nassertv_always(!is_empty());
05707   CPT(RenderEffect) billboard = BillboardEffect::make
05708     (LVector3f::up(), false, false,
05709      offset, camera, LPoint3f(0.0f, 0.0f, 0.0f));
05710   node()->set_effect(billboard);
05711 }
05712 
05713 ////////////////////////////////////////////////////////////////////
05714 //     Function: NodePath::clear_billboard
05715 //       Access: Published
05716 //  Description: Removes any billboard effect from the node.
05717 ////////////////////////////////////////////////////////////////////
05718 void NodePath::
05719 clear_billboard() {
05720   nassertv_always(!is_empty());
05721   node()->clear_effect(BillboardEffect::get_class_type());
05722 }
05723 
05724 ////////////////////////////////////////////////////////////////////
05725 //     Function: NodePath::has_billboard
05726 //       Access: Published
05727 //  Description: Returns true if there is any billboard effect on
05728 //               the node.
05729 ////////////////////////////////////////////////////////////////////
05730 bool NodePath::
05731 has_billboard() const {
05732   nassertr_always(!is_empty(), false);
05733   return node()->has_effect(BillboardEffect::get_class_type());
05734 }
05735 
05736 ////////////////////////////////////////////////////////////////////
05737 //     Function: NodePath::set_compass
05738 //       Access: Published
05739 //  Description: Puts a compass effect on the node, so that it will
05740 //               retain a fixed rotation relative to the reference
05741 //               node (or render if the reference node is empty)
05742 //               regardless of the transforms above it.
05743 ////////////////////////////////////////////////////////////////////
05744 void NodePath::
05745 set_compass(const NodePath &reference) {
05746   nassertv_always(!is_empty());
05747   node()->set_effect(CompassEffect::make(reference));
05748 }
05749 
05750 ////////////////////////////////////////////////////////////////////
05751 //     Function: NodePath::clear_compass
05752 //       Access: Published
05753 //  Description: Removes any compass effect from the node.
05754 ////////////////////////////////////////////////////////////////////
05755 void NodePath::
05756 clear_compass() {
05757   nassertv_always(!is_empty());
05758   node()->clear_effect(CompassEffect::get_class_type());
05759 }
05760 
05761 ////////////////////////////////////////////////////////////////////
05762 //     Function: NodePath::has_compass
05763 //       Access: Published
05764 //  Description: Returns true if there is any compass effect on
05765 //               the node.
05766 ////////////////////////////////////////////////////////////////////
05767 bool NodePath::
05768 has_compass() const {
05769   nassertr_always(!is_empty(), false);
05770   return node()->has_effect(CompassEffect::get_class_type());
05771 }
05772 
05773 ////////////////////////////////////////////////////////////////////
05774 //     Function: NodePath::set_transparency
05775 //       Access: Published
05776 //  Description: Specifically sets or disables transparent rendering
05777 //               mode on this particular node.  If no other nodes
05778 //               override, this will cause items with a non-1 value
05779 //               for alpha color to be rendered partially transparent.
05780 ////////////////////////////////////////////////////////////////////
05781 void NodePath::
05782 set_transparency(TransparencyAttrib::Mode mode, int priority) {
05783   nassertv_always(!is_empty());
05784 
05785   node()->set_attrib(TransparencyAttrib::make(mode), priority);
05786 }
05787 
05788 ////////////////////////////////////////////////////////////////////
05789 //     Function: NodePath::clear_transparency
05790 //       Access: Published
05791 //  Description: Completely removes any transparency adjustment that
05792 //               may have been set on this node via set_transparency().
05793 //               The geometry at this level and below will
05794 //               subsequently be rendered either transparent or not,
05795 //               to whatever other nodes may have had
05796 //               set_transparency() on them.
05797 ////////////////////////////////////////////////////////////////////
05798 void NodePath::
05799 clear_transparency() {
05800   nassertv_always(!is_empty());
05801   node()->clear_attrib(TransparencyAttrib::get_class_slot());
05802 }
05803 
05804 ////////////////////////////////////////////////////////////////////
05805 //     Function: NodePath::has_transparency
05806 //       Access: Published
05807 //  Description: Returns true if a transparent-rendering adjustment
05808 //               has been explicitly set on this particular node via
05809 //               set_transparency().  If this returns true, then
05810 //               get_transparency() may be called to determine whether
05811 //               transparency has been explicitly enabled or
05812 //               explicitly disabled for this node.
05813 ////////////////////////////////////////////////////////////////////
05814 bool NodePath::
05815 has_transparency() const {
05816   nassertr_always(!is_empty(), false);
05817   return node()->has_attrib(TransparencyAttrib::get_class_slot());
05818 }
05819 
05820 ////////////////////////////////////////////////////////////////////
05821 //     Function: NodePath::get_transparency
05822 //       Access: Published
05823 //  Description: Returns the transparent rendering that has been
05824 //               specifically set on this node via set_transparency(), or
05825 //               M_none if nontransparent rendering has been specifically
05826 //               set, or if nothing has been specifically set.  See
05827 //               also has_transparency().  This does not necessarily
05828 //               imply that the geometry will or will not be rendered
05829 //               transparent, as there may be other nodes that override.
05830 ////////////////////////////////////////////////////////////////////
05831 TransparencyAttrib::Mode NodePath::
05832 get_transparency() const {
05833   nassertr_always(!is_empty(), TransparencyAttrib::M_none);
05834   const RenderAttrib *attrib =
05835     node()->get_attrib(TransparencyAttrib::get_class_slot());
05836   if (attrib != (const RenderAttrib *)NULL) {
05837     const TransparencyAttrib *ta = DCAST(TransparencyAttrib, attrib);
05838     return ta->get_mode();
05839   }
05840 
05841   return TransparencyAttrib::M_none;
05842 }
05843 
05844 ////////////////////////////////////////////////////////////////////
05845 //     Function: NodePath::set_antialias
05846 //       Access: Published
05847 //  Description: Specifies the antialiasing type that should be
05848 //               applied at this node and below.  See AntialiasAttrib.
05849 ////////////////////////////////////////////////////////////////////
05850 void NodePath::
05851 set_antialias(unsigned short mode, int priority) {
05852   nassertv_always(!is_empty());
05853 
05854   node()->set_attrib(AntialiasAttrib::make(mode), priority);
05855 }
05856 
05857 ////////////////////////////////////////////////////////////////////
05858 //     Function: NodePath::clear_antialias
05859 //       Access: Published
05860 //  Description: Completely removes any antialias setting that
05861 //               may have been set on this node via set_antialias().
05862 ////////////////////////////////////////////////////////////////////
05863 void NodePath::
05864 clear_antialias() {
05865   nassertv_always(!is_empty());
05866   node()->clear_attrib(AntialiasAttrib::get_class_slot());
05867 }
05868 
05869 ////////////////////////////////////////////////////////////////////
05870 //     Function: NodePath::has_antialias
05871 //       Access: Published
05872 //  Description: Returns true if an antialias setting has been
05873 //               explicitly mode on this particular node via
05874 //               set_antialias().  If this returns true, then
05875 //               get_antialias() may be called to determine what the
05876 //               setting was.
05877 ////////////////////////////////////////////////////////////////////
05878 bool NodePath::
05879 has_antialias() const {
05880   nassertr_always(!is_empty(), false);
05881   return node()->has_attrib(AntialiasAttrib::get_class_slot());
05882 }
05883 
05884 ////////////////////////////////////////////////////////////////////
05885 //     Function: NodePath::get_antialias
05886 //       Access: Published
05887 //  Description: Returns the antialias setting that has been
05888 //               specifically set on this node via set_antialias(), or
05889 //               M_none if no setting has been made.
05890 ////////////////////////////////////////////////////////////////////
05891 unsigned short NodePath::
05892 get_antialias() const {
05893   nassertr_always(!is_empty(), AntialiasAttrib::M_none);
05894   const RenderAttrib *attrib =
05895     node()->get_attrib(AntialiasAttrib::get_class_slot());
05896   if (attrib != (const RenderAttrib *)NULL) {
05897     const AntialiasAttrib *ta = DCAST(AntialiasAttrib, attrib);
05898     return ta->get_mode();
05899   }
05900 
05901   return AntialiasAttrib::M_none;
05902 }
05903 
05904 ////////////////////////////////////////////////////////////////////
05905 //     Function: NodePath::has_audio_volume
05906 //       Access: Published
05907 //  Description: Returns true if an audio volume has been applied
05908 //               to the referenced node, false otherwise.  It is still
05909 //               possible that volume at this node might have been
05910 //               scaled by an ancestor node.
05911 ////////////////////////////////////////////////////////////////////
05912 bool NodePath::
05913 has_audio_volume() const {
05914   nassertr_always(!is_empty(), false);
05915   return node()->has_attrib(AudioVolumeAttrib::get_class_slot());
05916 }
05917 
05918 ////////////////////////////////////////////////////////////////////
05919 //     Function: NodePath::clear_audio_volume
05920 //       Access: Published
05921 //  Description: Completely removes any audio volume from the
05922 //               referenced node.  This is preferable to simply
05923 //               setting the audio volume to identity, as it also
05924 //               removes the overhead associated with having an audio
05925 //               volume at all.
05926 ////////////////////////////////////////////////////////////////////
05927 void NodePath::
05928 clear_audio_volume() {
05929   nassertv_always(!is_empty());
05930   node()->clear_attrib(AudioVolumeAttrib::get_class_slot());
05931 }
05932 
05933 ////////////////////////////////////////////////////////////////////
05934 //     Function: NodePath::set_audio_volume
05935 //       Access: Published
05936 //  Description: Sets the audio volume component of the transform
05937 ////////////////////////////////////////////////////////////////////
05938 void NodePath::
05939 set_audio_volume(float volume, int priority) {
05940   nassertv_always(!is_empty());
05941 
05942   const RenderAttrib *attrib =
05943     node()->get_attrib(AudioVolumeAttrib::get_class_slot());
05944   if (attrib != (const RenderAttrib *)NULL) {
05945     priority = max(priority,
05946                    node()->get_state()->get_override(AudioVolumeAttrib::get_class_slot()));
05947     CPT(AudioVolumeAttrib) ava = DCAST(AudioVolumeAttrib, attrib);
05948 
05949     // Modify the existing AudioVolumeAttrib to add the indicated
05950     // volume.
05951     node()->set_attrib(ava->set_volume(volume), priority);
05952 
05953   } else {
05954     // Create a new AudioVolumeAttrib for this node.
05955     node()->set_attrib(AudioVolumeAttrib::make(volume), priority);
05956   }
05957 }
05958 
05959 ////////////////////////////////////////////////////////////////////
05960 //     Function: NodePath::set_audio_volume_off
05961 //       Access: Published
05962 //  Description: Disables any audio volume attribute inherited from
05963 //               above.  This is not the same thing as
05964 //               clear_audio_volume(), which undoes any previous
05965 //               set_audio_volume() operation on this node; rather,
05966 //               this actively disables any set_audio_volume() that
05967 //               might be inherited from a parent node.
05968 //
05969 //               It is legal to specify a new volume on the same
05970 //               node with a subsequent call to set_audio_volume();
05971 //               this new scale will apply to lower nodes.
05972 ////////////////////////////////////////////////////////////////////
05973 void NodePath::
05974 set_audio_volume_off(int priority) {
05975   nassertv_always(!is_empty());
05976   node()->set_attrib(AudioVolumeAttrib::make_off(), priority);
05977 }
05978 
05979 ////////////////////////////////////////////////////////////////////
05980 //     Function: NodePath::get_audio_volume
05981 //       Access: Published
05982 //  Description: Returns the complete audio volume that has been
05983 //               applied to this node via a previous call to
05984 //               set_audio_volume(), or 1. (identity) if no volume has
05985 //               been applied to this particular node.
05986 ////////////////////////////////////////////////////////////////////
05987 float NodePath::
05988 get_audio_volume() const {
05989   const RenderAttrib *attrib =
05990     node()->get_attrib(AudioVolumeAttrib::get_class_slot());
05991   if (attrib != (const RenderAttrib *)NULL) {
05992     const AudioVolumeAttrib *ava = DCAST(AudioVolumeAttrib, attrib);
05993     return ava->get_volume();
05994   }
05995 
05996   return 1.0f;
05997 }
05998 
05999 ////////////////////////////////////////////////////////////////////
06000 //     Function: NodePath::get_net_audio_volume
06001 //       Access: Published
06002 //  Description: Returns the complete audio volume for this node
06003 //               taking highers nodes in the graph into account.
06004 ////////////////////////////////////////////////////////////////////
06005 float NodePath::
06006 get_net_audio_volume() const {
06007   CPT(RenderState) net_state = get_net_state();
06008   const RenderAttrib *attrib = net_state->get_attrib(AudioVolumeAttrib::get_class_slot());
06009   if (attrib != (const RenderAttrib *)NULL) {
06010     const AudioVolumeAttrib *ava = DCAST(AudioVolumeAttrib, attrib);
06011     if (ava != (const AudioVolumeAttrib *)NULL) {
06012       return ava->get_volume();
06013     }
06014   }
06015 
06016   return 1.0f;
06017 }
06018 
06019 ////////////////////////////////////////////////////////////////////
06020 //     Function: NodePath::get_hidden_ancestor
06021 //       Access: Published
06022 //  Description: Returns the NodePath at or above the referenced node
06023 //               that is hidden to the indicated camera(s), or an
06024 //               empty NodePath if no ancestor of the referenced node
06025 //               is hidden (and the node should be visible).
06026 ////////////////////////////////////////////////////////////////////
06027 NodePath NodePath::
06028 get_hidden_ancestor(DrawMask camera_mask, Thread *current_thread) const {
06029   int pipeline_stage = current_thread->get_pipeline_stage();
06030 
06031   NodePathComponent *comp;
06032   for (comp = _head; 
06033        comp != (NodePathComponent *)NULL; 
06034        comp = comp->get_next(pipeline_stage, current_thread)) {
06035     PandaNode *node = comp->get_node();
06036     if (node->is_overall_hidden() ||
06037         ((node->get_draw_show_mask() | ~node->get_draw_control_mask()) & camera_mask).is_zero()) {
06038       NodePath result;
06039       result._head = comp;
06040       return result;
06041     }
06042   }
06043 
06044   return not_found();
06045 }
06046 
06047 ////////////////////////////////////////////////////////////////////
06048 //     Function: NodePath::stash
06049 //       Access: Published
06050 //  Description: Removes the referenced node (and the entire subgraph
06051 //               below this node) from the scene graph in any normal
06052 //               sense.  The node will no longer be visible and is not
06053 //               tested for collisions; furthermore, no normal scene
06054 //               graph traversal will visit the node.  The node's
06055 //               bounding volume no longer contributes to its parent's
06056 //               bounding volume.
06057 //
06058 //               A stashed node cannot be located by a normal find()
06059 //               operation (although a special find string can still
06060 //               retrieve it).
06061 ////////////////////////////////////////////////////////////////////
06062 void NodePath::
06063 stash(int sort, Thread *current_thread) {
06064   nassertv_always(!is_singleton() && !is_empty());
06065   nassertv(verify_complete());
06066 
06067   int pipeline_stage = current_thread->get_pipeline_stage();
06068   bool reparented = PandaNode::reparent(_head->get_next(pipeline_stage, current_thread),
06069                                         _head, sort, true, pipeline_stage,
06070                                         current_thread);
06071   nassertv(reparented);
06072 }
06073 
06074 ////////////////////////////////////////////////////////////////////
06075 //     Function: NodePath::unstash
06076 //       Access: Published
06077 //  Description: Undoes the effect of a previous stash() on this
06078 //               node: makes the referenced node (and the entire
06079 //               subgraph below this node) once again part of the
06080 //               scene graph.
06081 ////////////////////////////////////////////////////////////////////
06082 void NodePath::
06083 unstash(int sort, Thread *current_thread) {
06084   nassertv_always(!is_singleton() && !is_empty());
06085   nassertv(verify_complete());
06086 
06087   int pipeline_stage = current_thread->get_pipeline_stage();
06088   bool reparented = PandaNode::reparent(_head->get_next(pipeline_stage, current_thread),
06089                                         _head, sort, false, pipeline_stage,
06090                                         current_thread);
06091   nassertv(reparented);
06092 }
06093 
06094 ////////////////////////////////////////////////////////////////////
06095 //     Function: NodePath::unstash_all
06096 //       Access: Published
06097 //  Description: Unstashes this node and all stashed child nodes.
06098 ////////////////////////////////////////////////////////////////////
06099 void NodePath::
06100 unstash_all(Thread *current_thread) {
06101   NodePathCollection stashed_descendents = find_all_matches("**/@@*");
06102   stashed_descendents.unstash();
06103   unstash(0, current_thread);
06104 }
06105 
06106 ////////////////////////////////////////////////////////////////////
06107 //     Function: NodePath::get_stashed_ancestor
06108 //       Access: Published
06109 //  Description: Returns the NodePath at or above the referenced node
06110 //               that is stashed, or an empty NodePath if no ancestor
06111 //               of the referenced node is stashed (and the node should
06112 //               be visible).
06113 ////////////////////////////////////////////////////////////////////
06114 NodePath NodePath::
06115 get_stashed_ancestor(Thread *current_thread) const {
06116   NodePathComponent *comp = _head;
06117   if (comp != (NodePathComponent *)NULL) {
06118     int pipeline_stage = current_thread->get_pipeline_stage();
06119     NodePathComponent *next = comp->get_next(pipeline_stage, current_thread);
06120 
06121     while (next != (NodePathComponent *)NULL) {
06122       PandaNode *node = comp->get_node();
06123       PandaNode *parent_node = next->get_node();
06124 
06125       if (parent_node->find_stashed(node) >= 0) {
06126         NodePath result;
06127         result._head = comp;
06128         return result;
06129       }
06130 
06131       comp = next;
06132       next = next->get_next(pipeline_stage, current_thread);
06133     }
06134   }
06135 
06136   return not_found();
06137 }
06138 
06139 ////////////////////////////////////////////////////////////////////
06140 //     Function: NodePath::verify_complete
06141 //       Access: Published
06142 //  Description: Returns true if all of the nodes described in the
06143 //               NodePath are connected, or false otherwise.
06144 ////////////////////////////////////////////////////////////////////
06145 bool NodePath::
06146 verify_complete(Thread *current_thread) const {
06147   if (is_empty()) {
06148     return true;
06149   }
06150 
06151 #ifdef HAVE_THREADS
06152   if (Thread::is_true_threads()) {
06153     // In a threaded environment, we can't reliably test this, since a
06154     // sub-thread may be mucking with the NodePath's ancestry as we
06155     // try to validate it.  NodePaths are inherently not thread-safe,
06156     // but generally that's not an issue.
06157     return true;
06158   }
06159 #endif  // HAVE_THREADS
06160 
06161   static PStatCollector _verify_complete_pcollector("*:NodePath:verify_complete");
06162   PStatTimer timer(_verify_complete_pcollector);
06163 
06164   const NodePathComponent *comp = _head;
06165   nassertr(comp != (const NodePathComponent *)NULL, false);
06166 
06167   int pipeline_stage = current_thread->get_pipeline_stage();
06168 
06169   PandaNode *node = comp->get_node();
06170   nassertr(node != (const PandaNode *)NULL, false);
06171   int length = comp->get_length(pipeline_stage, current_thread);
06172   
06173   comp = comp->get_next(pipeline_stage, current_thread);
06174   length--;
06175   while (comp != (const NodePathComponent *)NULL) {
06176     PandaNode *next_node = comp->get_node();
06177     nassertr(next_node != (const PandaNode *)NULL, false);
06178 
06179     if (node->find_parent(next_node) < 0) {
06180       pgraph_cat.warning()
06181         << *this << " is incomplete; " << *node << " is not a child of "
06182         << *next_node << "\n";
06183       return false;
06184     }
06185 
06186     if (comp->get_length(pipeline_stage, current_thread) != length) {
06187       pgraph_cat.warning()
06188         << *this << " is incomplete; length at " << *next_node
06189         << " indicates " << comp->get_length(pipeline_stage, current_thread)
06190         << " while length at " << *node << " indicates " << length << "\n";
06191       return false;
06192     }
06193 
06194     node = next_node;
06195     comp = comp->get_next(pipeline_stage, current_thread);
06196     length--;
06197   }
06198 
06199   return true;
06200 }
06201 
06202 ////////////////////////////////////////////////////////////////////
06203 //     Function: NodePath::premunge_scene
06204 //       Access: Published
06205 //  Description: Walks through the scene graph beginning at the bottom
06206 //               node, and internally adjusts any GeomVertexFormats
06207 //               for optimal rendering on the indicated GSG.  If this
06208 //               step is not done prior to rendering, the formats will
06209 //               be optimized at render time instead, for a small
06210 //               cost.
06211 //
06212 //               It is not normally necessary to do this on a model
06213 //               loaded directly from disk, since the loader will do
06214 //               this by default.
06215 ////////////////////////////////////////////////////////////////////
06216 void NodePath::
06217 premunge_scene(GraphicsStateGuardianBase *gsg) {
06218   nassertv_always(!is_empty());
06219 
06220   CPT(RenderState) state = RenderState::make_empty();
06221   if (has_parent()) {
06222     state = get_parent().get_net_state();
06223   }
06224 
06225   SceneGraphReducer gr(gsg);
06226   gr.premunge(node(), state);
06227 }
06228 
06229 ////////////////////////////////////////////////////////////////////
06230 //     Function: NodePath::prepare_scene
06231 //       Access: Published
06232 //  Description: Walks through the scene graph beginning at the bottom
06233 //               node, and does whatever initialization is required to
06234 //               render the scene properly with the indicated GSG.  It
06235 //               is not strictly necessary to call this, since the GSG
06236 //               will initialize itself when the scene is rendered,
06237 //               but this may take some of the overhead away from that
06238 //               process.
06239 //
06240 //               In particular, this will ensure that textures within
06241 //               the scene are loaded in texture memory, and display
06242 //               lists are built up from static geometry.
06243 ////////////////////////////////////////////////////////////////////
06244 void NodePath::
06245 prepare_scene(GraphicsStateGuardianBase *gsg) {
06246   nassertv_always(!is_empty());
06247 
06248   CPT(RenderState) net_state = get_net_state();
06249   node()->prepare_scene(gsg, net_state);
06250 }
06251 
06252 ////////////////////////////////////////////////////////////////////
06253 //     Function: NodePath::show_bounds
06254 //       Access: Published
06255 //  Description: Causes the bounding volume of the bottom node and all
06256 //               of its descendants (that is, the bounding volume
06257 //               associated with the the bottom arc) to be rendered,
06258 //               if possible.  The rendering method is less than
06259 //               optimal; this is intended primarily for debugging.
06260 ////////////////////////////////////////////////////////////////////
06261 void NodePath::
06262 show_bounds() {
06263   nassertv_always(!is_empty());
06264   node()->set_effect(ShowBoundsEffect::make(false));
06265 }
06266 
06267 ////////////////////////////////////////////////////////////////////
06268 //     Function: NodePath::show_tight_bounds
06269 //       Access: Published
06270 //  Description: Similar to show_bounds(), this draws a bounding box
06271 //               representing the "tight" bounds of this node and all
06272 //               of its descendants.  The bounding box is recomputed
06273 //               every frame by reexamining all of the vertices; this
06274 //               is far from efficient, but this is intended for
06275 //               debugging.
06276 ////////////////////////////////////////////////////////////////////
06277 void NodePath::
06278 show_tight_bounds() {
06279   nassertv_always(!is_empty());
06280   node()->set_effect(ShowBoundsEffect::make(true));
06281 }
06282 
06283 ////////////////////////////////////////////////////////////////////
06284 //     Function: NodePath::hide_bounds
06285 //       Access: Published
06286 //  Description: Stops the rendering of the bounding volume begun with
06287 //               show_bounds().
06288 ////////////////////////////////////////////////////////////////////
06289 void NodePath::
06290 hide_bounds() {
06291   nassertv_always(!is_empty());
06292   node()->clear_effect(ShowBoundsEffect::get_class_type());
06293 }
06294 
06295 ////////////////////////////////////////////////////////////////////
06296 //     Function: NodePath::get_bounds
06297 //       Access: Published
06298 //  Description: Returns a newly-allocated bounding volume containing
06299 //               the bottom node and all of its descendants.  This is
06300 //               the bounding volume on the bottom arc, converted to
06301 //               the local coordinate space of the node.
06302 ////////////////////////////////////////////////////////////////////
06303 PT(BoundingVolume) NodePath::
06304 get_bounds(Thread *current_thread) const {
06305   nassertr_always(!is_empty(), new BoundingSphere);
06306   return node()->get_bounds(current_thread)->make_copy();
06307 }
06308 
06309 ////////////////////////////////////////////////////////////////////
06310 //     Function: NodePath::force_recompute_bounds
06311 //       Access: Published
06312 //  Description: Forces the recomputing of all the bounding volumes at
06313 //               every node in the subgraph beginning at this node and
06314 //               below.
06315 //
06316 //               This should not normally need to be called, since the
06317 //               bounding volumes are supposed to be recomputed
06318 //               automatically when necessary.  It may be useful when
06319 //               debugging, to verify that the bounding volumes have
06320 //               not become inadvertently stale; it may also be useful
06321 //               to force animated characters to update their bounding
06322 //               volumes (which does not presently happen
06323 //               automatically).
06324 ////////////////////////////////////////////////////////////////////
06325 void NodePath::
06326 force_recompute_bounds() {
06327   nassertv_always(!is_empty());
06328   r_force_recompute_bounds(node());
06329 }
06330 
06331 ////////////////////////////////////////////////////////////////////
06332 //     Function: NodePath::write_bounds
06333 //       Access: Published
06334 //  Description: Writes a description of the bounding volume
06335 //               containing the bottom node and all of its descendants
06336 //               to the indicated output stream.
06337 ////////////////////////////////////////////////////////////////////
06338 void NodePath::
06339 write_bounds(ostream &out) const {
06340   get_bounds()->write(out);
06341 }
06342 
06343 ////////////////////////////////////////////////////////////////////
06344 //     Function: NodePath::calc_tight_bounds
06345 //       Access: Published
06346 //  Description: Calculates the minimum and maximum vertices of all
06347 //               Geoms at this NodePath's bottom node and below.  This
06348 //               is a tight bounding box; it will generally be tighter
06349 //               than the bounding volume returned by get_bounds()
06350 //               (but it is more expensive to compute).
06351 //
06352 //               The return value is true if any points are within the
06353 //               bounding volume, or false if none are.
06354 ////////////////////////////////////////////////////////////////////
06355 bool NodePath::
06356 calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
06357                   Thread *current_thread) const {
06358   min_point.set(0.0f, 0.0f, 0.0f);
06359   max_point.set(0.0f, 0.0f, 0.0f);
06360   nassertr_always(!is_empty(), false);
06361 
06362   bool found_any = false;
06363   node()->calc_tight_bounds(min_point, max_point, found_any, 
06364                             TransformState::make_identity(),
06365                             current_thread);
06366 
06367   return found_any;
06368 }
06369 
06370 /*
06371 
06372 NB: Had to remove this function to avoid circular dependency when
06373 moving SceneGraphAnalyzer into pgraphnodes, attempting to reduce size
06374 of pgraph.  This function is now defined as a Python extension
06375 function instead.
06376 
06377 ////////////////////////////////////////////////////////////////////
06378 //     Function: NodePath::analyze
06379 //       Access: Published
06380 //  Description: Analyzes the geometry below this node and reports the
06381 //               number of vertices, triangles, etc.  This is the same
06382 //               information reported by the bam-info program.
06383 ////////////////////////////////////////////////////////////////////
06384 void NodePath::
06385 analyze() const {
06386   nassertv_always(!is_empty());
06387   SceneGraphAnalyzer sga;
06388   sga.add_node(node());
06389 
06390   if (sga.get_num_lod_nodes() == 0) {
06391     sga.write(nout);
06392 
06393   } else {
06394     nout << "At highest LOD:\n";
06395     SceneGraphAnalyzer sga2;
06396     sga2.set_lod_mode(SceneGraphAnalyzer::LM_highest);
06397     sga2.add_node(node());
06398     sga2.write(nout);
06399 
06400     nout << "\nAt lowest LOD:\n";
06401     sga2.clear();
06402     sga2.set_lod_mode(SceneGraphAnalyzer::LM_lowest);
06403     sga2.add_node(node());
06404     sga2.write(nout);
06405 
06406     nout << "\nAll nodes:\n";
06407     sga.write(nout);
06408   }
06409 }
06410 */
06411 
06412 ////////////////////////////////////////////////////////////////////
06413 //     Function: NodePath::flatten_light
06414 //       Access: Published
06415 //  Description: Lightly flattens out the hierarchy below this node by
06416 //               applying transforms, colors, and texture matrices
06417 //               from the nodes onto the vertices, but does not remove
06418 //               any nodes.
06419 //
06420 //               This can result in improved rendering performance
06421 //               because there will be fewer transforms in the
06422 //               resulting scene graph, but the number of nodes will
06423 //               remain the same.
06424 //
06425 //               In particular, any NodePaths that reference nodes
06426 //               within this hierarchy will not be damaged.  However,
06427 //               since this operation will remove transforms from the
06428 //               scene graph, it may be dangerous to apply to nodes
06429 //               where you expect to dynamically modify the transform,
06430 //               or where you expect the geometry to remain in a
06431 //               particular local coordinate system.
06432 //
06433 //               The return value is always 0, since flatten_light
06434 //               does not remove any nodes.
06435 ////////////////////////////////////////////////////////////////////
06436 int NodePath::
06437 flatten_light() {
06438   nassertr_always(!is_empty(), 0);
06439   SceneGraphReducer gr;
06440   gr.apply_attribs(node());
06441 
06442   return 0;
06443 }
06444 
06445 ////////////////////////////////////////////////////////////////////
06446 //     Function: NodePath::flatten_medium
06447 //       Access: Published
06448 //  Description: A more thorough flattening than flatten_light(), this
06449 //               first applies all the transforms, colors, and texture
06450 //               matrices from the nodes onto the vertices, and then
06451 //               removes unneeded grouping nodes--nodes that have
06452 //               exactly one child, for instance, but have no special
06453 //               properties in themselves.
06454 //
06455 //               This results in improved performance over
06456 //               flatten_light() because the number of nodes in the
06457 //               scene graph is reduced.
06458 //
06459 //               The return value is the number of nodes removed.
06460 ////////////////////////////////////////////////////////////////////
06461 int NodePath::
06462 flatten_medium() {
06463   nassertr_always(!is_empty(), 0);
06464   SceneGraphReducer gr;
06465   gr.apply_attribs(node());
06466   int num_removed = gr.flatten(node(), 0);
06467 
06468   if (flatten_geoms) {
06469     gr.make_compatible_state(node());
06470     gr.collect_vertex_data(node());
06471     gr.unify(node(), true);
06472   }
06473 
06474   return num_removed;
06475 }
06476 
06477 ////////////////////////////////////////////////////////////////////
06478 //     Function: NodePath::flatten_strong
06479 //       Access: Published
06480 //  Description: The strongest possible flattening.  This first
06481 //               applies all of the transforms to the vertices, as in
06482 //               flatten_medium(), but then it will combine sibling
06483 //               nodes together when possible, in addition to removing
06484 //               unnecessary parent-child nodes.  This can result in
06485 //               substantially fewer nodes, but any nicely-grouped
06486 //               hierachical bounding volumes may be lost.
06487 //
06488 //               It is generally a good idea to apply this kind of
06489 //               flattening only to nodes that will be culled largely
06490 //               as a single unit, like a car.  Applying this to an
06491 //               entire scene may result in overall poorer performance
06492 //               because of less-effective culling.
06493 ////////////////////////////////////////////////////////////////////
06494 int NodePath::
06495 flatten_strong() {
06496   nassertr_always(!is_empty(), 0);
06497   SceneGraphReducer gr;
06498   gr.apply_attribs(node());
06499   int num_removed = gr.flatten(node(), ~0);
06500 
06501   if (flatten_geoms) {
06502     gr.make_compatible_state(node());
06503     gr.collect_vertex_data(node(), ~(SceneGraphReducer::CVD_format | SceneGraphReducer::CVD_name | SceneGraphReducer::CVD_animation_type));
06504     gr.unify(node(), false);
06505   }
06506 
06507   return num_removed;
06508 }
06509 
06510 ////////////////////////////////////////////////////////////////////
06511 //     Function: NodePath::apply_texture_colors
06512 //       Access: Published
06513 //  Description: Removes textures from Geoms at this node and below by
06514 //               applying the texture colors to the vertices.  This is
06515 //               primarily useful to simplify a low-LOD model.  The
06516 //               texture colors are replaced by flat colors that
06517 //               approximate the original textures.
06518 //
06519 //               Only the bottommost texture on each Geom is used (if
06520 //               there is more than one), and it is applied as if it
06521 //               were M_modulate, and WM_repeat, regardless of its
06522 //               actual settings.  If the texture has a
06523 //               simple_ram_image, this may be used if the main image
06524 //               isn't resident.
06525 //
06526 //               After this call, there will be no texturing specified
06527 //               at this level and below.  Of course, there might
06528 //               still be texturing inherited from above.
06529 ////////////////////////////////////////////////////////////////////
06530 void NodePath::
06531 apply_texture_colors() {
06532   nassertv_always(!is_empty());
06533   SceneGraphReducer gr;
06534   gr.apply_attribs(node(), SceneGraphReducer::TT_apply_texture_color | SceneGraphReducer::TT_tex_matrix | SceneGraphReducer::TT_other);
06535 }
06536 
06537 ////////////////////////////////////////////////////////////////////
06538 //     Function: NodePath::find_net_tag
06539 //       Access: Published
06540 //  Description: Returns the lowest ancestor of this node that
06541 //               contains a tag definition with the indicated key, if
06542 //               any, or an empty NodePath if no ancestor of this node
06543 //               contains this tag definition.  See set_tag().
06544 ////////////////////////////////////////////////////////////////////
06545 NodePath NodePath::
06546 find_net_tag(const string &key) const {
06547   if (is_empty()) {
06548     return NodePath::not_found();
06549   }
06550   if (has_tag(key)) {
06551     return *this;
06552   }
06553   return get_parent().find_net_tag(key);
06554 }
06555 
06556 #ifdef HAVE_PYTHON 
06557 ////////////////////////////////////////////////////////////////////
06558 //     Function: NodePath::find_net_python_tag
06559 //       Access: Published
06560 //  Description: Returns the lowest ancestor of this node that
06561 //               contains a tag definition with the indicated key, if
06562 //               any, or an empty NodePath if no ancestor of this node
06563 //               contains this tag definition.  See set_python_tag().
06564 ////////////////////////////////////////////////////////////////////
06565 NodePath NodePath::
06566 find_net_python_tag(const string &key) const {
06567   if (is_empty()) {
06568     return NodePath::not_found();
06569   }
06570   if (has_python_tag(key)) {
06571     return *this;
06572   }
06573   return get_parent().find_net_python_tag(key);
06574 }
06575 #endif  // HAVE_PYTHON
06576 
06577 ////////////////////////////////////////////////////////////////////
06578 //     Function: NodePath::write_bam_file
06579 //       Access: Published
06580 //  Description: Writes the contents of this node and below out to a
06581 //               bam file with the indicated filename.  This file may
06582 //               then be read in again, as is, at some later point.
06583 //               Returns true if successful, false on some kind of
06584 //               error.
06585 ////////////////////////////////////////////////////////////////////
06586 bool NodePath::
06587 write_bam_file(const Filename &filename) const {
06588   nassertr_always(!is_empty(), false);
06589 
06590   BamFile bam_file;
06591 
06592   bool okflag = false;
06593 
06594   if (bam_file.open_write(filename)) {
06595     if (bam_file.write_object(node())) {
06596       okflag = true;
06597     }
06598     bam_file.close();
06599   }
06600   return okflag;
06601 }
06602 
06603 ////////////////////////////////////////////////////////////////////
06604 //     Function: NodePath::write_bam_stream
06605 //       Access: Published
06606 //  Description: Writes the contents of this node and below out to the
06607 //               indicated stream.
06608 ////////////////////////////////////////////////////////////////////
06609 bool NodePath::
06610 write_bam_stream(ostream &out) const {
06611   nassertr_always(!is_empty(), false);
06612 
06613   BamFile bam_file;
06614 
06615   bool okflag = false;
06616 
06617   if (bam_file.open_write(out)) {
06618     if (bam_file.write_object(node())) {
06619       okflag = true;
06620     }
06621     bam_file.close();
06622   }
06623   return okflag;
06624 }
06625 
06626 ////////////////////////////////////////////////////////////////////
06627 //     Function: NodePath::encode_to_bam_stream
06628 //       Access: Published
06629 //  Description: Converts the NodePath object into a single
06630 //               stream of data using a BamWriter, and stores that
06631 //               data in the indicated string.  Returns true on
06632 //               success, false on failure.
06633 //
06634 //               If the BamWriter is NULL, this behaves the same way
06635 //               as NodePath::write_bam_stream() and
06636 //               PandaNode::encode_to_bam_stream(), in the sense that
06637 //               it only writes this node and all nodes below it.
06638 //
06639 //               However, if the BamWriter is not NULL, it behaves
06640 //               very differently.  In this case, it encodes the
06641 //               *entire graph* of all nodes connected to the
06642 //               NodePath, including all parent nodes and siblings.
06643 //               This is necessary for correct streaming of related
06644 //               NodePaths and restoration of instances, etc., but it
06645 //               does mean you must detach() a node before writing it
06646 //               if you want to limit the nodes that get written.
06647 //
06648 //               This method is used by __reduce__ to handle streaming
06649 //               of NodePaths to a pickle file.  The BamWriter case is
06650 //               used by the direct.stdpy.pickle module, while the
06651 //               saner, non-BamWriter case is used when the standard
06652 //               pickle module calls this function.
06653 ////////////////////////////////////////////////////////////////////
06654 bool NodePath::
06655 encode_to_bam_stream(string &data, BamWriter *writer) const {
06656   data.clear();
06657   ostringstream stream;
06658 
06659   DatagramOutputFile dout;
06660   if (!dout.open(stream)) {
06661     return false;
06662   }
06663   
06664   BamWriter local_writer;
06665   bool used_local_writer = false;
06666   if (writer == NULL) {
06667     // Create our own writer.
06668     
06669     if (!dout.write_header(_bam_header)) {
06670       return false;
06671     }
06672     writer = &local_writer;
06673     used_local_writer = true;
06674   }
06675   
06676   writer->set_target(&dout);
06677 
06678   int num_nodes = get_num_nodes();
06679   if (used_local_writer && num_nodes > 1) {
06680     // In this case--no BamWriter--we only write the bottom node.
06681     num_nodes = 1;
06682   }
06683 
06684   // Write an initial Datagram to represent the error type and
06685   // number of nodes.
06686   Datagram dg;
06687   dg.add_uint8(_error_type);
06688   dg.add_int32(num_nodes);
06689   
06690   if (!dout.put_datagram(dg)) {
06691     writer->set_target(NULL);
06692     return false;
06693   }
06694 
06695   // Now write the nodes, one at a time.
06696   for (int i = 0; i < num_nodes; ++i) {
06697     PandaNode *node = get_node(num_nodes - i - 1);
06698     nassertr(node != NULL, false);
06699     if (!writer->write_object(node)) {
06700       writer->set_target(NULL);
06701       return false;
06702     }
06703   }
06704   writer->set_target(NULL);
06705   
06706   data = stream.str();
06707   return true;
06708 }
06709 
06710 ////////////////////////////////////////////////////////////////////
06711 //     Function: NodePath::decode_from_bam_stream
06712 //       Access: Published, Static
06713 //  Description: Reads the string created by a previous call to
06714 //               encode_to_bam_stream(), and extracts and
06715 //               returns the NodePath on that string.  Returns NULL on
06716 //               error.
06717 ////////////////////////////////////////////////////////////////////
06718 NodePath NodePath::
06719 decode_from_bam_stream(const string &data, BamReader *reader) {
06720   NodePath result;
06721 
06722   istringstream stream(data);
06723 
06724   DatagramInputFile din;
06725   if (!din.open(stream)) {
06726     return NodePath::fail();
06727   }
06728 
06729   BamReader local_reader;
06730   if (reader == NULL) {
06731     // Create a local reader.
06732 
06733     string head;
06734     if (!din.read_header(head, _bam_header.size())) {
06735       return NodePath::fail();
06736     }
06737     
06738     if (head != _bam_header) {
06739       return NodePath::fail();
06740     }
06741 
06742     reader = &local_reader;
06743   }
06744   
06745   reader->set_source(&din);
06746 
06747   // One initial datagram to encode the error type, and the number of nodes.
06748   Datagram dg;
06749   if (!din.get_datagram(dg)) {
06750     return NodePath::fail();
06751   }
06752 
06753   DatagramIterator dgi(dg);
06754   ErrorType error_type = (ErrorType)dgi.get_uint8();
06755   int num_nodes = dgi.get_int32();
06756   if (num_nodes == 0) {
06757     // An empty NodePath.
06758     result._error_type = error_type;
06759 
06760   } else {
06761     // A real NodePath.  Ignore error_type.
06762     for (int i = 0; i < num_nodes; ++i) {
06763       TypedWritable *object = reader->read_object();
06764 
06765       if (object == (TypedWritable *)NULL ||
06766           !object->is_of_type(PandaNode::get_class_type())) {
06767         reader->set_source(NULL);
06768         return NodePath::fail();
06769       }
06770   
06771       if (!reader->resolve()) {
06772         reader->set_source(NULL);
06773         return NodePath::fail();
06774       }
06775 
06776       PandaNode *node = DCAST(PandaNode, object);
06777       result = NodePath(result, node);
06778     }
06779   }
06780   
06781   reader->set_source(NULL);
06782 
06783   return result;
06784 }
06785 
06786 ////////////////////////////////////////////////////////////////////
06787 //     Function: NodePath::find_common_ancestor
06788 //       Access: Private, Static
06789 //  Description: Walks up from both NodePaths to find the first node
06790 //               that both have in common, if any.  Fills a_count and
06791 //               b_count with the number of nodes below the common
06792 //               node in each path.
06793 //
06794 //               The return value is the NodePathComponent of the node
06795 //               they have in common, or NULL if they have nothing in
06796 //               common.
06797 ////////////////////////////////////////////////////////////////////
06798 NodePathComponent *NodePath::
06799 find_common_ancestor(const NodePath &a, const NodePath &b,
06800                      int &a_count, int &b_count, Thread *current_thread) {
06801   nassertr(!a.is_empty() && !b.is_empty(), NULL);
06802   NodePathComponent *ac = a._head;
06803   NodePathComponent *bc = b._head;
06804   a_count = 0;
06805   b_count = 0;
06806 
06807   int pipeline_stage = current_thread->get_pipeline_stage();
06808 
06809   // Shorten up the longer one until they are the same length.
06810   while (ac->get_length(pipeline_stage, current_thread) > bc->get_length(pipeline_stage, current_thread)) {
06811     nassertr(ac != (NodePathComponent *)NULL, NULL);
06812     ac = ac->get_next(pipeline_stage, current_thread);
06813     a_count++;
06814   }
06815   while (bc->get_length(pipeline_stage, current_thread) > ac->get_length(pipeline_stage, current_thread)) {
06816     nassertr(bc != (NodePathComponent *)NULL, NULL);
06817     bc = bc->get_next(pipeline_stage, current_thread);
06818     b_count++;
06819   }
06820 
06821   // Now shorten them both up until we reach the same component.
06822   while (ac != bc) {
06823     // These shouldn't go to NULL unless they both go there together. 
06824     nassertr(ac != (NodePathComponent *)NULL, NULL);
06825     nassertr(bc != (NodePathComponent *)NULL, NULL);
06826     ac = ac->get_next(pipeline_stage, current_thread);
06827     a_count++;
06828     bc = bc->get_next(pipeline_stage, current_thread);
06829     b_count++;
06830   }
06831 
06832   return ac;
06833 }
06834 
06835 ////////////////////////////////////////////////////////////////////
06836 //     Function: NodePath::r_get_net_state
06837 //       Access: Private
06838 //  Description: Recursively determines the net state changes to the
06839 //               indicated component node from the root of the graph.
06840 ////////////////////////////////////////////////////////////////////
06841 CPT(RenderState) NodePath::
06842 r_get_net_state(NodePathComponent *comp, Thread *current_thread) const {
06843   if (comp == (NodePathComponent *)NULL) {
06844     return RenderState::make_empty();
06845   } else {
06846     CPT(RenderState) state = comp->get_node()->get_state(current_thread);
06847     int pipeline_stage = current_thread->get_pipeline_stage();
06848     return r_get_net_state(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(state);
06849   }
06850 }
06851 
06852 ////////////////////////////////////////////////////////////////////
06853 //     Function: NodePath::r_get_partial_state
06854 //       Access: Private
06855 //  Description: Recursively determines the net state changes to the
06856 //               indicated component node from the nth node above it.
06857 //               If n exceeds the length of the path, this returns the
06858 //               net transform from the root of the graph.
06859 ////////////////////////////////////////////////////////////////////
06860 CPT(RenderState) NodePath::
06861 r_get_partial_state(NodePathComponent *comp, int n, 
06862                     Thread *current_thread) const {
06863   if (n == 0 || comp == (NodePathComponent *)NULL) {
06864     return RenderState::make_empty();
06865   } else {
06866     CPT(RenderState) state = comp->get_node()->get_state(current_thread);
06867     int pipeline_stage = current_thread->get_pipeline_stage();
06868     return r_get_partial_state(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(state);
06869   }
06870 }
06871 
06872 ////////////////////////////////////////////////////////////////////
06873 //     Function: NodePath::r_get_net_transform
06874 //       Access: Private
06875 //  Description: Recursively determines the net transform to the
06876 //               indicated component node from the root of the graph.
06877 ////////////////////////////////////////////////////////////////////
06878 CPT(TransformState) NodePath::
06879 r_get_net_transform(NodePathComponent *comp, Thread *current_thread) const {
06880   if (comp == (NodePathComponent *)NULL) {
06881     return TransformState::make_identity();
06882   } else {
06883     int pipeline_stage = current_thread->get_pipeline_stage();
06884     CPT(TransformState) net_transform = r_get_net_transform(comp->get_next(pipeline_stage, current_thread), current_thread);
06885     PandaNode *node = comp->get_node();
06886     CPT(TransformState) transform = node->get_transform(current_thread);
06887 
06888     CPT(RenderEffects) effects = node->get_effects(current_thread);
06889     if (effects->has_adjust_transform()) {
06890       effects->adjust_transform(net_transform, transform, node);
06891     }
06892       
06893     return net_transform->compose(transform);
06894   }
06895 }
06896 
06897 ////////////////////////////////////////////////////////////////////
06898 //     Function: NodePath::r_get_partial_transform
06899 //       Access: Private
06900 //  Description: Recursively determines the net transform to the
06901 //               indicated component node from the nth node above it.
06902 //               If n exceeds the length of the path, this returns the
06903 //               net transform from the root of the graph.
06904 //
06905 //               If any node in the path had a net_transform effect
06906 //               applied, returns NULL--in this case the partial
06907 //               transform cannot be easily determined.
06908 ////////////////////////////////////////////////////////////////////
06909 CPT(TransformState) NodePath::
06910 r_get_partial_transform(NodePathComponent *comp, int n, 
06911                         Thread *current_thread) const {
06912   if (n == 0 || comp == (NodePathComponent *)NULL) {
06913     return TransformState::make_identity();
06914   } else {
06915     if (comp->get_node()->get_effects(current_thread)->has_adjust_transform()) {
06916       return NULL;
06917     }
06918     CPT(TransformState) transform = comp->get_node()->get_transform(current_thread);
06919     int pipeline_stage = current_thread->get_pipeline_stage();
06920     CPT(TransformState) partial = r_get_partial_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread);
06921     if (partial == (const TransformState *)NULL) {
06922       return NULL;
06923     }
06924     return partial->compose(transform);
06925   }
06926 }
06927 
06928 ////////////////////////////////////////////////////////////////////
06929 //     Function: NodePath::r_get_net_prev_transform
06930 //       Access: Private
06931 //  Description: Recursively determines the net "previous" transform
06932 //               to the indicated component node from the root of the
06933 //               graph.
06934 ////////////////////////////////////////////////////////////////////
06935 CPT(TransformState) NodePath::
06936 r_get_net_prev_transform(NodePathComponent *comp, Thread *current_thread) const {
06937   if (comp == (NodePathComponent *)NULL) {
06938     return TransformState::make_identity();
06939   } else {
06940     CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread);
06941     int pipeline_stage = current_thread->get_pipeline_stage();
06942     return r_get_net_prev_transform(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(transform);
06943   }
06944 }
06945 
06946 ////////////////////////////////////////////////////////////////////
06947 //     Function: NodePath::r_get_partial_prev_transform
06948 //       Access: Private
06949 //  Description: Recursively determines the net "previous" transform
06950 //               to the indicated component node from the nth node
06951 //               above it.  If n exceeds the length of the path, this
06952 //               returns the net previous transform from the root of
06953 //               the graph.
06954 ////////////////////////////////////////////////////////////////////
06955 CPT(TransformState) NodePath::
06956 r_get_partial_prev_transform(NodePathComponent *comp, int n, Thread *current_thread) const {
06957   if (n == 0 || comp == (NodePathComponent *)NULL) {
06958     return TransformState::make_identity();
06959   } else {
06960     CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread);
06961     int pipeline_stage = current_thread->get_pipeline_stage();
06962     return r_get_partial_prev_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(transform);
06963   }
06964 }
06965 
06966 ////////////////////////////////////////////////////////////////////
06967 //     Function: NodePath::find_matches
06968 //       Access: Private
06969 //  Description: Finds up to max_matches matches against the given
06970 //               path string from this node and deeper.  The
06971 //               max_matches count indicates the maximum number of
06972 //               matches to return, or -1 not to limit the number
06973 //               returned.
06974 ////////////////////////////////////////////////////////////////////
06975 void NodePath::
06976 find_matches(NodePathCollection &result, const string &path,
06977              int max_matches) const {
06978   if (is_empty()) {
06979     pgraph_cat.warning()
06980       << "Attempt to extend an empty NodePath by '" << path
06981       << "'.\n";
06982     return;
06983   }
06984   FindApproxPath approx_path;
06985   if (approx_path.add_string(path)) {
06986     find_matches(result, approx_path, max_matches);
06987   }
06988 }
06989 
06990 ////////////////////////////////////////////////////////////////////
06991 //     Function: NodePath::find_matches
06992 //       Access: Private
06993 //  Description: Finds up to max_matches matches against the given
06994 //               approx_path from this node and deeper.  The
06995 //               max_matches count indicates the maximum number of
06996 //               matches to return, or -1 not to limit the number
06997 //               returned.
06998 ////////////////////////////////////////////////////////////////////
06999 void NodePath::
07000 find_matches(NodePathCollection &result, FindApproxPath &approx_path,
07001              int max_matches) const {
07002   if (is_empty()) {
07003     pgraph_cat.warning()
07004       << "Attempt to extend an empty NodePath by: " << approx_path << ".\n";
07005     return;
07006   }
07007 
07008   // We start with just one entry on the level.
07009   FindApproxLevelEntry *level = 
07010     new FindApproxLevelEntry(WorkingNodePath(*this), approx_path);
07011   nassertv(level->_node_path.is_valid());
07012 
07013   find_matches(result, level, max_matches);
07014 }
07015 
07016 ////////////////////////////////////////////////////////////////////
07017 //     Function: NodePath::find_matches
07018 //       Access: Private
07019 //  Description: The fundamental implementation of find_matches(),
07020 //               given a starting level (a linked list of
07021 //               FindApproxLevelEntry objects).
07022 ////////////////////////////////////////////////////////////////////
07023 void NodePath::
07024 find_matches(NodePathCollection &result, FindApproxLevelEntry *level,
07025              int max_matches) const {
07026   
07027   int num_levels_remaining = _max_search_depth;
07028 
07029   FindApproxLevelEntry *deleted_entries = NULL;
07030 
07031   while (num_levels_remaining > 0 && level != NULL) {
07032     if (pgraph_cat.is_spam()) {
07033       pgraph_cat.spam()
07034         << "find_matches pass: " << result << ", "
07035         << max_matches << ", " << num_levels_remaining << "\n";
07036       level->write_level(pgraph_cat.spam(false), 4);
07037     }
07038 
07039     num_levels_remaining--;
07040 
07041     FindApproxLevelEntry *next_level = NULL;
07042 
07043     // For each node in the current level, build up the set of possible
07044     // matches in the next level.
07045     FindApproxLevelEntry *entry = level;
07046     while (entry != (FindApproxLevelEntry *)NULL) {
07047       if (entry->consider_node(result, next_level, max_matches, 0)) {
07048         // If we found the requisite number of matches, we can stop.
07049         // Delete all remaining entries and return immediately.
07050 
07051         while (entry != (FindApproxLevelEntry *)NULL) {
07052           FindApproxLevelEntry *next = entry->_next;
07053           delete entry;
07054           entry = next;
07055         }
07056         while (next_level != (FindApproxLevelEntry *)NULL) {
07057           FindApproxLevelEntry *next = next_level->_next;
07058           delete next_level;
07059           next_level = next;
07060         }
07061         while (deleted_entries != (FindApproxLevelEntry *)NULL) {
07062           FindApproxLevelEntry *next = deleted_entries->_next;
07063           delete deleted_entries;
07064           deleted_entries = next;
07065         }
07066         return;
07067       }
07068 
07069       // Move the entry to the delete chain so we can delete it before
07070       // we return from this method.  (We can't delete it immediately,
07071       // because there might be WorkingNodePaths in the next_level
07072       // that reference the WorkingNodePath object within the entry.)
07073       FindApproxLevelEntry *next = entry->_next;
07074       entry->_next = deleted_entries;
07075       deleted_entries = entry;
07076 
07077       entry = next;
07078     }
07079     
07080     // Make sure the remaining entries from this level are added to
07081     // the delete chain.
07082     while (entry != (FindApproxLevelEntry *)NULL) {
07083       FindApproxLevelEntry *next = entry->_next;
07084       entry->_next = deleted_entries;
07085       deleted_entries = entry;
07086 
07087       entry = next;
07088     }
07089 
07090     level = next_level;
07091   }
07092 
07093   // Now it's safe to delete all entries on the delete chain.
07094   while (deleted_entries != (FindApproxLevelEntry *)NULL) {
07095     FindApproxLevelEntry *next = deleted_entries->_next;
07096     delete deleted_entries;
07097     deleted_entries = next;
07098   }
07099 }
07100 
07101 ////////////////////////////////////////////////////////////////////
07102 //     Function: NodePath::r_clear_model_nodes
07103 //       Access: Private
07104 //  Description: The recursive implementation of
07105 //               clear_model_nodes().  This walks through the
07106 //               subgraph defined by the indicated node and below.
07107 ////////////////////////////////////////////////////////////////////
07108 int NodePath::
07109 r_clear_model_nodes(PandaNode *node) {
07110   int count = 0;
07111 
07112   if (node->is_of_type(ModelNode::get_class_type())) {
07113     ModelNode *mnode;
07114     DCAST_INTO_R(mnode, node, count);
07115     mnode->set_preserve_transform(ModelNode::PT_drop_node);
07116     ++count;
07117   }
07118 
07119   PandaNode::Children cr = node->get_children();
07120   int num_children = cr.get_num_children();
07121   for (int i = 0; i < num_children; i++) {
07122     count += r_clear_model_nodes(cr.get_child(i));
07123   }
07124 
07125   return count;
07126 }
07127 
07128 ////////////////////////////////////////////////////////////////////
07129 //     Function: NodePath::r_adjust_all_priorities
07130 //       Access: Private
07131 //  Description: The recursive implementation of
07132 //               adjust_all_priorities().  This walks through the
07133 //               subgraph defined by the indicated node and below.
07134 ////////////////////////////////////////////////////////////////////
07135 void NodePath::
07136 r_adjust_all_priorities(PandaNode *node, int adjustment) {
07137   node->set_state(node->get_state()->adjust_all_priorities(adjustment));
07138   if (node->is_geom_node()) {
07139     GeomNode *gnode;
07140     DCAST_INTO_V(gnode, node);
07141 
07142     int num_geoms = gnode->get_num_geoms();
07143     for (int i = 0; i < num_geoms; i++) {
07144       gnode->set_geom_state(i, gnode->get_geom_state(i)->adjust_all_priorities(adjustment));
07145     }
07146   }
07147 
07148   PandaNode::Children cr = node->get_children();
07149   int num_children = cr.get_num_children();
07150   for (int i = 0; i < num_children; i++) {
07151     r_adjust_all_priorities(cr.get_child(i), adjustment);
07152   }
07153 }
07154 
07155 ////////////////////////////////////////////////////////////////////
07156 //     Function: NodePath::r_force_recompute_bounds
07157 //       Access: Private
07158 //  Description: 
07159 ////////////////////////////////////////////////////////////////////
07160 void NodePath::
07161 r_force_recompute_bounds(PandaNode *node) {
07162   if (node->is_geom_node()) {
07163     GeomNode *gnode;
07164     DCAST_INTO_V(gnode, node);
07165 
07166     int num_geoms = gnode->get_num_geoms();
07167     for (int i = 0; i < num_geoms; i++) {
07168       const Geom *geom = gnode->get_geom(i);
07169       geom->mark_bounds_stale();
07170     }
07171   }
07172 
07173   node->mark_bounds_stale();
07174 
07175   // Now consider children.
07176   PandaNode::Children cr = node->get_children();
07177   int num_children = cr.get_num_children();
07178   for (int i = 0; i < num_children; i++) {
07179     r_force_recompute_bounds(cr.get_child(i));
07180   }
07181 }
07182 
07183 ////////////////////////////////////////////////////////////////////
07184 //     Function: NodePath::r_set_collide_mask
07185 //       Access: Private
07186 //  Description: Recursively applies the indicated collide mask to the
07187 //               nodes at and below this node.
07188 ////////////////////////////////////////////////////////////////////
07189 void NodePath::
07190 r_set_collide_mask(PandaNode *node, 
07191                    CollideMask and_mask, CollideMask or_mask,
07192                    TypeHandle node_type) {
07193   if (node->is_of_type(node_type)) {
07194     CollideMask into_collide_mask = node->get_into_collide_mask();
07195     into_collide_mask = (into_collide_mask & and_mask) | or_mask;
07196     node->set_into_collide_mask(into_collide_mask);
07197   }
07198 
07199   PandaNode::Children cr = node->get_children();
07200   int num_children = cr.get_num_children();
07201   for (int i = 0; i < num_children; i++) {
07202     r_set_collide_mask(cr.get_child(i), and_mask, or_mask, node_type);
07203   }
07204 }
07205 
07206 ////////////////////////////////////////////////////////////////////
07207 //     Function: NodePath::r_has_vertex_column
07208 //       Access: Private
07209 //  Description: 
07210 ////////////////////////////////////////////////////////////////////
07211 bool NodePath::
07212 r_has_vertex_column(PandaNode *node, const InternalName *name) const {
07213   if (node->is_geom_node()) {
07214     GeomNode *gnode;
07215     DCAST_INTO_R(gnode, node, false);
07216 
07217     int num_geoms = gnode->get_num_geoms();
07218     for (int i = 0; i < num_geoms; i++) {
07219       const Geom *geom = gnode->get_geom(i);
07220       CPT(GeomVertexData) vdata = geom->get_vertex_data();
07221       if (vdata->has_column(name)) {
07222         return true;
07223       }
07224     }
07225   }
07226 
07227   // Now consider children.
07228   PandaNode::Children cr = node->get_children();
07229   int num_children = cr.get_num_children();
07230   for (int i = 0; i < num_children; i++) {
07231     PandaNode *child = cr.get_child(i);
07232     if (r_has_vertex_column(child, name)) {
07233       return true;
07234     }
07235   }
07236 
07237   return false;
07238 }
07239 
07240 ////////////////////////////////////////////////////////////////////
07241 //     Function: NodePath::r_find_all_vertex_columns
07242 //       Access: Private
07243 //  Description: 
07244 ////////////////////////////////////////////////////////////////////
07245 void NodePath::
07246 r_find_all_vertex_columns(PandaNode *node,
07247                           NodePath::InternalNames &vertex_columns) const {
07248   if (node->is_geom_node()) {
07249     GeomNode *gnode;
07250     DCAST_INTO_V(gnode, node);
07251 
07252     int num_geoms = gnode->get_num_geoms();
07253     for (int i = 0; i < num_geoms; ++i) {
07254       const Geom *geom = gnode->get_geom(i);
07255       const GeomVertexFormat *format = geom->get_vertex_data()->get_format();
07256       int num_arrays = format->get_num_arrays();
07257       for (int j = 0; j < num_arrays; ++j) {
07258         const GeomVertexArrayFormat *array = format->get_array(j);
07259         int num_columns = array->get_num_columns();
07260         for (int k = 0; k < num_columns; ++k) {
07261           const GeomVertexColumn *column = array->get_column(k);
07262           vertex_columns.insert(column->get_name());
07263         }
07264       }
07265     }
07266   }
07267 
07268   // Now consider children.
07269   PandaNode::Children cr = node->get_children();
07270   int num_children = cr.get_num_children();
07271   for (int i = 0; i < num_children; i++) {
07272     PandaNode *child = cr.get_child(i);
07273     r_find_all_vertex_columns(child, vertex_columns);
07274   }
07275 }
07276 
07277 ////////////////////////////////////////////////////////////////////
07278 //     Function: NodePath::r_find_texture
07279 //       Access: Private
07280 //  Description: 
07281 ////////////////////////////////////////////////////////////////////
07282 Texture *NodePath::
07283 r_find_texture(PandaNode *node, const RenderState *state,
07284                const GlobPattern &glob) const {
07285   if (node->is_geom_node()) {
07286     GeomNode *gnode;
07287     DCAST_INTO_R(gnode, node, NULL);
07288 
07289     int num_geoms = gnode->get_num_geoms();
07290     for (int i = 0; i < num_geoms; i++) {
07291       CPT(RenderState) geom_state = 
07292         state->compose(gnode->get_geom_state(i));
07293 
07294       // Look for a TextureAttrib on the state.
07295       const RenderAttrib *attrib =
07296         geom_state->get_attrib(TextureAttrib::get_class_slot());
07297       if (attrib != (const RenderAttrib *)NULL) {
07298         const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07299         for (int i = 0; i < ta->get_num_on_stages(); i++) {
07300           Texture *texture = ta->get_on_texture(ta->get_on_stage(i));
07301           if (texture != (Texture *)NULL) {
07302             if (glob.matches(texture->get_name())) {
07303               return texture;
07304             }
07305           }
07306         }
07307       }
07308     }
07309   }
07310 
07311   // Now consider children.
07312   PandaNode::Children cr = node->get_children();
07313   int num_children = cr.get_num_children();
07314   for (int i = 0; i < num_children; i++) {
07315     PandaNode *child = cr.get_child(i);
07316     CPT(RenderState) next_state = state->compose(child->get_state());
07317 
07318     Texture *result = r_find_texture(child, next_state, glob);
07319     if (result != (Texture *)NULL) {
07320       return result;
07321     }
07322   }
07323 
07324   return NULL;
07325 }
07326 
07327 ////////////////////////////////////////////////////////////////////
07328 //     Function: NodePath::r_find_all_textures
07329 //       Access: Private
07330 //  Description: 
07331 ////////////////////////////////////////////////////////////////////
07332 void NodePath::
07333 r_find_all_textures(PandaNode *node, const RenderState *state,
07334                     NodePath::Textures &textures) const {
07335   if (node->is_geom_node()) {
07336     GeomNode *gnode;
07337     DCAST_INTO_V(gnode, node);
07338 
07339     int num_geoms = gnode->get_num_geoms();
07340     for (int i = 0; i < num_geoms; i++) {
07341       CPT(RenderState) geom_state = 
07342         state->compose(gnode->get_geom_state(i));
07343 
07344       // Look for a TextureAttrib on the state.
07345       const RenderAttrib *attrib =
07346         geom_state->get_attrib(TextureAttrib::get_class_slot());
07347       if (attrib != (const RenderAttrib *)NULL) {
07348         const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07349         for (int i = 0; i < ta->get_num_on_stages(); i++) {
07350           Texture *texture = ta->get_on_texture(ta->get_on_stage(i));
07351           if (texture != (Texture *)NULL) {
07352             textures.insert(texture);
07353           }
07354         }
07355       }
07356     }
07357   }
07358 
07359   // Now consider children.
07360   PandaNode::Children cr = node->get_children();
07361   int num_children = cr.get_num_children();
07362   for (int i = 0; i < num_children; i++) {
07363     PandaNode *child = cr.get_child(i);
07364     CPT(RenderState) next_state = state->compose(child->get_state());
07365     r_find_all_textures(child, next_state, textures);
07366   }
07367 }
07368 
07369 ////////////////////////////////////////////////////////////////////
07370 //     Function: NodePath::r_find_texture
07371 //       Access: Private
07372 //  Description: 
07373 ////////////////////////////////////////////////////////////////////
07374 Texture * NodePath::
07375 r_find_texture(PandaNode *node, TextureStage *stage) const {
07376   // Look for a TextureAttrib on the node.
07377   const RenderAttrib *attrib =
07378     node->get_attrib(TextureAttrib::get_class_slot());
07379   if (attrib != (const RenderAttrib *)NULL) {
07380     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07381     if (ta->has_on_stage(stage)) {
07382       return ta->get_on_texture(stage);
07383     }
07384   }
07385 
07386   if (node->is_geom_node()) {
07387     GeomNode *gnode;
07388     DCAST_INTO_R(gnode, node, NULL);
07389 
07390     int num_geoms = gnode->get_num_geoms();
07391     for (int i = 0; i < num_geoms; i++) {
07392       CPT(RenderState) geom_state = gnode->get_geom_state(i);
07393 
07394       // Look for a TextureAttrib on the state.
07395       const RenderAttrib *attrib =
07396         geom_state->get_attrib(TextureAttrib::get_class_slot());
07397       if (attrib != (const RenderAttrib *)NULL) {
07398         const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07399         if (ta->has_on_stage(stage)) {
07400           return ta->get_on_texture(stage);
07401         }
07402       }
07403     }
07404   }
07405 
07406   // Now consider children.
07407   PandaNode::Children cr = node->get_children();
07408   int num_children = cr.get_num_children();
07409   for (int i = 0; i < num_children; i++) {
07410     PandaNode *child = cr.get_child(i);
07411 
07412     Texture *result = r_find_texture(child, stage);
07413     if (result != (Texture *)NULL) {
07414       return result;
07415     }
07416   }
07417 
07418   return NULL;
07419 }
07420 
07421 ////////////////////////////////////////////////////////////////////
07422 //     Function: NodePath::r_find_all_textures
07423 //       Access: Private
07424 //  Description: 
07425 ////////////////////////////////////////////////////////////////////
07426 void NodePath::
07427 r_find_all_textures(PandaNode *node, TextureStage *stage,
07428                     NodePath::Textures &textures) const {
07429   // Look for a TextureAttrib on the node.
07430   const RenderAttrib *attrib =
07431     node->get_attrib(TextureAttrib::get_class_slot());
07432   if (attrib != (const RenderAttrib *)NULL) {
07433     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07434     if (ta->has_on_stage(stage)) {
07435       textures.insert(ta->get_on_texture(stage));
07436     }
07437   }
07438 
07439   if (node->is_geom_node()) {
07440     GeomNode *gnode;
07441     DCAST_INTO_V(gnode, node);
07442 
07443     int num_geoms = gnode->get_num_geoms();
07444     for (int i = 0; i < num_geoms; i++) {
07445       CPT(RenderState) geom_state = gnode->get_geom_state(i);
07446 
07447       // Look for a TextureAttrib on the state.
07448       const RenderAttrib *attrib =
07449         geom_state->get_attrib(TextureAttrib::get_class_slot());
07450       if (attrib != (const RenderAttrib *)NULL) {
07451         const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07452         if (ta->has_on_stage(stage)) {
07453           textures.insert(ta->get_on_texture(stage));
07454         }
07455       }
07456     }
07457   }
07458 
07459   // Now consider children.
07460   PandaNode::Children cr = node->get_children();
07461   int num_children = cr.get_num_children();
07462   for (int i = 0; i < num_children; i++) {
07463     PandaNode *child = cr.get_child(i);
07464     r_find_all_textures(child, stage, textures);
07465   }
07466 }
07467 
07468 ////////////////////////////////////////////////////////////////////
07469 //     Function: NodePath::r_find_texture_stage
07470 //       Access: Private
07471 //  Description: 
07472 ////////////////////////////////////////////////////////////////////
07473 TextureStage * NodePath::
07474 r_find_texture_stage(PandaNode *node, const RenderState *state,
07475                      const GlobPattern &glob) const {
07476   if (node->is_geom_node()) {
07477     GeomNode *gnode;
07478     DCAST_INTO_R(gnode, node, NULL);
07479 
07480     int num_geoms = gnode->get_num_geoms();
07481     for (int i = 0; i < num_geoms; i++) {
07482       CPT(RenderState) geom_state = 
07483         state->compose(gnode->get_geom_state(i));
07484 
07485       // Look for a TextureAttrib on the state.
07486       const RenderAttrib *attrib =
07487         geom_state->get_attrib(TextureAttrib::get_class_slot());
07488       if (attrib != (const RenderAttrib *)NULL) {
07489         const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07490         for (int i = 0; i < ta->get_num_on_stages(); i++) {
07491           TextureStage *texture_stage = ta->get_on_stage(i);
07492           if (texture_stage != (TextureStage *)NULL) {
07493             if (glob.matches(texture_stage->get_name())) {
07494               return texture_stage;
07495             }
07496           }
07497         }
07498       }
07499     }
07500   }
07501 
07502   // Now consider children.
07503   PandaNode::Children cr = node->get_children();
07504   int num_children = cr.get_num_children();
07505   for (int i = 0; i < num_children; i++) {
07506     PandaNode *child = cr.get_child(i);
07507     CPT(RenderState) next_state = state->compose(child->get_state());
07508 
07509     TextureStage *result = r_find_texture_stage(child, next_state, glob);
07510     if (result != (TextureStage *)NULL) {
07511       return result;
07512     }
07513   }
07514 
07515   return NULL;
07516 }
07517 
07518 ////////////////////////////////////////////////////////////////////
07519 //     Function: NodePath::r_find_all_texture_stages
07520 //       Access: Private
07521 //  Description: 
07522 ////////////////////////////////////////////////////////////////////
07523 void NodePath::
07524 r_find_all_texture_stages(PandaNode *node, const RenderState *state,
07525                           NodePath::TextureStages &texture_stages) const {
07526   if (node->is_geom_node()) {
07527     GeomNode *gnode;
07528     DCAST_INTO_V(gnode, node);
07529 
07530     int num_geoms = gnode->get_num_geoms();
07531     for (int i = 0; i < num_geoms; i++) {
07532       CPT(RenderState) geom_state = 
07533         state->compose(gnode->get_geom_state(i));
07534 
07535       // Look for a TextureAttrib on the state.
07536       const RenderAttrib *attrib =
07537         geom_state->get_attrib(TextureAttrib::get_class_slot());
07538       if (attrib != (const RenderAttrib *)NULL) {
07539         const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07540         for (int i = 0; i < ta->get_num_on_stages(); i++) {
07541           TextureStage *texture_stage = ta->get_on_stage(i);
07542           if (texture_stage != (TextureStage *)NULL) {
07543             texture_stages.insert(texture_stage);
07544           }
07545         }
07546       }
07547     }
07548   }
07549 
07550   // Now consider children.
07551   PandaNode::Children cr = node->get_children();
07552   int num_children = cr.get_num_children();
07553   for (int i = 0; i < num_children; i++) {
07554     PandaNode *child = cr.get_child(i);
07555     CPT(RenderState) next_state = state->compose(child->get_state());
07556     r_find_all_texture_stages(child, next_state, texture_stages);
07557   }
07558 }
07559 
07560 ////////////////////////////////////////////////////////////////////
07561 //     Function: NodePath::r_unify_texture_stages
07562 //       Access: Private
07563 //  Description: 
07564 ////////////////////////////////////////////////////////////////////
07565 void NodePath::
07566 r_unify_texture_stages(PandaNode *node, TextureStage *stage) {
07567   // Look for a TextureAttrib on the state.
07568   const RenderAttrib *attrib =
07569     node->get_attrib(TextureAttrib::get_class_slot());
07570   if (attrib != (const RenderAttrib *)NULL) {
07571     const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07572     CPT(RenderAttrib) new_attrib = ta->unify_texture_stages(stage);
07573     if (new_attrib != ta) {
07574       node->set_attrib(new_attrib);
07575     }
07576   }
07577 
07578   if (node->is_geom_node()) {
07579     GeomNode *gnode;
07580     DCAST_INTO_V(gnode, node);
07581 
07582     int num_geoms = gnode->get_num_geoms();
07583     for (int i = 0; i < num_geoms; i++) {
07584       CPT(RenderState) state = gnode->get_geom_state(i);
07585 
07586       // Look for a TextureAttrib on the state.
07587       const RenderAttrib *attrib =
07588         state->get_attrib(TextureAttrib::get_class_slot());
07589       if (attrib != (const RenderAttrib *)NULL) {
07590         const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
07591         CPT(RenderAttrib) new_attrib = ta->unify_texture_stages(stage);
07592         if (new_attrib != ta) {
07593           CPT(RenderState) new_state = state->add_attrib(new_attrib);
07594           gnode->set_geom_state(i, new_state);
07595         }
07596       }
07597     }
07598   }
07599 
07600   // Now consider children.
07601   PandaNode::Children cr = node->get_children();
07602   int num_children = cr.get_num_children();
07603   for (int i = 0; i < num_children; i++) {
07604     PandaNode *child = cr.get_child(i);
07605     r_unify_texture_stages(child, stage);
07606   }
07607 }
07608 
07609 ////////////////////////////////////////////////////////////////////
07610 //     Function: NodePath::r_find_material
07611 //       Access: Private
07612 //  Description: 
07613 ////////////////////////////////////////////////////////////////////
07614 Material *NodePath::
07615 r_find_material(PandaNode *node, const RenderState *state,
07616                const GlobPattern &glob) const {
07617   if (node->is_geom_node()) {
07618     GeomNode *gnode;
07619     DCAST_INTO_R(gnode, node, NULL);
07620 
07621     int num_geoms = gnode->get_num_geoms();
07622     for (int i = 0; i < num_geoms; i++) {
07623       CPT(RenderState) geom_state = 
07624         state->compose(gnode->get_geom_state(i));
07625 
07626       // Look for a MaterialAttrib on the state.
07627       const RenderAttrib *attrib =
07628         geom_state->get_attrib(MaterialAttrib::get_class_slot());
07629       if (attrib != (const RenderAttrib *)NULL) {
07630         const MaterialAttrib *ta = DCAST(MaterialAttrib, attrib);
07631         if (!ta->is_off()) {
07632           Material *material = ta->get_material();
07633           if (material != (Material *)NULL) {
07634             if (glob.matches(material->get_name())) {
07635               return material;
07636             }
07637           }
07638         }
07639       }
07640     }
07641   }
07642 
07643   // Now consider children.
07644   PandaNode::Children cr = node->get_children();
07645   int num_children = cr.get_num_children();
07646   for (int i = 0; i < num_children; i++) {
07647     PandaNode *child = cr.get_child(i);
07648     CPT(RenderState) next_state = state->compose(child->get_state());
07649 
07650     Material *result = r_find_material(child, next_state, glob);
07651     if (result != (Material *)NULL) {
07652       return result;
07653     }
07654   }
07655 
07656   return NULL;
07657 }
07658 
07659 ////////////////////////////////////////////////////////////////////
07660 //     Function: NodePath::r_find_all_materials
07661 //       Access: Private
07662 //  Description: 
07663 ////////////////////////////////////////////////////////////////////
07664 void NodePath::
07665 r_find_all_materials(PandaNode *node, const RenderState *state,
07666                     NodePath::Materials &materials) const {
07667   if (node->is_geom_node()) {
07668     GeomNode *gnode;
07669     DCAST_INTO_V(gnode, node);
07670 
07671     int num_geoms = gnode->get_num_geoms();
07672     for (int i = 0; i < num_geoms; i++) {
07673       CPT(RenderState) geom_state = 
07674         state->compose(gnode->get_geom_state(i));
07675 
07676       // Look for a MaterialAttrib on the state.
07677       const RenderAttrib *attrib =
07678         geom_state->get_attrib(MaterialAttrib::get_class_slot());
07679       if (attrib != (const RenderAttrib *)NULL) {
07680         const MaterialAttrib *ta = DCAST(MaterialAttrib, attrib);
07681         if (!ta->is_off()) {
07682           Material *material = ta->get_material();
07683           if (material != (Material *)NULL) {
07684             materials.insert(material);
07685           }
07686         }
07687       }
07688     }
07689   }
07690 
07691   // Now consider children.
07692   PandaNode::Children cr = node->get_children();
07693   int num_children = cr.get_num_children();
07694   for (int i = 0; i < num_children; i++) {
07695     PandaNode *child = cr.get_child(i);
07696     CPT(RenderState) next_state = state->compose(child->get_state());
07697     r_find_all_materials(child, next_state, materials);
07698   }
07699 }
07700 
07701 #ifdef HAVE_PYTHON
07702 ////////////////////////////////////////////////////////////////////
07703 //     Function: py_decode_NodePath_from_bam_stream
07704 //       Access: Published
07705 //  Description: This wrapper is defined as a global function to suit
07706 //               pickle's needs.
07707 ////////////////////////////////////////////////////////////////////
07708 NodePath
07709 py_decode_NodePath_from_bam_stream(const string &data) {
07710   return py_decode_NodePath_from_bam_stream_persist(NULL, data);
07711 }
07712 #endif  // HAVE_PYTHON
07713 
07714 
07715 #ifdef HAVE_PYTHON
07716 ////////////////////////////////////////////////////////////////////
07717 //     Function: py_decode_NodePath_from_bam_stream_persist
07718 //       Access: Published
07719 //  Description: This wrapper is defined as a global function to suit
07720 //               pickle's needs.
07721 ////////////////////////////////////////////////////////////////////
07722 NodePath
07723 py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data) {
07724   BamReader *reader = NULL;
07725   if (unpickler != NULL) {
07726     PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader");
07727     if (py_reader == NULL) {
07728       // It's OK if there's no bamReader.
07729       PyErr_Clear();
07730     } else {
07731       DTOOL_Call_ExtractThisPointerForType(py_reader, &Dtool_BamReader, (void **)&reader);
07732       Py_DECREF(py_reader);
07733     }
07734   }
07735 
07736   return NodePath::decode_from_bam_stream(data, reader);
07737 }
07738 #endif  // HAVE_PYTHON
07739 
 All Classes Functions Variables Enumerations