// Filename: nodePath.cxx // Created by: drose (25Feb02) // Updated by: fperazzi, PandaSE (06Apr10) (added more overloads // for set_shader_input) // Updated by: weifengh, PandaSE(30Apr10) (added set_shader_auto) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "nodePath.h" #include "nodePathCollection.h" #include "findApproxPath.h" #include "findApproxLevelEntry.h" #include "internalNameCollection.h" #include "config_pgraph.h" #include "colorAttrib.h" #include "colorScaleAttrib.h" #include "cullBinAttrib.h" #include "textureAttrib.h" #include "texMatrixAttrib.h" #include "texGenAttrib.h" #include "materialAttrib.h" #include "materialCollection.h" #include "lightAttrib.h" #include "clipPlaneAttrib.h" #include "occluderEffect.h" #include "polylightEffect.h" #include "fogAttrib.h" #include "renderModeAttrib.h" #include "cullFaceAttrib.h" #include "alphaTestAttrib.h" #include "depthTestAttrib.h" #include "depthWriteAttrib.h" #include "depthOffsetAttrib.h" #include "shaderAttrib.h" #include "billboardEffect.h" #include "compassEffect.h" #include "showBoundsEffect.h" #include "transparencyAttrib.h" #include "antialiasAttrib.h" #include "audioVolumeAttrib.h" #include "texProjectorEffect.h" #include "scissorEffect.h" #include "texturePool.h" #include "planeNode.h" #include "occluderNode.h" #include "lensNode.h" #include "materialPool.h" #include "look_at.h" #include "plist.h" #include "boundingSphere.h" #include "geomNode.h" #include "sceneGraphReducer.h" #include "textureCollection.h" #include "textureStageCollection.h" #include "globPattern.h" #include "shader.h" #include "shaderInput.h" #include "config_gobj.h" #include "bamFile.h" #include "preparedGraphicsObjects.h" #include "dcast.h" #include "pStatCollector.h" #include "pStatTimer.h" #include "modelNode.h" #include "py_panda.h" #include "bam.h" #include "bamWriter.h" // stack seems to overflow on Intel C++ at 7000. If we need more than // 7000, need to increase stack size. int NodePath::_max_search_depth = 7000; TypeHandle NodePath::_type_handle; PStatCollector NodePath::_get_transform_pcollector("*:NodePath:get_transform"); PStatCollector NodePath::_verify_complete_pcollector("*:NodePath:verify_complete"); #ifdef HAVE_PYTHON #include "py_panda.h" #ifndef CPPPARSER extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter; extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamReader; #endif // CPPPARSER #endif // HAVE_PYTHON // ***Begin temporary transition code for operator bool enum EmptyNodePathType { ENP_future, ENP_transition, ENP_deprecated, ENP_notify, }; ostream &operator << (ostream &out, EmptyNodePathType enp) { switch (enp) { case ENP_future: return out << "future"; case ENP_transition: return out << "transition"; case ENP_deprecated: return out << "deprecated"; case ENP_notify: return out << "notify"; } return out << "**invalid EmptyNodePathType value (" << (int)enp << ")**"; } istream &operator >> (istream &in, EmptyNodePathType &enp) { string word; in >> word; if (word == "future") { enp = ENP_future; } else if (word == "transition") { enp = ENP_transition; } else if (word == "deprecated") { enp = ENP_deprecated; } else if (word == "notify") { enp = ENP_notify; } else { pgraph_cat.warning() << "Invalid EmptyNodePathType value (\"" << word << "\")\n"; enp = ENP_transition; } return in; } static ConfigVariableEnum empty_node_path ("empty-node-path", ENP_future, PRC_DESC("This is a temporary transition variable to control the behavior " "of a NodePath when it is used as a boolean false. Set this to " "'deprecated' to preserve the original behavior: every NodePath " "evaluates true, even an empty NodePath. Set it to 'future' to " "support the new behavior: non-empty NodePaths evaluate true, " "and empty NodePaths evaluate false. Set it to 'transition' to " "raise an exception if an empty NodePath is used as a boolean.")); // ***End temporary transition code for operator bool //////////////////////////////////////////////////////////////////// // Function: NodePath::Constructor // Access: Published // Description: Constructs a NodePath with the indicated parent // NodePath and child node; the child node must be a // stashed or unstashed child of the parent. //////////////////////////////////////////////////////////////////// NodePath:: NodePath(const NodePath &parent, PandaNode *child_node, Thread *current_thread) : _error_type(ET_fail) { nassertv(child_node != (PandaNode *)NULL); int pipeline_stage = current_thread->get_pipeline_stage(); if (parent.is_empty()) { // Special case: constructing a NodePath at the root. _head = PandaNode::get_top_component(child_node, true, pipeline_stage, current_thread); } else { _head = PandaNode::get_component(parent._head, child_node, pipeline_stage, current_thread); } nassertv(_head != (NodePathComponent *)NULL); if (_head != (NodePathComponent *)NULL) { _error_type = ET_ok; } _backup_key = 0; } #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: NodePath::__copy__ // Access: Published // Description: A special Python method that is invoked by // copy.copy(node). Unlike the NodePath copy // constructor, this makes a duplicate copy of the // underlying PandaNode (but shares children, instead of // copying them or omitting them). //////////////////////////////////////////////////////////////////// NodePath NodePath:: __copy__() const { if (is_empty()) { // Invoke the copy constructor if we have no node. return *this; } // If we do have a node, duplicate it, and wrap it in a new // NodePath. return NodePath(node()->__copy__()); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: NodePath::__deepcopy__ // Access: Published // Description: A special Python method that is invoked by // copy.deepcopy(np). This calls copy_to() unless the // NodePath is already present in the provided // dictionary. //////////////////////////////////////////////////////////////////// PyObject *NodePath:: __deepcopy__(PyObject *self, PyObject *memo) const { IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath; // Borrowed reference. PyObject *dupe = PyDict_GetItem(memo, self); if (dupe != NULL) { // Already in the memo dictionary. Py_INCREF(dupe); return dupe; } NodePath *np_dupe; if (is_empty()) { np_dupe = new NodePath(*this); } else { np_dupe = new NodePath(copy_to(NodePath())); } dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath, true, false); if (PyDict_SetItem(memo, self, dupe) != 0) { Py_DECREF(dupe); return NULL; } return dupe; } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: NodePath::__reduce__ // Access: Published // Description: This special Python method is implement to provide // support for the pickle module. // // This hooks into the native pickle and cPickle // modules, but it cannot properly handle // self-referential BAM objects. //////////////////////////////////////////////////////////////////// PyObject *NodePath:: __reduce__(PyObject *self) const { return __reduce_persist__(self, NULL); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: NodePath::__reduce_persist__ // Access: Published // Description: This special Python method is implement to provide // support for the pickle module. // // This is similar to __reduce__, but it provides // additional support for the missing persistent-state // object needed to properly support self-referential // BAM objects written to the pickle stream. This hooks // into the pickle and cPickle modules implemented in // direct/src/stdpy. //////////////////////////////////////////////////////////////////// PyObject *NodePath:: __reduce_persist__(PyObject *self, PyObject *pickler) const { // We should return at least a 2-tuple, (Class, (args)): the // necessary class object whose constructor we should call // (e.g. this), and the arguments necessary to reconstruct this // object. BamWriter *writer = NULL; if (pickler != NULL) { PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter"); if (py_writer == NULL) { // It's OK if there's no bamWriter. PyErr_Clear(); } else { DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); Py_DECREF(py_writer); } } // We have a non-empty NodePath. string bam_stream; if (!encode_to_bam_stream(bam_stream, writer)) { ostringstream stream; stream << "Could not bamify " << this; string message = stream.str(); PyErr_SetString(PyExc_TypeError, message.c_str()); return NULL; } // Start by getting this class object. PyObject *this_class = PyObject_Type(self); if (this_class == NULL) { return NULL; } PyObject *func; if (writer != NULL) { // The modified pickle support: call the "persistent" version of // this function, which receives the unpickler itself as an // additional parameter. func = TypedWritable::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist"); if (func == NULL) { PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()"); Py_DECREF(this_class); return NULL; } } else { // The traditional pickle support: call the non-persistent version // of this function. func = TypedWritable::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream"); if (func == NULL) { PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()"); Py_DECREF(this_class); return NULL; } } PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size()); Py_DECREF(func); Py_DECREF(this_class); return result; } #endif // HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: NodePath::operator bool // Access: Published // Description: Returns true if the NodePath is valid (not empty), // or false if it contains no nodes. //////////////////////////////////////////////////////////////////// NodePath:: operator bool () const { switch (empty_node_path) { case ENP_future: return !is_empty(); case ENP_deprecated: return true; case ENP_notify: { const char *msg = "NodePath being used as a Boolean (talk to Zac)"; #ifdef HAVE_PYTHON PyErr_Warn(PyExc_FutureWarning, (char *)msg); #endif return !is_empty(); } case ENP_transition: if (!is_empty()) { return true; } { 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."; pgraph_cat.warning() << message << "\n"; #ifdef HAVE_PYTHON PyErr_Warn(PyExc_FutureWarning, (char *)message); #endif } return true; } nassertr(false, true); return true; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_num_nodes // Access: Published // Description: Returns the number of nodes in the path. //////////////////////////////////////////////////////////////////// int NodePath:: get_num_nodes(Thread *current_thread) const { if (is_empty()) { return 0; } int pipeline_stage = current_thread->get_pipeline_stage(); return _head->get_length(pipeline_stage, current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_node // Access: Published // Description: Returns the nth node of the path, where 0 is the // referenced (bottom) node and get_num_nodes() - 1 is // the top node. This requires iterating through the // path. // // Also see node(), which is a convenience function to // return the same thing as get_node(0) (since the // bottom node is the most important node in the // NodePath, and is the one most frequently referenced). // // Note that this function returns the same thing as // get_ancestor(index).node(). //////////////////////////////////////////////////////////////////// PandaNode *NodePath:: get_node(int index, Thread *current_thread) const { nassertr(index >= 0 && index < get_num_nodes(), NULL); int pipeline_stage = current_thread->get_pipeline_stage(); NodePathComponent *comp = _head; while (index > 0) { // If this assertion fails, the index was out of range; the // component's length must have been invalid. nassertr(comp != (NodePathComponent *)NULL, NULL); comp = comp->get_next(pipeline_stage, current_thread); index--; } // If this assertion fails, the index was out of range; the // component's length must have been invalid. nassertr(comp != (NodePathComponent *)NULL, NULL); return comp->get_node(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_ancestor // Access: Published // Description: Returns the nth ancestor of the path, where 0 is the // NodePath itself and get_num_nodes() - 1 is get_top(). // This requires iterating through the path. // // Also see get_node(), which returns the same thing as // a PandaNode pointer, not a NodePath. //////////////////////////////////////////////////////////////////// NodePath NodePath:: get_ancestor(int index, Thread *current_thread) const { nassertr(index >= 0 && index < get_num_nodes(), NodePath::fail()); int pipeline_stage = current_thread->get_pipeline_stage(); NodePathComponent *comp = _head; while (index > 0) { // If this assertion fails, the index was out of range; the // component's length must have been invalid. nassertr(comp != (NodePathComponent *)NULL, NodePath::fail()); comp = comp->get_next(pipeline_stage, current_thread); index--; } // If this assertion fails, the index was out of range; the // component's length must have been invalid. nassertr(comp != (NodePathComponent *)NULL, NodePath::fail()); NodePath result; result._head = comp; return result; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_top // Access: Published // Description: Returns a singleton NodePath that represents the top // of the path, or empty NodePath if this path is empty. //////////////////////////////////////////////////////////////////// NodePath NodePath:: get_top(Thread *current_thread) const { if (is_empty()) { return *this; } int pipeline_stage = current_thread->get_pipeline_stage(); NodePathComponent *comp = _head; while (!comp->is_top_node(pipeline_stage, current_thread)) { comp = comp->get_next(pipeline_stage, current_thread); nassertr(comp != (NodePathComponent *)NULL, NodePath::fail()); } NodePath top; top._head = comp; return top; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_children // Access: Published // Description: Returns the set of all child nodes of the referenced // node. //////////////////////////////////////////////////////////////////// NodePathCollection NodePath:: get_children(Thread *current_thread) const { NodePathCollection result; nassertr_always(!is_empty(), result); PandaNode *bottom_node = node(); int pipeline_stage = current_thread->get_pipeline_stage(); PandaNode::Children cr = bottom_node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { NodePath child; child._head = PandaNode::get_component(_head, cr.get_child(i), pipeline_stage, current_thread); result.add_path(child); } return result; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_stashed_children // Access: Published // Description: Returns the set of all child nodes of the referenced // node that have been stashed. These children are not // normally visible on the node, and do not appear in // the list returned by get_children(). //////////////////////////////////////////////////////////////////// NodePathCollection NodePath:: get_stashed_children(Thread *current_thread) const { NodePathCollection result; nassertr_always(!is_empty(), result); PandaNode *bottom_node = node(); int pipeline_stage = current_thread->get_pipeline_stage(); int num_stashed = bottom_node->get_num_stashed(); for (int i = 0; i < num_stashed; i++) { NodePath stashed; stashed._head = PandaNode::get_component(_head, bottom_node->get_stashed(i), pipeline_stage, current_thread); result.add_path(stashed); } return result; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_sort // Access: Published // Description: Returns the sort value of the referenced node within // its parent; that is, the sort number passed on the // last reparenting operation for this node. This will // control the position of the node within its parent's // list of children. //////////////////////////////////////////////////////////////////// int NodePath:: get_sort(Thread *current_thread) const { if (!has_parent()) { return 0; } int pipeline_stage = current_thread->get_pipeline_stage(); PandaNode *parent = _head->get_next(pipeline_stage, current_thread)->get_node(); PandaNode *child = node(); nassertr(parent != (PandaNode *)NULL && child != (PandaNode *)NULL, 0); int child_index = parent->find_child(child); if (child_index != -1) { return parent->get_child_sort(child_index); } child_index = parent->find_stashed(child); if (child_index != -1) { return parent->get_stashed_sort(child_index); } nassertr(false, 0); return 0; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find // Access: Published // Description: Searches for a node below the referenced node that // matches the indicated string. Returns the shortest // match found, if any, or an empty NodePath if no match // can be found. //////////////////////////////////////////////////////////////////// NodePath NodePath:: find(const string &path) const { nassertr_always(!is_empty(), fail()); NodePathCollection col; find_matches(col, path, 1); if (col.is_empty()) { return NodePath::not_found(); } return col.get_path(0); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_path_to // Access: Published // Description: Searches for the indicated node below this node and // returns the shortest NodePath that connects them. //////////////////////////////////////////////////////////////////// NodePath NodePath:: find_path_to(PandaNode *node) const { nassertr_always(!is_empty(), fail()); nassertr(node != (PandaNode *)NULL, fail()); NodePathCollection col; FindApproxPath approx_path; approx_path.add_match_many(0); approx_path.add_match_pointer(node, 0); find_matches(col, approx_path, 1); if (col.is_empty()) { return NodePath::not_found(); } return col.get_path(0); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_matches // Access: Published // Description: Returns the complete set of all NodePaths that begin // with this NodePath and can be extended by // path. The shortest paths will be listed // first. //////////////////////////////////////////////////////////////////// NodePathCollection NodePath:: find_all_matches(const string &path) const { NodePathCollection col; nassertr_always(!is_empty(), col); nassertr(verify_complete(), col); find_matches(col, path, -1); return col; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_paths_to // Access: Published // Description: Returns the set of all NodePaths that extend from // this NodePath down to the indicated node. The // shortest paths will be listed first. //////////////////////////////////////////////////////////////////// NodePathCollection NodePath:: find_all_paths_to(PandaNode *node) const { NodePathCollection col; nassertr_always(!is_empty(), col); nassertr(verify_complete(), col); nassertr(node != (PandaNode *)NULL, col); FindApproxPath approx_path; approx_path.add_match_many(0); approx_path.add_match_pointer(node, 0); find_matches(col, approx_path, -1); return col; } //////////////////////////////////////////////////////////////////// // Function: NodePath::reparent_to // Access: Published // Description: Removes the referenced node of the NodePath from its // current parent and attaches it to the referenced node // of the indicated NodePath. // // If the destination NodePath is empty, this is the // same thing as detach_node(). // // If the referenced node is already a child of the // indicated NodePath (via some other instance), this // operation fails and leaves the NodePath detached. //////////////////////////////////////////////////////////////////// void NodePath:: reparent_to(const NodePath &other, int sort, Thread *current_thread) { nassertv(verify_complete()); nassertv(other.verify_complete()); nassertv_always(!is_empty()); nassertv(other._error_type == ET_ok); // Reparenting implicitly resets the delta vector. node()->reset_prev_transform(); int pipeline_stage = current_thread->get_pipeline_stage(); bool reparented = PandaNode::reparent(other._head, _head, sort, false, pipeline_stage, current_thread); nassertv(reparented); } //////////////////////////////////////////////////////////////////// // Function: NodePath::stash_to // Access: Published // Description: Similar to reparent_to(), but the node is added to // its new parent's stashed list, so that the result is // equivalent to calling reparent_to() immediately // followed by stash(). //////////////////////////////////////////////////////////////////// void NodePath:: stash_to(const NodePath &other, int sort, Thread *current_thread) { nassertv(verify_complete()); nassertv(other.verify_complete()); nassertv_always(!is_empty()); nassertv(other._error_type == ET_ok); // Reparenting implicitly resets the delta vector. node()->reset_prev_transform(); int pipeline_stage = current_thread->get_pipeline_stage(); bool reparented = PandaNode::reparent(other._head, _head, sort, true, pipeline_stage, current_thread); nassertv(reparented); } //////////////////////////////////////////////////////////////////// // Function: NodePath::wrt_reparent_to // Access: Published // Description: This functions identically to reparent_to(), except // the transform on this node is also adjusted so that // the node remains in the same place in world // coordinates, even if it is reparented into a // different coordinate system. //////////////////////////////////////////////////////////////////// void NodePath:: wrt_reparent_to(const NodePath &other, int sort, Thread *current_thread) { nassertv(verify_complete(current_thread)); nassertv(other.verify_complete(current_thread)); nassertv_always(!is_empty()); nassertv(other._error_type == ET_ok); if (get_transform(current_thread) == get_prev_transform(current_thread)) { set_transform(get_transform(other, current_thread), current_thread); node()->reset_prev_transform(current_thread); } else { set_transform(get_transform(other, current_thread), current_thread); set_prev_transform(get_prev_transform(other, current_thread), current_thread); } reparent_to(other, sort, current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::instance_to // Access: Published // Description: Adds the referenced node of the NodePath as a child // of the referenced node of the indicated other // NodePath. Any other parent-child relations of the // node are unchanged; in particular, the node is not // removed from its existing parent, if any. // // If the node already had an existing parent, this // method will create a new instance of the node within // the scene graph. // // This does not change the NodePath itself, but does // return a new NodePath that reflects the new instance // node. // // If the destination NodePath is empty, this creates a // new instance which is not yet parented to any node. // A new instance of this sort cannot easily be // differentiated from other similar instances, but it // is nevertheless a different instance and it will // return a different get_id() value. // // If the referenced node is already a child of the // indicated NodePath, returns that already-existing // instance, unstashing it first if necessary. //////////////////////////////////////////////////////////////////// NodePath NodePath:: instance_to(const NodePath &other, int sort, Thread *current_thread) const { nassertr(verify_complete(), NodePath::fail()); nassertr(other.verify_complete(), NodePath::fail()); nassertr_always(!is_empty(), NodePath::fail()); nassertr(other._error_type == ET_ok, NodePath::fail()); NodePath new_instance; // First, we'll attach to NULL, to guarantee we get a brand new // instance. int pipeline_stage = current_thread->get_pipeline_stage(); new_instance._head = PandaNode::attach(NULL, node(), sort, pipeline_stage, current_thread); // Now, we'll reparent the new instance to the target node. bool reparented = PandaNode::reparent(other._head, new_instance._head, sort, false, pipeline_stage, current_thread); if (!reparented) { // Hmm, couldn't reparent. Either making this instance would // create a cycle, or it was already a child of that node. If it // was already a child, return that existing NodePath instead. NodePath orig(other, node(), current_thread); if (!orig.is_empty()) { if (orig.is_stashed()) { orig.unstash(); } return orig; } // Nope, it must be a cycle. nassertr(reparented, new_instance); } // instance_to() doesn't reset the velocity delta, unlike most of // the other reparenting operations. The reasoning is that // instance_to() is not necessarily a reparenting operation, since // it doesn't change the original instance. return new_instance; } //////////////////////////////////////////////////////////////////// // Function: NodePath::instance_under_node // Access: Published // Description: Behaves like instance_to(), but implicitly creates a // new node to instance the geometry under, and returns a // NodePath to that new node. This allows the // programmer to set a unique state and/or transform on // this instance. //////////////////////////////////////////////////////////////////// NodePath NodePath:: instance_under_node(const NodePath &other, const string &name, int sort, Thread *current_thread) const { NodePath new_node = other.attach_new_node(name, sort, current_thread); NodePath instance = instance_to(new_node, 0, current_thread); if (instance.is_empty()) { new_node.remove_node(current_thread); return instance; } return new_node; } //////////////////////////////////////////////////////////////////// // Function: NodePath::copy_to // Access: Published // Description: Functions like instance_to(), except a deep // copy is made of the referenced node and all of its // descendents, which is then parented to the indicated // node. A NodePath to the newly created copy is // returned. //////////////////////////////////////////////////////////////////// NodePath NodePath:: copy_to(const NodePath &other, int sort, Thread *current_thread) const { nassertr(verify_complete(current_thread), fail()); nassertr(other.verify_complete(current_thread), fail()); nassertr_always(!is_empty(), fail()); nassertr(other._error_type == ET_ok, fail()); PandaNode *source_node = node(); PT(PandaNode) copy_node = source_node->copy_subgraph(current_thread); nassertr(copy_node != (PandaNode *)NULL, fail()); copy_node->reset_prev_transform(current_thread); return other.attach_new_node(copy_node, sort, current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::attach_new_node // Access: Published // Description: Attaches a new node, with or without existing // parents, to the scene graph below the referenced node // of this NodePath. This is the preferred way to add // nodes to the graph. // // If the node was already a child of the parent, this // returns a NodePath to the existing child. // // This does *not* automatically extend the current // NodePath to reflect the attachment; however, a // NodePath that does reflect this extension is // returned. //////////////////////////////////////////////////////////////////// NodePath NodePath:: attach_new_node(PandaNode *node, int sort, Thread *current_thread) const { nassertr(verify_complete(current_thread), NodePath::fail()); nassertr(_error_type == ET_ok, NodePath::fail()); nassertr(node != (PandaNode *)NULL, NodePath::fail()); NodePath new_path(*this); int pipeline_stage = current_thread->get_pipeline_stage(); new_path._head = PandaNode::attach(_head, node, sort, pipeline_stage, current_thread); return new_path; } //////////////////////////////////////////////////////////////////// // Function: NodePath::remove_node // Access: Published // Description: Disconnects the referenced node from the scene graph. // This will also delete the node if there are no other // pointers to it. // // Normally, this should be called only when you are // really done with the node. If you want to remove a // node from the scene graph but keep it around for // later, you should probably use detach_node() instead. // // In practice, the only difference between // remove_node() and detach_node() is that remove_node() // also resets the NodePath to empty, which will cause // the node to be deleted immediately if there are no // other references. On the other hand, detach_node() // leaves the NodePath referencing the node, which will // keep at least one reference to the node for as long // as the NodePath exists. //////////////////////////////////////////////////////////////////// void NodePath:: remove_node(Thread *current_thread) { nassertv(_error_type != ET_not_found); // If we have no parents, remove_node() is just a do-nothing // operation; if we have no nodes, maybe we were already removed. // In either case, quietly do nothing except to ensure the // NodePath is clear. if (!is_empty() && !is_singleton(current_thread)) { node()->reset_prev_transform(current_thread); int pipeline_stage = current_thread->get_pipeline_stage(); PandaNode::detach(_head, pipeline_stage, current_thread); } if (is_empty() || _head->has_key()) { // Preserve the key we had on the node before we removed it. int key = get_key(); (*this) = NodePath::removed(); _backup_key = key; } else { // We didn't have a key; just clear the NodePath. (*this) = NodePath::removed(); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::detach_node // Access: Published // Description: Disconnects the referenced node from its parent, but // does not immediately delete it. The NodePath retains // a pointer to the node, and becomes a singleton // NodePath. // // This should be called to detach a node from the scene // graph, with the option of reattaching it later to the // same parent or to a different parent. // // In practice, the only difference between // remove_node() and detach_node() is that remove_node() // also resets the NodePath to empty, which will cause // the node to be deleted immediately if there are no // other references. On the other hand, detach_node() // leaves the NodePath referencing the node, which will // keep at least one reference to the node for as long // as the NodePath exists. //////////////////////////////////////////////////////////////////// void NodePath:: detach_node(Thread *current_thread) { nassertv(_error_type != ET_not_found); if (!is_empty() && !is_singleton()) { node()->reset_prev_transform(); int pipeline_stage = current_thread->get_pipeline_stage(); PandaNode::detach(_head, pipeline_stage, current_thread); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::output // Access: Published // Description: Writes a sensible description of the NodePath to the // indicated output stream. //////////////////////////////////////////////////////////////////// void NodePath:: output(ostream &out) const { switch (_error_type) { case ET_not_found: out << "**not found**"; return; case ET_removed: out << "**removed**"; return; case ET_fail: out << "**error**"; return; default: break; } if (_head == (NodePathComponent *)NULL) { out << "(empty)"; } else { _head->output(out); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_state // Access: Published // Description: Returns the complete state object set on this node. //////////////////////////////////////////////////////////////////// const RenderState *NodePath:: get_state(Thread *current_thread) const { // This method is declared non-inline to avoid a compiler bug in // gcc-3.4 and gcc-4.0. nassertr_always(!is_empty(), RenderState::make_empty()); return node()->get_state(current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_state // Access: Published // Description: Returns the state changes that must be made to // transition to the render state of this node from the // render state of the other node. //////////////////////////////////////////////////////////////////// CPT(RenderState) NodePath:: get_state(const NodePath &other, Thread *current_thread) const { nassertr(_error_type == ET_ok && other._error_type == ET_ok, RenderState::make_empty()); if (other.is_empty()) { return get_net_state(current_thread); } if (is_empty()) { return other.get_net_state(current_thread)->invert_compose(RenderState::make_empty()); } nassertr(verify_complete(current_thread), RenderState::make_empty()); nassertr(other.verify_complete(current_thread), RenderState::make_empty()); int a_count, b_count; if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) { if (allow_unrelated_wrt) { pgraph_cat.debug() << *this << " is not related to " << other << "\n"; } else { pgraph_cat.error() << *this << " is not related to " << other << "\n"; nassertr(false, RenderState::make_empty()); } } CPT(RenderState) a_state = r_get_partial_state(_head, a_count, current_thread); CPT(RenderState) b_state = r_get_partial_state(other._head, b_count, current_thread); return b_state->invert_compose(a_state); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_state // Access: Published // Description: Sets the state object on this node, relative to // the other node. This computes a new state object // that will have the indicated value when seen from the // other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_state(const NodePath &other, const RenderState *state, Thread *current_thread) { nassertv(_error_type == ET_ok && other._error_type == ET_ok); nassertv_always(!is_empty()); // First, we perform a wrt to the parent, to get the conversion. CPT(RenderState) rel_state; if (has_parent()) { rel_state = other.get_state(get_parent(current_thread), current_thread); } else { rel_state = other.get_state(NodePath(), current_thread); } CPT(RenderState) new_state = rel_state->compose(state); set_state(new_state, current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_transform // Access: Published // Description: Returns the complete transform object set on this node. //////////////////////////////////////////////////////////////////// const TransformState *NodePath:: get_transform(Thread *current_thread) const { // This method is declared non-inline to avoid a compiler bug in // gcc-3.4 and gcc-4.0. nassertr_always(!is_empty(), TransformState::make_identity()); return node()->get_transform(current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_transform // Access: Published // Description: Returns the relative transform to this node from the // other node; i.e. the transformation of this node // as seen from the other node. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: get_transform(const NodePath &other, Thread *current_thread) const { nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity()); PStatTimer timer(_get_transform_pcollector); if (other.is_empty()) { return get_net_transform(current_thread); } if (is_empty()) { return other.get_net_transform(current_thread)->invert_compose(TransformState::make_identity()); } nassertr(verify_complete(current_thread), TransformState::make_identity()); nassertr(other.verify_complete(current_thread), TransformState::make_identity()); int a_count, b_count; if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) { if (allow_unrelated_wrt) { if (pgraph_cat.is_debug()) { pgraph_cat.debug() << *this << " is not related to " << other << "\n"; } } else { pgraph_cat.error() << *this << " is not related to " << other << "\n"; nassertr(false, TransformState::make_identity()); } } CPT(TransformState) a_transform, b_transform; a_transform = r_get_partial_transform(_head, a_count, current_thread); if (a_transform != (TransformState *)NULL) { b_transform = r_get_partial_transform(other._head, b_count, current_thread); } if (b_transform == (TransformState *)NULL) { // If either path involved a node with a net_transform // RenderEffect applied, we have to go all the way up to the root // to get the right answer. a_transform = r_get_net_transform(_head, current_thread); b_transform = r_get_net_transform(other._head, current_thread); } return b_transform->invert_compose(a_transform); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_transform // Access: Published // Description: Sets the transform object on this node, relative to // the other node. This computes a new transform object // that will have the indicated value when seen from the // other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_transform(const NodePath &other, const TransformState *transform, Thread *current_thread) { nassertv(_error_type == ET_ok && other._error_type == ET_ok); nassertv_always(!is_empty()); // First, we perform a wrt to the parent, to get the conversion. CPT(TransformState) rel_trans; if (has_parent()) { rel_trans = other.get_transform(get_parent(current_thread), current_thread); } else { rel_trans = other.get_transform(NodePath(), current_thread); } CPT(TransformState) new_trans = rel_trans->compose(transform); set_transform(new_trans, current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_prev_transform // Access: Published // Description: Returns the transform that has been set as this // node's "previous" position. See // set_prev_transform(). //////////////////////////////////////////////////////////////////// const TransformState *NodePath:: get_prev_transform(Thread *current_thread) const { // This method is declared non-inline to avoid a compiler bug in // gcc-3.4 and gcc-4.0. nassertr_always(!is_empty(), TransformState::make_identity()); return node()->get_prev_transform(current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_prev_transform // Access: Published // Description: Returns the relative "previous" transform to this // node from the other node; i.e. the position of this // node in the previous frame, as seen by the other node // in the previous frame. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: get_prev_transform(const NodePath &other, Thread *current_thread) const { nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity()); if (other.is_empty()) { return get_net_prev_transform(current_thread); } if (is_empty()) { return other.get_net_prev_transform(current_thread)->invert_compose(TransformState::make_identity()); } nassertr(verify_complete(current_thread), TransformState::make_identity()); nassertr(other.verify_complete(current_thread), TransformState::make_identity()); int a_count, b_count; if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) { if (allow_unrelated_wrt) { pgraph_cat.debug() << *this << " is not related to " << other << "\n"; } else { pgraph_cat.error() << *this << " is not related to " << other << "\n"; nassertr(false, TransformState::make_identity()); } } CPT(TransformState) a_prev_transform = r_get_partial_prev_transform(_head, a_count, current_thread); CPT(TransformState) b_prev_transform = r_get_partial_prev_transform(other._head, b_count, current_thread); return b_prev_transform->invert_compose(a_prev_transform); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_prev_transform // Access: Published // Description: Sets the "previous" transform object on this node, // relative to the other node. This computes a new // transform object that will have the indicated value // when seen from the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_prev_transform(const NodePath &other, const TransformState *transform, Thread *current_thread) { nassertv(_error_type == ET_ok && other._error_type == ET_ok); nassertv_always(!is_empty()); // First, we perform a wrt to the parent, to get the conversion. CPT(TransformState) rel_trans; if (has_parent(current_thread)) { rel_trans = other.get_prev_transform(get_parent(current_thread), current_thread); } else { rel_trans = other.get_prev_transform(NodePath(), current_thread); } CPT(TransformState) new_trans = rel_trans->compose(transform); set_prev_transform(new_trans, current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos // Access: Published // Description: Sets the translation component of the transform, // leaving rotation and scale untouched. This also // resets the node's "previous" position, so that the // collision system will see the node as having suddenly // appeared in the new position, without passing any // points in between. // See Also: NodePath::set_fluid_pos //////////////////////////////////////////////////////////////////// void NodePath:: set_pos(const LVecBase3 &pos) { nassertv_always(!is_empty()); set_transform(get_transform()->set_pos(pos)); node()->reset_prev_transform(); } void NodePath:: set_x(PN_stdfloat x) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); pos[0] = x; set_pos(pos); } void NodePath:: set_y(PN_stdfloat y) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); pos[1] = y; set_pos(pos); } void NodePath:: set_z(PN_stdfloat z) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); pos[2] = z; set_pos(pos); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_fluid_pos // Access: Published // Description: Sets the translation component, without changing the // "previous" position, so that the collision system // will see the node as moving fluidly from its previous // position to its new position. // See Also: NodePath::set_pos //////////////////////////////////////////////////////////////////// void NodePath:: set_fluid_pos(const LVecBase3 &pos) { nassertv_always(!is_empty()); set_transform(get_transform()->set_pos(pos)); } void NodePath:: set_fluid_x(PN_stdfloat x) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); pos[0] = x; set_fluid_pos(pos); } void NodePath:: set_fluid_y(PN_stdfloat y) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); pos[1] = y; set_fluid_pos(pos); } void NodePath:: set_fluid_z(PN_stdfloat z) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); pos[2] = z; set_fluid_pos(pos); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_pos // Access: Published // Description: Retrieves the translation component of the transform. //////////////////////////////////////////////////////////////////// LPoint3 NodePath:: get_pos() const { nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f)); return get_transform()->get_pos(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_pos_delta // Access: Published // Description: Returns the delta vector from this node's position in // the previous frame (according to // set_prev_transform(), typically set via the use of // set_fluid_pos()) and its position in the current // frame. This is the vector used to determine // collisions. Generally, if the node was last // repositioned via set_pos(), the delta will be zero; // if it was adjusted via set_fluid_pos(), the delta // will represent the change from the previous frame's // position. //////////////////////////////////////////////////////////////////// LVector3 NodePath:: get_pos_delta() const { nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f)); return get_transform()->get_pos() - get_prev_transform()->get_pos(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_hpr // Access: Published // Description: Sets the rotation component of the transform, // leaving translation and scale untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_hpr(const LVecBase3 &hpr) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); nassertv(transform->has_hpr()); set_transform(transform->set_hpr(hpr)); } void NodePath:: set_h(PN_stdfloat h) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); nassertv(transform->has_hpr()); LVecBase3 hpr = transform->get_hpr(); hpr[0] = h; set_transform(transform->set_hpr(hpr)); } void NodePath:: set_p(PN_stdfloat p) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); nassertv(transform->has_hpr()); LVecBase3 hpr = transform->get_hpr(); hpr[1] = p; set_transform(transform->set_hpr(hpr)); } void NodePath:: set_r(PN_stdfloat r) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); nassertv(transform->has_hpr()); LVecBase3 hpr = transform->get_hpr(); hpr[2] = r; set_transform(transform->set_hpr(hpr)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_hpr // Access: Published // Description: Retrieves the rotation component of the transform. //////////////////////////////////////////////////////////////////// LVecBase3 NodePath:: get_hpr() const { nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f)); CPT(TransformState) transform = get_transform(); nassertr(transform->has_hpr(), LVecBase3(0.0f, 0.0f, 0.0f)); return transform->get_hpr(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_quat // Access: Published // Description: Sets the rotation component of the transform, // leaving translation and scale untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_quat(const LQuaternion &quat) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); set_transform(transform->set_quat(quat)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_quat // Access: Published // Description: Retrieves the rotation component of the transform. //////////////////////////////////////////////////////////////////// LQuaternion NodePath:: get_quat() const { nassertr_always(!is_empty(), LQuaternion::ident_quat()); CPT(TransformState) transform = get_transform(); return transform->get_quat(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_scale // Access: Published // Description: Sets the scale component of the transform, // leaving translation and rotation untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_scale(const LVecBase3 &scale) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); set_transform(transform->set_scale(scale)); } void NodePath:: set_sx(PN_stdfloat sx) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); LVecBase3 scale = transform->get_scale(); scale[0] = sx; set_transform(transform->set_scale(scale)); } void NodePath:: set_sy(PN_stdfloat sy) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); LVecBase3 scale = transform->get_scale(); scale[1] = sy; set_transform(transform->set_scale(scale)); } void NodePath:: set_sz(PN_stdfloat sz) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); LVecBase3 scale = transform->get_scale(); scale[2] = sz; set_transform(transform->set_scale(scale)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_scale // Access: Published // Description: Retrieves the scale component of the transform. //////////////////////////////////////////////////////////////////// LVecBase3 NodePath:: get_scale() const { nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f)); CPT(TransformState) transform = get_transform(); return transform->get_scale(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shear // Access: Published // Description: Sets the shear component of the transform, // leaving translation and rotation untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_shear(const LVecBase3 &shear) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); set_transform(transform->set_shear(shear)); } void NodePath:: set_shxy(PN_stdfloat shxy) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); LVecBase3 shear = transform->get_shear(); shear[0] = shxy; set_transform(transform->set_shear(shear)); } void NodePath:: set_shxz(PN_stdfloat shxz) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); LVecBase3 shear = transform->get_shear(); shear[1] = shxz; set_transform(transform->set_shear(shear)); } void NodePath:: set_shyz(PN_stdfloat shyz) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); LVecBase3 shear = transform->get_shear(); shear[2] = shyz; set_transform(transform->set_shear(shear)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_shear // Access: Published // Description: Retrieves the shear component of the transform. //////////////////////////////////////////////////////////////////// LVecBase3 NodePath:: get_shear() const { nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f)); CPT(TransformState) transform = get_transform(); return transform->get_shear(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_hpr // Access: Published // Description: Sets the translation and rotation component of the // transform, leaving scale untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_hpr(const LVecBase3 &pos, const LVecBase3 &hpr) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); transform = TransformState::make_pos_hpr_scale_shear (pos, hpr, transform->get_scale(), transform->get_shear()); set_transform(transform); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_quat // Access: Published // Description: Sets the translation and rotation component of the // transform, leaving scale untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_quat(const LVecBase3 &pos, const LQuaternion &quat) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); transform = TransformState::make_pos_quat_scale_shear (pos, quat, transform->get_scale(), transform->get_shear()); set_transform(transform); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_hpr_scale // Access: Published // Description: Sets the rotation and scale components of the // transform, leaving translation untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_hpr_scale(const LVecBase3 &hpr, const LVecBase3 &scale) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); transform = TransformState::make_pos_hpr_scale_shear (transform->get_pos(), hpr, scale, transform->get_shear()); set_transform(transform); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_quat_scale // Access: Published // Description: Sets the rotation and scale components of the // transform, leaving translation untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_quat_scale(const LQuaternion &quat, const LVecBase3 &scale) { nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(); transform = TransformState::make_pos_quat_scale_shear (transform->get_pos(), quat, scale, transform->get_shear()); set_transform(transform); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_hpr_scale // Access: Published // Description: Replaces the translation, rotation, and scale // components, implicitly setting shear to 0. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_hpr_scale(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale) { nassertv_always(!is_empty()); set_transform(TransformState::make_pos_hpr_scale (pos, hpr, scale)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_quat_scale // Access: Published // Description: Replaces the translation, rotation, and scale // components, implicitly setting shear to 0. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_quat_scale(const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale) { nassertv_always(!is_empty()); set_transform(TransformState::make_pos_quat_scale (pos, quat, scale)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_hpr_scale_shear // Access: Published // Description: Completely replaces the transform with new // translation, rotation, scale, and shear components. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_hpr_scale_shear(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale, const LVecBase3 &shear) { nassertv_always(!is_empty()); set_transform(TransformState::make_pos_hpr_scale_shear (pos, hpr, scale, shear)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_quat_scale_shear // Access: Published // Description: Completely replaces the transform with new // translation, rotation, scale, and shear components. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_quat_scale_shear(const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale, const LVecBase3 &shear) { nassertv_always(!is_empty()); set_transform(TransformState::make_pos_quat_scale_shear (pos, quat, scale, shear)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_mat // Access: Published // Description: Directly sets an arbitrary 4x4 transform matrix. //////////////////////////////////////////////////////////////////// void NodePath:: set_mat(const LMatrix4 &mat) { nassertv_always(!is_empty()); set_transform(TransformState::make_mat(mat)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::look_at // Access: Published // Description: Sets the hpr on this NodePath so that it // rotates to face the indicated point in space. //////////////////////////////////////////////////////////////////// void NodePath:: look_at(const LPoint3 &point, const LVector3 &up) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); LQuaternion quat; ::look_at(quat, point - pos, up); set_quat(quat); } //////////////////////////////////////////////////////////////////// // Function: NodePath::heads_up // Access: Published // Description: Behaves like look_at(), but with a strong preference // to keeping the up vector oriented in the indicated // "up" direction. //////////////////////////////////////////////////////////////////// void NodePath:: heads_up(const LPoint3 &point, const LVector3 &up) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(); LQuaternion quat; ::heads_up(quat, point - pos, up); set_quat(quat); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos // Access: Published // Description: Sets the translation component of the transform, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos(const NodePath &other, const LVecBase3 &pos) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other three components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_hpr = orig_transform->get_hpr(); const LVecBase3 &orig_scale = orig_transform->get_scale(); const LVecBase3 &orig_shear = orig_transform->get_shear(); set_transform(other, rel_transform->set_pos(pos)); set_pos_hpr_scale_shear(get_transform()->get_pos(), orig_hpr, orig_scale, orig_shear); } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, rel_transform->set_pos(pos)); } node()->reset_prev_transform(); } void NodePath:: set_x(const NodePath &other, PN_stdfloat x) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(other); pos[0] = x; set_pos(other, pos); } void NodePath:: set_y(const NodePath &other, PN_stdfloat y) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(other); pos[1] = y; set_pos(other, pos); } void NodePath:: set_z(const NodePath &other, PN_stdfloat z) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(other); pos[2] = z; set_pos(other, pos); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_fluid_pos // Access: Published // Description: Sets the translation component of the transform, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_fluid_pos(const NodePath &other, const LVecBase3 &pos) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other three components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_hpr = orig_transform->get_hpr(); const LVecBase3 &orig_scale = orig_transform->get_scale(); const LVecBase3 &orig_shear = orig_transform->get_shear(); // Use the relative set_transform() to compute the relative pos, and // then reset all of the other components back to the way they were. set_transform(other, rel_transform->set_pos(pos)); set_transform(TransformState::make_pos_hpr_scale_shear (get_transform()->get_pos(), orig_hpr, orig_scale, orig_shear)); } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, rel_transform->set_pos(pos)); } } void NodePath:: set_fluid_x(const NodePath &other, PN_stdfloat x) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(other); pos[0] = x; set_fluid_pos(other, pos); } void NodePath:: set_fluid_y(const NodePath &other, PN_stdfloat y) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(other); pos[1] = y; set_fluid_pos(other, pos); } void NodePath:: set_fluid_z(const NodePath &other, PN_stdfloat z) { nassertv_always(!is_empty()); LPoint3 pos = get_pos(other); pos[2] = z; set_fluid_pos(other, pos); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_pos // Access: Published // Description: Returns the relative position of the referenced node // as seen from the other node. //////////////////////////////////////////////////////////////////// LPoint3 NodePath:: get_pos(const NodePath &other) const { nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f)); return get_transform(other)->get_pos(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_pos_delta // Access: Published // Description: Returns the delta vector from this node's position in // the previous frame (according to // set_prev_transform(), typically set via the use of // set_fluid_pos()) and its position in the current // frame, as seen in the indicated node's coordinate // space. This is the vector used to determine // collisions. Generally, if the node was last // repositioned via set_pos(), the delta will be zero; // if it was adjusted via set_fluid_pos(), the delta // will represent the change from the previous frame's // position. //////////////////////////////////////////////////////////////////// LVector3 NodePath:: get_pos_delta(const NodePath &other) const { nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f)); return get_transform(other)->get_pos() - get_prev_transform(other)->get_pos(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_hpr // Access: Published // Description: Sets the rotation component of the transform, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_hpr(const NodePath &other, const LVecBase3 &hpr) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); nassertv(rel_transform->has_hpr()); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other three components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_pos = orig_transform->get_pos(); const LVecBase3 &orig_scale = orig_transform->get_scale(); const LVecBase3 &orig_shear = orig_transform->get_shear(); set_transform(other, rel_transform->set_hpr(hpr)); const TransformState *new_transform = get_transform(); if (new_transform->has_components()) { set_transform(TransformState::make_pos_hpr_scale_shear (orig_pos, new_transform->get_hpr(), orig_scale, orig_shear)); } } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, rel_transform->set_hpr(hpr)); } } void NodePath:: set_h(const NodePath &other, PN_stdfloat h) { nassertv_always(!is_empty()); LVecBase3 hpr = get_hpr(other); hpr[0] = h; set_hpr(other, hpr); } void NodePath:: set_p(const NodePath &other, PN_stdfloat p) { nassertv_always(!is_empty()); LVecBase3 hpr = get_hpr(other); hpr[1] = p; set_hpr(other, hpr); } void NodePath:: set_r(const NodePath &other, PN_stdfloat r) { nassertv_always(!is_empty()); LVecBase3 hpr = get_hpr(other); hpr[2] = r; set_hpr(other, hpr); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_hpr // Access: Published // Description: Returns the relative orientation of the bottom node // as seen from the other node. //////////////////////////////////////////////////////////////////// LVecBase3 NodePath:: get_hpr(const NodePath &other) const { nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f)); CPT(TransformState) transform = get_transform(other); nassertr(transform->has_hpr(), LVecBase3(0.0f, 0.0f, 0.0f)); return transform->get_hpr(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_quat // Access: Published // Description: Sets the rotation component of the transform, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_quat(const NodePath &other, const LQuaternion &quat) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other three components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_pos = orig_transform->get_pos(); const LVecBase3 &orig_scale = orig_transform->get_scale(); const LVecBase3 &orig_shear = orig_transform->get_shear(); set_transform(other, rel_transform->set_quat(quat)); const TransformState *new_transform = get_transform(); if (new_transform->has_components()) { set_transform(TransformState::make_pos_quat_scale_shear (orig_pos, new_transform->get_quat(), orig_scale, orig_shear)); } } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, rel_transform->set_quat(quat)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_quat // Access: Published // Description: Returns the relative orientation of the bottom node // as seen from the other node. //////////////////////////////////////////////////////////////////// LQuaternion NodePath:: get_quat(const NodePath &other) const { nassertr_always(!is_empty(), LQuaternion::ident_quat()); CPT(TransformState) transform = get_transform(other); return transform->get_quat(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_scale // Access: Published // Description: Sets the scale component of the transform, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_scale(const NodePath &other, const LVecBase3 &scale) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other three components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_pos = orig_transform->get_pos(); const LVecBase3 &orig_hpr = orig_transform->get_hpr(); const LVecBase3 &orig_shear = orig_transform->get_shear(); set_transform(other, rel_transform->set_scale(scale)); const TransformState *new_transform = get_transform(); if (new_transform->has_components()) { set_transform(TransformState::make_pos_hpr_scale_shear (orig_pos, orig_hpr, new_transform->get_scale(), orig_shear)); } } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, rel_transform->set_scale(scale)); } } void NodePath:: set_sx(const NodePath &other, PN_stdfloat sx) { nassertv_always(!is_empty()); LVecBase3 scale = get_scale(other); scale[0] = sx; set_scale(other, scale); } void NodePath:: set_sy(const NodePath &other, PN_stdfloat sy) { nassertv_always(!is_empty()); LVecBase3 scale = get_scale(other); scale[1] = sy; set_scale(other, scale); } void NodePath:: set_sz(const NodePath &other, PN_stdfloat sz) { nassertv_always(!is_empty()); LVecBase3 scale = get_scale(other); scale[2] = sz; set_scale(other, scale); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_scale // Access: Published // Description: Returns the relative scale of the bottom node // as seen from the other node. //////////////////////////////////////////////////////////////////// LVecBase3 NodePath:: get_scale(const NodePath &other) const { nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f)); CPT(TransformState) transform = get_transform(other); return transform->get_scale(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shear // Access: Published // Description: Sets the shear component of the transform, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_shear(const NodePath &other, const LVecBase3 &shear) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other three components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_pos = orig_transform->get_pos(); const LVecBase3 &orig_hpr = orig_transform->get_hpr(); const LVecBase3 &orig_scale = orig_transform->get_scale(); set_transform(other, rel_transform->set_shear(shear)); const TransformState *new_transform = get_transform(); if (new_transform->has_components()) { set_transform(TransformState::make_pos_hpr_scale_shear (orig_pos, orig_hpr, orig_scale, new_transform->get_shear())); } } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, rel_transform->set_shear(shear)); } } void NodePath:: set_shxy(const NodePath &other, PN_stdfloat shxy) { nassertv_always(!is_empty()); LVecBase3 shear = get_shear(other); shear[0] = shxy; set_shear(other, shear); } void NodePath:: set_shxz(const NodePath &other, PN_stdfloat shxz) { nassertv_always(!is_empty()); LVecBase3 shear = get_shear(other); shear[1] = shxz; set_shear(other, shear); } void NodePath:: set_shyz(const NodePath &other, PN_stdfloat shyz) { nassertv_always(!is_empty()); LVecBase3 shear = get_shear(other); shear[2] = shyz; set_shear(other, shear); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_shear // Access: Published // Description: Returns the relative shear of the bottom node // as seen from the other node. //////////////////////////////////////////////////////////////////// LVecBase3 NodePath:: get_shear(const NodePath &other) const { nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f)); CPT(TransformState) transform = get_transform(other); return transform->get_shear(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_hpr // Access: Published // Description: Sets the translation and rotation component of the // transform, relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_hpr(const NodePath &other, const LVecBase3 &pos, const LVecBase3 &hpr) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other two components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_scale = orig_transform->get_scale(); const LVecBase3 &orig_shear = orig_transform->get_shear(); set_transform(other, TransformState::make_pos_hpr_scale_shear (pos, hpr, rel_transform->get_scale(), rel_transform->get_shear())); const TransformState *new_transform = get_transform(); if (new_transform->has_components()) { set_pos_hpr_scale_shear(new_transform->get_pos(), new_transform->get_hpr(), orig_scale, orig_shear); } } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, TransformState::make_pos_hpr_scale_shear (pos, hpr, rel_transform->get_scale(), rel_transform->get_shear())); node()->reset_prev_transform(); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_quat // Access: Published // Description: Sets the translation and rotation component of the // transform, relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_quat(const NodePath &other, const LVecBase3 &pos, const LQuaternion &quat) { nassertv_always(!is_empty()); CPT(TransformState) rel_transform = get_transform(other); CPT(TransformState) orig_transform = get_transform(); if (orig_transform->has_components()) { // If we had a componentwise transform before we started, we // should be careful to preserve the other two components. We // wouldn't need to do this, except for the possibility of // numerical error or decompose ambiguity. const LVecBase3 &orig_scale = orig_transform->get_scale(); const LVecBase3 &orig_shear = orig_transform->get_shear(); set_transform(other, TransformState::make_pos_quat_scale_shear (pos, quat, rel_transform->get_scale(), rel_transform->get_shear())); const TransformState *new_transform = get_transform(); if (new_transform->has_components()) { set_pos_quat_scale_shear(new_transform->get_pos(), new_transform->get_quat(), orig_scale, orig_shear); } } else { // If we didn't have a componentwise transform already, never // mind. set_transform(other, TransformState::make_pos_quat_scale_shear (pos, quat, rel_transform->get_scale(), rel_transform->get_shear())); node()->reset_prev_transform(); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_hpr_scale // Access: Published // Description: Sets the rotation and scale components of the // transform, leaving translation untouched. This, or // set_pos_hpr_scale, is the preferred way to update a // transform when both hpr and scale are to be changed. //////////////////////////////////////////////////////////////////// void NodePath:: set_hpr_scale(const NodePath &other, const LVecBase3 &hpr, const LVecBase3 &scale) { // We don't bother trying very hard to preserve pos across this // operation, unlike the work we do above to preserve hpr or scale, // since it generally doesn't matter that much if pos is off by a // few thousandths. nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(other); transform = TransformState::make_pos_hpr_scale_shear (transform->get_pos(), hpr, scale, transform->get_shear()); set_transform(other, transform); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_quat_scale // Access: Published // Description: Sets the rotation and scale components of the // transform, leaving translation untouched. This, or // set_pos_quat_scale, is the preferred way to update a // transform when both quat and scale are to be changed. //////////////////////////////////////////////////////////////////// void NodePath:: set_quat_scale(const NodePath &other, const LQuaternion &quat, const LVecBase3 &scale) { // We don't bother trying very hard to preserve pos across this // operation, unlike the work we do above to preserve quat or scale, // since it generally doesn't matter that much if pos is off by a // few thousandths. nassertv_always(!is_empty()); CPT(TransformState) transform = get_transform(other); transform = TransformState::make_pos_quat_scale_shear (transform->get_pos(), quat, scale, transform->get_shear()); set_transform(other, transform); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_hpr_scale // Access: Published // Description: Completely replaces the transform with new // translation, rotation, and scale components, relative // to the other node, implicitly setting shear to 0. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_hpr_scale(const NodePath &other, const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale) { nassertv_always(!is_empty()); set_transform(other, TransformState::make_pos_hpr_scale (pos, hpr, scale)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_quat_scale // Access: Published // Description: Completely replaces the transform with new // translation, rotation, and scale components, relative // to the other node, implicitly setting shear to 0. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_quat_scale(const NodePath &other, const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale) { nassertv_always(!is_empty()); set_transform(other, TransformState::make_pos_quat_scale (pos, quat, scale)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_hpr_scale_shear // Access: Published // Description: Completely replaces the transform with new // translation, rotation, scale, and shear components, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_hpr_scale_shear(const NodePath &other, const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale, const LVecBase3 &shear) { nassertv_always(!is_empty()); set_transform(other, TransformState::make_pos_hpr_scale_shear (pos, hpr, scale, shear)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_pos_quat_scale_shear // Access: Published // Description: Completely replaces the transform with new // translation, rotation, scale, and shear components, // relative to the other node. //////////////////////////////////////////////////////////////////// void NodePath:: set_pos_quat_scale_shear(const NodePath &other, const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale, const LVecBase3 &shear) { nassertv_always(!is_empty()); set_transform(other, TransformState::make_pos_quat_scale_shear (pos, quat, scale, shear)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_mat // Access: Published // Description: Returns the matrix that describes the coordinate // space of the bottom node, relative to the other // path's bottom node's coordinate space. //////////////////////////////////////////////////////////////////// LMatrix4 NodePath:: get_mat(const NodePath &other) const { CPT(TransformState) transform = get_transform(other); // We can't safely return a reference to the matrix, because we // can't assume the transform won't go away when the function // returns. If the transform was partially modified by, say, a // CompassEffect, it won't be stored in the cache, and thus we might // have the only reference to it. return transform->get_mat(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_mat // Access: Published // Description: Converts the indicated matrix from the other's // coordinate space to the local coordinate space, and // applies it to the node. //////////////////////////////////////////////////////////////////// void NodePath:: set_mat(const NodePath &other, const LMatrix4 &mat) { nassertv_always(!is_empty()); set_transform(other, TransformState::make_mat(mat)); node()->reset_prev_transform(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_relative_point // Access: Published // Description: Given that the indicated point is in the coordinate // system of the other node, returns the same point in // this node's coordinate system. //////////////////////////////////////////////////////////////////// LPoint3 NodePath:: get_relative_point(const NodePath &other, const LVecBase3 &point) const { CPT(TransformState) transform = other.get_transform(*this); LPoint3 rel_point = LPoint3(point) * transform->get_mat(); return rel_point; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_relative_vector // Access: Published // Description: Given that the indicated vector is in the coordinate // system of the other node, returns the same vector in // this node's coordinate system. //////////////////////////////////////////////////////////////////// LVector3 NodePath:: get_relative_vector(const NodePath &other, const LVecBase3 &vec) const { CPT(TransformState) transform = other.get_transform(*this); LVector3 rel_vector = LVector3(vec) * transform->get_mat(); return rel_vector; } //////////////////////////////////////////////////////////////////// // Function: NodePath::look_at // Access: Published // Description: Sets the transform on this NodePath so that it // rotates to face the indicated point in space, which // is relative to the other NodePath. //////////////////////////////////////////////////////////////////// void NodePath:: look_at(const NodePath &other, const LPoint3 &point, const LVector3 &up) { nassertv_always(!is_empty()); CPT(TransformState) transform = other.get_transform(get_parent()); LPoint3 rel_point = point * transform->get_mat(); LPoint3 pos = get_pos(); LQuaternion quat; ::look_at(quat, rel_point - pos, up); set_quat(quat); } //////////////////////////////////////////////////////////////////// // Function: NodePath::heads_up // Access: Published // Description: Behaves like look_at(), but with a strong preference // to keeping the up vector oriented in the indicated // "up" direction. //////////////////////////////////////////////////////////////////// void NodePath:: heads_up(const NodePath &other, const LPoint3 &point, const LVector3 &up) { nassertv_always(!is_empty()); CPT(TransformState) transform = other.get_transform(get_parent()); LPoint3 rel_point = point * transform->get_mat(); LPoint3 pos = get_pos(); LQuaternion quat; ::heads_up(quat, rel_point - pos, up); set_quat(quat); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_color // Access: Published // Description: Applies a scene-graph color to the referenced node. // This color will apply to all geometry at this level // and below (that does not specify a new color or a // set_color_off()). //////////////////////////////////////////////////////////////////// void NodePath:: set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a, int priority) { set_color(LColor(r, g, b, a), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_color // Access: Published // Description: Applies a scene-graph color to the referenced node. // This color will apply to all geometry at this level // and below (that does not specify a new color or a // set_color_off()). //////////////////////////////////////////////////////////////////// void NodePath:: set_color(const LColor &color, int priority) { nassertv_always(!is_empty()); node()->set_attrib(ColorAttrib::make_flat(color), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_color_off // Access: Published // Description: Sets the geometry at this level and below to render // using the geometry color. This is normally the // default, but it may be useful to use this to // contradict set_color() at a higher node level (or, // with a priority, to override a set_color() at a lower // level). //////////////////////////////////////////////////////////////////// void NodePath:: set_color_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(ColorAttrib::make_vertex(), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_color // Access: Published // Description: Completely removes any color adjustment from the node. // This allows the natural color of the geometry, or // whatever color transitions might be otherwise // affecting the geometry, to show instead. //////////////////////////////////////////////////////////////////// void NodePath:: clear_color() { nassertv_always(!is_empty()); node()->clear_attrib(ColorAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_color // Access: Published // Description: Returns true if a color has been applied to the given // node, false otherwise. //////////////////////////////////////////////////////////////////// bool NodePath:: has_color() const { nassertr_always(!is_empty(), false); return node()->has_attrib(ColorAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_color // Access: Published // Description: Returns the color that has been assigned to the node, // or black if no color has been assigned. //////////////////////////////////////////////////////////////////// LColor NodePath:: get_color() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(ColorAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ColorAttrib *ca = DCAST(ColorAttrib, attrib); if (ca->get_color_type() == ColorAttrib::T_flat) { return ca->get_color(); } } pgraph_cat.warning() << "get_color() called on " << *this << " which has no color set.\n"; return LColor(1.0f, 1.0f, 1.0f, 1.0f); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_color_scale // Access: Published // Description: Returns true if a color scale has been applied // to the referenced node, false otherwise. It is still // possible that color at this node might have been // scaled by an ancestor node. //////////////////////////////////////////////////////////////////// bool NodePath:: has_color_scale() const { nassertr_always(!is_empty(), false); return node()->has_attrib(ColorScaleAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_color_scale // Access: Published // Description: Completely removes any color scale from the // referenced node. This is preferable to simply // setting the color scale to identity, as it also // removes the overhead associated with having a color // scale at all. //////////////////////////////////////////////////////////////////// void NodePath:: clear_color_scale() { nassertv_always(!is_empty()); node()->clear_attrib(ColorScaleAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::compose_color_scale // Access: Published // Description: multiplies the color scale component of the transform, // with previous color scale leaving translation and // rotation untouched. //////////////////////////////////////////////////////////////////// void NodePath:: compose_color_scale(const LVecBase4 &scale, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ColorScaleAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ColorScaleAttrib::get_class_slot())); const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib); // Modify the existing ColorScaleAttrib by multiplying with the // indicated colorScale. LVecBase4 prev_color_scale = csa->get_scale(); LVecBase4 new_color_scale(prev_color_scale[0]*scale[0], prev_color_scale[1]*scale[1], prev_color_scale[2]*scale[2], prev_color_scale[3]*scale[3]); node()->set_attrib(csa->set_scale(new_color_scale), priority); } else { // Create a new ColorScaleAttrib for this node. node()->set_attrib(ColorScaleAttrib::make(scale), priority); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_color_scale // Access: Published // Description: Sets the color scale component of the transform, // leaving translation and rotation untouched. //////////////////////////////////////////////////////////////////// void NodePath:: set_color_scale(const LVecBase4 &scale, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ColorScaleAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ColorScaleAttrib::get_class_slot())); const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib); // Modify the existing ColorScaleAttrib to add the indicated // colorScale. node()->set_attrib(csa->set_scale(scale), priority); } else { // Create a new ColorScaleAttrib for this node. node()->set_attrib(ColorScaleAttrib::make(scale), priority); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_color_scale_off // Access: Published // Description: Disables any color scale attribute inherited from // above. This is not the same thing as // clear_color_scale(), which undoes any previous // set_color_scale() operation on this node; rather, // this actively disables any set_color_scale() that // might be inherited from a parent node. This also // disables set_alpha_scale() at the same time. // // It is legal to specify a new color scale on the same // node with a subsequent call to set_color_scale() or // set_alpha_scale(); this new scale will apply to lower // geometry. //////////////////////////////////////////////////////////////////// void NodePath:: set_color_scale_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(ColorScaleAttrib::make_off(), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_alpha_scale // Access: Published // Description: Sets the alpha scale component of the transform // without (much) affecting the color scale. Note that // any priority specified will also apply to the color // scale. //////////////////////////////////////////////////////////////////// void NodePath:: set_alpha_scale(PN_stdfloat scale, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ColorScaleAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ColorScaleAttrib::get_class_slot())); const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib); // Modify the existing ColorScaleAttrib to add the indicated // colorScale. const LVecBase4 &sc = csa->get_scale(); node()->set_attrib(csa->set_scale(LVecBase4(sc[0], sc[1], sc[2], scale)), priority); } else { // Create a new ColorScaleAttrib for this node. node()->set_attrib(ColorScaleAttrib::make(LVecBase4(1.0f, 1.0f, 1.0f, scale)), priority); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_all_color_scale // Access: Published // Description: Scales all the color components of the object by the // same amount, darkening the object, without (much) // affecting alpha. Note that any priority specified // will also apply to the alpha scale. //////////////////////////////////////////////////////////////////// void NodePath:: set_all_color_scale(PN_stdfloat scale, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ColorScaleAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ColorScaleAttrib::get_class_slot())); const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib); // Modify the existing ColorScaleAttrib to add the indicated // colorScale. const LVecBase4 &sc = csa->get_scale(); node()->set_attrib(csa->set_scale(LVecBase4(scale, scale, scale, sc[3])), priority); } else { // Create a new ColorScaleAttrib for this node. node()->set_attrib(ColorScaleAttrib::make(LVecBase4(scale, scale, scale, 1.0f)), priority); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_color_scale // Access: Published // Description: Returns the complete color scale vector that has been // applied to this node via a previous call to // set_color_scale() and/or set_alpha_scale(), or all // 1's (identity) if no scale has been applied to this // particular node. //////////////////////////////////////////////////////////////////// const LVecBase4 &NodePath:: get_color_scale() const { static const LVecBase4 ident_scale(1.0f, 1.0f, 1.0f, 1.0f); nassertr_always(!is_empty(), ident_scale); const RenderAttrib *attrib = node()->get_attrib(ColorScaleAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib); return csa->get_scale(); } return ident_scale; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_light // Access: Published // Description: Adds the indicated Light or PolylightNode to the list // of lights that illuminate geometry at this node and // below. The light itself should be parented into the // scene graph elsewhere, to represent the light's // position in space; but until set_light() is called it // will illuminate no geometry. //////////////////////////////////////////////////////////////////// void NodePath:: set_light(const NodePath &light, int priority) { nassertv_always(!is_empty()); if (!light.is_empty()) { Light *light_obj = light.node()->as_light(); if (light_obj != (Light *)NULL) { // It's an actual Light object. const RenderAttrib *attrib = node()->get_attrib(LightAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(LightAttrib::get_class_slot())); const LightAttrib *la = DCAST(LightAttrib, attrib); // Modify the existing LightAttrib to add the indicated // light. node()->set_attrib(la->add_on_light(light), priority); } else { // Create a new LightAttrib for this node. CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make()); node()->set_attrib(la->add_on_light(light), priority); } return; } else if (light.node()->is_of_type(PolylightNode::get_class_type())) { // It's a Polylight object. if (priority != 0) { // PolylightEffects can't have a priority, since they're just // an effect to be applied immediately. pgraph_cat.warning() << "Ignoring priority on set_light(" << light << ")\n"; } const RenderEffect *effect = node()->get_effect(PolylightEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { const PolylightEffect *ple = DCAST(PolylightEffect, effect); // Modify the existing PolylightEffect to add the indicated // light. node()->set_effect(ple->add_light(light)); } else { // Create a new PolylightEffect for this node. CPT(PolylightEffect) ple = DCAST(PolylightEffect, PolylightEffect::make()); node()->set_effect(ple->add_light(light)); } return; } } nassert_raise("Not a Light object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_light_off // Access: Published // Description: Sets the geometry at this level and below to render // using no lights at all. This is different // from not specifying a light; rather, this // specifically contradicts set_light() at a higher // node level (or, with a priority, overrides a // set_light() at a lower level). // // If no lights are in effect on a particular piece of // geometry, that geometry is rendered with lighting // disabled. //////////////////////////////////////////////////////////////////// void NodePath:: set_light_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(LightAttrib::make_all_off(), priority); node()->clear_effect(PolylightEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_light_off // Access: Published // Description: Sets the geometry at this level and below to render // without using the indicated Light. This is different // from not specifying the Light; rather, this // specifically contradicts set_light() at a higher node // level (or, with a priority, overrides a set_light() // at a lower level). // // This interface does not support PolylightNodes, which // cannot be turned off at a lower level. //////////////////////////////////////////////////////////////////// void NodePath:: set_light_off(const NodePath &light, int priority) { nassertv_always(!is_empty()); if (!light.is_empty()) { Light *light_obj = light.node()->as_light(); if (light_obj != (Light *)NULL) { const RenderAttrib *attrib = node()->get_attrib(LightAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(LightAttrib::get_class_slot())); const LightAttrib *la = DCAST(LightAttrib, attrib); // Modify the existing LightAttrib to add the indicated light // to the "off" list. This also, incidentally, removes it from // the "on" list if it is there. node()->set_attrib(la->add_off_light(light), priority); } else { // Create a new LightAttrib for this node that turns off the // indicated light. CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make()); node()->set_attrib(la->add_off_light(light), priority); } return; } } nassert_raise("Not a Light object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_light // Access: Published // Description: Completely removes any lighting operations that may // have been set via set_light() or set_light_off() // from this particular node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_light() { nassertv_always(!is_empty()); node()->clear_attrib(LightAttrib::get_class_slot()); node()->clear_effect(PolylightEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_light // Access: Published // Description: Removes any reference to the indicated Light or // PolylightNode from the NodePath. //////////////////////////////////////////////////////////////////// void NodePath:: clear_light(const NodePath &light) { nassertv_always(!is_empty()); if (!light.is_empty()) { Light *light_obj = light.node()->as_light(); if (light_obj != (Light *)NULL) { const RenderAttrib *attrib = node()->get_attrib(LightAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { CPT(LightAttrib) la = DCAST(LightAttrib, attrib); la = DCAST(LightAttrib, la->remove_on_light(light)); la = DCAST(LightAttrib, la->remove_off_light(light)); if (la->is_identity()) { node()->clear_attrib(LightAttrib::get_class_slot()); } else { int priority = node()->get_state()->get_override(LightAttrib::get_class_slot()); node()->set_attrib(la, priority); } } return; } else if (light.node()->is_of_type(PolylightNode::get_class_type())) { const RenderEffect *effect = node()->get_effect(PolylightEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { CPT(PolylightEffect) ple = DCAST(PolylightEffect, effect); ple = DCAST(PolylightEffect, ple->remove_light(light)); node()->set_effect(ple); } return; } } nassert_raise("Not a Light object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_light // Access: Published // Description: Returns true if the indicated Light or PolylightNode // has been specifically enabled on this particular // node. This means that someone called set_light() on // this node with the indicated light. //////////////////////////////////////////////////////////////////// bool NodePath:: has_light(const NodePath &light) const { nassertr_always(!is_empty(), false); if (!light.is_empty()) { Light *light_obj = light.node()->as_light(); if (light_obj != (Light *)NULL) { const RenderAttrib *attrib = node()->get_attrib(LightAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const LightAttrib *la = DCAST(LightAttrib, attrib); return la->has_on_light(light); } return false; } else if (light.node()->is_of_type(PolylightNode::get_class_type())) { const RenderEffect *effect = node()->get_effect(PolylightEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { const PolylightEffect *ple = DCAST(PolylightEffect, effect); return ple->has_light(light); } return false; } } nassert_raise("Not a Light object."); return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_light_off // Access: Published // Description: Returns true if all Lights have been specifically // disabled on this particular node. This means that // someone called set_light_off() on this node with no // parameters. //////////////////////////////////////////////////////////////////// bool NodePath:: has_light_off() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(LightAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const LightAttrib *la = DCAST(LightAttrib, attrib); return la->has_all_off(); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_light_off // Access: Published // Description: Returns true if the indicated Light has been // specifically disabled on this particular node. This // means that someone called set_light_off() on this // node with the indicated light. // // This interface does not support PolylightNodes, which // cannot be turned off at a lower level. //////////////////////////////////////////////////////////////////// bool NodePath:: has_light_off(const NodePath &light) const { nassertr_always(!is_empty(), false); if (!light.is_empty()) { Light *light_obj = light.node()->as_light(); if (light_obj != (Light *)NULL) { const RenderAttrib *attrib = node()->get_attrib(LightAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const LightAttrib *la = DCAST(LightAttrib, attrib); return la->has_off_light(light); } } } nassert_raise("Not a Light object."); return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_clip_plane // Access: Published // Description: Adds the indicated clipping plane to the list of // planes that apply to geometry at this node and below. // The clipping plane itself, a PlaneNode, should be // parented into the scene graph elsewhere, to represent // the plane's position in space; but until // set_clip_plane() is called it will clip no geometry. //////////////////////////////////////////////////////////////////// void NodePath:: set_clip_plane(const NodePath &clip_plane, int priority) { nassertv_always(!is_empty()); if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) { const RenderAttrib *attrib = node()->get_attrib(ClipPlaneAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot())); const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib); // Modify the existing ClipPlaneAttrib to add the indicated // clip_plane. node()->set_attrib(la->add_on_plane(clip_plane), priority); } else { // Create a new ClipPlaneAttrib for this node. CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make()); node()->set_attrib(la->add_on_plane(clip_plane), priority); } return; } nassert_raise("Not a PlaneNode object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_clip_plane_off // Access: Published // Description: Sets the geometry at this level and below to render // using no clip_planes at all. This is different // from not specifying a clip_plane; rather, this // specifically contradicts set_clip_plane() at a higher // node level (or, with a priority, overrides a // set_clip_plane() at a lower level). // // If no clip_planes are in effect on a particular piece // of geometry, that geometry is rendered without being // clipped (other than by the viewing frustum). //////////////////////////////////////////////////////////////////// void NodePath:: set_clip_plane_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(ClipPlaneAttrib::make_all_off(), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_clip_plane_off // Access: Published // Description: Sets the geometry at this level and below to render // without being clipped by the indicated PlaneNode. // This is different from not specifying the PlaneNode; // rather, this specifically contradicts // set_clip_plane() at a higher node level (or, with a // priority, overrides a set_clip_plane() at a lower // level). //////////////////////////////////////////////////////////////////// void NodePath:: set_clip_plane_off(const NodePath &clip_plane, int priority) { nassertv_always(!is_empty()); if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) { const RenderAttrib *attrib = node()->get_attrib(ClipPlaneAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot())); const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib); // Modify the existing ClipPlaneAttrib to add the indicated clip_plane // to the "off" list. This also, incidentally, removes it from // the "on" list if it is there. node()->set_attrib(la->add_off_plane(clip_plane), priority); } else { // Create a new ClipPlaneAttrib for this node that turns off the // indicated clip_plane. CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make()); node()->set_attrib(la->add_off_plane(clip_plane), priority); } return; } nassert_raise("Not a PlaneNode object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_clip_plane // Access: Published // Description: Completely removes any clip planes that may have been // set via set_clip_plane() or set_clip_plane_off() from // this particular node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_clip_plane() { nassertv_always(!is_empty()); node()->clear_attrib(ClipPlaneAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_clip_plane // Access: Published // Description: Removes any reference to the indicated clipping plane // from the NodePath. //////////////////////////////////////////////////////////////////// void NodePath:: clear_clip_plane(const NodePath &clip_plane) { nassertv_always(!is_empty()); if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) { const RenderAttrib *attrib = node()->get_attrib(ClipPlaneAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, attrib); la = DCAST(ClipPlaneAttrib, la->remove_on_plane(clip_plane)); la = DCAST(ClipPlaneAttrib, la->remove_off_plane(clip_plane)); if (la->is_identity()) { node()->clear_attrib(ClipPlaneAttrib::get_class_slot()); } else { int priority = node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot()); node()->set_attrib(la, priority); } } return; } nassert_raise("Not a PlaneNode object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_clip_plane // Access: Published // Description: Returns true if the indicated clipping plane has been // specifically applied to this particular node. This // means that someone called set_clip_plane() on this // node with the indicated clip_plane. //////////////////////////////////////////////////////////////////// bool NodePath:: has_clip_plane(const NodePath &clip_plane) const { nassertr_always(!is_empty(), false); if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) { const RenderAttrib *attrib = node()->get_attrib(ClipPlaneAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib); return la->has_on_plane(clip_plane); } return false; } nassert_raise("Not a PlaneNode object."); return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_clip_plane_off // Access: Published // Description: Returns true if all clipping planes have been // specifically disabled on this particular node. This // means that someone called set_clip_plane_off() on // this node with no parameters. //////////////////////////////////////////////////////////////////// bool NodePath:: has_clip_plane_off() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(ClipPlaneAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib); return la->has_all_off(); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_clip_plane_off // Access: Published // Description: Returns true if the indicated clipping plane has been // specifically disabled on this particular node. This // means that someone called set_clip_plane_off() on // this node with the indicated clip_plane. //////////////////////////////////////////////////////////////////// bool NodePath:: has_clip_plane_off(const NodePath &clip_plane) const { nassertr_always(!is_empty(), false); if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) { const RenderAttrib *attrib = node()->get_attrib(ClipPlaneAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib); return la->has_off_plane(clip_plane); } } nassert_raise("Not a PlaneNode object."); return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_occluder // Access: Published // Description: Adds the indicated occluder to the list of // occluders that apply to geometry at this node and below. // The occluder itself, an OccluderNode, should be // parented into the scene graph elsewhere, to represent // the occluder's position in space; but until // set_occluder() is called it will clip no geometry. //////////////////////////////////////////////////////////////////// void NodePath:: set_occluder(const NodePath &occluder) { nassertv_always(!is_empty()); if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) { const RenderEffect *effect = node()->get_effect(OccluderEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { const OccluderEffect *la = DCAST(OccluderEffect, effect); // Modify the existing OccluderEffect to add the indicated // occluder. node()->set_effect(la->add_on_occluder(occluder)); } else { // Create a new OccluderEffect for this node. CPT(OccluderEffect) la = DCAST(OccluderEffect, OccluderEffect::make()); node()->set_effect(la->add_on_occluder(occluder)); } return; } nassert_raise("Not an OccluderNode object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_occluder // Access: Published // Description: Completely removes any occluders that may have been // set via set_occluder() from this particular node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_occluder() { nassertv_always(!is_empty()); node()->clear_effect(OccluderEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_occluder // Access: Published // Description: Removes any reference to the indicated occluder // from the NodePath. //////////////////////////////////////////////////////////////////// void NodePath:: clear_occluder(const NodePath &occluder) { nassertv_always(!is_empty()); if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) { const RenderEffect *effect = node()->get_effect(OccluderEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { CPT(OccluderEffect) la = DCAST(OccluderEffect, effect); la = DCAST(OccluderEffect, la->remove_on_occluder(occluder)); if (la->is_identity()) { node()->clear_effect(OccluderEffect::get_class_type()); } else { node()->set_effect(la); } } return; } nassert_raise("Not an OccluderNode object."); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_occluder // Access: Published // Description: Returns true if the indicated occluder has been // specifically applied to this particular node. This // means that someone called set_occluder() on this // node with the indicated occluder. //////////////////////////////////////////////////////////////////// bool NodePath:: has_occluder(const NodePath &occluder) const { nassertr_always(!is_empty(), false); if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) { const RenderEffect *effect = node()->get_effect(OccluderEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { const OccluderEffect *la = DCAST(OccluderEffect, effect); return la->has_on_occluder(occluder); } return false; } nassert_raise("Not an OccluderNode object."); return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_scissor // Access: Published // Description: Sets up a scissor region on the nodes rendered at // this level and below. The four coordinates are // understood to define a rectangle in screen space. // These numbers are relative to the current // DisplayRegion, where (0,0) is the lower-left corner // of the DisplayRegion, and (1,1) is the upper-right // corner. //////////////////////////////////////////////////////////////////// void NodePath:: set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { set_effect(ScissorEffect::make_screen(LVecBase4(left, right, bottom, top))); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_scissor // Access: Published // Description: Sets up a scissor region on the nodes rendered at // this level and below. The two points are understood // to be relative to this node. When these points are // projected into screen space, they define the // diagonally-opposite points that determine the scissor // region. //////////////////////////////////////////////////////////////////// void NodePath:: set_scissor(const LPoint3 &a, const LPoint3 &b) { set_effect(ScissorEffect::make_node(a, b)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_scissor // Access: Published // Description: Sets up a scissor region on the nodes rendered at // this level and below. The four points are understood // to be relative to this node. When these points are // projected into screen space, they define the // bounding volume of the scissor region (the scissor // region is the smallest onscreen rectangle that // encloses all four points). //////////////////////////////////////////////////////////////////// void NodePath:: set_scissor(const LPoint3 &a, const LPoint3 &b, const LPoint3 &c, const LPoint3 &d) { set_effect(ScissorEffect::make_node(a, b, c, d)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_scissor // Access: Published // Description: Sets up a scissor region on the nodes rendered at // this level and below. The two points are understood // to be relative to the indicated other node. When // these points are projected into screen space, they // define the diagonally-opposite points that determine // the scissor region. //////////////////////////////////////////////////////////////////// void NodePath:: set_scissor(const NodePath &other, const LPoint3 &a, const LPoint3 &b) { set_effect(ScissorEffect::make_node(a, b, other)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_scissor // Access: Published // Description: Sets up a scissor region on the nodes rendered at // this level and below. The four points are understood // to be relative to the indicated other node. When // these points are projected into screen space, they // define the bounding volume of the scissor region (the // scissor region is the smallest onscreen rectangle // that encloses all four points). //////////////////////////////////////////////////////////////////// void NodePath:: set_scissor(const NodePath &other, const LPoint3 &a, const LPoint3 &b, const LPoint3 &c, const LPoint3 &d) { set_effect(ScissorEffect::make_node(a, b, c, d, other)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_scissor // Access: Published // Description: Removes the scissor region that was defined at this // node level by a previous call to set_scissor(). //////////////////////////////////////////////////////////////////// void NodePath:: clear_scissor() { clear_effect(ScissorEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_scissor // Access: Published // Description: Returns true if a scissor region was defined at this // node by a previous call to set_scissor(). This does // not check for scissor regions inherited from a parent // class. It also does not check for the presence of a // low-level ScissorAttrib, which is different from the // ScissorEffect added by set_scissor. //////////////////////////////////////////////////////////////////// bool NodePath:: has_scissor() const { return has_effect(ScissorEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_bin // Access: Published // Description: Assigns the geometry at this level and below to the // named rendering bin. It is the user's responsibility // to ensure that such a bin already exists, either via // the cull-bin Configrc variable, or by explicitly // creating a GeomBin of the appropriate type at // runtime. // // There are two default bins created when Panda is // started: "default" and "fixed". Normally, all // geometry is assigned to "default" unless specified // otherwise. This bin renders opaque geometry in // state-sorted order, followed by transparent geometry // sorted back-to-front. If any geometry is assigned to // "fixed", this will be rendered following all the // geometry in "default", in the order specified by // draw_order for each piece of geometry so assigned. // // The draw_order parameter is meaningful only for // GeomBinFixed type bins, e.g. "fixed". Other kinds of // bins ignore it. //////////////////////////////////////////////////////////////////// void NodePath:: set_bin(const string &bin_name, int draw_order, int priority) { nassertv_always(!is_empty()); node()->set_attrib(CullBinAttrib::make(bin_name, draw_order), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_bin // Access: Published // Description: Completely removes any bin adjustment that may have // been set via set_bin() from this particular node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_bin() { nassertv_always(!is_empty()); node()->clear_attrib(CullBinAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_bin // Access: Published // Description: Returns true if the node has been assigned to the a // particular rendering bin via set_bin(), false // otherwise. //////////////////////////////////////////////////////////////////// bool NodePath:: has_bin() const { nassertr_always(!is_empty(), false); return node()->has_attrib(CullBinAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_bin_name // Access: Published // Description: Returns the name of the bin that this particular node // was assigned to via set_bin(), or the empty string if // no bin was assigned. See set_bin() and has_bin(). //////////////////////////////////////////////////////////////////// string NodePath:: get_bin_name() const { nassertr_always(!is_empty(), string()); const RenderAttrib *attrib = node()->get_attrib(CullBinAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const CullBinAttrib *ba = DCAST(CullBinAttrib, attrib); return ba->get_bin_name(); } return string(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_bin_draw_order // Access: Published // Description: Returns the drawing order associated with the bin // that this particular node was assigned to via // set_bin(), or 0 if no bin was assigned. See // set_bin() and has_bin(). //////////////////////////////////////////////////////////////////// int NodePath:: get_bin_draw_order() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(CullBinAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const CullBinAttrib *ba = DCAST(CullBinAttrib, attrib); return ba->get_draw_order(); } return 0; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_texture // Access: Published // Description: Adds the indicated texture to the list of textures // that will be rendered on the default texture stage. // // This is the convenience single-texture variant of // this method; it is now superceded by set_texture() // that accepts a stage and texture. You may use this // method if you just want to adjust the default stage. //////////////////////////////////////////////////////////////////// void NodePath:: set_texture(Texture *tex, int priority) { nassertv_always(!is_empty()); PT(TextureStage) stage = TextureStage::get_default(); set_texture(stage, tex, priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_texture // Access: Published // Description: Adds the indicated texture to the list of textures // that will be rendered on the indicated multitexture // stage. If there are multiple texture stages // specified (possibly on multiple different nodes at // different levels), they will all be applied to // geometry together, according to the stage // specification set up in the TextureStage object. //////////////////////////////////////////////////////////////////// void NodePath:: set_texture(TextureStage *stage, Texture *tex, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *tsa = DCAST(TextureAttrib, attrib); int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot()); // Modify the existing TextureAttrib to add the indicated // texture. node()->set_attrib(tsa->add_on_stage(stage, tex, priority), sg_priority); } else { // Create a new TextureAttrib for this node. CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make()); node()->set_attrib(tsa->add_on_stage(stage, tex, priority)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_texture_off // Access: Published // Description: Sets the geometry at this level and below to render // using no texture, on any stage. This is different // from not specifying a texture; rather, this // specifically contradicts set_texture() at a higher // node level (or, with a priority, overrides a // set_texture() at a lower level). //////////////////////////////////////////////////////////////////// void NodePath:: set_texture_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(TextureAttrib::make_all_off(), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_texture_off // Access: Published // Description: Sets the geometry at this level and below to render // using no texture, on the indicated stage. This is // different from not specifying a texture; rather, this // specifically contradicts set_texture() at a higher // node level (or, with a priority, overrides a // set_texture() at a lower level). //////////////////////////////////////////////////////////////////// void NodePath:: set_texture_off(TextureStage *stage, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *tsa = DCAST(TextureAttrib, attrib); int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot()); // Modify the existing TextureAttrib to add the indicated texture // to the "off" list. This also, incidentally, removes it from // the "on" list if it is there. node()->set_attrib(tsa->add_off_stage(stage, priority), sg_priority); } else { // Create a new TextureAttrib for this node that turns off the // indicated stage. CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make()); node()->set_attrib(tsa->add_off_stage(stage, priority)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_texture // Access: Published // Description: Completely removes any texture adjustment that may // have been set via set_texture() or set_texture_off() // from this particular node. This allows whatever // textures might be otherwise affecting the geometry to // show instead. //////////////////////////////////////////////////////////////////// void NodePath:: clear_texture() { nassertv_always(!is_empty()); node()->clear_attrib(TextureAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_texture // Access: Published // Description: Removes any reference to the indicated texture stage // from the NodePath. //////////////////////////////////////////////////////////////////// void NodePath:: clear_texture(TextureStage *stage) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { CPT(TextureAttrib) tsa = DCAST(TextureAttrib, attrib); tsa = DCAST(TextureAttrib, tsa->remove_on_stage(stage)); tsa = DCAST(TextureAttrib, tsa->remove_off_stage(stage)); if (tsa->is_identity()) { node()->clear_attrib(TextureAttrib::get_class_slot()); } else { int priority = node()->get_state()->get_override(TextureAttrib::get_class_slot()); node()->set_attrib(tsa, priority); } } } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_texture // Access: Published // Description: Returns true if a texture has been applied to this // particular node via set_texture(), false otherwise. // This is not the same thing as asking whether the // geometry at this node will be rendered with // texturing, as there may be a texture in effect from a // higher or lower level. //////////////////////////////////////////////////////////////////// bool NodePath:: has_texture() const { return get_texture() != (Texture *)NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_texture // Access: Published // Description: Returns true if texturing has been specifically // enabled on this particular node for the indicated // stage. This means that someone called // set_texture() on this node with the indicated stage // name, or the stage_name is the default stage_name, // and someone called set_texture() on this node. //////////////////////////////////////////////////////////////////// bool NodePath:: has_texture(TextureStage *stage) const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); return ta->has_on_stage(stage); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_texture_off // Access: Published // Description: Returns true if texturing has been specifically // disabled on this particular node via // set_texture_off(), false otherwise. This is not the // same thing as asking whether the geometry at this // node will be rendered untextured, as there may be a // texture in effect from a higher or lower level. //////////////////////////////////////////////////////////////////// bool NodePath:: has_texture_off() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); return ta->has_all_off(); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_texture_off // Access: Published // Description: Returns true if texturing has been specifically // disabled on this particular node for the indicated // stage. This means that someone called // set_texture_off() on this node with the indicated // stage name, or that someone called set_texture_off() // on this node to remove all stages. //////////////////////////////////////////////////////////////////// bool NodePath:: has_texture_off(TextureStage *stage) const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); return ta->has_off_stage(stage); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_texture // Access: Published // Description: Returns the base-level texture that has been set on // this particular node, or NULL if no texture has been // set. This is not necessarily the texture that will // be applied to the geometry at or below this level, as // another texture at a higher or lower level may // override. // // See also find_texture(). //////////////////////////////////////////////////////////////////// Texture *NodePath:: get_texture() const { nassertr_always(!is_empty(), NULL); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); return ta->get_texture(); } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_texture // Access: Published // Description: Returns the texture that has been set on the // indicated stage for this particular node, or NULL if // no texture has been set for this stage. //////////////////////////////////////////////////////////////////// Texture *NodePath:: get_texture(TextureStage *stage) const { nassertr_always(!is_empty(), NULL); const RenderAttrib *attrib = node()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); return ta->get_on_texture(stage); } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader(const Shader *sha, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ShaderAttrib::get_class_slot())); const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); node()->set_attrib(sa->set_shader(sha, priority)); } else { // Create a new ShaderAttrib for this node. CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); node()->set_attrib(sa->set_shader(sha, priority)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_off // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_off(int priority) { set_shader(NULL, priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_auto // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_auto(int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ShaderAttrib::get_class_slot())); const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); node()->set_attrib(sa->set_shader_auto(priority)); } else { // Create a new ShaderAttrib for this node. CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); node()->set_attrib(sa->set_shader_auto(priority)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_auto // Access: Published // Description: overloaded for auto shader customization //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_auto(BitMask32 shader_switch, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(ShaderAttrib::get_class_slot())); const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); node()->set_attrib(sa->set_shader_auto(shader_switch, priority)); } else { // Create a new ShaderAttrib for this node. CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); node()->set_attrib(sa->set_shader_auto(shader_switch, priority)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_shader // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: clear_shader() { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); node()->set_attrib(sa->clear_shader()); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_shader // Access: Published // Description: //////////////////////////////////////////////////////////////////// const Shader *NodePath:: get_shader() const { nassertr_always(!is_empty(), NULL); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); return sa->get_shader(); } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const ShaderInput *inp) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); node()->set_attrib(sa->set_shader_input(inp)); } else { // Create a new ShaderAttrib for this node. CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); node()->set_attrib(sa->set_shader_input(inp)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// const ShaderInput *NodePath:: get_shader_input(const InternalName *id) const { nassertr_always(!is_empty(), NULL); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); return sa->get_shader_input(id); } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_instance_count // Access: Published // Description: Returns the geometry instance count, or 0 if // disabled. See set_instance_count. //////////////////////////////////////////////////////////////////// const int NodePath:: get_instance_count() const { nassertr_always(!is_empty(), 0); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); return sa->get_instance_count(); } return 0; } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: clear_shader_input(const InternalName *id) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); node()->set_attrib(sa->clear_shader_input(id)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const PTA_float &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const PTA_double &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const PTA_LVecBase4 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const PTA_LVecBase3 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const PTA_LVecBase2 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const LVecBase4 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const LVecBase3 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const LVecBase2 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const PTA_LMatrix4 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const PTA_LMatrix3 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const LMatrix4 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const LMatrix3 &v, int priority) { set_shader_input(new ShaderInput(id,v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, Texture *tex, int priority) { set_shader_input(new ShaderInput(id,tex,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, const NodePath &np, int priority) { set_shader_input(new ShaderInput(id,np,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const InternalName *id, double n1, double n2, double n3, double n4, int priority) { set_shader_input(new ShaderInput(id, LVecBase4(n1, n2, n3, n4), priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const PTA_float &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const PTA_double &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const PTA_LVecBase4 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const PTA_LVecBase3 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const PTA_LVecBase2 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const LVecBase4 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const LVecBase3 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const LVecBase2 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const PTA_LMatrix4 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const PTA_LMatrix3 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const LMatrix4 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const LMatrix3 &v, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),v,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, Texture *tex, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),tex,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, const NodePath &np, int priority) { set_shader_input(new ShaderInput(InternalName::make(id),np,priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: set_shader_input(const string &id, double n1, double n2, double n3, double n4, int priority) { set_shader_input(new ShaderInput(InternalName::make(id), LVecBase4(n1, n2, n3, n4), priority)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// const ShaderInput *NodePath:: get_shader_input(const string &id) const { return get_shader_input(InternalName::make(id)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_shader_input // Access: Published // Description: //////////////////////////////////////////////////////////////////// void NodePath:: clear_shader_input(const string &id) { clear_shader_input(InternalName::make(id)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_instance_count // Access: Published // Description: Sets the geometry instance count, or 0 if // geometry instancing should be disabled. Do not // confuse with instanceTo which only applies to // animation instancing. //////////////////////////////////////////////////////////////////// void NodePath:: set_instance_count(int instance_count) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(ShaderAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib); node()->set_attrib(sa->set_instance_count(instance_count)); } else { // Create a new ShaderAttrib for this node. CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); node()->set_attrib(sa->set_instance_count(instance_count)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_tex_transform // Access: Published // Description: Sets the texture matrix on the current node to the // indicated transform for the given stage. //////////////////////////////////////////////////////////////////// void NodePath:: set_tex_transform(TextureStage *stage, const TransformState *transform) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TexMatrixAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib); // Modify the existing TexMatrixAttrib to add the indicated // stage. node()->set_attrib(tma->add_stage(stage, transform)); } else { // Create a new TexMatrixAttrib for this node. node()->set_attrib(TexMatrixAttrib::make(stage, transform)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_tex_transform // Access: Published // Description: Removes all texture matrices from the current node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_tex_transform() { nassertv_always(!is_empty()); node()->clear_attrib(TexMatrixAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_tex_transform // Access: Published // Description: Removes the texture matrix on the current node for // the given stage. //////////////////////////////////////////////////////////////////// void NodePath:: clear_tex_transform(TextureStage *stage) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TexMatrixAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { CPT(TexMatrixAttrib) tma = DCAST(TexMatrixAttrib, attrib); tma = DCAST(TexMatrixAttrib, tma->remove_stage(stage)); if (tma->is_empty()) { node()->clear_attrib(TexMatrixAttrib::get_class_slot()); } else { node()->set_attrib(tma); } } } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_tex_transform // Access: Published // Description: Returns true if there is an explicit texture matrix // on the current node for the given stage. //////////////////////////////////////////////////////////////////// bool NodePath:: has_tex_transform(TextureStage *stage) const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(TexMatrixAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib); return tma->has_stage(stage); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_tex_transform // Access: Published // Description: Returns the texture matrix on the current node for the // given stage, or identity transform if there is no // explicit transform set for the given stage. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: get_tex_transform(TextureStage *stage) const { nassertr_always(!is_empty(), NULL); const RenderAttrib *attrib = node()->get_attrib(TexMatrixAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib); return tma->get_transform(stage); } return TransformState::make_identity(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_tex_transform // Access: Published // Description: Sets the texture matrix on the current node to the // indicated transform for the given stage. //////////////////////////////////////////////////////////////////// void NodePath:: set_tex_transform(const NodePath &other, TextureStage *stage, const TransformState *transform) { nassertv(_error_type == ET_ok && other._error_type == ET_ok); nassertv_always(!is_empty()); CPT(RenderState) state = get_state(other); const RenderAttrib *attrib = state->get_attrib(TexMatrixAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib); // Modify the existing TexMatrixAttrib to add the indicated // stage. state = state->add_attrib(tma->add_stage(stage, transform)); } else { // Create a new TexMatrixAttrib for this node. state = state->add_attrib(TexMatrixAttrib::make(stage, transform)); } // Now compose that with our parent's state. CPT(RenderState) rel_state; if (has_parent()) { rel_state = other.get_state(get_parent()); } else { rel_state = other.get_state(NodePath()); } CPT(RenderState) new_state = rel_state->compose(state); // And apply only the TexMatrixAttrib to the current node, leaving // the others unchanged. node()->set_attrib(new_state->get_attrib(TexMatrixAttrib::get_class_slot())); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_tex_transform // Access: Published // Description: Returns the texture matrix on the current node for the // given stage, relative to the other node. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: get_tex_transform(const NodePath &other, TextureStage *stage) const { nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity()); CPT(RenderState) state = get_state(other); const RenderAttrib *attrib = state->get_attrib(TexMatrixAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib); return tma->get_transform(stage); } return TransformState::make_identity(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_tex_gen // Access: Published // Description: Enables automatic texture coordinate generation for // the indicated texture stage. //////////////////////////////////////////////////////////////////// void NodePath:: set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TexGenAttrib::get_class_slot()); CPT(TexGenAttrib) tga; if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(TextureAttrib::get_class_slot())); tga = DCAST(TexGenAttrib, attrib); } else { tga = DCAST(TexGenAttrib, TexGenAttrib::make()); } node()->set_attrib(tga->add_stage(stage, mode), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_tex_gen // Access: Published // Description: Enables automatic texture coordinate generation for // the indicated texture stage. This version of this // method is useful when setting M_light_vector, which // requires the name of the texture coordinate set that // supplies the tangent and binormal, as well as the // specific light to generate coordinates for. //////////////////////////////////////////////////////////////////// void NodePath:: set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, const string &source_name, const NodePath &light, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TexGenAttrib::get_class_slot()); CPT(TexGenAttrib) tga; if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(TextureAttrib::get_class_slot())); tga = DCAST(TexGenAttrib, attrib); } else { tga = DCAST(TexGenAttrib, TexGenAttrib::make()); } node()->set_attrib(tga->add_stage(stage, mode, source_name, light), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_tex_gen // Access: Published // Description: Enables automatic texture coordinate generation for // the indicated texture stage. This version of this // method is useful when setting M_constant, which // requires a constant texture coordinate value. //////////////////////////////////////////////////////////////////// void NodePath:: set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, const LTexCoord3 &constant_value, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TexGenAttrib::get_class_slot()); CPT(TexGenAttrib) tga; if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(TextureAttrib::get_class_slot())); tga = DCAST(TexGenAttrib, attrib); } else { tga = DCAST(TexGenAttrib, TexGenAttrib::make()); } node()->set_attrib(tga->add_stage(stage, mode, constant_value), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_tex_gen // Access: Published // Description: Removes the texture coordinate generation mode from // all texture stages on this node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_tex_gen() { nassertv_always(!is_empty()); node()->clear_attrib(TexGenAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_tex_gen // Access: Published // Description: Disables automatic texture coordinate generation for // the indicated texture stage. //////////////////////////////////////////////////////////////////// void NodePath:: clear_tex_gen(TextureStage *stage) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(TexGenAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { CPT(TexGenAttrib) tga = DCAST(TexGenAttrib, attrib); tga = DCAST(TexGenAttrib, tga->remove_stage(stage)); if (tga->is_empty()) { node()->clear_attrib(TexGenAttrib::get_class_slot()); } else { node()->set_attrib(tga); } } } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_tex_gen // Access: Published // Description: Returns true if there is a mode for automatic texture // coordinate generation on the current node for the // given stage. //////////////////////////////////////////////////////////////////// bool NodePath:: has_tex_gen(TextureStage *stage) const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(TexGenAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib); return tga->has_stage(stage); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_tex_gen // Access: Published // Description: Returns the texture coordinate generation mode for // the given stage, or M_off if there is no explicit // mode set for the given stage. //////////////////////////////////////////////////////////////////// RenderAttrib::TexGenMode NodePath:: get_tex_gen(TextureStage *stage) const { nassertr_always(!is_empty(), TexGenAttrib::M_off); const RenderAttrib *attrib = node()->get_attrib(TexGenAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib); return tga->get_mode(stage); } return TexGenAttrib::M_off; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_tex_gen_light // Access: Published // Description: Returns the particular Light set for the indicated // texgen mode's texture stage, or empty NodePath if no // light is set. This is only meaningful if the texgen // mode (returned by get_tex_gen()) is M_light_vector. //////////////////////////////////////////////////////////////////// NodePath NodePath:: get_tex_gen_light(TextureStage *stage) const { nassertr_always(!is_empty(), NodePath::fail()); const RenderAttrib *attrib = node()->get_attrib(TexGenAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib); return tga->get_light(stage); } return NodePath(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_tex_projector // Access: Published // Description: Establishes a TexProjectorEffect on this node, which // can be used to establish projective texturing (but // see also the NodePath::project_texture() convenience // function), or it can be used to bind this node's // texture transform to particular node's position in // space, allowing a LerpInterval (for instance) to // adjust this node's texture coordinates. //////////////////////////////////////////////////////////////////// void NodePath:: set_tex_projector(TextureStage *stage, const NodePath &from, const NodePath &to) { nassertv_always(!is_empty()); const RenderEffect *effect = node()->get_effect(TexProjectorEffect::get_class_type()); CPT(TexProjectorEffect) tpe; if (effect != (const RenderEffect *)NULL) { tpe = DCAST(TexProjectorEffect, effect); } else { tpe = DCAST(TexProjectorEffect, TexProjectorEffect::make()); } node()->set_effect(tpe->add_stage(stage, from, to)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_tex_projector // Access: Published // Description: Removes the TexProjectorEffect for the indicated // stage from this node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_tex_projector(TextureStage *stage) { nassertv_always(!is_empty()); const RenderEffect *effect = node()->get_effect(TexProjectorEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { CPT(TexProjectorEffect) tpe = DCAST(TexProjectorEffect, effect); tpe = DCAST(TexProjectorEffect, tpe->remove_stage(stage)); if (tpe->is_empty()) { node()->clear_effect(TexProjectorEffect::get_class_type()); } else { node()->set_effect(tpe); } } } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_tex_projector // Access: Published // Description: Removes the TexProjectorEffect for all stages from // this node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_tex_projector() { nassertv_always(!is_empty()); node()->clear_effect(TexProjectorEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_tex_projector // Access: Published // Description: Returns true if this node has a TexProjectorEffect // for the indicated stage, false otherwise. //////////////////////////////////////////////////////////////////// bool NodePath:: has_tex_projector(TextureStage *stage) const { nassertr_always(!is_empty(), false); const RenderEffect *effect = node()->get_effect(TexProjectorEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect); return tpe->has_stage(stage); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_tex_projector_from // Access: Published // Description: Returns the "from" node associated with the // TexProjectorEffect on the indicated stage. The // relative transform between the "from" and the "to" // nodes is automatically applied to the texture // transform each frame. //////////////////////////////////////////////////////////////////// NodePath NodePath:: get_tex_projector_from(TextureStage *stage) const { nassertr_always(!is_empty(), NodePath::fail()); const RenderEffect *effect = node()->get_effect(TexProjectorEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect); return tpe->get_from(stage); } return NodePath::not_found(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_tex_projector_to // Access: Published // Description: Returns the "to" node associated with the // TexProjectorEffect on the indicated stage. The // relative transform between the "from" and the "to" // nodes is automatically applied to the texture // transform each frame. //////////////////////////////////////////////////////////////////// NodePath NodePath:: get_tex_projector_to(TextureStage *stage) const { nassertr_always(!is_empty(), NodePath::fail()); const RenderEffect *effect = node()->get_effect(TexProjectorEffect::get_class_type()); if (effect != (const RenderEffect *)NULL) { const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect); return tpe->get_to(stage); } return NodePath::not_found(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::project_texture // Access: Published // Description: A convenience function to enable projective texturing // at this node level and below, using the indicated // NodePath (which should contain a LensNode) as the // projector. //////////////////////////////////////////////////////////////////// void NodePath:: project_texture(TextureStage *stage, Texture *tex, const NodePath &projector) { nassertv(!projector.is_empty() && projector.node()->is_of_type(LensNode::get_class_type())); set_texture(stage, tex); set_tex_gen(stage, TexGenAttrib::M_world_position); set_tex_projector(stage, NodePath(), projector); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_normal_map // Access: Published // Description: A convenience function to set up a normal map on this // geometry. This uses the single highest-priority // light on the object only. It also requires // multitexture, and consumes at least two texture // stages, in addition to what may already be in use. // // The normal_map parameter is the texture that contains // the normal map information (with a 3-d delta vector // encoded into the r,g,b of each texel). texcoord_name is // the name of the texture coordinate set that contains // the tangent and binormal we wish to use. If // preserve_color is true, then one additional texture // stage is consumed to blend in the geometry's original // vertex color. // // Only one normal map may be in effect through this // interface at any given time. //////////////////////////////////////////////////////////////////// void NodePath:: set_normal_map(Texture *normal_map, const string &texcoord_name, bool preserve_color) { clear_normal_map(); // First, we apply the normal map itself, to the bottom layer. PT(TextureStage) normal_map_ts = new TextureStage("__normal_map"); normal_map_ts->set_texcoord_name(texcoord_name); normal_map_ts->set_sort(-20); normal_map_ts->set_mode(TextureStage::M_replace); set_texture(normal_map_ts, normal_map); // Then, we apply a normalization map, to normalize, per-pixel, the // vector to the light. PT(Texture) normalization_map = TexturePool::get_normalization_cube_map(32); PT(TextureStage) normalization_map_ts = new TextureStage("__normalization_map"); normalization_map_ts->set_combine_rgb (TextureStage::CM_dot3_rgb, TextureStage::CS_texture, TextureStage::CO_src_color, TextureStage::CS_previous, TextureStage::CO_src_color); normalization_map_ts->set_texcoord_name("light_vector"); normalization_map_ts->set_sort(-15); set_texture(normalization_map_ts, normalization_map); // Finally, we enable M_light_vector texture coordinate generation. set_tex_gen(normalization_map_ts, TexGenAttrib::M_light_vector, texcoord_name, NodePath()); if (preserve_color) { // One more stage to get back the original color. PT(TextureStage) orig_color_ts = new TextureStage("__orig_color"); orig_color_ts->set_combine_rgb (TextureStage::CM_modulate, TextureStage::CS_primary_color, TextureStage::CO_src_color, TextureStage::CS_previous, TextureStage::CO_src_color); set_texture(orig_color_ts, normal_map); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_normal_map // Access: Published // Description: Undoes the effect of a previous call to // set_normal_map(). //////////////////////////////////////////////////////////////////// void NodePath:: clear_normal_map() { // Scan through the TextureStages, and if we find any whose name // matches one of the stages that would have been left by // set_normal_map(), remove it from the state. CPT(RenderAttrib) attrib = get_state()->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); for (int i = 0; i < ta->get_num_on_stages(); i++) { TextureStage *stage = ta->get_on_stage(i); if (stage->get_name() == "__normal_map") { clear_texture(stage); } else if (stage->get_name() == "__normalization_map") { clear_texture(stage); clear_tex_gen(stage); } else if (stage->get_name() == "__orig_color") { clear_texture(stage); } } } } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_vertex_column // Access: Published // Description: Returns true if there are at least some vertices at // this node and below that contain a reference to the // indicated vertex data column name, false otherwise. // // This is particularly useful for testing whether a // particular model has a given texture coordinate set // (but see has_texcoord()). //////////////////////////////////////////////////////////////////// bool NodePath:: has_vertex_column(const InternalName *name) const { nassertr_always(!is_empty(), false); return r_has_vertex_column(node(), name); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_vertex_columns // Access: Published // Description: Returns a list of all vertex array columns stored on // some geometry found at this node level and below. //////////////////////////////////////////////////////////////////// InternalNameCollection NodePath:: find_all_vertex_columns() const { nassertr_always(!is_empty(), InternalNameCollection()); InternalNames vertex_columns; r_find_all_vertex_columns(node(), vertex_columns); InternalNameCollection tc; InternalNames::iterator ti; for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) { tc.add_name(*ti); } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_vertex_columns // Access: Published // Description: Returns a list of all vertex array columns stored on // some geometry found at this node level and below that // match the indicated name (which may contain wildcard // characters). //////////////////////////////////////////////////////////////////// InternalNameCollection NodePath:: find_all_vertex_columns(const string &name) const { nassertr_always(!is_empty(), InternalNameCollection()); InternalNames vertex_columns; r_find_all_vertex_columns(node(), vertex_columns); GlobPattern glob(name); InternalNameCollection tc; InternalNames::iterator ti; for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) { InternalName *name = (*ti); if (glob.matches(name->get_name())) { tc.add_name(name); } } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_texcoords // Access: Published // Description: Returns a list of all texture coordinate sets used by // any geometry at this node level and below. //////////////////////////////////////////////////////////////////// InternalNameCollection NodePath:: find_all_texcoords() const { nassertr_always(!is_empty(), InternalNameCollection()); InternalNames vertex_columns; r_find_all_vertex_columns(node(), vertex_columns); CPT(InternalName) texcoord_name = InternalName::get_texcoord(); InternalNameCollection tc; InternalNames::iterator ti; for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) { if ((*ti)->get_top() == texcoord_name) { tc.add_name(*ti); } } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_texcoords // Access: Published // Description: Returns a list of all texture coordinate sets used by // any geometry at this node level and below that match // the indicated name (which may contain wildcard // characters). //////////////////////////////////////////////////////////////////// InternalNameCollection NodePath:: find_all_texcoords(const string &name) const { nassertr_always(!is_empty(), InternalNameCollection()); InternalNames vertex_columns; r_find_all_vertex_columns(node(), vertex_columns); GlobPattern glob(name); CPT(InternalName) texcoord_name = InternalName::get_texcoord(); InternalNameCollection tc; InternalNames::iterator ti; for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) { InternalName *name = (*ti); if (name->get_top() == texcoord_name) { // This is a texture coordinate name. Figure out the basename // of the texture coordinates. int index = name->find_ancestor("texcoord"); nassertr(index != -1, InternalNameCollection()); string net_basename = name->get_net_basename(index - 1); if (glob.matches(net_basename)) { tc.add_name(name); } } } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_texture // Access: Published // Description: Returns the first texture found applied to geometry // at this node or below that matches the indicated name // (which may contain wildcards). Returns the texture // if it is found, or NULL if it is not. //////////////////////////////////////////////////////////////////// Texture *NodePath:: find_texture(const string &name) const { nassertr_always(!is_empty(), NULL); GlobPattern glob(name); return r_find_texture(node(), get_net_state(), glob); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_texture // Access: Published // Description: Returns the first texture found applied to geometry // at this node or below that is assigned to the // indicated texture stage. Returns the texture if it // is found, or NULL if it is not. //////////////////////////////////////////////////////////////////// Texture *NodePath:: find_texture(TextureStage *stage) const { nassertr_always(!is_empty(), NULL); return r_find_texture(node(), stage); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_textures // Access: Published // Description: Returns a list of a textures applied to geometry at // this node and below. //////////////////////////////////////////////////////////////////// TextureCollection NodePath:: find_all_textures() const { nassertr_always(!is_empty(), TextureCollection()); Textures textures; r_find_all_textures(node(), get_net_state(), textures); TextureCollection tc; Textures::iterator ti; for (ti = textures.begin(); ti != textures.end(); ++ti) { tc.add_texture(*ti); } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_textures // Access: Published // Description: Returns a list of a textures applied to geometry at // this node and below that match the indicated name // (which may contain wildcard characters). //////////////////////////////////////////////////////////////////// TextureCollection NodePath:: find_all_textures(const string &name) const { nassertr_always(!is_empty(), TextureCollection()); Textures textures; r_find_all_textures(node(), get_net_state(), textures); GlobPattern glob(name); TextureCollection tc; Textures::iterator ti; for (ti = textures.begin(); ti != textures.end(); ++ti) { Texture *texture = (*ti); if (glob.matches(texture->get_name())) { tc.add_texture(texture); } } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_textures // Access: Published // Description: Returns a list of a textures on geometry at // this node and below that are assigned to the // indicated texture stage. //////////////////////////////////////////////////////////////////// TextureCollection NodePath:: find_all_textures(TextureStage *stage) const { nassertr_always(!is_empty(), TextureCollection()); Textures textures; r_find_all_textures(node(), stage, textures); TextureCollection tc; Textures::iterator ti; for (ti = textures.begin(); ti != textures.end(); ++ti) { Texture *texture = (*ti); tc.add_texture(texture); } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_texture_stage // Access: Published // Description: Returns the first TextureStage found applied to // geometry at this node or below that matches the // indicated name (which may contain wildcards). // Returns the TextureStage if it is found, or NULL if // it is not. //////////////////////////////////////////////////////////////////// TextureStage *NodePath:: find_texture_stage(const string &name) const { nassertr_always(!is_empty(), NULL); GlobPattern glob(name); return r_find_texture_stage(node(), get_net_state(), glob); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_texture_stages // Access: Published // Description: Returns a list of a TextureStages applied to geometry // at this node and below. //////////////////////////////////////////////////////////////////// TextureStageCollection NodePath:: find_all_texture_stages() const { nassertr_always(!is_empty(), TextureStageCollection()); TextureStages texture_stages; r_find_all_texture_stages(node(), get_net_state(), texture_stages); TextureStageCollection tc; TextureStages::iterator ti; for (ti = texture_stages.begin(); ti != texture_stages.end(); ++ti) { tc.add_texture_stage(*ti); } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::unify_texture_stages // Access: Published // Description: Searches through all TextureStages at this node and // below. Any TextureStages that share the same name as // the indicated TextureStage object are replaced with // this object, thus ensuring that all geometry at this // node and below with a particular TextureStage name is // using the same TextureStage object. //////////////////////////////////////////////////////////////////// void NodePath:: unify_texture_stages(TextureStage *stage) { nassertv_always(!is_empty()); r_unify_texture_stages(node(), stage); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_texture_stages // Access: Published // Description: Returns a list of a TextureStages applied to geometry // at this node and below that match the indicated name // (which may contain wildcard characters). //////////////////////////////////////////////////////////////////// TextureStageCollection NodePath:: find_all_texture_stages(const string &name) const { nassertr_always(!is_empty(), TextureStageCollection()); TextureStages texture_stages; r_find_all_texture_stages(node(), get_net_state(), texture_stages); GlobPattern glob(name); TextureStageCollection tc; TextureStages::iterator ti; for (ti = texture_stages.begin(); ti != texture_stages.end(); ++ti) { TextureStage *texture_stage = (*ti); if (glob.matches(texture_stage->get_name())) { tc.add_texture_stage(texture_stage); } } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_material // Access: Published // Description: Returns the first material found applied to geometry // at this node or below that matches the indicated name // (which may contain wildcards). Returns the material // if it is found, or NULL if it is not. //////////////////////////////////////////////////////////////////// Material *NodePath:: find_material(const string &name) const { nassertr_always(!is_empty(), NULL); GlobPattern glob(name); return r_find_material(node(), get_net_state(), glob); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_materials // Access: Published // Description: Returns a list of a materials applied to geometry at // this node and below. //////////////////////////////////////////////////////////////////// MaterialCollection NodePath:: find_all_materials() const { nassertr_always(!is_empty(), MaterialCollection()); Materials materials; r_find_all_materials(node(), get_net_state(), materials); MaterialCollection tc; Materials::iterator ti; for (ti = materials.begin(); ti != materials.end(); ++ti) { tc.add_material(*ti); } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_all_materials // Access: Published // Description: Returns a list of a materials applied to geometry at // this node and below that match the indicated name // (which may contain wildcard characters). //////////////////////////////////////////////////////////////////// MaterialCollection NodePath:: find_all_materials(const string &name) const { nassertr_always(!is_empty(), MaterialCollection()); Materials materials; r_find_all_materials(node(), get_net_state(), materials); GlobPattern glob(name); MaterialCollection tc; Materials::iterator ti; for (ti = materials.begin(); ti != materials.end(); ++ti) { Material *material = (*ti); if (glob.matches(material->get_name())) { tc.add_material(material); } } return tc; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_material // Access: Published // Description: Sets the geometry at this level and below to render // using the indicated material. // // Previously, this operation made a copy of the // material structure, but nowadays it assigns the // pointer directly. //////////////////////////////////////////////////////////////////// void NodePath:: set_material(Material *mat, int priority) { nassertv_always(!is_empty()); nassertv(mat != NULL); node()->set_attrib(MaterialAttrib::make(mat), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_material_off // Access: Published // Description: Sets the geometry at this level and below to render // using no material. This is normally the default, but // it may be useful to use this to contradict // set_material() at a higher node level (or, with a // priority, to override a set_material() at a lower // level). //////////////////////////////////////////////////////////////////// void NodePath:: set_material_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(MaterialAttrib::make_off(), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_material // Access: Published // Description: Completely removes any material adjustment that may // have been set via set_material() from this particular // node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_material() { nassertv_always(!is_empty()); node()->clear_attrib(MaterialAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_material // Access: Published // Description: Returns true if a material has been applied to this // particular node via set_material(), false otherwise. //////////////////////////////////////////////////////////////////// bool NodePath:: has_material() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(MaterialAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const MaterialAttrib *ma = DCAST(MaterialAttrib, attrib); return !ma->is_off(); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_material // Access: Published // Description: Returns the material that has been set on this // particular node, or NULL if no material has been set. // This is not necessarily the material that will be // applied to the geometry at or below this level, as // another material at a higher or lower level may // override. // See also find_material(). //////////////////////////////////////////////////////////////////// PT(Material) NodePath:: get_material() const { nassertr_always(!is_empty(), NULL); const RenderAttrib *attrib = node()->get_attrib(MaterialAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const MaterialAttrib *ma = DCAST(MaterialAttrib, attrib); return ma->get_material(); } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_fog // Access: Published // Description: Sets the geometry at this level and below to render // using the indicated fog. //////////////////////////////////////////////////////////////////// void NodePath:: set_fog(Fog *fog, int priority) { nassertv_always(!is_empty()); node()->set_attrib(FogAttrib::make(fog), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_fog_off // Access: Published // Description: Sets the geometry at this level and below to render // using no fog. This is normally the default, but // it may be useful to use this to contradict // set_fog() at a higher node level (or, with a // priority, to override a set_fog() at a lower // level). //////////////////////////////////////////////////////////////////// void NodePath:: set_fog_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(FogAttrib::make_off(), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_fog // Access: Published // Description: Completely removes any fog adjustment that may // have been set via set_fog() or set_fog_off() // from this particular node. This allows whatever // fogs might be otherwise affecting the geometry to // show instead. //////////////////////////////////////////////////////////////////// void NodePath:: clear_fog() { nassertv_always(!is_empty()); node()->clear_attrib(FogAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_fog // Access: Published // Description: Returns true if a fog has been applied to this // particular node via set_fog(), false otherwise. // This is not the same thing as asking whether the // geometry at this node will be rendered with // fog, as there may be a fog in effect from a higher or // lower level. //////////////////////////////////////////////////////////////////// bool NodePath:: has_fog() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(FogAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const FogAttrib *fa = DCAST(FogAttrib, attrib); return !fa->is_off(); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_fog_off // Access: Published // Description: Returns true if a fog has been specifically // disabled on this particular node via // set_fog_off(), false otherwise. This is not the // same thing as asking whether the geometry at this // node will be rendered unfogged, as there may be a // fog in effect from a higher or lower level. //////////////////////////////////////////////////////////////////// bool NodePath:: has_fog_off() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(FogAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const FogAttrib *fa = DCAST(FogAttrib, attrib); return fa->is_off(); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_fog // Access: Published // Description: Returns the fog that has been set on this // particular node, or NULL if no fog has been set. // This is not necessarily the fog that will be // applied to the geometry at or below this level, as // another fog at a higher or lower level may // override. //////////////////////////////////////////////////////////////////// Fog *NodePath:: get_fog() const { nassertr_always(!is_empty(), NULL); const RenderAttrib *attrib = node()->get_attrib(FogAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const FogAttrib *fa = DCAST(FogAttrib, attrib); return fa->get_fog(); } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_render_mode_wireframe // Access: Published // Description: Sets up the geometry at this level and below (unless // overridden) to render in wireframe mode. //////////////////////////////////////////////////////////////////// void NodePath:: set_render_mode_wireframe(int priority) { nassertv_always(!is_empty()); PN_stdfloat thickness = get_render_mode_thickness(); bool perspective = get_render_mode_perspective(); node()->set_attrib(RenderModeAttrib::make(RenderModeAttrib::M_wireframe, thickness, perspective), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_render_mode_filled // Access: Published // Description: Sets up the geometry at this level and below (unless // overridden) to render in filled (i.e. not wireframe) // mode. //////////////////////////////////////////////////////////////////// void NodePath:: set_render_mode_filled(int priority) { nassertv_always(!is_empty()); PN_stdfloat thickness = get_render_mode_thickness(); bool perspective = get_render_mode_perspective(); node()->set_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled, thickness, perspective), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_render_mode_perspective // Access: Published // Description: Sets up the point geometry at this level and below to // render as perspective sprites (that is, billboarded // quads). The thickness, as specified with // set_render_mode_thickness(), is the width of each // point in 3-D units, unless it is overridden on a // per-vertex basis. This does not affect geometry // other than points. // // If you want the quads to be individually textured, // you should also set a TexGenAttrib::M_point_sprite on // the node. //////////////////////////////////////////////////////////////////// void NodePath:: set_render_mode_perspective(bool perspective, int priority) { nassertv_always(!is_empty()); RenderModeAttrib::Mode mode = get_render_mode(); PN_stdfloat thickness = get_render_mode_thickness(); node()->set_attrib(RenderModeAttrib::make(mode, thickness, perspective), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_render_mode_thickness // Access: Published // Description: Sets up the point geometry at this level and below to // render as thick points (that is, billboarded // quads). The thickness is in pixels, unless // set_render_mode_perspective is also true, in which // case it is in 3-D units. // // If you want the quads to be individually textured, // you should also set a TexGenAttrib::M_point_sprite on // the node. //////////////////////////////////////////////////////////////////// void NodePath:: set_render_mode_thickness(PN_stdfloat thickness, int priority) { nassertv_always(!is_empty()); RenderModeAttrib::Mode mode = get_render_mode(); bool perspective = get_render_mode_perspective(); node()->set_attrib(RenderModeAttrib::make(mode, thickness, perspective), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_render_mode // Access: Published // Description: Sets up the geometry at this level and below (unless // overridden) to render in the specified mode and with // the indicated line and/or point thickness. //////////////////////////////////////////////////////////////////// void NodePath:: set_render_mode(RenderModeAttrib::Mode mode, PN_stdfloat thickness, int priority) { nassertv_always(!is_empty()); node()->set_attrib(RenderModeAttrib::make(mode, thickness), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_render_mode // Access: Published // Description: Completely removes any render mode adjustment that // may have been set on this node via // set_render_mode_wireframe() or // set_render_mode_filled(). //////////////////////////////////////////////////////////////////// void NodePath:: clear_render_mode() { nassertv_always(!is_empty()); node()->clear_attrib(RenderModeAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_render_mode // Access: Published // Description: Returns true if a render mode has been explicitly set // on this particular node via set_render_mode() (or // set_render_mode_wireframe() or // set_render_mode_filled()), false otherwise. //////////////////////////////////////////////////////////////////// bool NodePath:: has_render_mode() const { nassertr_always(!is_empty(), false); return node()->has_attrib(RenderModeAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_render_mode // Access: Published // Description: Returns the render mode that has been specifically // set on this node via set_render_mode(), or // M_unchanged if nothing has been set. //////////////////////////////////////////////////////////////////// RenderModeAttrib::Mode NodePath:: get_render_mode() const { nassertr_always(!is_empty(), RenderModeAttrib::M_unchanged); const RenderAttrib *attrib = node()->get_attrib(RenderModeAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib); return ta->get_mode(); } return RenderModeAttrib::M_unchanged; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_render_mode_thickness // Access: Published // Description: Returns the render mode thickness that has been // specifically set on this node via set_render_mode(), // or 1.0 if nothing has been set. //////////////////////////////////////////////////////////////////// PN_stdfloat NodePath:: get_render_mode_thickness() const { nassertr_always(!is_empty(), 0.0f); const RenderAttrib *attrib = node()->get_attrib(RenderModeAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib); return ta->get_thickness(); } return 1.0f; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_render_mode_perspective // Access: Published // Description: Returns the flag that has been set on this node via // set_render_mode_perspective(), or false if no flag // has been set. //////////////////////////////////////////////////////////////////// bool NodePath:: get_render_mode_perspective() const { nassertr_always(!is_empty(), 0.0f); const RenderAttrib *attrib = node()->get_attrib(RenderModeAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib); return ta->get_perspective(); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_two_sided // Access: Published // Description: Specifically sets or disables two-sided rendering // mode on this particular node. If no other nodes // override, this will cause backfacing polygons to be // drawn (in two-sided mode, true) or culled (in // one-sided mode, false). //////////////////////////////////////////////////////////////////// void NodePath:: set_two_sided(bool two_sided, int priority) { nassertv_always(!is_empty()); CullFaceAttrib::Mode mode = two_sided ? CullFaceAttrib::M_cull_none : CullFaceAttrib::M_cull_clockwise; node()->set_attrib(CullFaceAttrib::make(mode), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_two_sided // Access: Published // Description: Completely removes any two-sided adjustment that // may have been set on this node via set_two_sided(). // The geometry at this level and below will // subsequently be rendered either two-sided or // one-sided, according to whatever other nodes may have // had set_two_sided() on it, or according to the // initial state otherwise. //////////////////////////////////////////////////////////////////// void NodePath:: clear_two_sided() { nassertv_always(!is_empty()); node()->clear_attrib(CullFaceAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_two_sided // Access: Published // Description: Returns true if a two-sided adjustment has been // explicitly set on this particular node via // set_two_sided(). If this returns true, then // get_two_sided() may be called to determine which has // been set. //////////////////////////////////////////////////////////////////// bool NodePath:: has_two_sided() const { nassertr_always(!is_empty(), false); return node()->has_attrib(CullFaceAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_two_sided // Access: Published // Description: Returns true if two-sided rendering has been // specifically set on this node via set_two_sided(), or // false if one-sided rendering has been specifically // set, or if nothing has been specifically set. See // also has_two_sided(). This does not necessarily // imply that the geometry will or will not be rendered // two-sided, as there may be other nodes that override. //////////////////////////////////////////////////////////////////// bool NodePath:: get_two_sided() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(CullFaceAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, attrib); return (cfa->get_actual_mode() == CullFaceAttrib::M_cull_none); } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_depth_test // Access: Published // Description: Specifically sets or disables the testing of the // depth buffer on this particular node. This is // normally on in the 3-d scene graph and off in the 2-d // scene graph; it should be on for rendering most 3-d // objects properly. //////////////////////////////////////////////////////////////////// void NodePath:: set_depth_test(bool depth_test, int priority) { nassertv_always(!is_empty()); DepthTestAttrib::PandaCompareFunc mode = depth_test ? DepthTestAttrib::M_less : DepthTestAttrib::M_none; node()->set_attrib(DepthTestAttrib::make(mode), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_depth_test // Access: Published // Description: Completely removes any depth-test adjustment that // may have been set on this node via set_depth_test(). //////////////////////////////////////////////////////////////////// void NodePath:: clear_depth_test() { nassertv_always(!is_empty()); node()->clear_attrib(DepthTestAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_depth_test // Access: Published // Description: Returns true if a depth-test adjustment has been // explicitly set on this particular node via // set_depth_test(). If this returns true, then // get_depth_test() may be called to determine which has // been set. //////////////////////////////////////////////////////////////////// bool NodePath:: has_depth_test() const { nassertr_always(!is_empty(), false); return node()->has_attrib(DepthTestAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_depth_test // Access: Published // Description: Returns true if depth-test rendering has been // specifically set on this node via set_depth_test(), or // false if depth-test rendering has been specifically // disabled. If nothing has been specifically set, // returns true. See also has_depth_test(). //////////////////////////////////////////////////////////////////// bool NodePath:: get_depth_test() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(DepthTestAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const DepthTestAttrib *dta = DCAST(DepthTestAttrib, attrib); return (dta->get_mode() != DepthTestAttrib::M_none); } return true; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_depth_write // Access: Published // Description: Specifically sets or disables the writing to the // depth buffer on this particular node. This is // normally on in the 3-d scene graph and off in the 2-d // scene graph; it should be on for rendering most 3-d // objects properly. //////////////////////////////////////////////////////////////////// void NodePath:: set_depth_write(bool depth_write, int priority) { nassertv_always(!is_empty()); DepthWriteAttrib::Mode mode = depth_write ? DepthWriteAttrib::M_on : DepthWriteAttrib::M_off; node()->set_attrib(DepthWriteAttrib::make(mode), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_depth_write // Access: Published // Description: Completely removes any depth-write adjustment that // may have been set on this node via set_depth_write(). //////////////////////////////////////////////////////////////////// void NodePath:: clear_depth_write() { nassertv_always(!is_empty()); node()->clear_attrib(DepthWriteAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_depth_write // Access: Published // Description: Returns true if a depth-write adjustment has been // explicitly set on this particular node via // set_depth_write(). If this returns true, then // get_depth_write() may be called to determine which has // been set. //////////////////////////////////////////////////////////////////// bool NodePath:: has_depth_write() const { nassertr_always(!is_empty(), false); return node()->has_attrib(DepthWriteAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_depth_write // Access: Published // Description: Returns true if depth-write rendering has been // specifically set on this node via set_depth_write(), or // false if depth-write rendering has been specifically // disabled. If nothing has been specifically set, // returns true. See also has_depth_write(). //////////////////////////////////////////////////////////////////// bool NodePath:: get_depth_write() const { nassertr_always(!is_empty(), false); const RenderAttrib *attrib = node()->get_attrib(DepthWriteAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const DepthWriteAttrib *dta = DCAST(DepthWriteAttrib, attrib); return (dta->get_mode() != DepthWriteAttrib::M_off); } return true; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_depth_offset // Access: Published // Description: This instructs the graphics driver to apply an // offset or bias to the generated depth values for // rendered polygons, before they are written to the // depth buffer. This can be used to shift polygons // forward slightly, to resolve depth conflicts, or // self-shadowing artifacts on thin objects. // The bias is always an integer number, and each // integer increment represents the smallest possible // increment in Z that is sufficient to completely // resolve two coplanar polygons. Positive numbers // are closer towards the camera. //////////////////////////////////////////////////////////////////// void NodePath:: set_depth_offset(int bias, int priority) { nassertv_always(!is_empty()); node()->set_attrib(DepthOffsetAttrib::make(bias), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_depth_offset // Access: Published // Description: Completely removes any depth-offset adjustment that // may have been set on this node via set_depth_offset(). //////////////////////////////////////////////////////////////////// void NodePath:: clear_depth_offset() { nassertv_always(!is_empty()); node()->clear_attrib(DepthOffsetAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_depth_offset // Access: Published // Description: Returns true if a depth-offset adjustment has been // explicitly set on this particular node via // set_depth_offset(). If this returns true, then // get_depth_offset() may be called to determine which has // been set. //////////////////////////////////////////////////////////////////// bool NodePath:: has_depth_offset() const { nassertr_always(!is_empty(), false); return node()->has_attrib(DepthOffsetAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_depth_offset // Access: Published // Description: Returns the depth offset value if it has been // specified using set_depth_offset, or 0 if not. //////////////////////////////////////////////////////////////////// int NodePath:: get_depth_offset() const { nassertr_always(!is_empty(), 0); const RenderAttrib *attrib = node()->get_attrib(DepthOffsetAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const DepthOffsetAttrib *doa = DCAST(DepthOffsetAttrib, attrib); return doa->get_offset(); } return 0; } //////////////////////////////////////////////////////////////////// // Function: NodePath::do_billboard_axis // Access: Published // Description: Performs a billboard-type rotate to the indicated // camera node, one time only, and leaves the object // rotated. This is similar in principle to heads_up(). //////////////////////////////////////////////////////////////////// void NodePath:: do_billboard_axis(const NodePath &camera, PN_stdfloat offset) { nassertv_always(!is_empty()); CPT(TransformState) transform = camera.get_transform(get_parent()); const LMatrix4 &rel_mat = transform->get_mat(); LVector3 up = LVector3::up(); LVector3 rel_pos = -rel_mat.get_row3(3); LQuaternion quat; ::heads_up(quat, rel_pos, up); set_quat(quat); // Also slide the geometry towards the camera according to the // offset factor. if (offset != 0.0f) { LVector3 translate = rel_mat.get_row3(3); translate.normalize(); translate *= offset; set_pos(translate); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::do_billboard_point_eye // Access: Published // Description: Performs a billboard-type rotate to the indicated // camera node, one time only, and leaves the object // rotated. This is similar in principle to look_at(), // although the point_eye billboard effect cannot be // achieved using the ordinary look_at() call. //////////////////////////////////////////////////////////////////// void NodePath:: do_billboard_point_eye(const NodePath &camera, PN_stdfloat offset) { nassertv_always(!is_empty()); CPT(TransformState) transform = camera.get_transform(get_parent()); const LMatrix4 &rel_mat = transform->get_mat(); LVector3 up = LVector3::up() * rel_mat; LVector3 rel_pos = LVector3::forward() * rel_mat; LQuaternion quat; ::look_at(quat, rel_pos, up); set_quat(quat); // Also slide the geometry towards the camera according to the // offset factor. if (offset != 0.0f) { LVector3 translate = rel_mat.get_row3(3); translate.normalize(); translate *= offset; set_pos(translate); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::do_billboard_point_world // Access: Published // Description: Performs a billboard-type rotate to the indicated // camera node, one time only, and leaves the object // rotated. This is similar in principle to look_at(). //////////////////////////////////////////////////////////////////// void NodePath:: do_billboard_point_world(const NodePath &camera, PN_stdfloat offset) { nassertv_always(!is_empty()); CPT(TransformState) transform = camera.get_transform(get_parent()); const LMatrix4 &rel_mat = transform->get_mat(); LVector3 up = LVector3::up(); LVector3 rel_pos = -rel_mat.get_row3(3); LQuaternion quat; ::look_at(quat, rel_pos, up); set_quat(quat); // Also slide the geometry towards the camera according to the // offset factor. if (offset != 0.0f) { LVector3 translate = rel_mat.get_row3(3); translate.normalize(); translate *= offset; set_pos(translate); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_billboard_axis // Access: Published // Description: Puts a billboard transition on the node such that it // will rotate in two dimensions around the up axis, // towards a specified "camera" instead of to the // viewing camera. //////////////////////////////////////////////////////////////////// void NodePath:: set_billboard_axis(const NodePath &camera, PN_stdfloat offset) { nassertv_always(!is_empty()); CPT(RenderEffect) billboard = BillboardEffect::make (LVector3::up(), false, true, offset, camera, LPoint3(0.0f, 0.0f, 0.0f)); node()->set_effect(billboard); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_billboard_point_eye // Access: Published // Description: Puts a billboard transition on the node such that it // will rotate in three dimensions about the origin, // keeping its up vector oriented to the top of the // camera, towards a specified "camera" instead of to // the viewing camera. //////////////////////////////////////////////////////////////////// void NodePath:: set_billboard_point_eye(const NodePath &camera, PN_stdfloat offset) { nassertv_always(!is_empty()); CPT(RenderEffect) billboard = BillboardEffect::make (LVector3::up(), true, false, offset, camera, LPoint3(0.0f, 0.0f, 0.0f)); node()->set_effect(billboard); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_billboard_point_world // Access: Published // Description: Puts a billboard transition on the node such that it // will rotate in three dimensions about the origin, // keeping its up vector oriented to the sky, towards a // specified "camera" instead of to the viewing camera. //////////////////////////////////////////////////////////////////// void NodePath:: set_billboard_point_world(const NodePath &camera, PN_stdfloat offset) { nassertv_always(!is_empty()); CPT(RenderEffect) billboard = BillboardEffect::make (LVector3::up(), false, false, offset, camera, LPoint3(0.0f, 0.0f, 0.0f)); node()->set_effect(billboard); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_billboard // Access: Published // Description: Removes any billboard effect from the node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_billboard() { nassertv_always(!is_empty()); node()->clear_effect(BillboardEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_billboard // Access: Published // Description: Returns true if there is any billboard effect on // the node. //////////////////////////////////////////////////////////////////// bool NodePath:: has_billboard() const { nassertr_always(!is_empty(), false); return node()->has_effect(BillboardEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_compass // Access: Published // Description: Puts a compass effect on the node, so that it will // retain a fixed rotation relative to the reference // node (or render if the reference node is empty) // regardless of the transforms above it. //////////////////////////////////////////////////////////////////// void NodePath:: set_compass(const NodePath &reference) { nassertv_always(!is_empty()); node()->set_effect(CompassEffect::make(reference)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_compass // Access: Published // Description: Removes any compass effect from the node. //////////////////////////////////////////////////////////////////// void NodePath:: clear_compass() { nassertv_always(!is_empty()); node()->clear_effect(CompassEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_compass // Access: Published // Description: Returns true if there is any compass effect on // the node. //////////////////////////////////////////////////////////////////// bool NodePath:: has_compass() const { nassertr_always(!is_empty(), false); return node()->has_effect(CompassEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_transparency // Access: Published // Description: Specifically sets or disables transparent rendering // mode on this particular node. If no other nodes // override, this will cause items with a non-1 value // for alpha color to be rendered partially transparent. //////////////////////////////////////////////////////////////////// void NodePath:: set_transparency(TransparencyAttrib::Mode mode, int priority) { nassertv_always(!is_empty()); node()->set_attrib(TransparencyAttrib::make(mode), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_transparency // Access: Published // Description: Completely removes any transparency adjustment that // may have been set on this node via set_transparency(). // The geometry at this level and below will // subsequently be rendered either transparent or not, // to whatever other nodes may have had // set_transparency() on them. //////////////////////////////////////////////////////////////////// void NodePath:: clear_transparency() { nassertv_always(!is_empty()); node()->clear_attrib(TransparencyAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_transparency // Access: Published // Description: Returns true if a transparent-rendering adjustment // has been explicitly set on this particular node via // set_transparency(). If this returns true, then // get_transparency() may be called to determine whether // transparency has been explicitly enabled or // explicitly disabled for this node. //////////////////////////////////////////////////////////////////// bool NodePath:: has_transparency() const { nassertr_always(!is_empty(), false); return node()->has_attrib(TransparencyAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_transparency // Access: Published // Description: Returns the transparent rendering that has been // specifically set on this node via set_transparency(), or // M_none if nontransparent rendering has been specifically // set, or if nothing has been specifically set. See // also has_transparency(). This does not necessarily // imply that the geometry will or will not be rendered // transparent, as there may be other nodes that override. //////////////////////////////////////////////////////////////////// TransparencyAttrib::Mode NodePath:: get_transparency() const { nassertr_always(!is_empty(), TransparencyAttrib::M_none); const RenderAttrib *attrib = node()->get_attrib(TransparencyAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TransparencyAttrib *ta = DCAST(TransparencyAttrib, attrib); return ta->get_mode(); } return TransparencyAttrib::M_none; } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_antialias // Access: Published // Description: Specifies the antialiasing type that should be // applied at this node and below. See AntialiasAttrib. //////////////////////////////////////////////////////////////////// void NodePath:: set_antialias(unsigned short mode, int priority) { nassertv_always(!is_empty()); node()->set_attrib(AntialiasAttrib::make(mode), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_antialias // Access: Published // Description: Completely removes any antialias setting that // may have been set on this node via set_antialias(). //////////////////////////////////////////////////////////////////// void NodePath:: clear_antialias() { nassertv_always(!is_empty()); node()->clear_attrib(AntialiasAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_antialias // Access: Published // Description: Returns true if an antialias setting has been // explicitly mode on this particular node via // set_antialias(). If this returns true, then // get_antialias() may be called to determine what the // setting was. //////////////////////////////////////////////////////////////////// bool NodePath:: has_antialias() const { nassertr_always(!is_empty(), false); return node()->has_attrib(AntialiasAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_antialias // Access: Published // Description: Returns the antialias setting that has been // specifically set on this node via set_antialias(), or // M_none if no setting has been made. //////////////////////////////////////////////////////////////////// unsigned short NodePath:: get_antialias() const { nassertr_always(!is_empty(), AntialiasAttrib::M_none); const RenderAttrib *attrib = node()->get_attrib(AntialiasAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const AntialiasAttrib *ta = DCAST(AntialiasAttrib, attrib); return ta->get_mode(); } return AntialiasAttrib::M_none; } //////////////////////////////////////////////////////////////////// // Function: NodePath::has_audio_volume // Access: Published // Description: Returns true if an audio volume has been applied // to the referenced node, false otherwise. It is still // possible that volume at this node might have been // scaled by an ancestor node. //////////////////////////////////////////////////////////////////// bool NodePath:: has_audio_volume() const { nassertr_always(!is_empty(), false); return node()->has_attrib(AudioVolumeAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_audio_volume // Access: Published // Description: Completely removes any audio volume from the // referenced node. This is preferable to simply // setting the audio volume to identity, as it also // removes the overhead associated with having an audio // volume at all. //////////////////////////////////////////////////////////////////// void NodePath:: clear_audio_volume() { nassertv_always(!is_empty()); node()->clear_attrib(AudioVolumeAttrib::get_class_slot()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_audio_volume // Access: Published // Description: Sets the audio volume component of the transform //////////////////////////////////////////////////////////////////// void NodePath:: set_audio_volume(PN_stdfloat volume, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = node()->get_attrib(AudioVolumeAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { priority = max(priority, node()->get_state()->get_override(AudioVolumeAttrib::get_class_slot())); CPT(AudioVolumeAttrib) ava = DCAST(AudioVolumeAttrib, attrib); // Modify the existing AudioVolumeAttrib to add the indicated // volume. node()->set_attrib(ava->set_volume(volume), priority); } else { // Create a new AudioVolumeAttrib for this node. node()->set_attrib(AudioVolumeAttrib::make(volume), priority); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::set_audio_volume_off // Access: Published // Description: Disables any audio volume attribute inherited from // above. This is not the same thing as // clear_audio_volume(), which undoes any previous // set_audio_volume() operation on this node; rather, // this actively disables any set_audio_volume() that // might be inherited from a parent node. // // It is legal to specify a new volume on the same // node with a subsequent call to set_audio_volume(); // this new scale will apply to lower nodes. //////////////////////////////////////////////////////////////////// void NodePath:: set_audio_volume_off(int priority) { nassertv_always(!is_empty()); node()->set_attrib(AudioVolumeAttrib::make_off(), priority); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_audio_volume // Access: Published // Description: Returns the complete audio volume that has been // applied to this node via a previous call to // set_audio_volume(), or 1. (identity) if no volume has // been applied to this particular node. //////////////////////////////////////////////////////////////////// PN_stdfloat NodePath:: get_audio_volume() const { const RenderAttrib *attrib = node()->get_attrib(AudioVolumeAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const AudioVolumeAttrib *ava = DCAST(AudioVolumeAttrib, attrib); return ava->get_volume(); } return 1.0f; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_net_audio_volume // Access: Published // Description: Returns the complete audio volume for this node // taking highers nodes in the graph into account. //////////////////////////////////////////////////////////////////// PN_stdfloat NodePath:: get_net_audio_volume() const { CPT(RenderState) net_state = get_net_state(); const RenderAttrib *attrib = net_state->get_attrib(AudioVolumeAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const AudioVolumeAttrib *ava = DCAST(AudioVolumeAttrib, attrib); if (ava != (const AudioVolumeAttrib *)NULL) { return ava->get_volume(); } } return 1.0f; } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_hidden_ancestor // Access: Published // Description: Returns the NodePath at or above the referenced node // that is hidden to the indicated camera(s), or an // empty NodePath if no ancestor of the referenced node // is hidden (and the node should be visible). //////////////////////////////////////////////////////////////////// NodePath NodePath:: get_hidden_ancestor(DrawMask camera_mask, Thread *current_thread) const { int pipeline_stage = current_thread->get_pipeline_stage(); NodePathComponent *comp; for (comp = _head; comp != (NodePathComponent *)NULL; comp = comp->get_next(pipeline_stage, current_thread)) { PandaNode *node = comp->get_node(); if (node->is_overall_hidden() || ((node->get_draw_show_mask() | ~node->get_draw_control_mask()) & camera_mask).is_zero()) { NodePath result; result._head = comp; return result; } } return not_found(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::stash // Access: Published // Description: Removes the referenced node (and the entire subgraph // below this node) from the scene graph in any normal // sense. The node will no longer be visible and is not // tested for collisions; furthermore, no normal scene // graph traversal will visit the node. The node's // bounding volume no longer contributes to its parent's // bounding volume. // // A stashed node cannot be located by a normal find() // operation (although a special find string can still // retrieve it). //////////////////////////////////////////////////////////////////// void NodePath:: stash(int sort, Thread *current_thread) { nassertv_always(!is_singleton() && !is_empty()); nassertv(verify_complete()); int pipeline_stage = current_thread->get_pipeline_stage(); bool reparented = PandaNode::reparent(_head->get_next(pipeline_stage, current_thread), _head, sort, true, pipeline_stage, current_thread); nassertv(reparented); } //////////////////////////////////////////////////////////////////// // Function: NodePath::unstash // Access: Published // Description: Undoes the effect of a previous stash() on this // node: makes the referenced node (and the entire // subgraph below this node) once again part of the // scene graph. //////////////////////////////////////////////////////////////////// void NodePath:: unstash(int sort, Thread *current_thread) { nassertv_always(!is_singleton() && !is_empty()); nassertv(verify_complete()); int pipeline_stage = current_thread->get_pipeline_stage(); bool reparented = PandaNode::reparent(_head->get_next(pipeline_stage, current_thread), _head, sort, false, pipeline_stage, current_thread); nassertv(reparented); } //////////////////////////////////////////////////////////////////// // Function: NodePath::unstash_all // Access: Published // Description: Unstashes this node and all stashed child nodes. //////////////////////////////////////////////////////////////////// void NodePath:: unstash_all(Thread *current_thread) { NodePathCollection stashed_descendents = find_all_matches("**/@@*"); stashed_descendents.unstash(); unstash(0, current_thread); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_stashed_ancestor // Access: Published // Description: Returns the NodePath at or above the referenced node // that is stashed, or an empty NodePath if no ancestor // of the referenced node is stashed (and the node should // be visible). //////////////////////////////////////////////////////////////////// NodePath NodePath:: get_stashed_ancestor(Thread *current_thread) const { NodePathComponent *comp = _head; if (comp != (NodePathComponent *)NULL) { int pipeline_stage = current_thread->get_pipeline_stage(); NodePathComponent *next = comp->get_next(pipeline_stage, current_thread); while (next != (NodePathComponent *)NULL) { PandaNode *node = comp->get_node(); PandaNode *parent_node = next->get_node(); if (parent_node->find_stashed(node) >= 0) { NodePath result; result._head = comp; return result; } comp = next; next = next->get_next(pipeline_stage, current_thread); } } return not_found(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::verify_complete // Access: Published // Description: Returns true if all of the nodes described in the // NodePath are connected, or false otherwise. //////////////////////////////////////////////////////////////////// bool NodePath:: verify_complete(Thread *current_thread) const { if (is_empty()) { return true; } #ifdef HAVE_THREADS if (Thread::is_true_threads()) { // In a threaded environment, we can't reliably test this, since a // sub-thread may be mucking with the NodePath's ancestry as we // try to validate it. NodePaths are inherently not thread-safe, // but generally that's not an issue. return true; } #endif // HAVE_THREADS PStatTimer timer(_verify_complete_pcollector); const NodePathComponent *comp = _head; nassertr(comp != (const NodePathComponent *)NULL, false); int pipeline_stage = current_thread->get_pipeline_stage(); PandaNode *node = comp->get_node(); nassertr(node != (const PandaNode *)NULL, false); int length = comp->get_length(pipeline_stage, current_thread); comp = comp->get_next(pipeline_stage, current_thread); length--; while (comp != (const NodePathComponent *)NULL) { PandaNode *next_node = comp->get_node(); nassertr(next_node != (const PandaNode *)NULL, false); if (node->find_parent(next_node) < 0) { pgraph_cat.warning() << *this << " is incomplete; " << *node << " is not a child of " << *next_node << "\n"; return false; } if (comp->get_length(pipeline_stage, current_thread) != length) { pgraph_cat.warning() << *this << " is incomplete; length at " << *next_node << " indicates " << comp->get_length(pipeline_stage, current_thread) << " while length at " << *node << " indicates " << length << "\n"; return false; } node = next_node; comp = comp->get_next(pipeline_stage, current_thread); length--; } return true; } //////////////////////////////////////////////////////////////////// // Function: NodePath::premunge_scene // Access: Published // Description: Walks through the scene graph beginning at the bottom // node, and internally adjusts any GeomVertexFormats // for optimal rendering on the indicated GSG. If this // step is not done prior to rendering, the formats will // be optimized at render time instead, for a small // cost. // // It is not normally necessary to do this on a model // loaded directly from disk, since the loader will do // this by default. //////////////////////////////////////////////////////////////////// void NodePath:: premunge_scene(GraphicsStateGuardianBase *gsg) { nassertv_always(!is_empty()); CPT(RenderState) state = RenderState::make_empty(); if (has_parent()) { state = get_parent().get_net_state(); } SceneGraphReducer gr(gsg); gr.premunge(node(), state); } //////////////////////////////////////////////////////////////////// // Function: NodePath::prepare_scene // Access: Published // Description: Walks through the scene graph beginning at the bottom // node, and does whatever initialization is required to // render the scene properly with the indicated GSG. It // is not strictly necessary to call this, since the GSG // will initialize itself when the scene is rendered, // but this may take some of the overhead away from that // process. // // In particular, this will ensure that textures and // vertex buffers within the scene are loaded into // graphics memory. //////////////////////////////////////////////////////////////////// void NodePath:: prepare_scene(GraphicsStateGuardianBase *gsg) { nassertv_always(!is_empty()); node()->prepare_scene(gsg, get_net_state()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::show_bounds // Access: Published // Description: Causes the bounding volume of the bottom node and all // of its descendants (that is, the bounding volume // associated with the the bottom arc) to be rendered, // if possible. The rendering method is less than // optimal; this is intended primarily for debugging. //////////////////////////////////////////////////////////////////// void NodePath:: show_bounds() { nassertv_always(!is_empty()); node()->set_effect(ShowBoundsEffect::make(false)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::show_tight_bounds // Access: Published // Description: Similar to show_bounds(), this draws a bounding box // representing the "tight" bounds of this node and all // of its descendants. The bounding box is recomputed // every frame by reexamining all of the vertices; this // is far from efficient, but this is intended for // debugging. //////////////////////////////////////////////////////////////////// void NodePath:: show_tight_bounds() { nassertv_always(!is_empty()); node()->set_effect(ShowBoundsEffect::make(true)); } //////////////////////////////////////////////////////////////////// // Function: NodePath::hide_bounds // Access: Published // Description: Stops the rendering of the bounding volume begun with // show_bounds(). //////////////////////////////////////////////////////////////////// void NodePath:: hide_bounds() { nassertv_always(!is_empty()); node()->clear_effect(ShowBoundsEffect::get_class_type()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::get_bounds // Access: Published // Description: Returns a newly-allocated bounding volume containing // the bottom node and all of its descendants. This is // the bounding volume on the bottom arc, converted to // the local coordinate space of the node. //////////////////////////////////////////////////////////////////// PT(BoundingVolume) NodePath:: get_bounds(Thread *current_thread) const { nassertr_always(!is_empty(), new BoundingSphere); return node()->get_bounds(current_thread)->make_copy(); } //////////////////////////////////////////////////////////////////// // Function: NodePath::force_recompute_bounds // Access: Published // Description: Forces the recomputing of all the bounding volumes at // every node in the subgraph beginning at this node and // below. // // This should not normally need to be called, since the // bounding volumes are supposed to be recomputed // automatically when necessary. It may be useful when // debugging, to verify that the bounding volumes have // not become inadvertently stale; it may also be useful // to force animated characters to update their bounding // volumes (which does not presently happen // automatically). //////////////////////////////////////////////////////////////////// void NodePath:: force_recompute_bounds() { nassertv_always(!is_empty()); r_force_recompute_bounds(node()); } //////////////////////////////////////////////////////////////////// // Function: NodePath::write_bounds // Access: Published // Description: Writes a description of the bounding volume // containing the bottom node and all of its descendants // to the indicated output stream. //////////////////////////////////////////////////////////////////// void NodePath:: write_bounds(ostream &out) const { get_bounds()->write(out); } //////////////////////////////////////////////////////////////////// // Function: NodePath::calc_tight_bounds // Access: Published // Description: Calculates the minimum and maximum vertices of all // Geoms at this NodePath's bottom node and below. This // is a tight bounding box; it will generally be tighter // than the bounding volume returned by get_bounds() // (but it is more expensive to compute). // // The return value is true if any points are within the // bounding volume, or false if none are. //////////////////////////////////////////////////////////////////// bool NodePath:: calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, Thread *current_thread) const { min_point.set(0.0f, 0.0f, 0.0f); max_point.set(0.0f, 0.0f, 0.0f); nassertr_always(!is_empty(), false); bool found_any = false; node()->calc_tight_bounds(min_point, max_point, found_any, TransformState::make_identity(), current_thread); return found_any; } /* NB: Had to remove this function to avoid circular dependency when moving SceneGraphAnalyzer into pgraphnodes, attempting to reduce size of pgraph. This function is now defined as a Python extension function instead. //////////////////////////////////////////////////////////////////// // Function: NodePath::analyze // Access: Published // Description: Analyzes the geometry below this node and reports the // number of vertices, triangles, etc. This is the same // information reported by the bam-info program. //////////////////////////////////////////////////////////////////// void NodePath:: analyze() const { nassertv_always(!is_empty()); SceneGraphAnalyzer sga; sga.add_node(node()); if (sga.get_num_lod_nodes() == 0) { sga.write(nout); } else { nout << "At highest LOD:\n"; SceneGraphAnalyzer sga2; sga2.set_lod_mode(SceneGraphAnalyzer::LM_highest); sga2.add_node(node()); sga2.write(nout); nout << "\nAt lowest LOD:\n"; sga2.clear(); sga2.set_lod_mode(SceneGraphAnalyzer::LM_lowest); sga2.add_node(node()); sga2.write(nout); nout << "\nAll nodes:\n"; sga.write(nout); } } */ //////////////////////////////////////////////////////////////////// // Function: NodePath::flatten_light // Access: Published // Description: Lightly flattens out the hierarchy below this node by // applying transforms, colors, and texture matrices // from the nodes onto the vertices, but does not remove // any nodes. // // This can result in improved rendering performance // because there will be fewer transforms in the // resulting scene graph, but the number of nodes will // remain the same. // // In particular, any NodePaths that reference nodes // within this hierarchy will not be damaged. However, // since this operation will remove transforms from the // scene graph, it may be dangerous to apply to nodes // where you expect to dynamically modify the transform, // or where you expect the geometry to remain in a // particular local coordinate system. // // The return value is always 0, since flatten_light // does not remove any nodes. //////////////////////////////////////////////////////////////////// int NodePath:: flatten_light() { nassertr_always(!is_empty(), 0); SceneGraphReducer gr; gr.apply_attribs(node()); return 0; } //////////////////////////////////////////////////////////////////// // Function: NodePath::flatten_medium // Access: Published // Description: A more thorough flattening than flatten_light(), this // first applies all the transforms, colors, and texture // matrices from the nodes onto the vertices, and then // removes unneeded grouping nodes--nodes that have // exactly one child, for instance, but have no special // properties in themselves. // // This results in improved performance over // flatten_light() because the number of nodes in the // scene graph is reduced. // // The return value is the number of nodes removed. //////////////////////////////////////////////////////////////////// int NodePath:: flatten_medium() { nassertr_always(!is_empty(), 0); SceneGraphReducer gr; gr.apply_attribs(node()); int num_removed = gr.flatten(node(), 0); if (flatten_geoms) { gr.make_compatible_state(node()); gr.collect_vertex_data(node()); gr.unify(node(), true); } return num_removed; } //////////////////////////////////////////////////////////////////// // Function: NodePath::flatten_strong // Access: Published // Description: The strongest possible flattening. This first // applies all of the transforms to the vertices, as in // flatten_medium(), but then it will combine sibling // nodes together when possible, in addition to removing // unnecessary parent-child nodes. This can result in // substantially fewer nodes, but any nicely-grouped // hierachical bounding volumes may be lost. // // It is generally a good idea to apply this kind of // flattening only to nodes that will be culled largely // as a single unit, like a car. Applying this to an // entire scene may result in overall poorer performance // because of less-effective culling. //////////////////////////////////////////////////////////////////// int NodePath:: flatten_strong() { nassertr_always(!is_empty(), 0); SceneGraphReducer gr; gr.apply_attribs(node()); int num_removed = gr.flatten(node(), ~0); if (flatten_geoms) { gr.make_compatible_state(node()); gr.collect_vertex_data(node(), ~(SceneGraphReducer::CVD_format | SceneGraphReducer::CVD_name | SceneGraphReducer::CVD_animation_type)); gr.unify(node(), false); } return num_removed; } //////////////////////////////////////////////////////////////////// // Function: NodePath::apply_texture_colors // Access: Published // Description: Removes textures from Geoms at this node and below by // applying the texture colors to the vertices. This is // primarily useful to simplify a low-LOD model. The // texture colors are replaced by flat colors that // approximate the original textures. // // Only the bottommost texture on each Geom is used (if // there is more than one), and it is applied as if it // were M_modulate, and WM_repeat, regardless of its // actual settings. If the texture has a // simple_ram_image, this may be used if the main image // isn't resident. // // After this call, there will be no texturing specified // at this level and below. Of course, there might // still be texturing inherited from above. //////////////////////////////////////////////////////////////////// void NodePath:: apply_texture_colors() { nassertv_always(!is_empty()); SceneGraphReducer gr; gr.apply_attribs(node(), SceneGraphReducer::TT_apply_texture_color | SceneGraphReducer::TT_tex_matrix | SceneGraphReducer::TT_other); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_net_tag // Access: Published // Description: Returns the lowest ancestor of this node that // contains a tag definition with the indicated key, if // any, or an empty NodePath if no ancestor of this node // contains this tag definition. See set_tag(). //////////////////////////////////////////////////////////////////// NodePath NodePath:: find_net_tag(const string &key) const { if (is_empty()) { return NodePath::not_found(); } if (has_tag(key)) { return *this; } return get_parent().find_net_tag(key); } #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: NodePath::find_net_python_tag // Access: Published // Description: Returns the lowest ancestor of this node that // contains a tag definition with the indicated key, if // any, or an empty NodePath if no ancestor of this node // contains this tag definition. See set_python_tag(). //////////////////////////////////////////////////////////////////// NodePath NodePath:: find_net_python_tag(const string &key) const { if (is_empty()) { return NodePath::not_found(); } if (has_python_tag(key)) { return *this; } return get_parent().find_net_python_tag(key); } #endif // HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: NodePath::write_bam_file // Access: Published // Description: Writes the contents of this node and below out to a // bam file with the indicated filename. This file may // then be read in again, as is, at some later point. // Returns true if successful, false on some kind of // error. //////////////////////////////////////////////////////////////////// bool NodePath:: write_bam_file(const Filename &filename) const { nassertr_always(!is_empty(), false); BamFile bam_file; bool okflag = false; if (bam_file.open_write(filename)) { if (bam_file.write_object(node())) { okflag = true; } bam_file.close(); } return okflag; } //////////////////////////////////////////////////////////////////// // Function: NodePath::write_bam_stream // Access: Published // Description: Writes the contents of this node and below out to the // indicated stream. //////////////////////////////////////////////////////////////////// bool NodePath:: write_bam_stream(ostream &out) const { nassertr_always(!is_empty(), false); BamFile bam_file; bool okflag = false; if (bam_file.open_write(out)) { if (bam_file.write_object(node())) { okflag = true; } bam_file.close(); } return okflag; } //////////////////////////////////////////////////////////////////// // Function: NodePath::encode_to_bam_stream // Access: Published // Description: Converts the NodePath object into a single // stream of data using a BamWriter, and stores that // data in the indicated string. Returns true on // success, false on failure. // // If the BamWriter is NULL, this behaves the same way // as NodePath::write_bam_stream() and // PandaNode::encode_to_bam_stream(), in the sense that // it only writes this node and all nodes below it. // // However, if the BamWriter is not NULL, it behaves // very differently. In this case, it encodes the // *entire graph* of all nodes connected to the // NodePath, including all parent nodes and siblings. // This is necessary for correct streaming of related // NodePaths and restoration of instances, etc., but it // does mean you must detach() a node before writing it // if you want to limit the nodes that get written. // // This method is used by __reduce__ to handle streaming // of NodePaths to a pickle file. The BamWriter case is // used by the direct.stdpy.pickle module, while the // saner, non-BamWriter case is used when the standard // pickle module calls this function. //////////////////////////////////////////////////////////////////// bool NodePath:: encode_to_bam_stream(string &data, BamWriter *writer) const { data.clear(); ostringstream stream; DatagramOutputFile dout; if (!dout.open(stream)) { return false; } BamWriter local_writer; bool used_local_writer = false; if (writer == NULL) { // Create our own writer. if (!dout.write_header(_bam_header)) { return false; } writer = &local_writer; used_local_writer = true; } writer->set_target(&dout); int num_nodes = get_num_nodes(); if (used_local_writer && num_nodes > 1) { // In this case--no BamWriter--we only write the bottom node. num_nodes = 1; } // Write an initial Datagram to represent the error type and // number of nodes. Datagram dg; dg.add_uint8(_error_type); dg.add_int32(num_nodes); if (!dout.put_datagram(dg)) { writer->set_target(NULL); return false; } // Now write the nodes, one at a time. for (int i = 0; i < num_nodes; ++i) { PandaNode *node = get_node(num_nodes - i - 1); nassertr(node != NULL, false); if (!writer->write_object(node)) { writer->set_target(NULL); return false; } } writer->set_target(NULL); data = stream.str(); return true; } //////////////////////////////////////////////////////////////////// // Function: NodePath::decode_from_bam_stream // Access: Published, Static // Description: Reads the string created by a previous call to // encode_to_bam_stream(), and extracts and // returns the NodePath on that string. Returns NULL on // error. //////////////////////////////////////////////////////////////////// NodePath NodePath:: decode_from_bam_stream(const string &data, BamReader *reader) { NodePath result; istringstream stream(data); DatagramInputFile din; if (!din.open(stream)) { return NodePath::fail(); } BamReader local_reader; if (reader == NULL) { // Create a local reader. string head; if (!din.read_header(head, _bam_header.size())) { return NodePath::fail(); } if (head != _bam_header) { return NodePath::fail(); } reader = &local_reader; } reader->set_source(&din); // One initial datagram to encode the error type, and the number of nodes. Datagram dg; if (!din.get_datagram(dg)) { return NodePath::fail(); } DatagramIterator dgi(dg); ErrorType error_type = (ErrorType)dgi.get_uint8(); int num_nodes = dgi.get_int32(); if (num_nodes == 0) { // An empty NodePath. result._error_type = error_type; } else { // A real NodePath. Ignore error_type. for (int i = 0; i < num_nodes; ++i) { TypedWritable *object = reader->read_object(); if (object == (TypedWritable *)NULL || !object->is_of_type(PandaNode::get_class_type())) { reader->set_source(NULL); return NodePath::fail(); } if (!reader->resolve()) { reader->set_source(NULL); return NodePath::fail(); } PandaNode *node = DCAST(PandaNode, object); result = NodePath(result, node); } } reader->set_source(NULL); return result; } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_common_ancestor // Access: Private, Static // Description: Walks up from both NodePaths to find the first node // that both have in common, if any. Fills a_count and // b_count with the number of nodes below the common // node in each path. // // The return value is the NodePathComponent of the node // they have in common, or NULL if they have nothing in // common. //////////////////////////////////////////////////////////////////// NodePathComponent *NodePath:: find_common_ancestor(const NodePath &a, const NodePath &b, int &a_count, int &b_count, Thread *current_thread) { nassertr(!a.is_empty() && !b.is_empty(), NULL); NodePathComponent *ac = a._head; NodePathComponent *bc = b._head; a_count = 0; b_count = 0; int pipeline_stage = current_thread->get_pipeline_stage(); // Shorten up the longer one until they are the same length. while (ac->get_length(pipeline_stage, current_thread) > bc->get_length(pipeline_stage, current_thread)) { nassertr(ac != (NodePathComponent *)NULL, NULL); ac = ac->get_next(pipeline_stage, current_thread); a_count++; } while (bc->get_length(pipeline_stage, current_thread) > ac->get_length(pipeline_stage, current_thread)) { nassertr(bc != (NodePathComponent *)NULL, NULL); bc = bc->get_next(pipeline_stage, current_thread); b_count++; } // Now shorten them both up until we reach the same component. while (ac != bc) { // These shouldn't go to NULL unless they both go there together. nassertr(ac != (NodePathComponent *)NULL, NULL); nassertr(bc != (NodePathComponent *)NULL, NULL); ac = ac->get_next(pipeline_stage, current_thread); a_count++; bc = bc->get_next(pipeline_stage, current_thread); b_count++; } return ac; } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_get_net_state // Access: Private // Description: Recursively determines the net state changes to the // indicated component node from the root of the graph. //////////////////////////////////////////////////////////////////// CPT(RenderState) NodePath:: r_get_net_state(NodePathComponent *comp, Thread *current_thread) const { if (comp == (NodePathComponent *)NULL) { return RenderState::make_empty(); } else { CPT(RenderState) state = comp->get_node()->get_state(current_thread); int pipeline_stage = current_thread->get_pipeline_stage(); return r_get_net_state(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(state); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_get_partial_state // Access: Private // Description: Recursively determines the net state changes to the // indicated component node from the nth node above it. // If n exceeds the length of the path, this returns the // net transform from the root of the graph. //////////////////////////////////////////////////////////////////// CPT(RenderState) NodePath:: r_get_partial_state(NodePathComponent *comp, int n, Thread *current_thread) const { if (n == 0 || comp == (NodePathComponent *)NULL) { return RenderState::make_empty(); } else { CPT(RenderState) state = comp->get_node()->get_state(current_thread); int pipeline_stage = current_thread->get_pipeline_stage(); return r_get_partial_state(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(state); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_get_net_transform // Access: Private // Description: Recursively determines the net transform to the // indicated component node from the root of the graph. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: r_get_net_transform(NodePathComponent *comp, Thread *current_thread) const { if (comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { int pipeline_stage = current_thread->get_pipeline_stage(); CPT(TransformState) net_transform = r_get_net_transform(comp->get_next(pipeline_stage, current_thread), current_thread); PandaNode *node = comp->get_node(); CPT(TransformState) transform = node->get_transform(current_thread); CPT(RenderEffects) effects = node->get_effects(current_thread); if (effects->has_adjust_transform()) { effects->adjust_transform(net_transform, transform, node); } return net_transform->compose(transform); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_get_partial_transform // Access: Private // Description: Recursively determines the net transform to the // indicated component node from the nth node above it. // If n exceeds the length of the path, this returns the // net transform from the root of the graph. // // If any node in the path had a net_transform effect // applied, returns NULL--in this case the partial // transform cannot be easily determined. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: r_get_partial_transform(NodePathComponent *comp, int n, Thread *current_thread) const { if (n == 0 || comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { if (comp->get_node()->get_effects(current_thread)->has_adjust_transform()) { return NULL; } CPT(TransformState) transform = comp->get_node()->get_transform(current_thread); int pipeline_stage = current_thread->get_pipeline_stage(); CPT(TransformState) partial = r_get_partial_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread); if (partial == (const TransformState *)NULL) { return NULL; } return partial->compose(transform); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_get_net_prev_transform // Access: Private // Description: Recursively determines the net "previous" transform // to the indicated component node from the root of the // graph. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: r_get_net_prev_transform(NodePathComponent *comp, Thread *current_thread) const { if (comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread); int pipeline_stage = current_thread->get_pipeline_stage(); return r_get_net_prev_transform(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(transform); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_get_partial_prev_transform // Access: Private // Description: Recursively determines the net "previous" transform // to the indicated component node from the nth node // above it. If n exceeds the length of the path, this // returns the net previous transform from the root of // the graph. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: r_get_partial_prev_transform(NodePathComponent *comp, int n, Thread *current_thread) const { if (n == 0 || comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread); int pipeline_stage = current_thread->get_pipeline_stage(); return r_get_partial_prev_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(transform); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_matches // Access: Private // Description: Finds up to max_matches matches against the given // path string from this node and deeper. The // max_matches count indicates the maximum number of // matches to return, or -1 not to limit the number // returned. //////////////////////////////////////////////////////////////////// void NodePath:: find_matches(NodePathCollection &result, const string &path, int max_matches) const { if (is_empty()) { pgraph_cat.warning() << "Attempt to extend an empty NodePath by '" << path << "'.\n"; return; } FindApproxPath approx_path; if (approx_path.add_string(path)) { find_matches(result, approx_path, max_matches); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_matches // Access: Private // Description: Finds up to max_matches matches against the given // approx_path from this node and deeper. The // max_matches count indicates the maximum number of // matches to return, or -1 not to limit the number // returned. //////////////////////////////////////////////////////////////////// void NodePath:: find_matches(NodePathCollection &result, FindApproxPath &approx_path, int max_matches) const { if (is_empty()) { pgraph_cat.warning() << "Attempt to extend an empty NodePath by: " << approx_path << ".\n"; return; } // We start with just one entry on the level. FindApproxLevelEntry *level = new FindApproxLevelEntry(WorkingNodePath(*this), approx_path); nassertv(level->_node_path.is_valid()); find_matches(result, level, max_matches); } //////////////////////////////////////////////////////////////////// // Function: NodePath::find_matches // Access: Private // Description: The fundamental implementation of find_matches(), // given a starting level (a linked list of // FindApproxLevelEntry objects). //////////////////////////////////////////////////////////////////// void NodePath:: find_matches(NodePathCollection &result, FindApproxLevelEntry *level, int max_matches) const { int num_levels_remaining = _max_search_depth; FindApproxLevelEntry *deleted_entries = NULL; while (num_levels_remaining > 0 && level != NULL) { if (pgraph_cat.is_spam()) { pgraph_cat.spam() << "find_matches pass: " << result << ", " << max_matches << ", " << num_levels_remaining << "\n"; level->write_level(pgraph_cat.spam(false), 4); } num_levels_remaining--; FindApproxLevelEntry *next_level = NULL; // For each node in the current level, build up the set of possible // matches in the next level. FindApproxLevelEntry *entry = level; while (entry != (FindApproxLevelEntry *)NULL) { if (entry->consider_node(result, next_level, max_matches, 0)) { // If we found the requisite number of matches, we can stop. // Delete all remaining entries and return immediately. while (entry != (FindApproxLevelEntry *)NULL) { FindApproxLevelEntry *next = entry->_next; delete entry; entry = next; } while (next_level != (FindApproxLevelEntry *)NULL) { FindApproxLevelEntry *next = next_level->_next; delete next_level; next_level = next; } while (deleted_entries != (FindApproxLevelEntry *)NULL) { FindApproxLevelEntry *next = deleted_entries->_next; delete deleted_entries; deleted_entries = next; } return; } // Move the entry to the delete chain so we can delete it before // we return from this method. (We can't delete it immediately, // because there might be WorkingNodePaths in the next_level // that reference the WorkingNodePath object within the entry.) FindApproxLevelEntry *next = entry->_next; entry->_next = deleted_entries; deleted_entries = entry; entry = next; } // Make sure the remaining entries from this level are added to // the delete chain. while (entry != (FindApproxLevelEntry *)NULL) { FindApproxLevelEntry *next = entry->_next; entry->_next = deleted_entries; deleted_entries = entry; entry = next; } level = next_level; } // Now it's safe to delete all entries on the delete chain. while (deleted_entries != (FindApproxLevelEntry *)NULL) { FindApproxLevelEntry *next = deleted_entries->_next; delete deleted_entries; deleted_entries = next; } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_clear_model_nodes // Access: Private // Description: The recursive implementation of // clear_model_nodes(). This walks through the // subgraph defined by the indicated node and below. //////////////////////////////////////////////////////////////////// int NodePath:: r_clear_model_nodes(PandaNode *node) { int count = 0; if (node->is_of_type(ModelNode::get_class_type())) { ModelNode *mnode; DCAST_INTO_R(mnode, node, count); mnode->set_preserve_transform(ModelNode::PT_drop_node); ++count; } PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { count += r_clear_model_nodes(cr.get_child(i)); } return count; } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_adjust_all_priorities // Access: Private // Description: The recursive implementation of // adjust_all_priorities(). This walks through the // subgraph defined by the indicated node and below. //////////////////////////////////////////////////////////////////// void NodePath:: r_adjust_all_priorities(PandaNode *node, int adjustment) { node->set_state(node->get_state()->adjust_all_priorities(adjustment)); if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { gnode->set_geom_state(i, gnode->get_geom_state(i)->adjust_all_priorities(adjustment)); } } PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { r_adjust_all_priorities(cr.get_child(i), adjustment); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_force_recompute_bounds // Access: Private // Description: //////////////////////////////////////////////////////////////////// void NodePath:: r_force_recompute_bounds(PandaNode *node) { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { const Geom *geom = gnode->get_geom(i); geom->mark_bounds_stale(); } } node->mark_bounds_stale(); // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { r_force_recompute_bounds(cr.get_child(i)); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_set_collide_mask // Access: Private // Description: Recursively applies the indicated collide mask to the // nodes at and below this node. //////////////////////////////////////////////////////////////////// void NodePath:: r_set_collide_mask(PandaNode *node, CollideMask and_mask, CollideMask or_mask, TypeHandle node_type) { if (node->is_of_type(node_type)) { CollideMask into_collide_mask = node->get_into_collide_mask(); into_collide_mask = (into_collide_mask & and_mask) | or_mask; node->set_into_collide_mask(into_collide_mask); } PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { r_set_collide_mask(cr.get_child(i), and_mask, or_mask, node_type); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_has_vertex_column // Access: Private // Description: //////////////////////////////////////////////////////////////////// bool NodePath:: r_has_vertex_column(PandaNode *node, const InternalName *name) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_R(gnode, node, false); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { const Geom *geom = gnode->get_geom(i); CPT(GeomVertexData) vdata = geom->get_vertex_data(); if (vdata->has_column(name)) { return true; } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); if (r_has_vertex_column(child, name)) { return true; } } return false; } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_all_vertex_columns // Access: Private // Description: //////////////////////////////////////////////////////////////////// void NodePath:: r_find_all_vertex_columns(PandaNode *node, NodePath::InternalNames &vertex_columns) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; ++i) { const Geom *geom = gnode->get_geom(i); const GeomVertexFormat *format = geom->get_vertex_data()->get_format(); int num_arrays = format->get_num_arrays(); for (int j = 0; j < num_arrays; ++j) { const GeomVertexArrayFormat *array = format->get_array(j); int num_columns = array->get_num_columns(); for (int k = 0; k < num_columns; ++k) { const GeomVertexColumn *column = array->get_column(k); vertex_columns.insert(column->get_name()); } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); r_find_all_vertex_columns(child, vertex_columns); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_texture // Access: Private // Description: //////////////////////////////////////////////////////////////////// Texture *NodePath:: r_find_texture(PandaNode *node, const RenderState *state, const GlobPattern &glob) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_R(gnode, node, NULL); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = state->compose(gnode->get_geom_state(i)); // Look for a TextureAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); for (int i = 0; i < ta->get_num_on_stages(); i++) { Texture *texture = ta->get_on_texture(ta->get_on_stage(i)); if (texture != (Texture *)NULL) { if (glob.matches(texture->get_name())) { return texture; } } } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); CPT(RenderState) next_state = state->compose(child->get_state()); Texture *result = r_find_texture(child, next_state, glob); if (result != (Texture *)NULL) { return result; } } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_all_textures // Access: Private // Description: //////////////////////////////////////////////////////////////////// void NodePath:: r_find_all_textures(PandaNode *node, const RenderState *state, NodePath::Textures &textures) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = state->compose(gnode->get_geom_state(i)); // Look for a TextureAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); for (int i = 0; i < ta->get_num_on_stages(); i++) { Texture *texture = ta->get_on_texture(ta->get_on_stage(i)); if (texture != (Texture *)NULL) { textures.insert(texture); } } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); CPT(RenderState) next_state = state->compose(child->get_state()); r_find_all_textures(child, next_state, textures); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_texture // Access: Private // Description: //////////////////////////////////////////////////////////////////// Texture * NodePath:: r_find_texture(PandaNode *node, TextureStage *stage) const { // Look for a TextureAttrib on the node. const RenderAttrib *attrib = node->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); if (ta->has_on_stage(stage)) { return ta->get_on_texture(stage); } } if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_R(gnode, node, NULL); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = gnode->get_geom_state(i); // Look for a TextureAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); if (ta->has_on_stage(stage)) { return ta->get_on_texture(stage); } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); Texture *result = r_find_texture(child, stage); if (result != (Texture *)NULL) { return result; } } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_all_textures // Access: Private // Description: //////////////////////////////////////////////////////////////////// void NodePath:: r_find_all_textures(PandaNode *node, TextureStage *stage, NodePath::Textures &textures) const { // Look for a TextureAttrib on the node. const RenderAttrib *attrib = node->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); if (ta->has_on_stage(stage)) { textures.insert(ta->get_on_texture(stage)); } } if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = gnode->get_geom_state(i); // Look for a TextureAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); if (ta->has_on_stage(stage)) { textures.insert(ta->get_on_texture(stage)); } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); r_find_all_textures(child, stage, textures); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_texture_stage // Access: Private // Description: //////////////////////////////////////////////////////////////////// TextureStage * NodePath:: r_find_texture_stage(PandaNode *node, const RenderState *state, const GlobPattern &glob) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_R(gnode, node, NULL); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = state->compose(gnode->get_geom_state(i)); // Look for a TextureAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); for (int i = 0; i < ta->get_num_on_stages(); i++) { TextureStage *texture_stage = ta->get_on_stage(i); if (texture_stage != (TextureStage *)NULL) { if (glob.matches(texture_stage->get_name())) { return texture_stage; } } } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); CPT(RenderState) next_state = state->compose(child->get_state()); TextureStage *result = r_find_texture_stage(child, next_state, glob); if (result != (TextureStage *)NULL) { return result; } } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_all_texture_stages // Access: Private // Description: //////////////////////////////////////////////////////////////////// void NodePath:: r_find_all_texture_stages(PandaNode *node, const RenderState *state, NodePath::TextureStages &texture_stages) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = state->compose(gnode->get_geom_state(i)); // Look for a TextureAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); for (int i = 0; i < ta->get_num_on_stages(); i++) { TextureStage *texture_stage = ta->get_on_stage(i); if (texture_stage != (TextureStage *)NULL) { texture_stages.insert(texture_stage); } } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); CPT(RenderState) next_state = state->compose(child->get_state()); r_find_all_texture_stages(child, next_state, texture_stages); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_unify_texture_stages // Access: Private // Description: //////////////////////////////////////////////////////////////////// void NodePath:: r_unify_texture_stages(PandaNode *node, TextureStage *stage) { // Look for a TextureAttrib on the state. const RenderAttrib *attrib = node->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); CPT(RenderAttrib) new_attrib = ta->unify_texture_stages(stage); if (new_attrib != ta) { node->set_attrib(new_attrib); } } if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) state = gnode->get_geom_state(i); // Look for a TextureAttrib on the state. const RenderAttrib *attrib = state->get_attrib(TextureAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const TextureAttrib *ta = DCAST(TextureAttrib, attrib); CPT(RenderAttrib) new_attrib = ta->unify_texture_stages(stage); if (new_attrib != ta) { CPT(RenderState) new_state = state->add_attrib(new_attrib); gnode->set_geom_state(i, new_state); } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); r_unify_texture_stages(child, stage); } } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_material // Access: Private // Description: //////////////////////////////////////////////////////////////////// Material *NodePath:: r_find_material(PandaNode *node, const RenderState *state, const GlobPattern &glob) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_R(gnode, node, NULL); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = state->compose(gnode->get_geom_state(i)); // Look for a MaterialAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(MaterialAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const MaterialAttrib *ta = DCAST(MaterialAttrib, attrib); if (!ta->is_off()) { Material *material = ta->get_material(); if (material != (Material *)NULL) { if (glob.matches(material->get_name())) { return material; } } } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); CPT(RenderState) next_state = state->compose(child->get_state()); Material *result = r_find_material(child, next_state, glob); if (result != (Material *)NULL) { return result; } } return NULL; } //////////////////////////////////////////////////////////////////// // Function: NodePath::r_find_all_materials // Access: Private // Description: //////////////////////////////////////////////////////////////////// void NodePath:: r_find_all_materials(PandaNode *node, const RenderState *state, NodePath::Materials &materials) const { if (node->is_geom_node()) { GeomNode *gnode; DCAST_INTO_V(gnode, node); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; i++) { CPT(RenderState) geom_state = state->compose(gnode->get_geom_state(i)); // Look for a MaterialAttrib on the state. const RenderAttrib *attrib = geom_state->get_attrib(MaterialAttrib::get_class_slot()); if (attrib != (const RenderAttrib *)NULL) { const MaterialAttrib *ta = DCAST(MaterialAttrib, attrib); if (!ta->is_off()) { Material *material = ta->get_material(); if (material != (Material *)NULL) { materials.insert(material); } } } } } // Now consider children. PandaNode::Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { PandaNode *child = cr.get_child(i); CPT(RenderState) next_state = state->compose(child->get_state()); r_find_all_materials(child, next_state, materials); } } #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: py_decode_NodePath_from_bam_stream // Access: Published // Description: This wrapper is defined as a global function to suit // pickle's needs. //////////////////////////////////////////////////////////////////// NodePath py_decode_NodePath_from_bam_stream(const string &data) { return py_decode_NodePath_from_bam_stream_persist(NULL, data); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: py_decode_NodePath_from_bam_stream_persist // Access: Published // Description: This wrapper is defined as a global function to suit // pickle's needs. //////////////////////////////////////////////////////////////////// NodePath py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data) { BamReader *reader = NULL; if (unpickler != NULL) { PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader"); if (py_reader == NULL) { // It's OK if there's no bamReader. PyErr_Clear(); } else { DTOOL_Call_ExtractThisPointerForType(py_reader, &Dtool_BamReader, (void **)&reader); Py_DECREF(py_reader); } } return NodePath::decode_from_bam_stream(data, reader); } #endif // HAVE_PYTHON