Panda3D
nodePath.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file nodePath.cxx
10  * @author drose
11  * @date 2002-02-25
12 
13  */
14 
15 #include "nodePath.h"
16 #include "nodePathCollection.h"
17 #include "findApproxPath.h"
18 #include "findApproxLevelEntry.h"
19 #include "internalNameCollection.h"
20 #include "config_pgraph.h"
21 #include "colorAttrib.h"
22 #include "colorScaleAttrib.h"
23 #include "cullBinAttrib.h"
24 #include "textureAttrib.h"
25 #include "texMatrixAttrib.h"
26 #include "texGenAttrib.h"
27 #include "materialAttrib.h"
28 #include "materialCollection.h"
29 #include "lightAttrib.h"
30 #include "clipPlaneAttrib.h"
31 #include "occluderEffect.h"
32 #include "polylightEffect.h"
33 #include "fogAttrib.h"
34 #include "renderModeAttrib.h"
35 #include "cullFaceAttrib.h"
36 #include "alphaTestAttrib.h"
37 #include "depthTestAttrib.h"
38 #include "depthWriteAttrib.h"
39 #include "depthOffsetAttrib.h"
40 #include "shaderAttrib.h"
41 #include "billboardEffect.h"
42 #include "compassEffect.h"
43 #include "showBoundsEffect.h"
44 #include "transparencyAttrib.h"
45 #include "antialiasAttrib.h"
46 #include "audioVolumeAttrib.h"
47 #include "texProjectorEffect.h"
48 #include "scissorEffect.h"
49 #include "texturePool.h"
50 #include "planeNode.h"
51 #include "occluderNode.h"
52 #include "lensNode.h"
53 #include "materialPool.h"
54 #include "look_at.h"
55 #include "plist.h"
56 #include "boundingSphere.h"
57 #include "geomNode.h"
58 #include "sceneGraphReducer.h"
59 #include "textureCollection.h"
60 #include "textureStageCollection.h"
61 #include "globPattern.h"
62 #include "shader.h"
63 #include "shaderInput.h"
64 #include "config_gobj.h"
65 #include "bamFile.h"
67 #include "dcast.h"
68 #include "pStatCollector.h"
69 #include "pStatTimer.h"
70 #include "modelNode.h"
71 #include "bam.h"
72 #include "bamWriter.h"
73 #include "datagramBuffer.h"
74 #include "weakNodePath.h"
75 
76 using std::max;
77 using std::move;
78 using std::ostream;
79 using std::ostringstream;
80 using std::string;
81 
82 // stack seems to overflow on Intel C++ at 7000. If we need more than 7000,
83 // need to increase stack size.
84 int NodePath::_max_search_depth = 7000;
85 TypeHandle NodePath::_type_handle;
86 
87 PStatCollector NodePath::_get_transform_pcollector("*:NodePath:get_transform");
88 PStatCollector NodePath::_verify_complete_pcollector("*:NodePath:verify_complete");
89 
90 /**
91  * Constructs a NodePath with the indicated parent NodePath and child node;
92  * the child node must be a stashed or unstashed child of the parent.
93  */
95 NodePath(const NodePath &parent, PandaNode *child_node,
96  Thread *current_thread) :
97  _error_type(ET_fail)
98 {
99  nassertv(child_node != nullptr);
100  int pipeline_stage = current_thread->get_pipeline_stage();
101 
102  if (parent.is_empty()) {
103  // Special case: constructing a NodePath at the root.
104  _head = PandaNode::get_top_component(child_node, true,
105  pipeline_stage, current_thread);
106 
107  } else {
108  _head = PandaNode::get_component(parent._head, child_node, pipeline_stage,
109  current_thread);
110  }
111  nassertv(_head != nullptr);
112 
113  if (_head != nullptr) {
114  _error_type = ET_ok;
115  }
116  _backup_key = 0;
117 }
118 
119 /**
120  * Returns true if the NodePath is valid (not empty), or false if it contains
121  * no nodes.
122  */
123 NodePath::
124 operator bool () const {
125  return !is_empty();
126 }
127 
128 /**
129  * Returns the number of nodes in the path.
130  */
131 int NodePath::
132 get_num_nodes(Thread *current_thread) const {
133  if (is_empty()) {
134  return 0;
135  }
136  int pipeline_stage = current_thread->get_pipeline_stage();
137  return _head->get_length(pipeline_stage, current_thread);
138 }
139 
140 /**
141  * Returns the nth node of the path, where 0 is the referenced (bottom) node
142  * and get_num_nodes() - 1 is the top node. This requires iterating through
143  * the path.
144  *
145  * Also see node(), which is a convenience function to return the same thing
146  * as get_node(0) (since the bottom node is the most important node in the
147  * NodePath, and is the one most frequently referenced).
148  *
149  * Note that this function returns the same thing as
150  * get_ancestor(index).node().
151  */
153 get_node(int index, Thread *current_thread) const {
154  nassertr(index >= 0 && index < get_num_nodes(), nullptr);
155 
156  int pipeline_stage = current_thread->get_pipeline_stage();
157 
158  NodePathComponent *comp = _head;
159  while (index > 0) {
160  // If this assertion fails, the index was out of range; the component's
161  // length must have been invalid.
162  nassertr(comp != nullptr, nullptr);
163  comp = comp->get_next(pipeline_stage, current_thread);
164  index--;
165  }
166 
167  // If this assertion fails, the index was out of range; the component's
168  // length must have been invalid.
169  nassertr(comp != nullptr, nullptr);
170  return comp->get_node();
171 }
172 
173 /**
174  * Returns the nth ancestor of the path, where 0 is the NodePath itself and
175  * get_num_nodes() - 1 is get_top(). This requires iterating through the path.
176  *
177  * Also see get_node(), which returns the same thing as a PandaNode pointer,
178  * not a NodePath.
179  */
181 get_ancestor(int index, Thread *current_thread) const {
182  nassertr(index >= 0 && index < get_num_nodes(), NodePath::fail());
183 
184  int pipeline_stage = current_thread->get_pipeline_stage();
185 
186  NodePathComponent *comp = _head;
187  while (index > 0) {
188  // If this assertion fails, the index was out of range; the component's
189  // length must have been invalid.
190  nassertr(comp != nullptr, NodePath::fail());
191  comp = comp->get_next(pipeline_stage, current_thread);
192  index--;
193  }
194 
195  // If this assertion fails, the index was out of range; the component's
196  // length must have been invalid.
197  nassertr(comp != nullptr, NodePath::fail());
198 
199  NodePath result;
200  result._head = comp;
201  return result;
202 }
203 
204 /**
205  * Returns a singleton NodePath that represents the top of the path, or empty
206  * NodePath if this path is empty.
207  */
209 get_top(Thread *current_thread) const {
210  if (is_empty()) {
211  return *this;
212  }
213 
214  int pipeline_stage = current_thread->get_pipeline_stage();
215 
216  NodePathComponent *comp = _head;
217  while (!comp->is_top_node(pipeline_stage, current_thread)) {
218  comp = comp->get_next(pipeline_stage, current_thread);
219  nassertr(comp != nullptr, NodePath::fail());
220  }
221 
222  NodePath top;
223  top._head = comp;
224  return top;
225 }
226 
227 
228 /**
229  * Returns the set of all child nodes of the referenced node.
230  */
232 get_children(Thread *current_thread) const {
233  NodePathCollection result;
234  nassertr_always(!is_empty(), result);
235 
236  PandaNode *bottom_node = node();
237 
238  int pipeline_stage = current_thread->get_pipeline_stage();
239 
240  PandaNode::Children cr = bottom_node->get_children();
241  int num_children = cr.get_num_children();
242  for (int i = 0; i < num_children; i++) {
243  NodePath child;
244  child._head = PandaNode::get_component(_head, cr.get_child(i),
245  pipeline_stage, current_thread);
246  result.add_path(child);
247  }
248 
249  return result;
250 }
251 
252 /**
253  * Returns the set of all child nodes of the referenced node that have been
254  * stashed. These children are not normally visible on the node, and do not
255  * appear in the list returned by get_children().
256  */
258 get_stashed_children(Thread *current_thread) const {
259  NodePathCollection result;
260  nassertr_always(!is_empty(), result);
261 
262  PandaNode *bottom_node = node();
263 
264  int pipeline_stage = current_thread->get_pipeline_stage();
265 
266  int num_stashed = bottom_node->get_num_stashed();
267  for (int i = 0; i < num_stashed; i++) {
268  NodePath stashed;
269  stashed._head = PandaNode::get_component(_head, bottom_node->get_stashed(i),
270  pipeline_stage, current_thread);
271  result.add_path(stashed);
272  }
273 
274  return result;
275 }
276 
277 /**
278  * Returns the sort value of the referenced node within its parent; that is,
279  * the sort number passed on the last reparenting operation for this node.
280  * This will control the position of the node within its parent's list of
281  * children.
282  */
283 int NodePath::
284 get_sort(Thread *current_thread) const {
285  if (!has_parent()) {
286  return 0;
287  }
288 
289  int pipeline_stage = current_thread->get_pipeline_stage();
290 
291  PandaNode *parent = _head->get_next(pipeline_stage, current_thread)->get_node();
292  PandaNode *child = node();
293  nassertr(parent != nullptr && child != nullptr, 0);
294  int child_index = parent->find_child(child);
295  if (child_index != -1) {
296  return parent->get_child_sort(child_index);
297  }
298 
299  child_index = parent->find_stashed(child);
300  if (child_index != -1) {
301  return parent->get_stashed_sort(child_index);
302  }
303 
304  nassertr(false, 0);
305  return 0;
306 }
307 
308 /**
309  * Searches for a node below the referenced node that matches the indicated
310  * string. Returns the shortest match found, if any, or an empty NodePath if
311  * no match can be found.
312  */
314 find(const string &path) const {
315  nassertr_always(!is_empty(), fail());
316 
317  NodePathCollection col;
318  find_matches(col, path, 1);
319 
320  if (col.is_empty()) {
321  return NodePath::not_found();
322  }
323 
324  return col.get_path(0);
325 }
326 
327 /**
328  * Searches for the indicated node below this node and returns the shortest
329  * NodePath that connects them.
330  */
332 find_path_to(PandaNode *node) const {
333  nassertr_always(!is_empty(), fail());
334  nassertr(node != nullptr, fail());
335 
336  NodePathCollection col;
337  FindApproxPath approx_path;
338  approx_path.add_match_many(0);
339  approx_path.add_match_pointer(node, 0);
340  find_matches(col, approx_path, 1);
341 
342  if (col.is_empty()) {
343  return NodePath::not_found();
344  }
345 
346  return col.get_path(0);
347 }
348 
349 /**
350  * Returns the complete set of all NodePaths that begin with this NodePath and
351  * can be extended by path. The shortest paths will be listed first.
352  */
354 find_all_matches(const string &path) const {
355  NodePathCollection col;
356  nassertr_always(!is_empty(), col);
357  nassertr(verify_complete(), col);
358  find_matches(col, path, -1);
359  return col;
360 }
361 
362 /**
363  * Returns the set of all NodePaths that extend from this NodePath down to the
364  * indicated node. The shortest paths will be listed first.
365  */
368  NodePathCollection col;
369  nassertr_always(!is_empty(), col);
370  nassertr(verify_complete(), col);
371  nassertr(node != nullptr, col);
372  FindApproxPath approx_path;
373  approx_path.add_match_many(0);
374  approx_path.add_match_pointer(node, 0);
375  find_matches(col, approx_path, -1);
376  return col;
377 }
378 
379 /**
380  * Removes the referenced node of the NodePath from its current parent and
381  * attaches it to the referenced node of the indicated NodePath.
382  *
383  * If the destination NodePath is empty, this is the same thing as
384  * detach_node().
385  *
386  * If the referenced node is already a child of the indicated NodePath (via
387  * some other instance), this operation fails and leaves the NodePath
388  * detached.
389  */
390 void NodePath::
391 reparent_to(const NodePath &other, int sort, Thread *current_thread) {
392  nassertv(verify_complete());
393  nassertv(other.verify_complete());
394  nassertv_always(!is_empty());
395  nassertv(other._error_type == ET_ok);
396 
397  // Reparenting implicitly resets the delta vector.
399 
400  int pipeline_stage = current_thread->get_pipeline_stage();
401  bool reparented = PandaNode::reparent(other._head, _head, sort, false,
402  pipeline_stage, current_thread);
403  nassertv(reparented);
404 }
405 
406 /**
407  * Similar to reparent_to(), but the node is added to its new parent's stashed
408  * list, so that the result is equivalent to calling reparent_to() immediately
409  * followed by stash().
410  */
411 void NodePath::
412 stash_to(const NodePath &other, int sort, Thread *current_thread) {
413  nassertv(verify_complete());
414  nassertv(other.verify_complete());
415  nassertv_always(!is_empty());
416  nassertv(other._error_type == ET_ok);
417 
418  // Reparenting implicitly resets the delta vector.
420 
421  int pipeline_stage = current_thread->get_pipeline_stage();
422  bool reparented = PandaNode::reparent(other._head, _head, sort, true,
423  pipeline_stage, current_thread);
424  nassertv(reparented);
425 }
426 
427 /**
428  * This functions identically to reparent_to(), except the transform on this
429  * node is also adjusted so that the node remains in the same place in world
430  * coordinates, even if it is reparented into a different coordinate system.
431  */
432 void NodePath::
433 wrt_reparent_to(const NodePath &other, int sort, Thread *current_thread) {
434  nassertv(verify_complete(current_thread));
435  nassertv(other.verify_complete(current_thread));
436  nassertv_always(!is_empty());
437  nassertv(other._error_type == ET_ok);
438 
439  if (get_transform(current_thread) == get_prev_transform(current_thread)) {
440  set_transform(get_transform(other, current_thread), current_thread);
441  node()->reset_prev_transform(current_thread);
442  } else {
443  set_transform(get_transform(other, current_thread), current_thread);
444  set_prev_transform(get_prev_transform(other, current_thread), current_thread);
445  }
446 
447  reparent_to(other, sort, current_thread);
448 }
449 
450 /**
451  * Adds the referenced node of the NodePath as a child of the referenced node
452  * of the indicated other NodePath. Any other parent-child relations of the
453  * node are unchanged; in particular, the node is not removed from its
454  * existing parent, if any.
455  *
456  * If the node already had an existing parent, this method will create a new
457  * instance of the node within the scene graph.
458  *
459  * This does not change the NodePath itself, but does return a new NodePath
460  * that reflects the new instance node.
461  *
462  * If the destination NodePath is empty, this creates a new instance which is
463  * not yet parented to any node. A new instance of this sort cannot easily be
464  * differentiated from other similar instances, but it is nevertheless a
465  * different instance and it will return a different get_id() value.
466  *
467  * If the referenced node is already a child of the indicated NodePath,
468  * returns that already-existing instance, unstashing it first if necessary.
469  */
471 instance_to(const NodePath &other, int sort, Thread *current_thread) const {
472  nassertr(verify_complete(), NodePath::fail());
473  nassertr(other.verify_complete(), NodePath::fail());
474  nassertr_always(!is_empty(), NodePath::fail());
475  nassertr(other._error_type == ET_ok, NodePath::fail());
476 
477  NodePath new_instance;
478 
479  // First, we'll attach to NULL, to guarantee we get a brand new instance.
480  int pipeline_stage = current_thread->get_pipeline_stage();
481  new_instance._head = PandaNode::attach(nullptr, node(), sort, pipeline_stage,
482  current_thread);
483 
484  // Now, we'll reparent the new instance to the target node.
485  bool reparented = PandaNode::reparent(other._head, new_instance._head,
486  sort, false, pipeline_stage,
487  current_thread);
488  if (!reparented) {
489  // Hmm, couldn't reparent. Either making this instance would create a
490  // cycle, or it was already a child of that node. If it was already a
491  // child, return that existing NodePath instead.
492  NodePath orig(other, node(), current_thread);
493  if (!orig.is_empty()) {
494  if (orig.is_stashed()) {
495  orig.unstash();
496  }
497  return orig;
498  }
499 
500  // Nope, it must be a cycle.
501  nassertr(reparented, new_instance);
502  }
503 
504  // instance_to() doesn't reset the velocity delta, unlike most of the other
505  // reparenting operations. The reasoning is that instance_to() is not
506  // necessarily a reparenting operation, since it doesn't change the original
507  // instance.
508 
509  return new_instance;
510 }
511 
512 /**
513  * Behaves like instance_to(), but implicitly creates a new node to instance
514  * the geometry under, and returns a NodePath to that new node. This allows
515  * the programmer to set a unique state and/or transform on this instance.
516  */
518 instance_under_node(const NodePath &other, const string &name, int sort,
519  Thread *current_thread) const {
520  NodePath new_node = other.attach_new_node(name, sort, current_thread);
521  NodePath instance = instance_to(new_node, 0, current_thread);
522  if (instance.is_empty()) {
523  new_node.remove_node(current_thread);
524  return instance;
525  }
526  return new_node;
527 }
528 
529 /**
530  * Functions like instance_to(), except a deep copy is made of the referenced
531  * node and all of its descendents, which is then parented to the indicated
532  * node. A NodePath to the newly created copy is returned.
533  */
535 copy_to(const NodePath &other, int sort, Thread *current_thread) const {
536  nassertr(verify_complete(current_thread), fail());
537  nassertr(other.verify_complete(current_thread), fail());
538  nassertr_always(!is_empty(), fail());
539  nassertr(other._error_type == ET_ok, fail());
540 
541  PandaNode *source_node = node();
542  PT(PandaNode) copy_node = source_node->copy_subgraph(current_thread);
543  nassertr(copy_node != nullptr, fail());
544 
545  copy_node->reset_prev_transform(current_thread);
546 
547  return other.attach_new_node(copy_node, sort, current_thread);
548 }
549 
550 /**
551  * Attaches a new node, with or without existing parents, to the scene graph
552  * below the referenced node of this NodePath. This is the preferred way to
553  * add nodes to the graph.
554  *
555  * If the node was already a child of the parent, this returns a NodePath to
556  * the existing child.
557  *
558  * This does *not* automatically extend the current NodePath to reflect the
559  * attachment; however, a NodePath that does reflect this extension is
560  * returned.
561  */
563 attach_new_node(PandaNode *node, int sort, Thread *current_thread) const {
564  nassertr(verify_complete(current_thread), NodePath::fail());
565  nassertr(_error_type == ET_ok, NodePath::fail());
566  nassertr(node != nullptr, NodePath::fail());
567 
568  NodePath new_path(*this);
569  int pipeline_stage = current_thread->get_pipeline_stage();
570  new_path._head = PandaNode::attach(_head, node, sort, pipeline_stage,
571  current_thread);
572  return new_path;
573 }
574 
575 /**
576  * Disconnects the referenced node from the scene graph. This will also
577  * delete the node if there are no other pointers to it.
578  *
579  * Normally, this should be called only when you are really done with the
580  * node. If you want to remove a node from the scene graph but keep it around
581  * for later, you should probably use detach_node() instead.
582  *
583  * In practice, the only difference between remove_node() and detach_node() is
584  * that remove_node() also resets the NodePath to empty, which will cause the
585  * node to be deleted immediately if there are no other references. On the
586  * other hand, detach_node() leaves the NodePath referencing the node, which
587  * will keep at least one reference to the node for as long as the NodePath
588  * exists.
589  */
590 void NodePath::
591 remove_node(Thread *current_thread) {
592  nassertv(_error_type != ET_not_found);
593 
594  // If we have no parents, remove_node() is just a do-nothing operation; if
595  // we have no nodes, maybe we were already removed. In either case, quietly
596  // do nothing except to ensure the NodePath is clear.
597  if (!is_empty() && !is_singleton(current_thread)) {
598  node()->reset_prev_transform(current_thread);
599  int pipeline_stage = current_thread->get_pipeline_stage();
600  PandaNode::detach(_head, pipeline_stage, current_thread);
601  }
602 
603  if (is_empty() || _head->has_key()) {
604  // Preserve the key we had on the node before we removed it.
605  int key = get_key();
606  (*this) = NodePath::removed();
607  _backup_key = key;
608 
609  } else {
610  // We didn't have a key; just clear the NodePath.
611  (*this) = NodePath::removed();
612  }
613 }
614 
615 /**
616  * Disconnects the referenced node from its parent, but does not immediately
617  * delete it. The NodePath retains a pointer to the node, and becomes a
618  * singleton NodePath.
619  *
620  * This should be called to detach a node from the scene graph, with the
621  * option of reattaching it later to the same parent or to a different parent.
622  *
623  * In practice, the only difference between remove_node() and detach_node() is
624  * that remove_node() also resets the NodePath to empty, which will cause the
625  * node to be deleted immediately if there are no other references. On the
626  * other hand, detach_node() leaves the NodePath referencing the node, which
627  * will keep at least one reference to the node for as long as the NodePath
628  * exists.
629  */
630 void NodePath::
631 detach_node(Thread *current_thread) {
632  nassertv(_error_type != ET_not_found);
633  if (!is_empty() && !is_singleton()) {
635  int pipeline_stage = current_thread->get_pipeline_stage();
636  PandaNode::detach(_head, pipeline_stage, current_thread);
637  }
638 }
639 
640 /**
641  * Lists the hierarchy at and above the referenced node.
642  */
643 int NodePath::
644 reverse_ls(ostream &out, int indent_level) const {
645  if (is_empty()) {
646  out << "(empty)\n";
647  return 0;
648  } else if (has_parent()) {
649  indent_level = get_parent().reverse_ls(out, indent_level);
650  }
651  node()->write(out, indent_level);
652  return indent_level + 2;
653 }
654 
655 /**
656  * Writes a sensible description of the NodePath to the indicated output
657  * stream.
658  */
659 void NodePath::
660 output(ostream &out) const {
661  switch (_error_type) {
662  case ET_not_found:
663  out << "**not found**";
664  return;
665  case ET_removed:
666  out << "**removed**";
667  return;
668  case ET_fail:
669  out << "**error**";
670  return;
671  default:
672  break;
673  }
674 
675  if (_head == nullptr) {
676  out << "(empty)";
677  } else {
678  _head->output(out);
679  }
680 }
681 
682 /**
683  * Returns the complete state object set on this node.
684  */
685 const RenderState *NodePath::
686 get_state(Thread *current_thread) const {
687  // This method is declared non-inline to avoid a compiler bug in gcc-3.4 and
688  // gcc-4.0.
689  nassertr_always(!is_empty(), RenderState::make_empty());
690  return node()->get_state(current_thread);
691 }
692 
693 /**
694  * Returns the state changes that must be made to transition to the render
695  * state of this node from the render state of the other node.
696  */
698 get_state(const NodePath &other, Thread *current_thread) const {
699  nassertr(_error_type == ET_ok && other._error_type == ET_ok, RenderState::make_empty());
700 
701  if (other.is_empty()) {
702  return get_net_state(current_thread);
703  }
704  if (is_empty()) {
705  return other.get_net_state(current_thread)->invert_compose(RenderState::make_empty());
706  }
707 
708 #if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS))
709  nassertr(verify_complete(current_thread), RenderState::make_empty());
710  nassertr(other.verify_complete(current_thread), RenderState::make_empty());
711 #endif
712 
713  int a_count, b_count;
714  if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == nullptr) {
715  if (allow_unrelated_wrt) {
716  pgraph_cat.debug()
717  << *this << " is not related to " << other << "\n";
718  } else {
719  pgraph_cat.error()
720  << *this << " is not related to " << other << "\n";
721  nassert_raise("unrelated nodes");
722  return RenderState::make_empty();
723  }
724  }
725 
726  CPT(RenderState) a_state = r_get_partial_state(_head, a_count, current_thread);
727  CPT(RenderState) b_state = r_get_partial_state(other._head, b_count, current_thread);
728  return b_state->invert_compose(a_state);
729 }
730 
731 /**
732  * Sets the state object on this node, relative to the other node. This
733  * computes a new state object that will have the indicated value when seen
734  * from the other node.
735  */
736 void NodePath::
737 set_state(const NodePath &other, const RenderState *state,
738  Thread *current_thread) {
739  nassertv(_error_type == ET_ok && other._error_type == ET_ok);
740  nassertv_always(!is_empty());
741 
742  // First, we perform a wrt to the parent, to get the conversion.
743  CPT(RenderState) rel_state;
744  if (has_parent()) {
745  rel_state = other.get_state(get_parent(current_thread), current_thread);
746  } else {
747  rel_state = other.get_state(NodePath(), current_thread);
748  }
749 
750  CPT(RenderState) new_state = rel_state->compose(state);
751  set_state(new_state, current_thread);
752 }
753 
754 /**
755  * Returns the complete transform object set on this node.
756  */
758 get_transform(Thread *current_thread) const {
759  // This method is declared non-inline to avoid a compiler bug in gcc-3.4 and
760  // gcc-4.0.
761  nassertr_always(!is_empty(), TransformState::make_identity());
762  return node()->get_transform(current_thread);
763 }
764 
765 /**
766  * Returns the relative transform to this node from the other node; i.e. the
767  * transformation of this node as seen from the other node.
768  */
770 get_transform(const NodePath &other, Thread *current_thread) const {
771  nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity());
772  PStatTimer timer(_get_transform_pcollector);
773 
774  if (other.is_empty()) {
775  return get_net_transform(current_thread);
776  }
777  if (is_empty()) {
778  return other.get_net_transform(current_thread)->invert_compose(TransformState::make_identity());
779  }
780 
781 #if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS))
782  nassertr(verify_complete(current_thread), TransformState::make_identity());
783  nassertr(other.verify_complete(current_thread), TransformState::make_identity());
784 #endif
785 
786  int a_count, b_count;
787  if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == nullptr) {
788  if (allow_unrelated_wrt) {
789  if (pgraph_cat.is_debug()) {
790  pgraph_cat.debug()
791  << *this << " is not related to " << other << "\n";
792  }
793  } else {
794  pgraph_cat.error()
795  << *this << " is not related to " << other << "\n";
796  nassert_raise("unrelated nodes");
797  return TransformState::make_identity();
798  }
799  }
800 
801  CPT(TransformState) a_transform, b_transform;
802 
803  a_transform = r_get_partial_transform(_head, a_count, current_thread);
804  if (a_transform != nullptr) {
805  b_transform = r_get_partial_transform(other._head, b_count, current_thread);
806  }
807  if (b_transform == nullptr) {
808  // If either path involved a node with a net_transform RenderEffect
809  // applied, we have to go all the way up to the root to get the right
810  // answer.
811  a_transform = r_get_net_transform(_head, current_thread);
812  b_transform = r_get_net_transform(other._head, current_thread);
813  }
814 
815  return b_transform->invert_compose(a_transform);
816 }
817 
818 /**
819  * Sets the transform object on this node, relative to the other node. This
820  * computes a new transform object that will have the indicated value when
821  * seen from the other node.
822  */
823 void NodePath::
824 set_transform(const NodePath &other, const TransformState *transform,
825  Thread *current_thread) {
826  nassertv(_error_type == ET_ok && other._error_type == ET_ok);
827  nassertv_always(!is_empty());
828 
829  // First, we perform a wrt to the parent, to get the conversion.
830  CPT(TransformState) rel_trans;
831  if (has_parent()) {
832  rel_trans = other.get_transform(get_parent(current_thread), current_thread);
833  } else {
834  rel_trans = other.get_transform(NodePath(), current_thread);
835  }
836 
837  CPT(TransformState) new_trans = rel_trans->compose(transform);
838  set_transform(new_trans, current_thread);
839 }
840 
841 /**
842  * Returns the transform that has been set as this node's "previous" position.
843  * See set_prev_transform().
844  */
846 get_prev_transform(Thread *current_thread) const {
847  // This method is declared non-inline to avoid a compiler bug in gcc-3.4 and
848  // gcc-4.0.
849  nassertr_always(!is_empty(), TransformState::make_identity());
850  return node()->get_prev_transform(current_thread);
851 }
852 
853 /**
854  * Returns the relative "previous" transform to this node from the other node;
855  * i.e. the position of this node in the previous frame, as seen by the other
856  * node in the previous frame.
857  */
859 get_prev_transform(const NodePath &other, Thread *current_thread) const {
860  nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity());
861 
862  if (other.is_empty()) {
863  return get_net_prev_transform(current_thread);
864  }
865  if (is_empty()) {
866  return other.get_net_prev_transform(current_thread)->invert_compose(TransformState::make_identity());
867  }
868 
869 #if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS))
870  nassertr(verify_complete(current_thread), TransformState::make_identity());
871  nassertr(other.verify_complete(current_thread), TransformState::make_identity());
872 #endif
873 
874  int a_count, b_count;
875  if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == nullptr) {
876  if (allow_unrelated_wrt) {
877  pgraph_cat.debug()
878  << *this << " is not related to " << other << "\n";
879  } else {
880  pgraph_cat.error()
881  << *this << " is not related to " << other << "\n";
882  nassert_raise("unrelated nodes");
883  return TransformState::make_identity();
884  }
885  }
886 
887  CPT(TransformState) a_prev_transform = r_get_partial_prev_transform(_head, a_count, current_thread);
888  CPT(TransformState) b_prev_transform = r_get_partial_prev_transform(other._head, b_count, current_thread);
889  return b_prev_transform->invert_compose(a_prev_transform);
890 }
891 
892 /**
893  * Sets the "previous" transform object on this node, relative to the other
894  * node. This computes a new transform object that will have the indicated
895  * value when seen from the other node.
896  */
897 void NodePath::
898 set_prev_transform(const NodePath &other, const TransformState *transform,
899  Thread *current_thread) {
900  nassertv(_error_type == ET_ok && other._error_type == ET_ok);
901  nassertv_always(!is_empty());
902 
903  // First, we perform a wrt to the parent, to get the conversion.
904  CPT(TransformState) rel_trans;
905  if (has_parent(current_thread)) {
906  rel_trans = other.get_prev_transform(get_parent(current_thread), current_thread);
907  } else {
908  rel_trans = other.get_prev_transform(NodePath(), current_thread);
909  }
910 
911  CPT(TransformState) new_trans = rel_trans->compose(transform);
912  set_prev_transform(new_trans, current_thread);
913 }
914 
915 /**
916  * Sets the translation component of the transform, leaving rotation and scale
917  * untouched. This also resets the node's "previous" position, so that the
918  * collision system will see the node as having suddenly appeared in the new
919  * position, without passing any points in between. See Also:
920  * NodePath::set_fluid_pos
921  */
922 void NodePath::
923 set_pos(const LVecBase3 &pos) {
924  nassertv_always(!is_empty());
925  set_transform(get_transform()->set_pos(pos));
927 }
928 
929 void NodePath::
930 set_x(PN_stdfloat x) {
931  nassertv_always(!is_empty());
932  LPoint3 pos = get_pos();
933  pos[0] = x;
934  set_pos(pos);
935 }
936 
937 void NodePath::
938 set_y(PN_stdfloat y) {
939  nassertv_always(!is_empty());
940  LPoint3 pos = get_pos();
941  pos[1] = y;
942  set_pos(pos);
943 }
944 
945 void NodePath::
946 set_z(PN_stdfloat z) {
947  nassertv_always(!is_empty());
948  LPoint3 pos = get_pos();
949  pos[2] = z;
950  set_pos(pos);
951 }
952 
953 /**
954  * Sets the translation component, without changing the "previous" position,
955  * so that the collision system will see the node as moving fluidly from its
956  * previous position to its new position. See Also: NodePath::set_pos
957  */
958 void NodePath::
959 set_fluid_pos(const LVecBase3 &pos) {
960  nassertv_always(!is_empty());
961  set_transform(get_transform()->set_pos(pos));
962 }
963 
964 void NodePath::
965 set_fluid_x(PN_stdfloat x) {
966  nassertv_always(!is_empty());
967  LPoint3 pos = get_pos();
968  pos[0] = x;
969  set_fluid_pos(pos);
970 }
971 
972 void NodePath::
973 set_fluid_y(PN_stdfloat y) {
974  nassertv_always(!is_empty());
975  LPoint3 pos = get_pos();
976  pos[1] = y;
977  set_fluid_pos(pos);
978 }
979 
980 void NodePath::
981 set_fluid_z(PN_stdfloat z) {
982  nassertv_always(!is_empty());
983  LPoint3 pos = get_pos();
984  pos[2] = z;
985  set_fluid_pos(pos);
986 }
987 
988 /**
989  * Retrieves the translation component of the transform.
990  */
991 LPoint3 NodePath::
992 get_pos() const {
993  nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f));
994  return get_transform()->get_pos();
995 }
996 
997 /**
998  * Returns the delta vector from this node's position in the previous frame
999  * (according to set_prev_transform(), typically set via the use of
1000  * set_fluid_pos()) and its position in the current frame. This is the vector
1001  * used to determine collisions. Generally, if the node was last repositioned
1002  * via set_pos(), the delta will be zero; if it was adjusted via
1003  * set_fluid_pos(), the delta will represent the change from the previous
1004  * frame's position.
1005  */
1006 LVector3 NodePath::
1007 get_pos_delta() const {
1008  nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f));
1009  return get_transform()->get_pos() - get_prev_transform()->get_pos();
1010 }
1011 
1012 /**
1013  * Sets the rotation component of the transform, leaving translation and scale
1014  * untouched.
1015  */
1016 void NodePath::
1017 set_hpr(const LVecBase3 &hpr) {
1018  nassertv_always(!is_empty());
1019  CPT(TransformState) transform = get_transform();
1020  nassertv(transform->has_hpr());
1021  set_transform(transform->set_hpr(hpr));
1022 }
1023 
1024 void NodePath::
1025 set_h(PN_stdfloat h) {
1026  nassertv_always(!is_empty());
1027  CPT(TransformState) transform = get_transform();
1028  nassertv(transform->has_hpr());
1029  LVecBase3 hpr = transform->get_hpr();
1030  hpr[0] = h;
1031  set_transform(transform->set_hpr(hpr));
1032 }
1033 
1034 void NodePath::
1035 set_p(PN_stdfloat p) {
1036  nassertv_always(!is_empty());
1037  CPT(TransformState) transform = get_transform();
1038  nassertv(transform->has_hpr());
1039  LVecBase3 hpr = transform->get_hpr();
1040  hpr[1] = p;
1041  set_transform(transform->set_hpr(hpr));
1042 }
1043 
1044 void NodePath::
1045 set_r(PN_stdfloat r) {
1046  nassertv_always(!is_empty());
1047  CPT(TransformState) transform = get_transform();
1048  nassertv(transform->has_hpr());
1049  LVecBase3 hpr = transform->get_hpr();
1050  hpr[2] = r;
1051  set_transform(transform->set_hpr(hpr));
1052 }
1053 
1054 /**
1055  * Retrieves the rotation component of the transform.
1056  */
1057 LVecBase3 NodePath::
1058 get_hpr() const {
1059  nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f));
1060  CPT(TransformState) transform = get_transform();
1061  nassertr(transform->has_hpr(), LVecBase3(0.0f, 0.0f, 0.0f));
1062  return transform->get_hpr();
1063 }
1064 
1065 /**
1066  * Sets the rotation component of the transform, leaving translation and scale
1067  * untouched.
1068  */
1069 void NodePath::
1070 set_quat(const LQuaternion &quat) {
1071  nassertv_always(!is_empty());
1072  CPT(TransformState) transform = get_transform();
1073  set_transform(transform->set_quat(quat));
1074 }
1075 
1076 /**
1077  * Retrieves the rotation component of the transform.
1078  */
1079 LQuaternion NodePath::
1080 get_quat() const {
1081  nassertr_always(!is_empty(), LQuaternion::ident_quat());
1082  CPT(TransformState) transform = get_transform();
1083  return transform->get_quat();
1084 }
1085 
1086 /**
1087  * Sets the scale component of the transform, leaving translation and rotation
1088  * untouched.
1089  */
1090 void NodePath::
1091 set_scale(const LVecBase3 &scale) {
1092  nassertv_always(!is_empty());
1093  CPT(TransformState) transform = get_transform();
1094  set_transform(transform->set_scale(scale));
1095 }
1096 
1097 void NodePath::
1098 set_sx(PN_stdfloat sx) {
1099  nassertv_always(!is_empty());
1100  CPT(TransformState) transform = get_transform();
1101  LVecBase3 scale = transform->get_scale();
1102  scale[0] = sx;
1103  set_transform(transform->set_scale(scale));
1104 }
1105 
1106 void NodePath::
1107 set_sy(PN_stdfloat sy) {
1108  nassertv_always(!is_empty());
1109  CPT(TransformState) transform = get_transform();
1110  LVecBase3 scale = transform->get_scale();
1111  scale[1] = sy;
1112  set_transform(transform->set_scale(scale));
1113 }
1114 
1115 void NodePath::
1116 set_sz(PN_stdfloat sz) {
1117  nassertv_always(!is_empty());
1118  CPT(TransformState) transform = get_transform();
1119  LVecBase3 scale = transform->get_scale();
1120  scale[2] = sz;
1121  set_transform(transform->set_scale(scale));
1122 }
1123 
1124 /**
1125  * Retrieves the scale component of the transform.
1126  */
1127 LVecBase3 NodePath::
1128 get_scale() const {
1129  nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f));
1130  CPT(TransformState) transform = get_transform();
1131  return transform->get_scale();
1132 }
1133 
1134 /**
1135  * Sets the shear component of the transform, leaving translation and rotation
1136  * untouched.
1137  */
1138 void NodePath::
1139 set_shear(const LVecBase3 &shear) {
1140  nassertv_always(!is_empty());
1141  CPT(TransformState) transform = get_transform();
1142  set_transform(transform->set_shear(shear));
1143 }
1144 
1145 void NodePath::
1146 set_shxy(PN_stdfloat shxy) {
1147  nassertv_always(!is_empty());
1148  CPT(TransformState) transform = get_transform();
1149  LVecBase3 shear = transform->get_shear();
1150  shear[0] = shxy;
1151  set_transform(transform->set_shear(shear));
1152 }
1153 
1154 void NodePath::
1155 set_shxz(PN_stdfloat shxz) {
1156  nassertv_always(!is_empty());
1157  CPT(TransformState) transform = get_transform();
1158  LVecBase3 shear = transform->get_shear();
1159  shear[1] = shxz;
1160  set_transform(transform->set_shear(shear));
1161 }
1162 
1163 void NodePath::
1164 set_shyz(PN_stdfloat shyz) {
1165  nassertv_always(!is_empty());
1166  CPT(TransformState) transform = get_transform();
1167  LVecBase3 shear = transform->get_shear();
1168  shear[2] = shyz;
1169  set_transform(transform->set_shear(shear));
1170 }
1171 
1172 /**
1173  * Retrieves the shear component of the transform.
1174  */
1175 LVecBase3 NodePath::
1176 get_shear() const {
1177  nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f));
1178  CPT(TransformState) transform = get_transform();
1179  return transform->get_shear();
1180 }
1181 
1182 /**
1183  * Sets the translation and rotation component of the transform, leaving scale
1184  * untouched.
1185  */
1186 void NodePath::
1187 set_pos_hpr(const LVecBase3 &pos, const LVecBase3 &hpr) {
1188  nassertv_always(!is_empty());
1189  CPT(TransformState) transform = get_transform();
1190  transform = TransformState::make_pos_hpr_scale_shear
1191  (pos, hpr, transform->get_scale(), transform->get_shear());
1192  set_transform(transform);
1194 }
1195 
1196 /**
1197  * Sets the translation and rotation component of the transform, leaving scale
1198  * untouched.
1199  */
1200 void NodePath::
1201 set_pos_quat(const LVecBase3 &pos, const LQuaternion &quat) {
1202  nassertv_always(!is_empty());
1203  CPT(TransformState) transform = get_transform();
1204  transform = TransformState::make_pos_quat_scale_shear
1205  (pos, quat, transform->get_scale(), transform->get_shear());
1206  set_transform(transform);
1208 }
1209 
1210 /**
1211  * Sets the rotation and scale components of the transform, leaving
1212  * translation untouched.
1213  */
1214 void NodePath::
1215 set_hpr_scale(const LVecBase3 &hpr, const LVecBase3 &scale) {
1216  nassertv_always(!is_empty());
1217  CPT(TransformState) transform = get_transform();
1218  transform = TransformState::make_pos_hpr_scale_shear
1219  (transform->get_pos(), hpr, scale, transform->get_shear());
1220  set_transform(transform);
1221 }
1222 
1223 /**
1224  * Sets the rotation and scale components of the transform, leaving
1225  * translation untouched.
1226  */
1227 void NodePath::
1228 set_quat_scale(const LQuaternion &quat, const LVecBase3 &scale) {
1229  nassertv_always(!is_empty());
1230  CPT(TransformState) transform = get_transform();
1231  transform = TransformState::make_pos_quat_scale_shear
1232  (transform->get_pos(), quat, scale, transform->get_shear());
1233  set_transform(transform);
1234 }
1235 
1236 /**
1237  * Replaces the translation, rotation, and scale components, implicitly
1238  * setting shear to 0.
1239  */
1240 void NodePath::
1241 set_pos_hpr_scale(const LVecBase3 &pos, const LVecBase3 &hpr,
1242  const LVecBase3 &scale) {
1243  nassertv_always(!is_empty());
1244  set_transform(TransformState::make_pos_hpr_scale
1245  (pos, hpr, scale));
1247 }
1248 
1249 /**
1250  * Replaces the translation, rotation, and scale components, implicitly
1251  * setting shear to 0.
1252  */
1253 void NodePath::
1254 set_pos_quat_scale(const LVecBase3 &pos, const LQuaternion &quat,
1255  const LVecBase3 &scale) {
1256  nassertv_always(!is_empty());
1257  set_transform(TransformState::make_pos_quat_scale
1258  (pos, quat, scale));
1260 }
1261 
1262 /**
1263  * Completely replaces the transform with new translation, rotation, scale,
1264  * and shear components.
1265  */
1266 void NodePath::
1267 set_pos_hpr_scale_shear(const LVecBase3 &pos, const LVecBase3 &hpr,
1268  const LVecBase3 &scale, const LVecBase3 &shear) {
1269  nassertv_always(!is_empty());
1270  set_transform(TransformState::make_pos_hpr_scale_shear
1271  (pos, hpr, scale, shear));
1273 }
1274 
1275 /**
1276  * Completely replaces the transform with new translation, rotation, scale,
1277  * and shear components.
1278  */
1279 void NodePath::
1280 set_pos_quat_scale_shear(const LVecBase3 &pos, const LQuaternion &quat,
1281  const LVecBase3 &scale, const LVecBase3 &shear) {
1282  nassertv_always(!is_empty());
1283  set_transform(TransformState::make_pos_quat_scale_shear
1284  (pos, quat, scale, shear));
1286 }
1287 
1288 /**
1289  * Directly sets an arbitrary 4x4 transform matrix.
1290  */
1291 void NodePath::
1292 set_mat(const LMatrix4 &mat) {
1293  nassertv_always(!is_empty());
1294  set_transform(TransformState::make_mat(mat));
1296 }
1297 
1298 /**
1299  * Sets the hpr on this NodePath so that it rotates to face the indicated
1300  * point in space.
1301  */
1302 void NodePath::
1303 look_at(const LPoint3 &point, const LVector3 &up) {
1304  nassertv_always(!is_empty());
1305 
1306  LPoint3 pos = get_pos();
1307 
1308  LQuaternion quat;
1309  ::look_at(quat, point - pos, up);
1310  set_quat(quat);
1311 }
1312 
1313 /**
1314  * Behaves like look_at(), but with a strong preference to keeping the up
1315  * vector oriented in the indicated "up" direction.
1316  */
1317 void NodePath::
1318 heads_up(const LPoint3 &point, const LVector3 &up) {
1319  nassertv_always(!is_empty());
1320 
1321  LPoint3 pos = get_pos();
1322 
1323  LQuaternion quat;
1324  ::heads_up(quat, point - pos, up);
1325  set_quat(quat);
1326 }
1327 
1328 /**
1329  * Sets the translation component of the transform, relative to the other
1330  * node.
1331  */
1332 void NodePath::
1333 set_pos(const NodePath &other, const LVecBase3 &pos) {
1334  nassertv_always(!is_empty());
1335  CPT(TransformState) rel_transform = get_transform(other);
1336 
1337  CPT(TransformState) orig_transform = get_transform();
1338  if (orig_transform->has_components()) {
1339  // If we had a componentwise transform before we started, we should be
1340  // careful to preserve the other three components. We wouldn't need to do
1341  // this, except for the possibility of numerical error or decompose
1342  // ambiguity.
1343  const LVecBase3 &orig_hpr = orig_transform->get_hpr();
1344  const LVecBase3 &orig_scale = orig_transform->get_scale();
1345  const LVecBase3 &orig_shear = orig_transform->get_shear();
1346 
1347  set_transform(other, rel_transform->set_pos(pos));
1348  set_pos_hpr_scale_shear(get_transform()->get_pos(), orig_hpr, orig_scale, orig_shear);
1349 
1350  } else {
1351  // If we didn't have a componentwise transform already, never mind.
1352  set_transform(other, rel_transform->set_pos(pos));
1353  }
1355 }
1356 
1357 void NodePath::
1358 set_x(const NodePath &other, PN_stdfloat x) {
1359  nassertv_always(!is_empty());
1360  LPoint3 pos = get_pos(other);
1361  pos[0] = x;
1362  set_pos(other, pos);
1363 }
1364 
1365 void NodePath::
1366 set_y(const NodePath &other, PN_stdfloat y) {
1367  nassertv_always(!is_empty());
1368  LPoint3 pos = get_pos(other);
1369  pos[1] = y;
1370  set_pos(other, pos);
1371 }
1372 
1373 void NodePath::
1374 set_z(const NodePath &other, PN_stdfloat z) {
1375  nassertv_always(!is_empty());
1376  LPoint3 pos = get_pos(other);
1377  pos[2] = z;
1378  set_pos(other, pos);
1379 }
1380 
1381 /**
1382  * Sets the translation component of the transform, relative to the other
1383  * node.
1384  */
1385 void NodePath::
1386 set_fluid_pos(const NodePath &other, const LVecBase3 &pos) {
1387  nassertv_always(!is_empty());
1388  CPT(TransformState) rel_transform = get_transform(other);
1389 
1390  CPT(TransformState) orig_transform = get_transform();
1391  if (orig_transform->has_components()) {
1392  // If we had a componentwise transform before we started, we should be
1393  // careful to preserve the other three components. We wouldn't need to do
1394  // this, except for the possibility of numerical error or decompose
1395  // ambiguity.
1396  const LVecBase3 &orig_hpr = orig_transform->get_hpr();
1397  const LVecBase3 &orig_scale = orig_transform->get_scale();
1398  const LVecBase3 &orig_shear = orig_transform->get_shear();
1399 
1400  // Use the relative set_transform() to compute the relative pos, and then
1401  // reset all of the other components back to the way they were.
1402  set_transform(other, rel_transform->set_pos(pos));
1403  set_transform(TransformState::make_pos_hpr_scale_shear
1404  (get_transform()->get_pos(), orig_hpr, orig_scale, orig_shear));
1405 
1406  } else {
1407  // If we didn't have a componentwise transform already, never mind.
1408  set_transform(other, rel_transform->set_pos(pos));
1409  }
1410 }
1411 
1412 void NodePath::
1413 set_fluid_x(const NodePath &other, PN_stdfloat x) {
1414  nassertv_always(!is_empty());
1415  LPoint3 pos = get_pos(other);
1416  pos[0] = x;
1417  set_fluid_pos(other, pos);
1418 }
1419 
1420 void NodePath::
1421 set_fluid_y(const NodePath &other, PN_stdfloat y) {
1422  nassertv_always(!is_empty());
1423  LPoint3 pos = get_pos(other);
1424  pos[1] = y;
1425  set_fluid_pos(other, pos);
1426 }
1427 
1428 void NodePath::
1429 set_fluid_z(const NodePath &other, PN_stdfloat z) {
1430  nassertv_always(!is_empty());
1431  LPoint3 pos = get_pos(other);
1432  pos[2] = z;
1433  set_fluid_pos(other, pos);
1434 }
1435 
1436 /**
1437  * Returns the relative position of the referenced node as seen from the other
1438  * node.
1439  */
1440 LPoint3 NodePath::
1441 get_pos(const NodePath &other) const {
1442  nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f));
1443  return get_transform(other)->get_pos();
1444 }
1445 
1446 /**
1447  * Returns the delta vector from this node's position in the previous frame
1448  * (according to set_prev_transform(), typically set via the use of
1449  * set_fluid_pos()) and its position in the current frame, as seen in the
1450  * indicated node's coordinate space. This is the vector used to determine
1451  * collisions. Generally, if the node was last repositioned via set_pos(),
1452  * the delta will be zero; if it was adjusted via set_fluid_pos(), the delta
1453  * will represent the change from the previous frame's position.
1454  */
1455 LVector3 NodePath::
1456 get_pos_delta(const NodePath &other) const {
1457  nassertr_always(!is_empty(), LPoint3(0.0f, 0.0f, 0.0f));
1458  return get_transform(other)->get_pos() - get_prev_transform(other)->get_pos();
1459 }
1460 
1461 /**
1462  * Sets the rotation component of the transform, relative to the other node.
1463  */
1464 void NodePath::
1465 set_hpr(const NodePath &other, const LVecBase3 &hpr) {
1466  nassertv_always(!is_empty());
1467  CPT(TransformState) rel_transform = get_transform(other);
1468  nassertv(rel_transform->has_hpr());
1469 
1470  CPT(TransformState) transform = get_transform();
1471  if (transform->has_components()) {
1472  // If we had a componentwise transform before we started, we should be
1473  // careful to preserve the other three components. We wouldn't need to do
1474  // this, except for the possibility of numerical error or decompose
1475  // ambiguity.
1476  const LVecBase3 &orig_pos = transform->get_pos();
1477  const LVecBase3 &orig_scale = transform->get_scale();
1478  const LVecBase3 &orig_shear = transform->get_shear();
1479 
1480  set_transform(other, rel_transform->set_hpr(hpr));
1481  transform = get_transform();
1482  if (transform->has_components()) {
1483  set_transform(TransformState::make_pos_hpr_scale_shear
1484  (orig_pos, transform->get_hpr(), orig_scale, orig_shear));
1485  }
1486 
1487  } else {
1488  // If we didn't have a componentwise transform already, never mind.
1489  set_transform(other, rel_transform->set_hpr(hpr));
1490  }
1491 }
1492 
1493 void NodePath::
1494 set_h(const NodePath &other, PN_stdfloat h) {
1495  nassertv_always(!is_empty());
1496  LVecBase3 hpr = get_hpr(other);
1497  hpr[0] = h;
1498  set_hpr(other, hpr);
1499 }
1500 
1501 void NodePath::
1502 set_p(const NodePath &other, PN_stdfloat p) {
1503  nassertv_always(!is_empty());
1504  LVecBase3 hpr = get_hpr(other);
1505  hpr[1] = p;
1506  set_hpr(other, hpr);
1507 }
1508 
1509 void NodePath::
1510 set_r(const NodePath &other, PN_stdfloat r) {
1511  nassertv_always(!is_empty());
1512  LVecBase3 hpr = get_hpr(other);
1513  hpr[2] = r;
1514  set_hpr(other, hpr);
1515 }
1516 
1517 /**
1518  * Returns the relative orientation of the bottom node as seen from the other
1519  * node.
1520  */
1521 LVecBase3 NodePath::
1522 get_hpr(const NodePath &other) const {
1523  nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f));
1524  CPT(TransformState) transform = get_transform(other);
1525  nassertr(transform->has_hpr(), LVecBase3(0.0f, 0.0f, 0.0f));
1526  return transform->get_hpr();
1527 }
1528 
1529 /**
1530  * Sets the rotation component of the transform, relative to the other node.
1531  */
1532 void NodePath::
1533 set_quat(const NodePath &other, const LQuaternion &quat) {
1534  nassertv_always(!is_empty());
1535  CPT(TransformState) rel_transform = get_transform(other);
1536 
1537  CPT(TransformState) transform = get_transform();
1538  if (transform->has_components()) {
1539  // If we had a componentwise transform before we started, we should be
1540  // careful to preserve the other three components. We wouldn't need to do
1541  // this, except for the possibility of numerical error or decompose
1542  // ambiguity.
1543  const LVecBase3 &orig_pos = transform->get_pos();
1544  const LVecBase3 &orig_scale = transform->get_scale();
1545  const LVecBase3 &orig_shear = transform->get_shear();
1546 
1547  set_transform(other, rel_transform->set_quat(quat));
1548  transform = get_transform();
1549  if (transform->has_components()) {
1550  set_transform(TransformState::make_pos_quat_scale_shear
1551  (orig_pos, transform->get_quat(), orig_scale, orig_shear));
1552  }
1553 
1554  } else {
1555  // If we didn't have a componentwise transform already, never mind.
1556  set_transform(other, rel_transform->set_quat(quat));
1557  }
1558 }
1559 
1560 /**
1561  * Returns the relative orientation of the bottom node as seen from the other
1562  * node.
1563  */
1564 LQuaternion NodePath::
1565 get_quat(const NodePath &other) const {
1566  nassertr_always(!is_empty(), LQuaternion::ident_quat());
1567  CPT(TransformState) transform = get_transform(other);
1568  return transform->get_quat();
1569 }
1570 
1571 /**
1572  * Sets the scale component of the transform, relative to the other node.
1573  */
1574 void NodePath::
1575 set_scale(const NodePath &other, const LVecBase3 &scale) {
1576  nassertv_always(!is_empty());
1577  CPT(TransformState) rel_transform = get_transform(other);
1578 
1579  CPT(TransformState) transform = get_transform();
1580  if (transform->has_components()) {
1581  // If we had a componentwise transform before we started, we should be
1582  // careful to preserve the other three components. We wouldn't need to do
1583  // this, except for the possibility of numerical error or decompose
1584  // ambiguity.
1585  const LVecBase3 &orig_pos = transform->get_pos();
1586  const LVecBase3 &orig_hpr = transform->get_hpr();
1587  const LVecBase3 &orig_shear = transform->get_shear();
1588 
1589  set_transform(other, rel_transform->set_scale(scale));
1590  transform = get_transform();
1591  if (transform->has_components()) {
1592  set_transform(TransformState::make_pos_hpr_scale_shear
1593  (orig_pos, orig_hpr, transform->get_scale(), orig_shear));
1594  }
1595 
1596  } else {
1597  // If we didn't have a componentwise transform already, never mind.
1598  set_transform(other, rel_transform->set_scale(scale));
1599  }
1600 }
1601 
1602 void NodePath::
1603 set_sx(const NodePath &other, PN_stdfloat sx) {
1604  nassertv_always(!is_empty());
1605  LVecBase3 scale = get_scale(other);
1606  scale[0] = sx;
1607  set_scale(other, scale);
1608 }
1609 
1610 void NodePath::
1611 set_sy(const NodePath &other, PN_stdfloat sy) {
1612  nassertv_always(!is_empty());
1613  LVecBase3 scale = get_scale(other);
1614  scale[1] = sy;
1615  set_scale(other, scale);
1616 }
1617 
1618 void NodePath::
1619 set_sz(const NodePath &other, PN_stdfloat sz) {
1620  nassertv_always(!is_empty());
1621  LVecBase3 scale = get_scale(other);
1622  scale[2] = sz;
1623  set_scale(other, scale);
1624 }
1625 
1626 /**
1627  * Returns the relative scale of the bottom node as seen from the other node.
1628  */
1629 LVecBase3 NodePath::
1630 get_scale(const NodePath &other) const {
1631  nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f));
1632  CPT(TransformState) transform = get_transform(other);
1633  return transform->get_scale();
1634 }
1635 
1636 /**
1637  * Sets the shear component of the transform, relative to the other node.
1638  */
1639 void NodePath::
1640 set_shear(const NodePath &other, const LVecBase3 &shear) {
1641  nassertv_always(!is_empty());
1642  CPT(TransformState) rel_transform = get_transform(other);
1643 
1644  CPT(TransformState) transform = get_transform();
1645  if (transform->has_components()) {
1646  // If we had a componentwise transform before we started, we should be
1647  // careful to preserve the other three components. We wouldn't need to do
1648  // this, except for the possibility of numerical error or decompose
1649  // ambiguity.
1650  const LVecBase3 &orig_pos = transform->get_pos();
1651  const LVecBase3 &orig_hpr = transform->get_hpr();
1652  const LVecBase3 &orig_scale = transform->get_scale();
1653 
1654  set_transform(other, rel_transform->set_shear(shear));
1655  transform = get_transform();
1656  if (transform->has_components()) {
1657  set_transform(TransformState::make_pos_hpr_scale_shear
1658  (orig_pos, orig_hpr, orig_scale, transform->get_shear()));
1659  }
1660 
1661  } else {
1662  // If we didn't have a componentwise transform already, never mind.
1663  set_transform(other, rel_transform->set_shear(shear));
1664  }
1665 }
1666 
1667 void NodePath::
1668 set_shxy(const NodePath &other, PN_stdfloat shxy) {
1669  nassertv_always(!is_empty());
1670  LVecBase3 shear = get_shear(other);
1671  shear[0] = shxy;
1672  set_shear(other, shear);
1673 }
1674 
1675 void NodePath::
1676 set_shxz(const NodePath &other, PN_stdfloat shxz) {
1677  nassertv_always(!is_empty());
1678  LVecBase3 shear = get_shear(other);
1679  shear[1] = shxz;
1680  set_shear(other, shear);
1681 }
1682 
1683 void NodePath::
1684 set_shyz(const NodePath &other, PN_stdfloat shyz) {
1685  nassertv_always(!is_empty());
1686  LVecBase3 shear = get_shear(other);
1687  shear[2] = shyz;
1688  set_shear(other, shear);
1689 }
1690 
1691 /**
1692  * Returns the relative shear of the bottom node as seen from the other node.
1693  */
1694 LVecBase3 NodePath::
1695 get_shear(const NodePath &other) const {
1696  nassertr_always(!is_empty(), LVecBase3(0.0f, 0.0f, 0.0f));
1697  CPT(TransformState) transform = get_transform(other);
1698  return transform->get_shear();
1699 }
1700 
1701 /**
1702  * Sets the translation and rotation component of the transform, relative to
1703  * the other node.
1704  */
1705 void NodePath::
1706 set_pos_hpr(const NodePath &other, const LVecBase3 &pos,
1707  const LVecBase3 &hpr) {
1708  nassertv_always(!is_empty());
1709  CPT(TransformState) rel_transform = get_transform(other);
1710 
1711  CPT(TransformState) transform = get_transform();
1712  if (transform->has_components()) {
1713  // If we had a componentwise transform before we started, we should be
1714  // careful to preserve the other two components. We wouldn't need to do
1715  // this, except for the possibility of numerical error or decompose
1716  // ambiguity.
1717  const LVecBase3 &orig_scale = transform->get_scale();
1718  const LVecBase3 &orig_shear = transform->get_shear();
1719 
1720  set_transform(other, TransformState::make_pos_hpr_scale_shear
1721  (pos, hpr, rel_transform->get_scale(), rel_transform->get_shear()));
1722  transform = get_transform();
1723  if (transform->has_components()) {
1724  set_pos_hpr_scale_shear(transform->get_pos(), transform->get_hpr(),
1725  orig_scale, orig_shear);
1726  }
1727 
1728  } else {
1729  // If we didn't have a componentwise transform already, never mind.
1730  set_transform(other, TransformState::make_pos_hpr_scale_shear
1731  (pos, hpr, rel_transform->get_scale(), rel_transform->get_shear()));
1733  }
1734 }
1735 
1736 /**
1737  * Sets the translation and rotation component of the transform, relative to
1738  * the other node.
1739  */
1740 void NodePath::
1741 set_pos_quat(const NodePath &other, const LVecBase3 &pos,
1742  const LQuaternion &quat) {
1743  nassertv_always(!is_empty());
1744  CPT(TransformState) rel_transform = get_transform(other);
1745 
1746  CPT(TransformState) transform = get_transform();
1747  if (transform->has_components()) {
1748  // If we had a componentwise transform before we started, we should be
1749  // careful to preserve the other two components. We wouldn't need to do
1750  // this, except for the possibility of numerical error or decompose
1751  // ambiguity.
1752  const LVecBase3 &orig_scale = transform->get_scale();
1753  const LVecBase3 &orig_shear = transform->get_shear();
1754 
1755  set_transform(other, TransformState::make_pos_quat_scale_shear
1756  (pos, quat, rel_transform->get_scale(), rel_transform->get_shear()));
1757  transform = get_transform();
1758  if (transform->has_components()) {
1759  set_pos_quat_scale_shear(transform->get_pos(), transform->get_quat(),
1760  orig_scale, orig_shear);
1761  }
1762 
1763  } else {
1764  // If we didn't have a componentwise transform already, never mind.
1765  set_transform(other, TransformState::make_pos_quat_scale_shear
1766  (pos, quat, rel_transform->get_scale(), rel_transform->get_shear()));
1768  }
1769 }
1770 
1771 /**
1772  * Sets the rotation and scale components of the transform, leaving
1773  * translation untouched. This, or set_pos_hpr_scale, is the preferred way to
1774  * update a transform when both hpr and scale are to be changed.
1775  */
1776 void NodePath::
1777 set_hpr_scale(const NodePath &other, const LVecBase3 &hpr, const LVecBase3 &scale) {
1778  // We don't bother trying very hard to preserve pos across this operation,
1779  // unlike the work we do above to preserve hpr or scale, since it generally
1780  // doesn't matter that much if pos is off by a few thousandths.
1781  nassertv_always(!is_empty());
1782  CPT(TransformState) transform = get_transform(other);
1783  transform = TransformState::make_pos_hpr_scale_shear
1784  (transform->get_pos(), hpr, scale, transform->get_shear());
1785  set_transform(other, transform);
1786 }
1787 
1788 /**
1789  * Sets the rotation and scale components of the transform, leaving
1790  * translation untouched. This, or set_pos_quat_scale, is the preferred way
1791  * to update a transform when both quat and scale are to be changed.
1792  */
1793 void NodePath::
1794 set_quat_scale(const NodePath &other, const LQuaternion &quat,
1795  const LVecBase3 &scale) {
1796  // We don't bother trying very hard to preserve pos across this operation,
1797  // unlike the work we do above to preserve quat or scale, since it generally
1798  // doesn't matter that much if pos is off by a few thousandths.
1799  nassertv_always(!is_empty());
1800  CPT(TransformState) transform = get_transform(other);
1801  transform = TransformState::make_pos_quat_scale_shear
1802  (transform->get_pos(), quat, scale, transform->get_shear());
1803  set_transform(other, transform);
1804 }
1805 
1806 /**
1807  * Completely replaces the transform with new translation, rotation, and scale
1808  * components, relative to the other node, implicitly setting shear to 0.
1809  */
1810 void NodePath::
1812  const LVecBase3 &pos, const LVecBase3 &hpr,
1813  const LVecBase3 &scale) {
1814  nassertv_always(!is_empty());
1815  set_transform(other, TransformState::make_pos_hpr_scale
1816  (pos, hpr, scale));
1818 }
1819 
1820 /**
1821  * Completely replaces the transform with new translation, rotation, and scale
1822  * components, relative to the other node, implicitly setting shear to 0.
1823  */
1824 void NodePath::
1826  const LVecBase3 &pos, const LQuaternion &quat,
1827  const LVecBase3 &scale) {
1828  nassertv_always(!is_empty());
1829  set_transform(other, TransformState::make_pos_quat_scale
1830  (pos, quat, scale));
1832 }
1833 
1834 /**
1835  * Completely replaces the transform with new translation, rotation, scale,
1836  * and shear components, relative to the other node.
1837  */
1838 void NodePath::
1840  const LVecBase3 &pos, const LVecBase3 &hpr,
1841  const LVecBase3 &scale, const LVecBase3 &shear) {
1842  nassertv_always(!is_empty());
1843  set_transform(other, TransformState::make_pos_hpr_scale_shear
1844  (pos, hpr, scale, shear));
1846 }
1847 
1848 /**
1849  * Completely replaces the transform with new translation, rotation, scale,
1850  * and shear components, relative to the other node.
1851  */
1852 void NodePath::
1854  const LVecBase3 &pos, const LQuaternion &quat,
1855  const LVecBase3 &scale, const LVecBase3 &shear) {
1856  nassertv_always(!is_empty());
1857  set_transform(other, TransformState::make_pos_quat_scale_shear
1858  (pos, quat, scale, shear));
1860 }
1861 
1862 /**
1863  * Returns the matrix that describes the coordinate space of the bottom node,
1864  * relative to the other path's bottom node's coordinate space.
1865  */
1866 LMatrix4 NodePath::
1867 get_mat(const NodePath &other) const {
1868  CPT(TransformState) transform = get_transform(other);
1869  // We can't safely return a reference to the matrix, because we can't assume
1870  // the transform won't go away when the function returns. If the transform
1871  // was partially modified by, say, a CompassEffect, it won't be stored in
1872  // the cache, and thus we might have the only reference to it.
1873  return transform->get_mat();
1874 }
1875 
1876 /**
1877  * Converts the indicated matrix from the other's coordinate space to the
1878  * local coordinate space, and applies it to the node.
1879  */
1880 void NodePath::
1881 set_mat(const NodePath &other, const LMatrix4 &mat) {
1882  nassertv_always(!is_empty());
1883  set_transform(other, TransformState::make_mat(mat));
1885 }
1886 
1887 /**
1888  * Given that the indicated point is in the coordinate system of the other
1889  * node, returns the same point in this node's coordinate system.
1890  */
1891 LPoint3 NodePath::
1892 get_relative_point(const NodePath &other, const LVecBase3 &point) const {
1893  CPT(TransformState) transform = other.get_transform(*this);
1894  LPoint3 rel_point = LPoint3(point) * transform->get_mat();
1895  return rel_point;
1896 }
1897 
1898 /**
1899  * Given that the indicated vector is in the coordinate system of the other
1900  * node, returns the same vector in this node's coordinate system.
1901  */
1902 LVector3 NodePath::
1903 get_relative_vector(const NodePath &other, const LVecBase3 &vec) const {
1904  CPT(TransformState) transform = other.get_transform(*this);
1905  LVector3 rel_vector = LVector3(vec) * transform->get_mat();
1906  return rel_vector;
1907 }
1908 
1909 /**
1910  * Sets the transform on this NodePath so that it rotates to face the
1911  * indicated point in space, which is relative to the other NodePath.
1912  */
1913 void NodePath::
1914 look_at(const NodePath &other, const LPoint3 &point, const LVector3 &up) {
1915  nassertv_always(!is_empty());
1916 
1917  CPT(TransformState) transform = other.get_transform(get_parent());
1918  LPoint3 rel_point = point * transform->get_mat();
1919 
1920  LPoint3 pos = get_pos();
1921 
1922  LQuaternion quat;
1923  ::look_at(quat, rel_point - pos, up);
1924  set_quat(quat);
1925 }
1926 
1927 /**
1928  * Behaves like look_at(), but with a strong preference to keeping the up
1929  * vector oriented in the indicated "up" direction.
1930  */
1931 void NodePath::
1932 heads_up(const NodePath &other, const LPoint3 &point, const LVector3 &up) {
1933  nassertv_always(!is_empty());
1934 
1935  CPT(TransformState) transform = other.get_transform(get_parent());
1936  LPoint3 rel_point = point * transform->get_mat();
1937 
1938  LPoint3 pos = get_pos();
1939 
1940  LQuaternion quat;
1941  ::heads_up(quat, rel_point - pos, up);
1942  set_quat(quat);
1943 }
1944 
1945 
1946 /**
1947  * Applies a scene-graph color to the referenced node. This color will apply
1948  * to all geometry at this level and below (that does not specify a new color
1949  * or a set_color_off()).
1950  */
1951 void NodePath::
1952 set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a,
1953  int priority) {
1954  set_color(LColor(r, g, b, a), priority);
1955 }
1956 
1957 /**
1958  * Applies a scene-graph color to the referenced node. This color will apply
1959  * to all geometry at this level and below (that does not specify a new color
1960  * or a set_color_off()).
1961  */
1962 void NodePath::
1963 set_color(const LColor &color, int priority) {
1964  nassertv_always(!is_empty());
1965  node()->set_attrib(ColorAttrib::make_flat(color), priority);
1966 }
1967 
1968 /**
1969  * Sets the geometry at this level and below to render using the geometry
1970  * color. This is normally the default, but it may be useful to use this to
1971  * contradict set_color() at a higher node level (or, with a priority, to
1972  * override a set_color() at a lower level).
1973  */
1974 void NodePath::
1975 set_color_off(int priority) {
1976  nassertv_always(!is_empty());
1977  node()->set_attrib(ColorAttrib::make_vertex(), priority);
1978 }
1979 
1980 /**
1981  * Completely removes any color adjustment from the node. This allows the
1982  * natural color of the geometry, or whatever color transitions might be
1983  * otherwise affecting the geometry, to show instead.
1984  */
1985 void NodePath::
1987  nassertv_always(!is_empty());
1988  node()->clear_attrib(ColorAttrib::get_class_slot());
1989 }
1990 
1991 /**
1992  * Returns true if a color has been applied to the given node, false
1993  * otherwise.
1994  */
1995 bool NodePath::
1996 has_color() const {
1997  nassertr_always(!is_empty(), false);
1998  return node()->has_attrib(ColorAttrib::get_class_slot());
1999 }
2000 
2001 /**
2002  * Returns the color that has been assigned to the node, or black if no color
2003  * has been assigned.
2004  */
2005 LColor NodePath::
2006 get_color() const {
2007  nassertr_always(!is_empty(), false);
2008  const RenderAttrib *attrib =
2009  node()->get_attrib(ColorAttrib::get_class_slot());
2010  if (attrib != nullptr) {
2011  const ColorAttrib *ca = DCAST(ColorAttrib, attrib);
2012  if (ca->get_color_type() == ColorAttrib::T_flat) {
2013  return ca->get_color();
2014  }
2015  }
2016 
2017  pgraph_cat.warning()
2018  << "get_color() called on " << *this << " which has no color set.\n";
2019 
2020  return LColor(1.0f, 1.0f, 1.0f, 1.0f);
2021 }
2022 
2023 /**
2024  * Returns true if a color scale has been applied to the referenced node,
2025  * false otherwise. It is still possible that color at this node might have
2026  * been scaled by an ancestor node.
2027  */
2028 bool NodePath::
2030  nassertr_always(!is_empty(), false);
2031  return node()->has_attrib(ColorScaleAttrib::get_class_slot());
2032 }
2033 
2034 /**
2035  * Completely removes any color scale from the referenced node. This is
2036  * preferable to simply setting the color scale to identity, as it also
2037  * removes the overhead associated with having a color scale at all.
2038  */
2039 void NodePath::
2041  nassertv_always(!is_empty());
2042  node()->clear_attrib(ColorScaleAttrib::get_class_slot());
2043 }
2044 
2045 /**
2046  * multiplies the color scale component of the transform, with previous color
2047  * scale leaving translation and rotation untouched.
2048  */
2049 void NodePath::
2050 compose_color_scale(const LVecBase4 &scale, int priority) {
2051  nassertv_always(!is_empty());
2052 
2053  const RenderAttrib *attrib =
2054  node()->get_attrib(ColorScaleAttrib::get_class_slot());
2055  if (attrib != nullptr) {
2056  priority = max(priority,
2057  node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
2058  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
2059 
2060  // Modify the existing ColorScaleAttrib by multiplying with the indicated
2061  // colorScale.
2062  LVecBase4 prev_color_scale = csa->get_scale();
2063  LVecBase4 new_color_scale(prev_color_scale[0]*scale[0],
2064  prev_color_scale[1]*scale[1],
2065  prev_color_scale[2]*scale[2],
2066  prev_color_scale[3]*scale[3]);
2067  node()->set_attrib(csa->set_scale(new_color_scale), priority);
2068 
2069  } else {
2070  // Create a new ColorScaleAttrib for this node.
2071  node()->set_attrib(ColorScaleAttrib::make(scale), priority);
2072  }
2073 }
2074 
2075 /**
2076  * Sets the color scale component of the transform, leaving translation and
2077  * rotation untouched.
2078  */
2079 void NodePath::
2080 set_color_scale(const LVecBase4 &scale, int priority) {
2081  nassertv_always(!is_empty());
2082 
2083  const RenderAttrib *attrib =
2084  node()->get_attrib(ColorScaleAttrib::get_class_slot());
2085  if (attrib != nullptr) {
2086  priority = max(priority,
2087  node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
2088  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
2089 
2090  // Modify the existing ColorScaleAttrib to add the indicated colorScale.
2091  node()->set_attrib(csa->set_scale(scale), priority);
2092 
2093  } else {
2094  // Create a new ColorScaleAttrib for this node.
2095  node()->set_attrib(ColorScaleAttrib::make(scale), priority);
2096  }
2097 }
2098 
2099 /**
2100  * Disables any color scale attribute inherited from above. This is not the
2101  * same thing as clear_color_scale(), which undoes any previous
2102  * set_color_scale() operation on this node; rather, this actively disables
2103  * any set_color_scale() that might be inherited from a parent node. This
2104  * also disables set_alpha_scale() at the same time.
2105  *
2106  * It is legal to specify a new color scale on the same node with a subsequent
2107  * call to set_color_scale() or set_alpha_scale(); this new scale will apply
2108  * to lower geometry.
2109  */
2110 void NodePath::
2111 set_color_scale_off(int priority) {
2112  nassertv_always(!is_empty());
2113  node()->set_attrib(ColorScaleAttrib::make_off(), priority);
2114 }
2115 
2116 /**
2117  * Sets the alpha scale component of the transform without (much) affecting
2118  * the color scale. Note that any priority specified will also apply to the
2119  * color scale.
2120  */
2121 void NodePath::
2122 set_alpha_scale(PN_stdfloat scale, int priority) {
2123  nassertv_always(!is_empty());
2124 
2125  const RenderAttrib *attrib =
2126  node()->get_attrib(ColorScaleAttrib::get_class_slot());
2127  if (attrib != nullptr) {
2128  priority = max(priority,
2129  node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
2130  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
2131 
2132  // Modify the existing ColorScaleAttrib to add the indicated colorScale.
2133  const LVecBase4 &sc = csa->get_scale();
2134  node()->set_attrib(csa->set_scale(LVecBase4(sc[0], sc[1], sc[2], scale)), priority);
2135 
2136  } else {
2137  // Create a new ColorScaleAttrib for this node.
2138  node()->set_attrib(ColorScaleAttrib::make(LVecBase4(1.0f, 1.0f, 1.0f, scale)), priority);
2139  }
2140 }
2141 
2142 /**
2143  * Scales all the color components of the object by the same amount, darkening
2144  * the object, without (much) affecting alpha. Note that any priority
2145  * specified will also apply to the alpha scale.
2146  */
2147 void NodePath::
2148 set_all_color_scale(PN_stdfloat scale, int priority) {
2149  nassertv_always(!is_empty());
2150 
2151  const RenderAttrib *attrib =
2152  node()->get_attrib(ColorScaleAttrib::get_class_slot());
2153  if (attrib != nullptr) {
2154  priority = max(priority,
2155  node()->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
2156  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
2157 
2158  // Modify the existing ColorScaleAttrib to add the indicated colorScale.
2159  const LVecBase4 &sc = csa->get_scale();
2160  node()->set_attrib(csa->set_scale(LVecBase4(scale, scale, scale, sc[3])), priority);
2161 
2162  } else {
2163  // Create a new ColorScaleAttrib for this node.
2164  node()->set_attrib(ColorScaleAttrib::make(LVecBase4(scale, scale, scale, 1.0f)), priority);
2165  }
2166 }
2167 
2168 /**
2169  * Returns the complete color scale vector that has been applied to this node
2170  * via a previous call to set_color_scale() and/or set_alpha_scale(), or all
2171  * 1's (identity) if no scale has been applied to this particular node.
2172  */
2173 const LVecBase4 &NodePath::
2175  static const LVecBase4 ident_scale(1.0f, 1.0f, 1.0f, 1.0f);
2176  nassertr_always(!is_empty(), ident_scale);
2177  const RenderAttrib *attrib =
2178  node()->get_attrib(ColorScaleAttrib::get_class_slot());
2179  if (attrib != nullptr) {
2180  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
2181  return csa->get_scale();
2182  }
2183 
2184  return ident_scale;
2185 }
2186 
2187 /**
2188  * Adds the indicated Light or PolylightNode to the list of lights that
2189  * illuminate geometry at this node and below. The light itself should be
2190  * parented into the scene graph elsewhere, to represent the light's position
2191  * in space; but until set_light() is called it will illuminate no geometry.
2192  */
2193 void NodePath::
2194 set_light(const NodePath &light, int priority) {
2195  nassertv_always(!is_empty());
2196  if (!light.is_empty()) {
2197  Light *light_obj = light.node()->as_light();
2198  if (light_obj != nullptr) {
2199  // It's an actual Light object.
2200  const RenderAttrib *attrib =
2201  node()->get_attrib(LightAttrib::get_class_slot());
2202  if (attrib != nullptr) {
2203  priority = max(priority,
2204  node()->get_state()->get_override(LightAttrib::get_class_slot()));
2205  const LightAttrib *la = DCAST(LightAttrib, attrib);
2206 
2207  // Modify the existing LightAttrib to add the indicated light.
2208  node()->set_attrib(la->add_on_light(light), priority);
2209 
2210  } else {
2211  // Create a new LightAttrib for this node.
2212  CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make());
2213  node()->set_attrib(la->add_on_light(light), priority);
2214  }
2215  return;
2216 
2217  } else if (light.node()->is_of_type(PolylightNode::get_class_type())) {
2218  // It's a Polylight object.
2219  if (priority != 0) {
2220  // PolylightEffects can't have a priority, since they're just an
2221  // effect to be applied immediately.
2222  pgraph_cat.warning()
2223  << "Ignoring priority on set_light(" << light << ")\n";
2224  }
2225 
2226  const RenderEffect *effect =
2227  node()->get_effect(PolylightEffect::get_class_type());
2228  if (effect != nullptr) {
2229  const PolylightEffect *ple = DCAST(PolylightEffect, effect);
2230 
2231  // Modify the existing PolylightEffect to add the indicated light.
2232  node()->set_effect(ple->add_light(light));
2233 
2234  } else {
2235  // Create a new PolylightEffect for this node.
2236  CPT(PolylightEffect) ple = DCAST(PolylightEffect, PolylightEffect::make());
2237  node()->set_effect(ple->add_light(light));
2238  }
2239  return;
2240  }
2241  }
2242  nassert_raise("Not a Light object.");
2243 }
2244 
2245 /**
2246  * Sets the geometry at this level and below to render using no lights at all.
2247  * This is different from not specifying a light; rather, this specifically
2248  * contradicts set_light() at a higher node level (or, with a priority,
2249  * overrides a set_light() at a lower level).
2250  *
2251  * If no lights are in effect on a particular piece of geometry, that geometry
2252  * is rendered with lighting disabled.
2253  */
2254 void NodePath::
2255 set_light_off(int priority) {
2256  nassertv_always(!is_empty());
2257  node()->set_attrib(LightAttrib::make_all_off(), priority);
2258  node()->clear_effect(PolylightEffect::get_class_type());
2259 }
2260 
2261 /**
2262  * Sets the geometry at this level and below to render without using the
2263  * indicated Light. This is different from not specifying the Light; rather,
2264  * this specifically contradicts set_light() at a higher node level (or, with
2265  * a priority, overrides a set_light() at a lower level).
2266  *
2267  * This interface does not support PolylightNodes, which cannot be turned off
2268  * at a lower level.
2269  */
2270 void NodePath::
2271 set_light_off(const NodePath &light, int priority) {
2272  nassertv_always(!is_empty());
2273 
2274  if (!light.is_empty()) {
2275  Light *light_obj = light.node()->as_light();
2276  if (light_obj != nullptr) {
2277  const RenderAttrib *attrib =
2278  node()->get_attrib(LightAttrib::get_class_slot());
2279  if (attrib != nullptr) {
2280  priority = max(priority,
2281  node()->get_state()->get_override(LightAttrib::get_class_slot()));
2282  const LightAttrib *la = DCAST(LightAttrib, attrib);
2283 
2284  // Modify the existing LightAttrib to add the indicated light to the
2285  // "off" list. This also, incidentally, removes it from the "on" list
2286  // if it is there.
2287  node()->set_attrib(la->add_off_light(light), priority);
2288 
2289  } else {
2290  // Create a new LightAttrib for this node that turns off the indicated
2291  // light.
2292  CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make());
2293  node()->set_attrib(la->add_off_light(light), priority);
2294  }
2295  return;
2296  }
2297  }
2298  nassert_raise("Not a Light object.");
2299 }
2300 
2301 /**
2302  * Completely removes any lighting operations that may have been set via
2303  * set_light() or set_light_off() from this particular node.
2304  */
2305 void NodePath::
2307  nassertv_always(!is_empty());
2308  node()->clear_attrib(LightAttrib::get_class_slot());
2309  node()->clear_effect(PolylightEffect::get_class_type());
2310 }
2311 
2312 /**
2313  * Removes any reference to the indicated Light or PolylightNode from the
2314  * NodePath.
2315  */
2316 void NodePath::
2317 clear_light(const NodePath &light) {
2318  nassertv_always(!is_empty());
2319 
2320  if (!light.is_empty()) {
2321  Light *light_obj = light.node()->as_light();
2322  if (light_obj != nullptr) {
2323  const RenderAttrib *attrib =
2324  node()->get_attrib(LightAttrib::get_class_slot());
2325  if (attrib != nullptr) {
2326  CPT(LightAttrib) la = DCAST(LightAttrib, attrib);
2327  la = DCAST(LightAttrib, la->remove_on_light(light));
2328  la = DCAST(LightAttrib, la->remove_off_light(light));
2329 
2330  if (la->is_identity()) {
2331  node()->clear_attrib(LightAttrib::get_class_slot());
2332 
2333  } else {
2334  int priority = node()->get_state()->get_override(LightAttrib::get_class_slot());
2335  node()->set_attrib(la, priority);
2336  }
2337  }
2338  return;
2339 
2340  } else if (light.node()->is_of_type(PolylightNode::get_class_type())) {
2341  const RenderEffect *effect =
2342  node()->get_effect(PolylightEffect::get_class_type());
2343  if (effect != nullptr) {
2344  CPT(PolylightEffect) ple = DCAST(PolylightEffect, effect);
2345  ple = DCAST(PolylightEffect, ple->remove_light(light));
2346  node()->set_effect(ple);
2347  }
2348  return;
2349  }
2350  }
2351  nassert_raise("Not a Light object.");
2352 }
2353 
2354 /**
2355  * Returns true if the indicated Light or PolylightNode has been specifically
2356  * enabled on this particular node. This means that someone called
2357  * set_light() on this node with the indicated light.
2358  */
2359 bool NodePath::
2360 has_light(const NodePath &light) const {
2361  nassertr_always(!is_empty(), false);
2362 
2363  if (!light.is_empty()) {
2364  Light *light_obj = light.node()->as_light();
2365  if (light_obj != nullptr) {
2366  const RenderAttrib *attrib =
2367  node()->get_attrib(LightAttrib::get_class_slot());
2368  if (attrib != nullptr) {
2369  const LightAttrib *la = DCAST(LightAttrib, attrib);
2370  return la->has_on_light(light);
2371  }
2372  return false;
2373 
2374  } else if (light.node()->is_of_type(PolylightNode::get_class_type())) {
2375  const RenderEffect *effect =
2376  node()->get_effect(PolylightEffect::get_class_type());
2377  if (effect != nullptr) {
2378  const PolylightEffect *ple = DCAST(PolylightEffect, effect);
2379  return ple->has_light(light);
2380  }
2381  return false;
2382  }
2383  }
2384  nassert_raise("Not a Light object.");
2385  return false;
2386 }
2387 
2388 /**
2389  * Returns true if all Lights have been specifically disabled on this
2390  * particular node. This means that someone called set_light_off() on this
2391  * node with no parameters.
2392  */
2393 bool NodePath::
2394 has_light_off() const {
2395  nassertr_always(!is_empty(), false);
2396 
2397  const RenderAttrib *attrib =
2398  node()->get_attrib(LightAttrib::get_class_slot());
2399  if (attrib != nullptr) {
2400  const LightAttrib *la = DCAST(LightAttrib, attrib);
2401  return la->has_all_off();
2402  }
2403 
2404  return false;
2405 }
2406 
2407 /**
2408  * Returns true if the indicated Light has been specifically disabled on this
2409  * particular node. This means that someone called set_light_off() on this
2410  * node with the indicated light.
2411  *
2412  * This interface does not support PolylightNodes, which cannot be turned off
2413  * at a lower level.
2414  */
2415 bool NodePath::
2416 has_light_off(const NodePath &light) const {
2417  nassertr_always(!is_empty(), false);
2418  if (!light.is_empty()) {
2419  Light *light_obj = light.node()->as_light();
2420  if (light_obj != nullptr) {
2421  const RenderAttrib *attrib =
2422  node()->get_attrib(LightAttrib::get_class_slot());
2423  if (attrib != nullptr) {
2424  const LightAttrib *la = DCAST(LightAttrib, attrib);
2425  return la->has_off_light(light);
2426  }
2427  }
2428  }
2429  nassert_raise("Not a Light object.");
2430  return false;
2431 }
2432 
2433 /**
2434  * Adds the indicated clipping plane to the list of planes that apply to
2435  * geometry at this node and below. The clipping plane itself, a PlaneNode,
2436  * should be parented into the scene graph elsewhere, to represent the plane's
2437  * position in space; but until set_clip_plane() is called it will clip no
2438  * geometry.
2439  */
2440 void NodePath::
2441 set_clip_plane(const NodePath &clip_plane, int priority) {
2442  nassertv_always(!is_empty());
2443  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
2444  const RenderAttrib *attrib =
2445  node()->get_attrib(ClipPlaneAttrib::get_class_slot());
2446  if (attrib != nullptr) {
2447  priority = max(priority,
2448  node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot()));
2449  const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
2450 
2451  // Modify the existing ClipPlaneAttrib to add the indicated clip_plane.
2452  node()->set_attrib(la->add_on_plane(clip_plane), priority);
2453 
2454  } else {
2455  // Create a new ClipPlaneAttrib for this node.
2456  CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make());
2457  node()->set_attrib(la->add_on_plane(clip_plane), priority);
2458  }
2459  return;
2460  }
2461  nassert_raise("Not a PlaneNode object.");
2462 }
2463 
2464 /**
2465  * Sets the geometry at this level and below to render using no clip_planes at
2466  * all. This is different from not specifying a clip_plane; rather, this
2467  * specifically contradicts set_clip_plane() at a higher node level (or, with
2468  * a priority, overrides a set_clip_plane() at a lower level).
2469  *
2470  * If no clip_planes are in effect on a particular piece of geometry, that
2471  * geometry is rendered without being clipped (other than by the viewing
2472  * frustum).
2473  */
2474 void NodePath::
2475 set_clip_plane_off(int priority) {
2476  nassertv_always(!is_empty());
2477  node()->set_attrib(ClipPlaneAttrib::make_all_off(), priority);
2478 }
2479 
2480 /**
2481  * Sets the geometry at this level and below to render without being clipped
2482  * by the indicated PlaneNode. This is different from not specifying the
2483  * PlaneNode; rather, this specifically contradicts set_clip_plane() at a
2484  * higher node level (or, with a priority, overrides a set_clip_plane() at a
2485  * lower level).
2486  */
2487 void NodePath::
2488 set_clip_plane_off(const NodePath &clip_plane, int priority) {
2489  nassertv_always(!is_empty());
2490 
2491  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
2492  const RenderAttrib *attrib =
2493  node()->get_attrib(ClipPlaneAttrib::get_class_slot());
2494  if (attrib != nullptr) {
2495  priority = max(priority,
2496  node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot()));
2497  const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
2498 
2499  // Modify the existing ClipPlaneAttrib to add the indicated clip_plane
2500  // to the "off" list. This also, incidentally, removes it from the "on"
2501  // list if it is there.
2502  node()->set_attrib(la->add_off_plane(clip_plane), priority);
2503 
2504  } else {
2505  // Create a new ClipPlaneAttrib for this node that turns off the
2506  // indicated clip_plane.
2507  CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make());
2508  node()->set_attrib(la->add_off_plane(clip_plane), priority);
2509  }
2510  return;
2511  }
2512  nassert_raise("Not a PlaneNode object.");
2513 }
2514 
2515 /**
2516  * Completely removes any clip planes that may have been set via
2517  * set_clip_plane() or set_clip_plane_off() from this particular node.
2518  */
2519 void NodePath::
2521  nassertv_always(!is_empty());
2522  node()->clear_attrib(ClipPlaneAttrib::get_class_slot());
2523 }
2524 
2525 /**
2526  * Removes any reference to the indicated clipping plane from the NodePath.
2527  */
2528 void NodePath::
2529 clear_clip_plane(const NodePath &clip_plane) {
2530  nassertv_always(!is_empty());
2531 
2532  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
2533  const RenderAttrib *attrib =
2534  node()->get_attrib(ClipPlaneAttrib::get_class_slot());
2535  if (attrib != nullptr) {
2536  CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, attrib);
2537  la = DCAST(ClipPlaneAttrib, la->remove_on_plane(clip_plane));
2538  la = DCAST(ClipPlaneAttrib, la->remove_off_plane(clip_plane));
2539 
2540  if (la->is_identity()) {
2541  node()->clear_attrib(ClipPlaneAttrib::get_class_slot());
2542 
2543  } else {
2544  int priority = node()->get_state()->get_override(ClipPlaneAttrib::get_class_slot());
2545  node()->set_attrib(la, priority);
2546  }
2547  }
2548  return;
2549  }
2550  nassert_raise("Not a PlaneNode object.");
2551 }
2552 
2553 /**
2554  * Returns true if the indicated clipping plane has been specifically applied
2555  * to this particular node. This means that someone called set_clip_plane()
2556  * on this node with the indicated clip_plane.
2557  */
2558 bool NodePath::
2559 has_clip_plane(const NodePath &clip_plane) const {
2560  nassertr_always(!is_empty(), false);
2561 
2562  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
2563  const RenderAttrib *attrib =
2564  node()->get_attrib(ClipPlaneAttrib::get_class_slot());
2565  if (attrib != nullptr) {
2566  const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
2567  return la->has_on_plane(clip_plane);
2568  }
2569  return false;
2570  }
2571  nassert_raise("Not a PlaneNode object.");
2572  return false;
2573 }
2574 
2575 /**
2576  * Returns true if all clipping planes have been specifically disabled on this
2577  * particular node. This means that someone called set_clip_plane_off() on
2578  * this node with no parameters.
2579  */
2580 bool NodePath::
2582  nassertr_always(!is_empty(), false);
2583 
2584  const RenderAttrib *attrib =
2585  node()->get_attrib(ClipPlaneAttrib::get_class_slot());
2586  if (attrib != nullptr) {
2587  const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
2588  return la->has_all_off();
2589  }
2590 
2591  return false;
2592 }
2593 
2594 /**
2595  * Returns true if the indicated clipping plane has been specifically disabled
2596  * on this particular node. This means that someone called
2597  * set_clip_plane_off() on this node with the indicated clip_plane.
2598  */
2599 bool NodePath::
2600 has_clip_plane_off(const NodePath &clip_plane) const {
2601  nassertr_always(!is_empty(), false);
2602  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
2603  const RenderAttrib *attrib =
2604  node()->get_attrib(ClipPlaneAttrib::get_class_slot());
2605  if (attrib != nullptr) {
2606  const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
2607  return la->has_off_plane(clip_plane);
2608  }
2609  }
2610  nassert_raise("Not a PlaneNode object.");
2611  return false;
2612 }
2613 
2614 /**
2615  * Adds the indicated occluder to the list of occluders that apply to geometry
2616  * at this node and below. The occluder itself, an OccluderNode, should be
2617  * parented into the scene graph elsewhere, to represent the occluder's
2618  * position in space; but until set_occluder() is called it will clip no
2619  * geometry.
2620  */
2621 void NodePath::
2622 set_occluder(const NodePath &occluder) {
2623  nassertv_always(!is_empty());
2624  if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) {
2625  const RenderEffect *effect =
2626  node()->get_effect(OccluderEffect::get_class_type());
2627  if (effect != nullptr) {
2628  const OccluderEffect *la = DCAST(OccluderEffect, effect);
2629 
2630  // Modify the existing OccluderEffect to add the indicated occluder.
2631  node()->set_effect(la->add_on_occluder(occluder));
2632 
2633  } else {
2634  // Create a new OccluderEffect for this node.
2635  CPT(OccluderEffect) la = DCAST(OccluderEffect, OccluderEffect::make());
2636  node()->set_effect(la->add_on_occluder(occluder));
2637  }
2638  return;
2639  }
2640  nassert_raise("Not an OccluderNode object.");
2641 }
2642 
2643 /**
2644  * Completely removes any occluders that may have been set via set_occluder()
2645  * from this particular node.
2646  */
2647 void NodePath::
2649  nassertv_always(!is_empty());
2650  node()->clear_effect(OccluderEffect::get_class_type());
2651 }
2652 
2653 /**
2654  * Removes any reference to the indicated occluder from the NodePath.
2655  */
2656 void NodePath::
2657 clear_occluder(const NodePath &occluder) {
2658  nassertv_always(!is_empty());
2659 
2660  if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) {
2661  const RenderEffect *effect =
2662  node()->get_effect(OccluderEffect::get_class_type());
2663  if (effect != nullptr) {
2664  CPT(OccluderEffect) la = DCAST(OccluderEffect, effect);
2665  la = DCAST(OccluderEffect, la->remove_on_occluder(occluder));
2666 
2667  if (la->is_identity()) {
2668  node()->clear_effect(OccluderEffect::get_class_type());
2669 
2670  } else {
2671  node()->set_effect(la);
2672  }
2673  }
2674  return;
2675  }
2676  nassert_raise("Not an OccluderNode object.");
2677 }
2678 
2679 /**
2680  * Returns true if the indicated occluder has been specifically applied to
2681  * this particular node. This means that someone called set_occluder() on
2682  * this node with the indicated occluder.
2683  */
2684 bool NodePath::
2685 has_occluder(const NodePath &occluder) const {
2686  nassertr_always(!is_empty(), false);
2687 
2688  if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) {
2689  const RenderEffect *effect =
2690  node()->get_effect(OccluderEffect::get_class_type());
2691  if (effect != nullptr) {
2692  const OccluderEffect *la = DCAST(OccluderEffect, effect);
2693  return la->has_on_occluder(occluder);
2694  }
2695  return false;
2696  }
2697  nassert_raise("Not an OccluderNode object.");
2698  return false;
2699 }
2700 
2701 /**
2702  * Sets up a scissor region on the nodes rendered at this level and below.
2703  * The four coordinates are understood to define a rectangle in screen space.
2704  * These numbers are relative to the current DisplayRegion, where (0,0) is the
2705  * lower-left corner of the DisplayRegion, and (1,1) is the upper-right
2706  * corner.
2707  */
2708 void NodePath::
2709 set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) {
2710  set_effect(ScissorEffect::make_screen(LVecBase4(left, right, bottom, top)));
2711 }
2712 
2713 /**
2714  * Sets up a scissor region on the nodes rendered at this level and below.
2715  * The two points are understood to be relative to this node. When these
2716  * points are projected into screen space, they define the diagonally-opposite
2717  * points that determine the scissor region.
2718  */
2719 void NodePath::
2720 set_scissor(const LPoint3 &a, const LPoint3 &b) {
2721  set_effect(ScissorEffect::make_node(a, b));
2722 }
2723 
2724 /**
2725  * Sets up a scissor region on the nodes rendered at this level and below.
2726  * The four points are understood to be relative to this node. When these
2727  * points are projected into screen space, they define the bounding volume of
2728  * the scissor region (the scissor region is the smallest onscreen rectangle
2729  * that encloses all four points).
2730  */
2731 void NodePath::
2732 set_scissor(const LPoint3 &a, const LPoint3 &b,
2733  const LPoint3 &c, const LPoint3 &d) {
2734  set_effect(ScissorEffect::make_node(a, b, c, d));
2735 }
2736 
2737 /**
2738  * Sets up a scissor region on the nodes rendered at this level and below.
2739  * The two points are understood to be relative to the indicated other node.
2740  * When these points are projected into screen space, they define the
2741  * diagonally-opposite points that determine the scissor region.
2742  */
2743 void NodePath::
2744 set_scissor(const NodePath &other, const LPoint3 &a, const LPoint3 &b) {
2745  set_effect(ScissorEffect::make_node(a, b, other));
2746 }
2747 
2748 /**
2749  * Sets up a scissor region on the nodes rendered at this level and below.
2750  * The four points are understood to be relative to the indicated other node.
2751  * When these points are projected into screen space, they define the bounding
2752  * volume of the scissor region (the scissor region is the smallest onscreen
2753  * rectangle that encloses all four points).
2754  */
2755 void NodePath::
2756 set_scissor(const NodePath &other,
2757  const LPoint3 &a, const LPoint3 &b,
2758  const LPoint3 &c, const LPoint3 &d) {
2759  set_effect(ScissorEffect::make_node(a, b, c, d, other));
2760 }
2761 
2762 /**
2763  * Removes the scissor region that was defined at this node level by a
2764  * previous call to set_scissor().
2765  */
2766 void NodePath::
2768  clear_effect(ScissorEffect::get_class_type());
2769 }
2770 
2771 /**
2772  * Returns true if a scissor region was defined at this node by a previous
2773  * call to set_scissor(). This does not check for scissor regions inherited
2774  * from a parent class. It also does not check for the presence of a low-
2775  * level ScissorAttrib, which is different from the ScissorEffect added by
2776  * set_scissor.
2777  */
2778 bool NodePath::
2779 has_scissor() const {
2780  return has_effect(ScissorEffect::get_class_type());
2781 }
2782 
2783 /**
2784  * Assigns the geometry at this level and below to the named rendering bin.
2785  * It is the user's responsibility to ensure that such a bin already exists,
2786  * either via the cull-bin Configrc variable, or by explicitly creating a
2787  * GeomBin of the appropriate type at runtime.
2788  *
2789  * There are two default bins created when Panda is started: "default" and
2790  * "fixed". Normally, all geometry is assigned to "default" unless specified
2791  * otherwise. This bin renders opaque geometry in state-sorted order,
2792  * followed by transparent geometry sorted back-to-front. If any geometry is
2793  * assigned to "fixed", this will be rendered following all the geometry in
2794  * "default", in the order specified by draw_order for each piece of geometry
2795  * so assigned.
2796  *
2797  * The draw_order parameter is meaningful only for GeomBinFixed type bins,
2798  * e.g. "fixed". Other kinds of bins ignore it.
2799  */
2800 void NodePath::
2801 set_bin(const string &bin_name, int draw_order, int priority) {
2802  nassertv_always(!is_empty());
2803  node()->set_attrib(CullBinAttrib::make(bin_name, draw_order), priority);
2804 }
2805 
2806 /**
2807  * Completely removes any bin adjustment that may have been set via set_bin()
2808  * from this particular node.
2809  */
2810 void NodePath::
2812  nassertv_always(!is_empty());
2813  node()->clear_attrib(CullBinAttrib::get_class_slot());
2814 }
2815 
2816 /**
2817  * Returns true if the node has been assigned to the a particular rendering
2818  * bin via set_bin(), false otherwise.
2819  */
2820 bool NodePath::
2821 has_bin() const {
2822  nassertr_always(!is_empty(), false);
2823  return node()->has_attrib(CullBinAttrib::get_class_slot());
2824 }
2825 
2826 /**
2827  * Returns the name of the bin that this particular node was assigned to via
2828  * set_bin(), or the empty string if no bin was assigned. See set_bin() and
2829  * has_bin().
2830  */
2831 string NodePath::
2832 get_bin_name() const {
2833  nassertr_always(!is_empty(), string());
2834  const RenderAttrib *attrib =
2835  node()->get_attrib(CullBinAttrib::get_class_slot());
2836  if (attrib != nullptr) {
2837  const CullBinAttrib *ba = DCAST(CullBinAttrib, attrib);
2838  return ba->get_bin_name();
2839  }
2840 
2841  return string();
2842 }
2843 
2844 /**
2845  * Returns the drawing order associated with the bin that this particular node
2846  * was assigned to via set_bin(), or 0 if no bin was assigned. See set_bin()
2847  * and has_bin().
2848  */
2849 int NodePath::
2851  nassertr_always(!is_empty(), false);
2852  const RenderAttrib *attrib =
2853  node()->get_attrib(CullBinAttrib::get_class_slot());
2854  if (attrib != nullptr) {
2855  const CullBinAttrib *ba = DCAST(CullBinAttrib, attrib);
2856  return ba->get_draw_order();
2857  }
2858 
2859  return 0;
2860 }
2861 
2862 /**
2863  * Adds the indicated texture to the list of textures that will be rendered on
2864  * the default texture stage.
2865  *
2866  * This is the convenience single-texture variant of this method; it is now
2867  * superceded by set_texture() that accepts a stage and texture. You may use
2868  * this method if you just want to adjust the default stage.
2869  */
2870 void NodePath::
2871 set_texture(Texture *tex, int priority) {
2872  nassertv_always(!is_empty());
2873  PT(TextureStage) stage = TextureStage::get_default();
2874  set_texture(stage, tex, priority);
2875 }
2876 
2877 /**
2878  * Adds the indicated texture to the list of textures that will be rendered on
2879  * the indicated multitexture stage. If there are multiple texture stages
2880  * specified (possibly on multiple different nodes at different levels), they
2881  * will all be applied to geometry together, according to the stage
2882  * specification set up in the TextureStage object.
2883  */
2884 void NodePath::
2885 set_texture(TextureStage *stage, Texture *tex, int priority) {
2886  nassertv_always(!is_empty());
2887 
2888  const RenderAttrib *attrib =
2889  node()->get_attrib(TextureAttrib::get_class_slot());
2890  if (attrib != nullptr) {
2891  const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
2892  int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
2893 
2894  // Modify the existing TextureAttrib to add the indicated texture.
2895  node()->set_attrib(tsa->add_on_stage(stage, tex, priority), sg_priority);
2896 
2897  } else {
2898  // Create a new TextureAttrib for this node.
2899  CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
2900  node()->set_attrib(tsa->add_on_stage(stage, tex, priority));
2901  }
2902 }
2903 
2904 /**
2905  * Adds the indicated texture to the list of textures that will be rendered on
2906  * the default texture stage.
2907  *
2908  * The given sampler state will override the sampling settings on the texture
2909  * itself. Note that this method makes a copy of the sampler settings that
2910  * you give; further changes to this object will not be reflected.
2911  *
2912  * This is the convenience single-texture variant of this method; it is now
2913  * superceded by set_texture() that accepts a stage and texture. You may use
2914  * this method if you just want to adjust the default stage.
2915  */
2916 void NodePath::
2917 set_texture(Texture *tex, const SamplerState &sampler, int priority) {
2918  nassertv_always(!is_empty());
2919  PT(TextureStage) stage = TextureStage::get_default();
2920  set_texture(stage, tex, sampler, priority);
2921 }
2922 
2923 /**
2924  * Adds the indicated texture to the list of textures that will be rendered on
2925  * the indicated multitexture stage. If there are multiple texture stages
2926  * specified (possibly on multiple different nodes at different levels), they
2927  * will all be applied to geometry together, according to the stage
2928  * specification set up in the TextureStage object.
2929  *
2930  * The given sampler state will override the sampling settings on the texture
2931  * itself. Note that this method makes a copy of the sampler settings that
2932  * you give; further changes to this object will not be reflected.
2933  */
2934 void NodePath::
2935 set_texture(TextureStage *stage, Texture *tex, const SamplerState &sampler, int priority) {
2936  nassertv_always(!is_empty());
2937 
2938  const RenderAttrib *attrib =
2939  node()->get_attrib(TextureAttrib::get_class_slot());
2940  if (attrib != nullptr) {
2941  const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
2942  int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
2943 
2944  // Modify the existing TextureAttrib to add the indicated texture.
2945  node()->set_attrib(tsa->add_on_stage(stage, tex, sampler, priority), sg_priority);
2946 
2947  } else {
2948  // Create a new TextureAttrib for this node.
2949  CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
2950  node()->set_attrib(tsa->add_on_stage(stage, tex, sampler, priority));
2951  }
2952 }
2953 
2954 /**
2955  * Sets the geometry at this level and below to render using no texture, on
2956  * any stage. This is different from not specifying a texture; rather, this
2957  * specifically contradicts set_texture() at a higher node level (or, with a
2958  * priority, overrides a set_texture() at a lower level).
2959  */
2960 void NodePath::
2961 set_texture_off(int priority) {
2962  nassertv_always(!is_empty());
2963  node()->set_attrib(TextureAttrib::make_all_off(), priority);
2964 }
2965 
2966 /**
2967  * Sets the geometry at this level and below to render using no texture, on
2968  * the indicated stage. This is different from not specifying a texture;
2969  * rather, this specifically contradicts set_texture() at a higher node level
2970  * (or, with a priority, overrides a set_texture() at a lower level).
2971  */
2972 void NodePath::
2973 set_texture_off(TextureStage *stage, int priority) {
2974  nassertv_always(!is_empty());
2975 
2976  const RenderAttrib *attrib =
2977  node()->get_attrib(TextureAttrib::get_class_slot());
2978  if (attrib != nullptr) {
2979  const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
2980  int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
2981 
2982  // Modify the existing TextureAttrib to add the indicated texture to the
2983  // "off" list. This also, incidentally, removes it from the "on" list if
2984  // it is there.
2985  node()->set_attrib(tsa->add_off_stage(stage, priority), sg_priority);
2986 
2987  } else {
2988  // Create a new TextureAttrib for this node that turns off the indicated
2989  // stage.
2990  CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
2991  node()->set_attrib(tsa->add_off_stage(stage, priority));
2992  }
2993 }
2994 
2995 /**
2996  * Completely removes any texture adjustment that may have been set via
2997  * set_texture() or set_texture_off() from this particular node. This allows
2998  * whatever textures might be otherwise affecting the geometry to show
2999  * instead.
3000  */
3001 void NodePath::
3003  nassertv_always(!is_empty());
3004  node()->clear_attrib(TextureAttrib::get_class_slot());
3005 }
3006 
3007 /**
3008  * Removes any reference to the indicated texture stage from the NodePath.
3009  */
3010 void NodePath::
3012  nassertv_always(!is_empty());
3013 
3014  const RenderAttrib *attrib =
3015  node()->get_attrib(TextureAttrib::get_class_slot());
3016  if (attrib != nullptr) {
3017  CPT(TextureAttrib) tsa = DCAST(TextureAttrib, attrib);
3018  tsa = DCAST(TextureAttrib, tsa->remove_on_stage(stage));
3019  tsa = DCAST(TextureAttrib, tsa->remove_off_stage(stage));
3020 
3021  if (tsa->is_identity()) {
3022  node()->clear_attrib(TextureAttrib::get_class_slot());
3023 
3024  } else {
3025  int priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
3026  node()->set_attrib(tsa, priority);
3027  }
3028  }
3029 }
3030 
3031 /**
3032  * Returns true if a texture has been applied to this particular node via
3033  * set_texture(), false otherwise. This is not the same thing as asking
3034  * whether the geometry at this node will be rendered with texturing, as there
3035  * may be a texture in effect from a higher or lower level.
3036  */
3037 bool NodePath::
3038 has_texture() const {
3039  return get_texture() != nullptr;
3040 }
3041 
3042 /**
3043  * Returns true if texturing has been specifically enabled on this particular
3044  * node for the indicated stage. This means that someone called set_texture()
3045  * on this node with the indicated stage name, or the stage_name is the
3046  * default stage_name, and someone called set_texture() on this node.
3047  */
3048 bool NodePath::
3049 has_texture(TextureStage *stage) const {
3050  nassertr_always(!is_empty(), false);
3051 
3052  const RenderAttrib *attrib =
3053  node()->get_attrib(TextureAttrib::get_class_slot());
3054  if (attrib != nullptr) {
3055  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
3056  return ta->has_on_stage(stage);
3057  }
3058 
3059  return false;
3060 }
3061 
3062 /**
3063  * Returns true if texturing has been specifically disabled on this particular
3064  * node via set_texture_off(), false otherwise. This is not the same thing as
3065  * asking whether the geometry at this node will be rendered untextured, as
3066  * there may be a texture in effect from a higher or lower level.
3067  */
3068 bool NodePath::
3070  nassertr_always(!is_empty(), false);
3071  const RenderAttrib *attrib =
3072  node()->get_attrib(TextureAttrib::get_class_slot());
3073  if (attrib != nullptr) {
3074  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
3075  return ta->has_all_off();
3076  }
3077 
3078  return false;
3079 }
3080 
3081 /**
3082  * Returns true if texturing has been specifically disabled on this particular
3083  * node for the indicated stage. This means that someone called
3084  * set_texture_off() on this node with the indicated stage name, or that
3085  * someone called set_texture_off() on this node to remove all stages.
3086  */
3087 bool NodePath::
3089  nassertr_always(!is_empty(), false);
3090 
3091  const RenderAttrib *attrib =
3092  node()->get_attrib(TextureAttrib::get_class_slot());
3093  if (attrib != nullptr) {
3094  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
3095  return ta->has_off_stage(stage);
3096  }
3097 
3098  return false;
3099 }
3100 
3101 /**
3102  * Returns the base-level texture that has been set on this particular node,
3103  * or NULL if no texture has been set. This is not necessarily the texture
3104  * that will be applied to the geometry at or below this level, as another
3105  * texture at a higher or lower level may override.
3106  *
3107  * See also find_texture().
3108  */
3110 get_texture() const {
3111  nassertr_always(!is_empty(), nullptr);
3112  const RenderAttrib *attrib =
3113  node()->get_attrib(TextureAttrib::get_class_slot());
3114  if (attrib != nullptr) {
3115  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
3116  return ta->get_texture();
3117  }
3118 
3119  return nullptr;
3120 }
3121 
3122 /**
3123  * Returns the texture that has been set on the indicated stage for this
3124  * particular node, or NULL if no texture has been set for this stage.
3125  */
3127 get_texture(TextureStage *stage) const {
3128  nassertr_always(!is_empty(), nullptr);
3129  const RenderAttrib *attrib =
3130  node()->get_attrib(TextureAttrib::get_class_slot());
3131  if (attrib != nullptr) {
3132  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
3133  return ta->get_on_texture(stage);
3134  }
3135 
3136  return nullptr;
3137 }
3138 
3139 /**
3140  * Returns the sampler state that has been given for the base-level texture
3141  * that has been set on this particular node. If no sampler state was given,
3142  * this returns the texture's default sampler settings.
3143  *
3144  * It is an error to call this if there is no base-level texture applied to
3145  * this particular node.
3146  */
3147 const SamplerState &NodePath::
3149  return get_texture_sampler(TextureStage::get_default());
3150 }
3151 
3152 /**
3153  * Returns the sampler state that has been given for the indicated texture
3154  * stage that has been set on this particular node. If no sampler state was
3155  * given, this returns the texture's default sampler settings.
3156  *
3157  * It is an error to call this if there is no texture set for this stage on
3158  * this particular node.
3159  */
3160 const SamplerState &NodePath::
3162  nassertr_always(!is_empty(), SamplerState::get_default());
3163  const RenderAttrib *attrib =
3164  node()->get_attrib(TextureAttrib::get_class_slot());
3165  nassertr_always(attrib != nullptr, SamplerState::get_default());
3166 
3167  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
3168  return ta->get_on_sampler(stage);
3169 }
3170 
3171 /**
3172  *
3173  */
3174 void NodePath::
3175 set_shader(const Shader *sha, int priority) {
3176  nassertv_always(!is_empty());
3177 
3178  const RenderAttrib *attrib =
3179  node()->get_attrib(ShaderAttrib::get_class_slot());
3180  if (attrib != nullptr) {
3181  priority = max(priority,
3182  node()->get_state()->get_override(ShaderAttrib::get_class_slot()));
3183  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3184  node()->set_attrib(sa->set_shader(sha, priority));
3185  } else {
3186  // Create a new ShaderAttrib for this node.
3187  CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
3188  node()->set_attrib(sa->set_shader(sha, priority));
3189  }
3190 }
3191 
3192 /**
3193  *
3194  */
3195 void NodePath::
3196 set_shader_off(int priority) {
3197  set_shader(nullptr, priority);
3198 }
3199 
3200 /**
3201  *
3202  */
3203 void NodePath::
3204 set_shader_auto(int priority) {
3205  nassertv_always(!is_empty());
3206 
3207  const RenderAttrib *attrib =
3208  node()->get_attrib(ShaderAttrib::get_class_slot());
3209  if (attrib != nullptr) {
3210  priority = max(priority,
3211  node()->get_state()->get_override(ShaderAttrib::get_class_slot()));
3212  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3213  node()->set_attrib(sa->set_shader_auto(priority));
3214  } else {
3215  // Create a new ShaderAttrib for this node.
3216  CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
3217  node()->set_attrib(sa->set_shader_auto(priority));
3218  }
3219 }
3220 
3221 /**
3222  * overloaded for auto shader customization
3223  */
3224 void NodePath::
3225 set_shader_auto(BitMask32 shader_switch, int priority) {
3226  nassertv_always(!is_empty());
3227 
3228  const RenderAttrib *attrib =
3229  node()->get_attrib(ShaderAttrib::get_class_slot());
3230  if (attrib != nullptr) {
3231  priority = max(priority,
3232  node()->get_state()->get_override(ShaderAttrib::get_class_slot()));
3233  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3234  node()->set_attrib(sa->set_shader_auto(shader_switch, priority));
3235  } else {
3236  // Create a new ShaderAttrib for this node.
3237  CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
3238  node()->set_attrib(sa->set_shader_auto(shader_switch, priority));
3239  }
3240 }
3241 /**
3242  *
3243  */
3244 void NodePath::
3245 clear_shader() {
3246  nassertv_always(!is_empty());
3247 
3248  const RenderAttrib *attrib =
3249  node()->get_attrib(ShaderAttrib::get_class_slot());
3250  if (attrib != nullptr) {
3251  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3252  node()->set_attrib(sa->clear_shader());
3253  }
3254 }
3255 
3256 /**
3257  *
3258  */
3259 const Shader *NodePath::
3260 get_shader() const {
3261  nassertr_always(!is_empty(), nullptr);
3262  const RenderAttrib *attrib =
3263  node()->get_attrib(ShaderAttrib::get_class_slot());
3264  if (attrib != nullptr) {
3265  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3266  return sa->get_shader();
3267  }
3268  return nullptr;
3269 }
3270 
3271 /**
3272  *
3273  */
3274 void NodePath::
3275 set_shader_input(const ShaderInput &inp) {
3276  nassertv_always(!is_empty());
3277 
3278  PandaNode *pnode = node();
3279  const RenderAttrib *attrib =
3280  pnode->get_attrib(ShaderAttrib::get_class_slot());
3281  if (attrib != nullptr) {
3282  const ShaderAttrib *sa = (const ShaderAttrib *)attrib;
3283  pnode->set_attrib(sa->set_shader_input(inp));
3284  } else {
3285  // Create a new ShaderAttrib for this node.
3286  CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
3287  pnode->set_attrib(sa->set_shader_input(inp));
3288  }
3289 }
3290 
3291 /**
3292  *
3293  */
3294 void NodePath::
3295 set_shader_input(ShaderInput &&inp) {
3296  nassertv_always(!is_empty());
3297 
3298  PandaNode *pnode = node();
3299  const RenderAttrib *attrib =
3300  pnode->get_attrib(ShaderAttrib::get_class_slot());
3301  if (attrib != nullptr) {
3302  const ShaderAttrib *sa = (const ShaderAttrib *)attrib;
3303  pnode->set_attrib(sa->set_shader_input(move(inp)));
3304  } else {
3305  // Create a new ShaderAttrib for this node.
3306  CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
3307  pnode->set_attrib(sa->set_shader_input(move(inp)));
3308  }
3309 }
3310 
3311 /**
3312  *
3313  */
3314 ShaderInput NodePath::
3315 get_shader_input(CPT_InternalName id) const {
3316  nassertr_always(!is_empty(), ShaderInput::get_blank());
3317 
3318  const RenderAttrib *attrib =
3319  node()->get_attrib(ShaderAttrib::get_class_slot());
3320  if (attrib != nullptr) {
3321  const ShaderAttrib *sa = (const ShaderAttrib *)attrib;
3322  return sa->get_shader_input(id);
3323  }
3324  return ShaderInput::get_blank();
3325 }
3326 
3327 /**
3328  * Returns the geometry instance count, or 0 if disabled. See
3329  * set_instance_count.
3330  */
3331 int NodePath::
3333  nassertr_always(!is_empty(), 0);
3334 
3335  const RenderAttrib *attrib =
3336  node()->get_attrib(ShaderAttrib::get_class_slot());
3337 
3338  if (attrib != nullptr) {
3339  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3340  return sa->get_instance_count();
3341  }
3342 
3343  return 0;
3344 }
3345 
3346 /**
3347  *
3348  */
3349 void NodePath::
3350 clear_shader_input(CPT_InternalName id) {
3351  nassertv_always(!is_empty());
3352 
3353  const RenderAttrib *attrib =
3354  node()->get_attrib(ShaderAttrib::get_class_slot());
3355  if (attrib != nullptr) {
3356  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3357  node()->set_attrib(sa->clear_shader_input(id));
3358  }
3359 }
3360 
3361 /**
3362  * Sets the geometry instance count, or 0 if geometry instancing should be
3363  * disabled. Do not confuse with instanceTo which only applies to animation
3364  * instancing.
3365  */
3366 void NodePath::
3367 set_instance_count(int instance_count) {
3368  nassertv_always(!is_empty());
3369 
3370  const RenderAttrib *attrib =
3371  node()->get_attrib(ShaderAttrib::get_class_slot());
3372  if (attrib != nullptr) {
3373  const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
3374  node()->set_attrib(sa->set_instance_count(instance_count));
3375  } else {
3376  // Create a new ShaderAttrib for this node.
3377  CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
3378  node()->set_attrib(sa->set_instance_count(instance_count));
3379  }
3380 }
3381 
3382 /**
3383  * Sets the texture matrix on the current node to the indicated transform for
3384  * the given stage.
3385  */
3386 void NodePath::
3387 set_tex_transform(TextureStage *stage, const TransformState *transform) {
3388  nassertv_always(!is_empty());
3389 
3390  const RenderAttrib *attrib =
3391  node()->get_attrib(TexMatrixAttrib::get_class_slot());
3392  if (attrib != nullptr) {
3393  const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
3394 
3395  // Modify the existing TexMatrixAttrib to add the indicated stage.
3396  node()->set_attrib(tma->add_stage(stage, transform));
3397 
3398  } else {
3399  // Create a new TexMatrixAttrib for this node.
3400  node()->set_attrib(TexMatrixAttrib::make(stage, transform));
3401  }
3402 }
3403 
3404 /**
3405  * Removes all texture matrices from the current node.
3406  */
3407 void NodePath::
3409  nassertv_always(!is_empty());
3410  node()->clear_attrib(TexMatrixAttrib::get_class_slot());
3411 }
3412 
3413 /**
3414  * Removes the texture matrix on the current node for the given stage.
3415  */
3416 void NodePath::
3418  nassertv_always(!is_empty());
3419 
3420  const RenderAttrib *attrib =
3421  node()->get_attrib(TexMatrixAttrib::get_class_slot());
3422  if (attrib != nullptr) {
3423  CPT(TexMatrixAttrib) tma = DCAST(TexMatrixAttrib, attrib);
3424  tma = DCAST(TexMatrixAttrib, tma->remove_stage(stage));
3425 
3426  if (tma->is_empty()) {
3427  node()->clear_attrib(TexMatrixAttrib::get_class_slot());
3428 
3429  } else {
3430  node()->set_attrib(tma);
3431  }
3432  }
3433 }
3434 
3435 /**
3436  * Returns true if there is an explicit texture matrix on the current node for
3437  * the given stage.
3438  */
3439 bool NodePath::
3441  nassertr_always(!is_empty(), false);
3442 
3443  const RenderAttrib *attrib =
3444  node()->get_attrib(TexMatrixAttrib::get_class_slot());
3445  if (attrib != nullptr) {
3446  const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
3447  return tma->has_stage(stage);
3448  }
3449 
3450  return false;
3451 }
3452 
3453 /**
3454  * Returns the texture matrix on the current node for the given stage, or
3455  * identity transform if there is no explicit transform set for the given
3456  * stage.
3457  */
3458 CPT(TransformState) NodePath::
3459 get_tex_transform(TextureStage *stage) const {
3460  nassertr_always(!is_empty(), nullptr);
3461 
3462  const RenderAttrib *attrib =
3463  node()->get_attrib(TexMatrixAttrib::get_class_slot());
3464  if (attrib != nullptr) {
3465  const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
3466  return tma->get_transform(stage);
3467  }
3468 
3469  return TransformState::make_identity();
3470 }
3471 
3472 /**
3473  * Sets the texture matrix on the current node to the indicated transform for
3474  * the given stage.
3475  */
3476 void NodePath::
3477 set_tex_transform(const NodePath &other, TextureStage *stage, const TransformState *transform) {
3478  nassertv(_error_type == ET_ok && other._error_type == ET_ok);
3479  nassertv_always(!is_empty());
3480 
3481  CPT(RenderState) state = get_state(other);
3482  const RenderAttrib *attrib =
3483  state->get_attrib(TexMatrixAttrib::get_class_slot());
3484  if (attrib != nullptr) {
3485  const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
3486 
3487  // Modify the existing TexMatrixAttrib to add the indicated stage.
3488  state = state->add_attrib(tma->add_stage(stage, transform));
3489 
3490  } else {
3491  // Create a new TexMatrixAttrib for this node.
3492  state = state->add_attrib(TexMatrixAttrib::make(stage, transform));
3493  }
3494 
3495  // Now compose that with our parent's state.
3496  CPT(RenderState) rel_state;
3497  if (has_parent()) {
3498  rel_state = other.get_state(get_parent());
3499  } else {
3500  rel_state = other.get_state(NodePath());
3501  }
3502  CPT(RenderState) new_state = rel_state->compose(state);
3503 
3504  // And apply only the TexMatrixAttrib to the current node, leaving the
3505  // others unchanged.
3506  node()->set_attrib(new_state->get_attrib(TexMatrixAttrib::get_class_slot()));
3507 }
3508 
3509 /**
3510  * Returns the texture matrix on the current node for the given stage,
3511  * relative to the other node.
3512  */
3513 CPT(TransformState) NodePath::
3514 get_tex_transform(const NodePath &other, TextureStage *stage) const {
3515  nassertr(_error_type == ET_ok && other._error_type == ET_ok, TransformState::make_identity());
3516 
3517  CPT(RenderState) state = get_state(other);
3518  const RenderAttrib *attrib =
3519  state->get_attrib(TexMatrixAttrib::get_class_slot());
3520  if (attrib != nullptr) {
3521  const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attrib);
3522  return tma->get_transform(stage);
3523  }
3524 
3525  return TransformState::make_identity();
3526 }
3527 
3528 /**
3529  * Enables automatic texture coordinate generation for the indicated texture
3530  * stage.
3531  */
3532 void NodePath::
3533 set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, int priority) {
3534  nassertv_always(!is_empty());
3535 
3536  const RenderAttrib *attrib =
3537  node()->get_attrib(TexGenAttrib::get_class_slot());
3538 
3539  CPT(TexGenAttrib) tga;
3540 
3541  if (attrib != nullptr) {
3542  priority = max(priority,
3543  node()->get_state()->get_override(TextureAttrib::get_class_slot()));
3544  tga = DCAST(TexGenAttrib, attrib);
3545 
3546  } else {
3547  tga = DCAST(TexGenAttrib, TexGenAttrib::make());
3548  }
3549 
3550  node()->set_attrib(tga->add_stage(stage, mode), priority);
3551 }
3552 
3553 /**
3554  * Enables automatic texture coordinate generation for the indicated texture
3555  * stage. This version of this method is useful when setting M_constant,
3556  * which requires a constant texture coordinate value.
3557  */
3558 void NodePath::
3559 set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode,
3560  const LTexCoord3 &constant_value, int priority) {
3561  nassertv_always(!is_empty());
3562 
3563  const RenderAttrib *attrib =
3564  node()->get_attrib(TexGenAttrib::get_class_slot());
3565 
3566  CPT(TexGenAttrib) tga;
3567 
3568  if (attrib != nullptr) {
3569  priority = max(priority,
3570  node()->get_state()->get_override(TextureAttrib::get_class_slot()));
3571  tga = DCAST(TexGenAttrib, attrib);
3572 
3573  } else {
3574  tga = DCAST(TexGenAttrib, TexGenAttrib::make());
3575  }
3576 
3577  node()->set_attrib(tga->add_stage(stage, mode, constant_value), priority);
3578 }
3579 
3580 /**
3581  * Removes the texture coordinate generation mode from all texture stages on
3582  * this node.
3583  */
3584 void NodePath::
3586  nassertv_always(!is_empty());
3587  node()->clear_attrib(TexGenAttrib::get_class_slot());
3588 }
3589 
3590 /**
3591  * Disables automatic texture coordinate generation for the indicated texture
3592  * stage.
3593  */
3594 void NodePath::
3596  nassertv_always(!is_empty());
3597 
3598  const RenderAttrib *attrib =
3599  node()->get_attrib(TexGenAttrib::get_class_slot());
3600  if (attrib != nullptr) {
3601  CPT(TexGenAttrib) tga = DCAST(TexGenAttrib, attrib);
3602  tga = DCAST(TexGenAttrib, tga->remove_stage(stage));
3603 
3604  if (tga->is_empty()) {
3605  node()->clear_attrib(TexGenAttrib::get_class_slot());
3606 
3607  } else {
3608  node()->set_attrib(tga);
3609  }
3610  }
3611 }
3612 
3613 /**
3614  * Returns true if there is a mode for automatic texture coordinate generation
3615  * on the current node for the given stage.
3616  */
3617 bool NodePath::
3618 has_tex_gen(TextureStage *stage) const {
3619  nassertr_always(!is_empty(), false);
3620 
3621  const RenderAttrib *attrib =
3622  node()->get_attrib(TexGenAttrib::get_class_slot());
3623  if (attrib != nullptr) {
3624  const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib);
3625  return tga->has_stage(stage);
3626  }
3627 
3628  return false;
3629 }
3630 
3631 /**
3632  * Returns the texture coordinate generation mode for the given stage, or
3633  * M_off if there is no explicit mode set for the given stage.
3634  */
3635 RenderAttrib::TexGenMode NodePath::
3636 get_tex_gen(TextureStage *stage) const {
3637  nassertr_always(!is_empty(), TexGenAttrib::M_off);
3638 
3639  const RenderAttrib *attrib =
3640  node()->get_attrib(TexGenAttrib::get_class_slot());
3641  if (attrib != nullptr) {
3642  const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib);
3643  return tga->get_mode(stage);
3644  }
3645 
3646  return TexGenAttrib::M_off;
3647 }
3648 
3649 /**
3650  * Establishes a TexProjectorEffect on this node, which can be used to
3651  * establish projective texturing (but see also the
3652  * NodePath::project_texture() convenience function), or it can be used to
3653  * bind this node's texture transform to particular node's position in space,
3654  * allowing a LerpInterval (for instance) to adjust this node's texture
3655  * coordinates.
3656  *
3657  * If to is a LensNode, then the fourth parameter, lens_index, can be provided
3658  * to select a particular lens to apply. Otherwise lens_index is not used.
3659  */
3660 void NodePath::
3661 set_tex_projector(TextureStage *stage, const NodePath &from, const NodePath &to,
3662  int lens_index) {
3663  nassertv_always(!is_empty());
3664 
3665  const RenderEffect *effect =
3666  node()->get_effect(TexProjectorEffect::get_class_type());
3667 
3668  CPT(TexProjectorEffect) tpe;
3669 
3670  if (effect != nullptr) {
3671  tpe = DCAST(TexProjectorEffect, effect);
3672 
3673  } else {
3674  tpe = DCAST(TexProjectorEffect, TexProjectorEffect::make());
3675  }
3676 
3677  node()->set_effect(tpe->add_stage(stage, from, to, lens_index));
3678 }
3679 
3680 /**
3681  * Removes the TexProjectorEffect for the indicated stage from this node.
3682  */
3683 void NodePath::
3685  nassertv_always(!is_empty());
3686 
3687  const RenderEffect *effect =
3688  node()->get_effect(TexProjectorEffect::get_class_type());
3689  if (effect != nullptr) {
3690  CPT(TexProjectorEffect) tpe = DCAST(TexProjectorEffect, effect);
3691  tpe = DCAST(TexProjectorEffect, tpe->remove_stage(stage));
3692 
3693  if (tpe->is_empty()) {
3694  node()->clear_effect(TexProjectorEffect::get_class_type());
3695 
3696  } else {
3697  node()->set_effect(tpe);
3698  }
3699  }
3700 }
3701 
3702 /**
3703  * Removes the TexProjectorEffect for all stages from this node.
3704  */
3705 void NodePath::
3707  nassertv_always(!is_empty());
3708  node()->clear_effect(TexProjectorEffect::get_class_type());
3709 }
3710 
3711 /**
3712  * Returns true if this node has a TexProjectorEffect for the indicated stage,
3713  * false otherwise.
3714  */
3715 bool NodePath::
3717  nassertr_always(!is_empty(), false);
3718 
3719  const RenderEffect *effect =
3720  node()->get_effect(TexProjectorEffect::get_class_type());
3721  if (effect != nullptr) {
3722  const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect);
3723  return tpe->has_stage(stage);
3724  }
3725 
3726  return false;
3727 }
3728 
3729 /**
3730  * Returns the "from" node associated with the TexProjectorEffect on the
3731  * indicated stage. The relative transform between the "from" and the "to"
3732  * nodes is automatically applied to the texture transform each frame.
3733  */
3736  nassertr_always(!is_empty(), NodePath::fail());
3737 
3738  const RenderEffect *effect =
3739  node()->get_effect(TexProjectorEffect::get_class_type());
3740  if (effect != nullptr) {
3741  const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect);
3742  return tpe->get_from(stage);
3743  }
3744 
3745  return NodePath::not_found();
3746 }
3747 
3748 /**
3749  * Returns the "to" node associated with the TexProjectorEffect on the
3750  * indicated stage. The relative transform between the "from" and the "to"
3751  * nodes is automatically applied to the texture transform each frame.
3752  */
3755  nassertr_always(!is_empty(), NodePath::fail());
3756 
3757  const RenderEffect *effect =
3758  node()->get_effect(TexProjectorEffect::get_class_type());
3759  if (effect != nullptr) {
3760  const TexProjectorEffect *tpe = DCAST(TexProjectorEffect, effect);
3761  return tpe->get_to(stage);
3762  }
3763 
3764  return NodePath::not_found();
3765 }
3766 
3767 /**
3768  * A convenience function to enable projective texturing at this node level
3769  * and below, using the indicated NodePath (which should contain a LensNode)
3770  * as the projector.
3771  */
3772 void NodePath::
3773 project_texture(TextureStage *stage, Texture *tex, const NodePath &projector) {
3774  nassertv(!projector.is_empty() && projector.node()->is_of_type(LensNode::get_class_type()));
3775  set_texture(stage, tex);
3776  set_tex_gen(stage, TexGenAttrib::M_world_position);
3777  set_tex_projector(stage, NodePath(), projector);
3778 }
3779 
3780 /**
3781  * Returns true if there are at least some vertices at this node and below
3782  * that contain a reference to the indicated vertex data column name, false
3783  * otherwise.
3784  *
3785  * This is particularly useful for testing whether a particular model has a
3786  * given texture coordinate set (but see has_texcoord()).
3787  */
3788 bool NodePath::
3789 has_vertex_column(const InternalName *name) const {
3790  nassertr_always(!is_empty(), false);
3791  return r_has_vertex_column(node(), name);
3792 }
3793 
3794 /**
3795  * Returns a list of all vertex array columns stored on some geometry found at
3796  * this node level and below.
3797  */
3800  nassertr_always(!is_empty(), InternalNameCollection());
3801  InternalNames vertex_columns;
3802  r_find_all_vertex_columns(node(), vertex_columns);
3803 
3805  InternalNames::iterator ti;
3806  for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
3807  tc.add_name(*ti);
3808  }
3809  return tc;
3810 }
3811 
3812 /**
3813  * Returns a list of all vertex array columns stored on some geometry found at
3814  * this node level and below that match the indicated name (which may contain
3815  * wildcard characters).
3816  */
3818 find_all_vertex_columns(const string &name) const {
3819  nassertr_always(!is_empty(), InternalNameCollection());
3820  InternalNames vertex_columns;
3821  r_find_all_vertex_columns(node(), vertex_columns);
3822 
3823  GlobPattern glob(name);
3824 
3826  InternalNames::iterator ti;
3827  for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
3828  const InternalName *name = (*ti);
3829  if (glob.matches(name->get_name())) {
3830  tc.add_name(name);
3831  }
3832  }
3833  return tc;
3834 }
3835 
3836 /**
3837  * Returns a list of all texture coordinate sets used by any geometry at this
3838  * node level and below.
3839  */
3842  nassertr_always(!is_empty(), InternalNameCollection());
3843  InternalNames vertex_columns;
3844  r_find_all_vertex_columns(node(), vertex_columns);
3845 
3846  CPT(InternalName) texcoord_name = InternalName::get_texcoord();
3847 
3849  InternalNames::iterator ti;
3850  for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
3851  if ((*ti)->get_top() == texcoord_name) {
3852  tc.add_name(*ti);
3853  }
3854  }
3855  return tc;
3856 }
3857 
3858 /**
3859  * Returns a list of all texture coordinate sets used by any geometry at this
3860  * node level and below that match the indicated name (which may contain
3861  * wildcard characters).
3862  */
3864 find_all_texcoords(const string &name) const {
3865  nassertr_always(!is_empty(), InternalNameCollection());
3866  InternalNames vertex_columns;
3867  r_find_all_vertex_columns(node(), vertex_columns);
3868 
3869  GlobPattern glob(name);
3870  CPT_InternalName texcoord_name = InternalName::get_texcoord();
3871 
3873  InternalNames::iterator ti;
3874  for (ti = vertex_columns.begin(); ti != vertex_columns.end(); ++ti) {
3875  const InternalName *name = (*ti);
3876  if (name->get_top() == texcoord_name) {
3877  // This is a texture coordinate name. Figure out the basename of the
3878  // texture coordinates.
3879  int index = name->find_ancestor("texcoord");
3880  nassertr(index != -1, InternalNameCollection());
3881  string net_basename = name->get_net_basename(index - 1);
3882 
3883  if (glob.matches(net_basename)) {
3884  tc.add_name(name);
3885  }
3886  }
3887  }
3888  return tc;
3889 }
3890 
3891 /**
3892  * Returns the first texture found applied to geometry at this node or below
3893  * that matches the indicated name (which may contain wildcards). Returns the
3894  * texture if it is found, or NULL if it is not.
3895  */
3897 find_texture(const string &name) const {
3898  nassertr_always(!is_empty(), nullptr);
3899  GlobPattern glob(name);
3900  return r_find_texture(node(), get_net_state(), glob);
3901 }
3902 
3903 /**
3904  * Returns the first texture found applied to geometry at this node or below
3905  * that is assigned to the indicated texture stage. Returns the texture if it
3906  * is found, or NULL if it is not.
3907  */
3910  nassertr_always(!is_empty(), nullptr);
3911  return r_find_texture(node(), stage);
3912 }
3913 
3914 /**
3915  * Returns a list of a textures applied to geometry at this node and below.
3916  */
3919  nassertr_always(!is_empty(), TextureCollection());
3920  Textures textures;
3921  r_find_all_textures(node(), get_net_state(), textures);
3922 
3923  TextureCollection tc;
3924  Textures::iterator ti;
3925  for (ti = textures.begin(); ti != textures.end(); ++ti) {
3926  tc.add_texture(*ti);
3927  }
3928  return tc;
3929 }
3930 
3931 /**
3932  * Returns a list of a textures applied to geometry at this node and below
3933  * that match the indicated name (which may contain wildcard characters).
3934  */
3936 find_all_textures(const string &name) const {
3937  nassertr_always(!is_empty(), TextureCollection());
3938  Textures textures;
3939  r_find_all_textures(node(), get_net_state(), textures);
3940 
3941  GlobPattern glob(name);
3942 
3943  TextureCollection tc;
3944  Textures::iterator ti;
3945  for (ti = textures.begin(); ti != textures.end(); ++ti) {
3946  Texture *texture = (*ti);
3947  if (glob.matches(texture->get_name())) {
3948  tc.add_texture(texture);
3949  }
3950  }
3951  return tc;
3952 }
3953 
3954 /**
3955  * Returns a list of a textures on geometry at this node and below that are
3956  * assigned to the indicated texture stage.
3957  */
3960  nassertr_always(!is_empty(), TextureCollection());
3961  Textures textures;
3962  r_find_all_textures(node(), stage, textures);
3963 
3964  TextureCollection tc;
3965  Textures::iterator ti;
3966  for (ti = textures.begin(); ti != textures.end(); ++ti) {
3967  Texture *texture = (*ti);
3968  tc.add_texture(texture);
3969  }
3970  return tc;
3971 }
3972 
3973 /**
3974  * Returns the first TextureStage found applied to geometry at this node or
3975  * below that matches the indicated name (which may contain wildcards).
3976  * Returns the TextureStage if it is found, or NULL if it is not.
3977  */
3979 find_texture_stage(const string &name) const {
3980  nassertr_always(!is_empty(), nullptr);
3981  GlobPattern glob(name);
3982  return r_find_texture_stage(node(), get_net_state(), glob);
3983 }
3984 
3985 /**
3986  * Returns a list of a TextureStages applied to geometry at this node and
3987  * below.
3988  */
3991  nassertr_always(!is_empty(), TextureStageCollection());
3992  TextureStages texture_stages;
3993  r_find_all_texture_stages(node(), get_net_state(), texture_stages);
3994 
3996  TextureStages::iterator ti;
3997  for (ti = texture_stages.begin(); ti != texture_stages.end(); ++ti) {
3998  tc.add_texture_stage(*ti);
3999  }
4000  return tc;
4001 }
4002 
4003 /**
4004  * Searches through all TextureStages at this node and below. Any
4005  * TextureStages that share the same name as the indicated TextureStage object
4006  * are replaced with this object, thus ensuring that all geometry at this node
4007  * and below with a particular TextureStage name is using the same
4008  * TextureStage object.
4009  */
4010 void NodePath::
4012  nassertv_always(!is_empty());
4013  r_unify_texture_stages(node(), stage);
4014 }
4015 
4016 /**
4017  * Returns a list of a TextureStages applied to geometry at this node and
4018  * below that match the indicated name (which may contain wildcard
4019  * characters).
4020  */
4022 find_all_texture_stages(const string &name) const {
4023  nassertr_always(!is_empty(), TextureStageCollection());
4024  TextureStages texture_stages;
4025  r_find_all_texture_stages(node(), get_net_state(), texture_stages);
4026 
4027  GlobPattern glob(name);
4028 
4030  TextureStages::iterator ti;
4031  for (ti = texture_stages.begin(); ti != texture_stages.end(); ++ti) {
4032  TextureStage *texture_stage = (*ti);
4033  if (glob.matches(texture_stage->get_name())) {
4034  tc.add_texture_stage(texture_stage);
4035  }
4036  }
4037  return tc;
4038 }
4039 
4040 /**
4041  * Returns the first material found applied to geometry at this node or below
4042  * that matches the indicated name (which may contain wildcards). Returns the
4043  * material if it is found, or NULL if it is not.
4044  */
4046 find_material(const string &name) const {
4047  nassertr_always(!is_empty(), nullptr);
4048  GlobPattern glob(name);
4049  return r_find_material(node(), get_net_state(), glob);
4050 }
4051 
4052 /**
4053  * Returns a list of a materials applied to geometry at this node and below.
4054  */
4057  nassertr_always(!is_empty(), MaterialCollection());
4058  Materials materials;
4059  r_find_all_materials(node(), get_net_state(), materials);
4060 
4061  MaterialCollection tc;
4062  Materials::iterator ti;
4063  for (ti = materials.begin(); ti != materials.end(); ++ti) {
4064  tc.add_material(*ti);
4065  }
4066  return tc;
4067 }
4068 
4069 /**
4070  * Returns a list of a materials applied to geometry at this node and below
4071  * that match the indicated name (which may contain wildcard characters).
4072  */
4074 find_all_materials(const string &name) const {
4075  nassertr_always(!is_empty(), MaterialCollection());
4076  Materials materials;
4077  r_find_all_materials(node(), get_net_state(), materials);
4078 
4079  GlobPattern glob(name);
4080 
4081  MaterialCollection tc;
4082  Materials::iterator ti;
4083  for (ti = materials.begin(); ti != materials.end(); ++ti) {
4084  Material *material = (*ti);
4085  if (glob.matches(material->get_name())) {
4086  tc.add_material(material);
4087  }
4088  }
4089  return tc;
4090 }
4091 
4092 /**
4093  * Sets the geometry at this level and below to render using the indicated
4094  * material.
4095  *
4096  * Previously, this operation made a copy of the material structure, but
4097  * nowadays it assigns the pointer directly.
4098  */
4099 void NodePath::
4100 set_material(Material *mat, int priority) {
4101  nassertv_always(!is_empty());
4102  nassertv(mat != nullptr);
4103  node()->set_attrib(MaterialAttrib::make(mat), priority);
4104 }
4105 
4106 /**
4107  * Sets the geometry at this level and below to render using no material.
4108  * This is normally the default, but it may be useful to use this to
4109  * contradict set_material() at a higher node level (or, with a priority, to
4110  * override a set_material() at a lower level).
4111  */
4112 void NodePath::
4113 set_material_off(int priority) {
4114  nassertv_always(!is_empty());
4115  node()->set_attrib(MaterialAttrib::make_off(), priority);
4116 }
4117 
4118 /**
4119  * Completely removes any material adjustment that may have been set via
4120  * set_material() from this particular node.
4121  */
4122 void NodePath::
4124  nassertv_always(!is_empty());
4125  node()->clear_attrib(MaterialAttrib::get_class_slot());
4126 }
4127 
4128 /**
4129  * Returns true if a material has been applied to this particular node via
4130  * set_material(), false otherwise.
4131  */
4132 bool NodePath::
4133 has_material() const {
4134  nassertr_always(!is_empty(), false);
4135  const RenderAttrib *attrib =
4136  node()->get_attrib(MaterialAttrib::get_class_slot());
4137  if (attrib != nullptr) {
4138  const MaterialAttrib *ma = DCAST(MaterialAttrib, attrib);
4139  return !ma->is_off();
4140  }
4141 
4142  return false;
4143 }
4144 
4145 /**
4146  * Returns the material that has been set on this particular node, or NULL if
4147  * no material has been set. This is not necessarily the material that will
4148  * be applied to the geometry at or below this level, as another material at a
4149  * higher or lower level may override.
4150  *
4151  * See also find_material().
4152  */
4153 PT(Material) NodePath::
4154 get_material() const {
4155  nassertr_always(!is_empty(), nullptr);
4156  const RenderAttrib *attrib =
4157  node()->get_attrib(MaterialAttrib::get_class_slot());
4158  if (attrib != nullptr) {
4159  const MaterialAttrib *ma = DCAST(MaterialAttrib, attrib);
4160  return ma->get_material();
4161  }
4162 
4163  return nullptr;
4164 }
4165 
4166 /**
4167  * Recursively searches the scene graph for references to the given material,
4168  * and replaces them with the new material.
4169  */
4170 void NodePath::
4171 replace_material(Material *mat, Material *new_mat) {
4172  nassertv_always(!is_empty());
4173  nassertv(mat != nullptr);
4174  nassertv(new_mat != nullptr);
4175 
4176  CPT(RenderAttrib) new_attrib = MaterialAttrib::make(new_mat);
4177  r_replace_material(node(), mat, (const MaterialAttrib *)new_attrib.p());
4178 }
4179 
4180 /**
4181  * Sets the geometry at this level and below to render using the indicated
4182  * fog.
4183  */
4184 void NodePath::
4185 set_fog(Fog *fog, int priority) {
4186  nassertv_always(!is_empty());
4187  node()->set_attrib(FogAttrib::make(fog), priority);
4188 }
4189 
4190 /**
4191  * Sets the geometry at this level and below to render using no fog. This is
4192  * normally the default, but it may be useful to use this to contradict
4193  * set_fog() at a higher node level (or, with a priority, to override a
4194  * set_fog() at a lower level).
4195  */
4196 void NodePath::
4197 set_fog_off(int priority) {
4198  nassertv_always(!is_empty());
4199  node()->set_attrib(FogAttrib::make_off(), priority);
4200 }
4201 
4202 /**
4203  * Completely removes any fog adjustment that may have been set via set_fog()
4204  * or set_fog_off() from this particular node. This allows whatever fogs
4205  * might be otherwise affecting the geometry to show instead.
4206  */
4207 void NodePath::
4209  nassertv_always(!is_empty());
4210  node()->clear_attrib(FogAttrib::get_class_slot());
4211 }
4212 
4213 /**
4214  * Returns true if a fog has been applied to this particular node via
4215  * set_fog(), false otherwise. This is not the same thing as asking whether
4216  * the geometry at this node will be rendered with fog, as there may be a fog
4217  * in effect from a higher or lower level.
4218  */
4219 bool NodePath::
4220 has_fog() const {
4221  nassertr_always(!is_empty(), false);
4222  const RenderAttrib *attrib =
4223  node()->get_attrib(FogAttrib::get_class_slot());
4224  if (attrib != nullptr) {
4225  const FogAttrib *fa = DCAST(FogAttrib, attrib);
4226  return !fa->is_off();
4227  }
4228 
4229  return false;
4230 }
4231 
4232 /**
4233  * Returns true if a fog has been specifically disabled on this particular
4234  * node via set_fog_off(), false otherwise. This is not the same thing as
4235  * asking whether the geometry at this node will be rendered unfogged, as
4236  * there may be a fog in effect from a higher or lower level.
4237  */
4238 bool NodePath::
4239 has_fog_off() const {
4240  nassertr_always(!is_empty(), false);
4241  const RenderAttrib *attrib =
4242  node()->get_attrib(FogAttrib::get_class_slot());
4243  if (attrib != nullptr) {
4244  const FogAttrib *fa = DCAST(FogAttrib, attrib);
4245  return fa->is_off();
4246  }
4247 
4248  return false;
4249 }
4250 
4251 /**
4252  * Returns the fog that has been set on this particular node, or NULL if no
4253  * fog has been set. This is not necessarily the fog that will be applied to
4254  * the geometry at or below this level, as another fog at a higher or lower
4255  * level may override.
4256  */
4257 Fog *NodePath::
4258 get_fog() const {
4259  nassertr_always(!is_empty(), nullptr);
4260  const RenderAttrib *attrib =
4261  node()->get_attrib(FogAttrib::get_class_slot());
4262  if (attrib != nullptr) {
4263  const FogAttrib *fa = DCAST(FogAttrib, attrib);
4264  return fa->get_fog();
4265  }
4266 
4267  return nullptr;
4268 }
4269 
4270 /**
4271  * Sets up the geometry at this level and below (unless overridden) to render
4272  * in wireframe mode.
4273  */
4274 void NodePath::
4276  nassertv_always(!is_empty());
4277  const RenderModeAttrib *rma;
4278  node()->get_state()->get_attrib_def(rma);
4279  node()->set_attrib(RenderModeAttrib::make(RenderModeAttrib::M_wireframe, rma->get_thickness(), rma->get_perspective()), priority);
4280 }
4281 
4282 /**
4283  * Sets up the geometry at this level and below (unless overridden) to render
4284  * in filled (i.e. not wireframe) mode.
4285  */
4286 void NodePath::
4287 set_render_mode_filled(int priority) {
4288  nassertv_always(!is_empty());
4289  const RenderModeAttrib *rma;
4290  node()->get_state()->get_attrib_def(rma);
4291  node()->set_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled, rma->get_thickness(), rma->get_perspective()), priority);
4292 }
4293 
4294 /**
4295  * Sets up the geometry at this level and below (unless overridden) to render
4296  * in filled, but overlay the wireframe on top with a fixed color. This is
4297  * useful for debug visualizations.
4298  */
4299 void NodePath::
4300 set_render_mode_filled_wireframe(const LColor &wireframe_color, int priority) {
4301  nassertv_always(!is_empty());
4302  const RenderModeAttrib *rma;
4303  node()->get_state()->get_attrib_def(rma);
4304  node()->set_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled_wireframe, rma->get_thickness(), rma->get_perspective(), wireframe_color), priority);
4305 }
4306 
4307 /**
4308  * Sets up the point geometry at this level and below to render as perspective
4309  * sprites (that is, billboarded quads). The thickness, as specified with
4310  * set_render_mode_thickness(), is the width of each point in 3-D units,
4311  * unless it is overridden on a per-vertex basis. This does not affect
4312  * geometry other than points.
4313  *
4314  * If you want the quads to be individually textured, you should also set a
4315  * TexGenAttrib::M_point_sprite on the node.
4316  */
4317 void NodePath::
4318 set_render_mode_perspective(bool perspective, int priority) {
4319  nassertv_always(!is_empty());
4320  const RenderModeAttrib *rma;
4321  node()->get_state()->get_attrib_def(rma);
4322  node()->set_attrib(RenderModeAttrib::make(rma->get_mode(), rma->get_thickness(), perspective, rma->get_wireframe_color()), priority);
4323 }
4324 
4325 /**
4326  * Sets up the point geometry at this level and below to render as thick
4327  * points (that is, billboarded quads). The thickness is in pixels, unless
4328  * set_render_mode_perspective is also true, in which case it is in 3-D units.
4329  *
4330  * If you want the quads to be individually textured, you should also set a
4331  * TexGenAttrib::M_point_sprite on the node.
4332  */
4333 void NodePath::
4334 set_render_mode_thickness(PN_stdfloat thickness, int priority) {
4335  nassertv_always(!is_empty());
4336  const RenderModeAttrib *rma;
4337  node()->get_state()->get_attrib_def(rma);
4338  node()->set_attrib(RenderModeAttrib::make(rma->get_mode(), thickness, rma->get_perspective(), rma->get_wireframe_color()), priority);
4339 }
4340 
4341 /**
4342  * Sets up the geometry at this level and below (unless overridden) to render
4343  * in the specified mode and with the indicated line and/or point thickness.
4344  */
4345 void NodePath::
4346 set_render_mode(RenderModeAttrib::Mode mode, PN_stdfloat thickness, int priority) {
4347  nassertv_always(!is_empty());
4348 
4349  node()->set_attrib(RenderModeAttrib::make(mode, thickness), priority);
4350 }
4351 
4352 /**
4353  * Completely removes any render mode adjustment that may have been set on
4354  * this node via set_render_mode_wireframe() or set_render_mode_filled().
4355  */
4356 void NodePath::
4358  nassertv_always(!is_empty());
4359  node()->clear_attrib(RenderModeAttrib::get_class_slot());
4360 }
4361 
4362 /**
4363  * Returns true if a render mode has been explicitly set on this particular
4364  * node via set_render_mode() (or set_render_mode_wireframe() or
4365  * set_render_mode_filled()), false otherwise.
4366  */
4367 bool NodePath::
4369  nassertr_always(!is_empty(), false);
4370  return node()->has_attrib(RenderModeAttrib::get_class_slot());
4371 }
4372 
4373 /**
4374  * Returns the render mode that has been specifically set on this node via
4375  * set_render_mode(), or M_unchanged if nothing has been set.
4376  */
4377 RenderModeAttrib::Mode NodePath::
4379  nassertr_always(!is_empty(), RenderModeAttrib::M_unchanged);
4380  const RenderAttrib *attrib =
4381  node()->get_attrib(RenderModeAttrib::get_class_slot());
4382  if (attrib != nullptr) {
4383  const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib);
4384  return ta->get_mode();
4385  }
4386 
4387  return RenderModeAttrib::M_unchanged;
4388 }
4389 
4390 /**
4391  * Returns the render mode thickness that has been specifically set on this
4392  * node via set_render_mode(), or 1.0 if nothing has been set.
4393  */
4394 PN_stdfloat NodePath::
4396  nassertr_always(!is_empty(), 0.0f);
4397  const RenderAttrib *attrib =
4398  node()->get_attrib(RenderModeAttrib::get_class_slot());
4399  if (attrib != nullptr) {
4400  const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib);
4401  return ta->get_thickness();
4402  }
4403 
4404  return 1.0f;
4405 }
4406 
4407 /**
4408  * Returns the flag that has been set on this node via
4409  * set_render_mode_perspective(), or false if no flag has been set.
4410  */
4411 bool NodePath::
4413  nassertr_always(!is_empty(), 0.0f);
4414  const RenderAttrib *attrib =
4415  node()->get_attrib(RenderModeAttrib::get_class_slot());
4416  if (attrib != nullptr) {
4417  const RenderModeAttrib *ta = DCAST(RenderModeAttrib, attrib);
4418  return ta->get_perspective();
4419  }
4420 
4421  return false;
4422 }
4423 
4424 /**
4425  * Specifically sets or disables two-sided rendering mode on this particular
4426  * node. If no other nodes override, this will cause backfacing polygons to
4427  * be drawn (in two-sided mode, true) or culled (in one-sided mode, false).
4428  */
4429 void NodePath::
4430 set_two_sided(bool two_sided, int priority) {
4431  nassertv_always(!is_empty());
4432 
4433  CullFaceAttrib::Mode mode =
4434  two_sided ?
4435  CullFaceAttrib::M_cull_none :
4436  CullFaceAttrib::M_cull_clockwise;
4437 
4438  node()->set_attrib(CullFaceAttrib::make(mode), priority);
4439 }
4440 
4441 /**
4442  * Completely removes any two-sided adjustment that may have been set on this
4443  * node via set_two_sided(). The geometry at this level and below will
4444  * subsequently be rendered either two-sided or one-sided, according to
4445  * whatever other nodes may have had set_two_sided() on it, or according to
4446  * the initial state otherwise.
4447  */
4448 void NodePath::
4450  nassertv_always(!is_empty());
4451  node()->clear_attrib(CullFaceAttrib::get_class_slot());
4452 }
4453 
4454 /**
4455  * Returns true if a two-sided adjustment has been explicitly set on this
4456  * particular node via set_two_sided(). If this returns true, then
4457  * get_two_sided() may be called to determine which has been set.
4458  */
4459 bool NodePath::
4460 has_two_sided() const {
4461  nassertr_always(!is_empty(), false);
4462  return node()->has_attrib(CullFaceAttrib::get_class_slot());
4463 }
4464 
4465 /**
4466  * Returns true if two-sided rendering has been specifically set on this node
4467  * via set_two_sided(), or false if one-sided rendering has been specifically
4468  * set, or if nothing has been specifically set. See also has_two_sided().
4469  * This does not necessarily imply that the geometry will or will not be
4470  * rendered two-sided, as there may be other nodes that override.
4471  */
4472 bool NodePath::
4473 get_two_sided() const {
4474  nassertr_always(!is_empty(), false);
4475  const RenderAttrib *attrib =
4476  node()->get_attrib(CullFaceAttrib::get_class_slot());
4477  if (attrib != nullptr) {
4478  const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, attrib);
4479  return (cfa->get_actual_mode() == CullFaceAttrib::M_cull_none);
4480  }
4481 
4482  return false;
4483 }
4484 
4485 /**
4486  * Specifically sets or disables the testing of the depth buffer on this
4487  * particular node. This is normally on in the 3-d scene graph and off in the
4488  * 2-d scene graph; it should be on for rendering most 3-d objects properly.
4489  */
4490 void NodePath::
4491 set_depth_test(bool depth_test, int priority) {
4492  nassertv_always(!is_empty());
4493 
4494  DepthTestAttrib::PandaCompareFunc mode =
4495  depth_test ?
4496  DepthTestAttrib::M_less :
4497  DepthTestAttrib::M_none;
4498 
4499  node()->set_attrib(DepthTestAttrib::make(mode), priority);
4500 }
4501 
4502 /**
4503  * Completely removes any depth-test adjustment that may have been set on this
4504  * node via set_depth_test().
4505  */
4506 void NodePath::
4508  nassertv_always(!is_empty());
4509  node()->clear_attrib(DepthTestAttrib::get_class_slot());
4510 }
4511 
4512 /**
4513  * Returns true if a depth-test adjustment has been explicitly set on this
4514  * particular node via set_depth_test(). If this returns true, then
4515  * get_depth_test() may be called to determine which has been set.
4516  */
4517 bool NodePath::
4519  nassertr_always(!is_empty(), false);
4520  return node()->has_attrib(DepthTestAttrib::get_class_slot());
4521 }
4522 
4523 /**
4524  * Returns true if depth-test rendering has been specifically set on this node
4525  * via set_depth_test(), or false if depth-test rendering has been
4526  * specifically disabled. If nothing has been specifically set, returns true.
4527  * See also has_depth_test().
4528  */
4529 bool NodePath::
4531  nassertr_always(!is_empty(), false);
4532  const RenderAttrib *attrib =
4533  node()->get_attrib(DepthTestAttrib::get_class_slot());
4534  if (attrib != nullptr) {
4535  const DepthTestAttrib *dta = DCAST(DepthTestAttrib, attrib);
4536  return (dta->get_mode() != DepthTestAttrib::M_none);
4537  }
4538 
4539  return true;
4540 }
4541 
4542 /**
4543  * Specifically sets or disables the writing to the depth buffer on this
4544  * particular node. This is normally on in the 3-d scene graph and off in the
4545  * 2-d scene graph; it should be on for rendering most 3-d objects properly.
4546  */
4547 void NodePath::
4548 set_depth_write(bool depth_write, int priority) {
4549  nassertv_always(!is_empty());
4550 
4551  DepthWriteAttrib::Mode mode =
4552  depth_write ?
4553  DepthWriteAttrib::M_on :
4554  DepthWriteAttrib::M_off;
4555 
4556  node()->set_attrib(DepthWriteAttrib::make(mode), priority);
4557 }
4558 
4559 /**
4560  * Completely removes any depth-write adjustment that may have been set on
4561  * this node via set_depth_write().
4562  */
4563 void NodePath::
4565  nassertv_always(!is_empty());
4566  node()->clear_attrib(DepthWriteAttrib::get_class_slot());
4567 }
4568 
4569 /**
4570  * Returns true if a depth-write adjustment has been explicitly set on this
4571  * particular node via set_depth_write(). If this returns true, then
4572  * get_depth_write() may be called to determine which has been set.
4573  */
4574 bool NodePath::
4576  nassertr_always(!is_empty(), false);
4577  return node()->has_attrib(DepthWriteAttrib::get_class_slot());
4578 }
4579 
4580 /**
4581  * Returns true if depth-write rendering has been specifically set on this
4582  * node via set_depth_write(), or false if depth-write rendering has been
4583  * specifically disabled. If nothing has been specifically set, returns true.
4584  * See also has_depth_write().
4585  */
4586 bool NodePath::
4588  nassertr_always(!is_empty(), false);
4589  const RenderAttrib *attrib =
4590  node()->get_attrib(DepthWriteAttrib::get_class_slot());
4591  if (attrib != nullptr) {
4592  const DepthWriteAttrib *dta = DCAST(DepthWriteAttrib, attrib);
4593  return (dta->get_mode() != DepthWriteAttrib::M_off);
4594  }
4595 
4596  return true;
4597 }
4598 
4599 /**
4600  * This instructs the graphics driver to apply an offset or bias to the
4601  * generated depth values for rendered polygons, before they are written to
4602  * the depth buffer. This can be used to shift polygons forward slightly, to
4603  * resolve depth conflicts, or self-shadowing artifacts on thin objects. The
4604  * bias is always an integer number, and each integer increment represents the
4605  * smallest possible increment in Z that is sufficient to completely resolve
4606  * two coplanar polygons. Positive numbers are closer towards the camera.
4607  */
4608 void NodePath::
4609 set_depth_offset(int bias, int priority) {
4610  nassertv_always(!is_empty());
4611 
4612  node()->set_attrib(DepthOffsetAttrib::make(bias), priority);
4613 }
4614 
4615 /**
4616  * Completely removes any depth-offset adjustment that may have been set on
4617  * this node via set_depth_offset().
4618  */
4619 void NodePath::
4621  nassertv_always(!is_empty());
4622  node()->clear_attrib(DepthOffsetAttrib::get_class_slot());
4623 }
4624 
4625 /**
4626  * Returns true if a depth-offset adjustment has been explicitly set on this
4627  * particular node via set_depth_offset(). If this returns true, then
4628  * get_depth_offset() may be called to determine which has been set.
4629  */
4630 bool NodePath::
4632  nassertr_always(!is_empty(), false);
4633  return node()->has_attrib(DepthOffsetAttrib::get_class_slot());
4634 }
4635 
4636 /**
4637  * Returns the depth offset value if it has been specified using
4638  * set_depth_offset, or 0 if not.
4639  */
4640 int NodePath::
4642  nassertr_always(!is_empty(), 0);
4643  const RenderAttrib *attrib =
4644  node()->get_attrib(DepthOffsetAttrib::get_class_slot());
4645  if (attrib != nullptr) {
4646  const DepthOffsetAttrib *doa = DCAST(DepthOffsetAttrib, attrib);
4647  return doa->get_offset();
4648  }
4649 
4650  return 0;
4651 }
4652 
4653 /**
4654  * Performs a billboard-type rotate to the indicated camera node, one time
4655  * only, and leaves the object rotated. This is similar in principle to
4656  * heads_up().
4657  */
4658 void NodePath::
4659 do_billboard_axis(const NodePath &camera, PN_stdfloat offset) {
4660  nassertv_always(!is_empty());
4661 
4662  CPT(TransformState) transform = camera.get_transform(get_parent());
4663  const LMatrix4 &rel_mat = transform->get_mat();
4664 
4665  LVector3 up = LVector3::up();
4666  LVector3 rel_pos = -rel_mat.get_row3(3);
4667 
4668  LQuaternion quat;
4669  ::heads_up(quat, rel_pos, up);
4670  set_quat(quat);
4671 
4672  // Also slide the geometry towards the camera according to the offset
4673  // factor.
4674  if (offset != 0.0f) {
4675  LVector3 translate = rel_mat.get_row3(3);
4676  translate.normalize();
4677  translate *= offset;
4678  set_pos(translate);
4679  }
4680 }
4681 
4682 /**
4683  * Performs a billboard-type rotate to the indicated camera node, one time
4684  * only, and leaves the object rotated. This is similar in principle to
4685  * look_at(), although the point_eye billboard effect cannot be achieved using
4686  * the ordinary look_at() call.
4687  */
4688 void NodePath::
4689 do_billboard_point_eye(const NodePath &camera, PN_stdfloat offset) {
4690  nassertv_always(!is_empty());
4691 
4692  CPT(TransformState) transform = camera.get_transform(get_parent());
4693  const LMatrix4 &rel_mat = transform->get_mat();
4694 
4695  LVector3 up = LVector3::up() * rel_mat;
4696  LVector3 rel_pos = LVector3::forward() * rel_mat;
4697 
4698  LQuaternion quat;
4699  ::look_at(quat, rel_pos, up);
4700  set_quat(quat);
4701 
4702  // Also slide the geometry towards the camera according to the offset
4703  // factor.
4704  if (offset != 0.0f) {
4705  LVector3 translate = rel_mat.get_row3(3);
4706  translate.normalize();
4707  translate *= offset;
4708  set_pos(translate);
4709  }
4710 }
4711 
4712 /**
4713  * Performs a billboard-type rotate to the indicated camera node, one time
4714  * only, and leaves the object rotated. This is similar in principle to
4715  * look_at().
4716  */
4717 void NodePath::
4718 do_billboard_point_world(const NodePath &camera, PN_stdfloat offset) {
4719  nassertv_always(!is_empty());
4720 
4721  CPT(TransformState) transform = camera.get_transform(get_parent());
4722  const LMatrix4 &rel_mat = transform->get_mat();
4723 
4724  LVector3 up = LVector3::up();
4725  LVector3 rel_pos = -rel_mat.get_row3(3);
4726 
4727  LQuaternion quat;
4728  ::look_at(quat, rel_pos, up);
4729  set_quat(quat);
4730 
4731  // Also slide the geometry towards the camera according to the offset
4732  // factor.
4733  if (offset != 0.0f) {
4734  LVector3 translate = rel_mat.get_row3(3);
4735  translate.normalize();
4736  translate *= offset;
4737  set_pos(translate);
4738  }
4739 }
4740 
4741 /**
4742  * Puts a billboard transition on the node such that it will rotate in two
4743  * dimensions around the up axis, towards a specified "camera" instead of to
4744  * the viewing camera.
4745  */
4746 void NodePath::
4747 set_billboard_axis(const NodePath &camera, PN_stdfloat offset) {
4748  nassertv_always(!is_empty());
4749  CPT(RenderEffect) billboard = BillboardEffect::make
4750  (LVector3::up(), false, true,
4751  offset, camera, LPoint3(0.0f, 0.0f, 0.0f));
4752  node()->set_effect(billboard);
4753 }
4754 
4755 /**
4756  * Puts a billboard transition on the node such that it will rotate in three
4757  * dimensions about the origin, keeping its up vector oriented to the top of
4758  * the camera, towards a specified "camera" instead of to the viewing camera.
4759  */
4760 void NodePath::
4761 set_billboard_point_eye(const NodePath &camera, PN_stdfloat offset, bool fixed_depth) {
4762  nassertv_always(!is_empty());
4763  CPT(RenderEffect) billboard = BillboardEffect::make
4764  (LVector3::up(), true, false,
4765  offset, camera, LPoint3(0.0f, 0.0f, 0.0f), fixed_depth);
4766  node()->set_effect(billboard);
4767 }
4768 
4769 /**
4770  * Puts a billboard transition on the node such that it will rotate in three
4771  * dimensions about the origin, keeping its up vector oriented to the sky,
4772  * towards a specified "camera" instead of to the viewing camera.
4773  */
4774 void NodePath::
4775 set_billboard_point_world(const NodePath &camera, PN_stdfloat offset) {
4776  nassertv_always(!is_empty());
4777  CPT(RenderEffect) billboard = BillboardEffect::make
4778  (LVector3::up(), false, false,
4779  offset, camera, LPoint3(0.0f, 0.0f, 0.0f));
4780  node()->set_effect(billboard);
4781 }
4782 
4783 /**
4784  * Removes any billboard effect from the node.
4785  */
4786 void NodePath::
4788  nassertv_always(!is_empty());
4789  node()->clear_effect(BillboardEffect::get_class_type());
4790 }
4791 
4792 /**
4793  * Returns true if there is any billboard effect on the node.
4794  */
4795 bool NodePath::
4796 has_billboard() const {
4797  nassertr_always(!is_empty(), false);
4798  return node()->has_effect(BillboardEffect::get_class_type());
4799 }
4800 
4801 /**
4802  * Puts a compass effect on the node, so that it will retain a fixed rotation
4803  * relative to the reference node (or render if the reference node is empty)
4804  * regardless of the transforms above it.
4805  */
4806 void NodePath::
4807 set_compass(const NodePath &reference) {
4808  nassertv_always(!is_empty());
4809  node()->set_effect(CompassEffect::make(reference));
4810 }
4811 
4812 /**
4813  * Removes any compass effect from the node.
4814  */
4815 void NodePath::
4817  nassertv_always(!is_empty());
4818  node()->clear_effect(CompassEffect::get_class_type());
4819 }
4820 
4821 /**
4822  * Returns true if there is any compass effect on the node.
4823  */
4824 bool NodePath::
4825 has_compass() const {
4826  nassertr_always(!is_empty(), false);
4827  return node()->has_effect(CompassEffect::get_class_type());
4828 }
4829 
4830 /**
4831  * Specifically sets or disables transparent rendering mode on this particular
4832  * node. If no other nodes override, this will cause items with a non-1 value
4833  * for alpha color to be rendered partially transparent.
4834  */
4835 void NodePath::
4836 set_transparency(TransparencyAttrib::Mode mode, int priority) {
4837  nassertv_always(!is_empty());
4838 
4839  node()->set_attrib(TransparencyAttrib::make(mode), priority);
4840 }
4841 
4842 /**
4843  * Completely removes any transparency adjustment that may have been set on
4844  * this node via set_transparency(). The geometry at this level and below will
4845  * subsequently be rendered either transparent or not, to whatever other nodes
4846  * may have had set_transparency() on them.
4847  */
4848 void NodePath::
4850  nassertv_always(!is_empty());
4851  node()->clear_attrib(TransparencyAttrib::get_class_slot());
4852 }
4853 
4854 /**
4855  * Returns true if a transparent-rendering adjustment has been explicitly set
4856  * on this particular node via set_transparency(). If this returns true, then
4857  * get_transparency() may be called to determine whether transparency has been
4858  * explicitly enabled or explicitly disabled for this node.
4859  */
4860 bool NodePath::
4862  nassertr_always(!is_empty(), false);
4863  return node()->has_attrib(TransparencyAttrib::get_class_slot());
4864 }
4865 
4866 /**
4867  * Returns the transparent rendering that has been specifically set on this
4868  * node via set_transparency(), or M_none if nontransparent rendering has been
4869  * specifically set, or if nothing has been specifically set. See also
4870  * has_transparency(). This does not necessarily imply that the geometry will
4871  * or will not be rendered transparent, as there may be other nodes that
4872  * override.
4873  */
4874 TransparencyAttrib::Mode NodePath::
4876  nassertr_always(!is_empty(), TransparencyAttrib::M_none);
4877  const RenderAttrib *attrib =
4878  node()->get_attrib(TransparencyAttrib::get_class_slot());
4879  if (attrib != nullptr) {
4880  const TransparencyAttrib *ta = DCAST(TransparencyAttrib, attrib);
4881  return ta->get_mode();
4882  }
4883 
4884  return TransparencyAttrib::M_none;
4885 }
4886 
4887 /**
4888  * Specifically sets or disables a logical operation on this particular node.
4889  * If no other nodes override, this will cause geometry to be rendered without
4890  * color blending but instead using the given logical operator.
4891  */
4892 void NodePath::
4893 set_logic_op(LogicOpAttrib::Operation op, int priority) {
4894  nassertv_always(!is_empty());
4895 
4896  node()->set_attrib(LogicOpAttrib::make(op), priority);
4897 }
4898 
4899 /**
4900  * Completely removes any logical operation that may have been set on this
4901  * node via set_logic_op(). The geometry at this level and below will
4902  * subsequently be rendered using standard color blending.
4903  */
4904 void NodePath::
4906  nassertv_always(!is_empty());
4907  node()->clear_attrib(LogicOpAttrib::get_class_slot());
4908 }
4909 
4910 /**
4911  * Returns true if a logical operation has been explicitly set on this
4912  * particular node via set_logic_op(). If this returns true, then
4913  * get_logic_op() may be called to determine whether a logical operation has
4914  * been explicitly disabled for this node or set to particular operation.
4915  */
4916 bool NodePath::
4917 has_logic_op() const {
4918  nassertr_always(!is_empty(), false);
4919  return node()->has_attrib(LogicOpAttrib::get_class_slot());
4920 }
4921 
4922 /**
4923  * Returns the logical operation that has been specifically set on this node
4924  * via set_logic_op(), or O_none if standard color blending has been
4925  * specifically set, or if nothing has been specifically set. See also
4926  * has_logic_op(). This does not necessarily imply that the geometry will
4927  * or will not be rendered with the given logical operation, as there may be
4928  * other nodes that override.
4929  */
4930 LogicOpAttrib::Operation NodePath::
4931 get_logic_op() const {
4932  nassertr_always(!is_empty(), LogicOpAttrib::O_none);
4933  const RenderAttrib *attrib =
4934  node()->get_attrib(LogicOpAttrib::get_class_slot());
4935  if (attrib != nullptr) {
4936  const LogicOpAttrib *ta = DCAST(LogicOpAttrib, attrib);
4937  return ta->get_operation();
4938  }
4939 
4940  return LogicOpAttrib::O_none;
4941 }
4942 
4943 /**
4944  * Specifies the antialiasing type that should be applied at this node and
4945  * below. See AntialiasAttrib.
4946  */
4947 void NodePath::
4948 set_antialias(unsigned short mode, int priority) {
4949  nassertv_always(!is_empty());
4950 
4951  node()->set_attrib(AntialiasAttrib::make(mode), priority);
4952 }
4953 
4954 /**
4955  * Completely removes any antialias setting that may have been set on this
4956  * node via set_antialias().
4957  */
4958 void NodePath::
4960  nassertv_always(!is_empty());
4961  node()->clear_attrib(AntialiasAttrib::get_class_slot());
4962 }
4963 
4964 /**
4965  * Returns true if an antialias setting has been explicitly mode on this
4966  * particular node via set_antialias(). If this returns true, then
4967  * get_antialias() may be called to determine what the setting was.
4968  */
4969 bool NodePath::
4970 has_antialias() const {
4971  nassertr_always(!is_empty(), false);
4972  return node()->has_attrib(AntialiasAttrib::get_class_slot());
4973 }
4974 
4975 /**
4976  * Returns the antialias setting that has been specifically set on this node
4977  * via set_antialias(), or M_none if no setting has been made.
4978  */
4979 unsigned short NodePath::
4980 get_antialias() const {
4981  nassertr_always(!is_empty(), AntialiasAttrib::M_none);
4982  const RenderAttrib *attrib =
4983  node()->get_attrib(AntialiasAttrib::get_class_slot());
4984  if (attrib != nullptr) {
4985  const AntialiasAttrib *ta = DCAST(AntialiasAttrib, attrib);
4986  return ta->get_mode();
4987  }
4988 
4989  return AntialiasAttrib::M_none;
4990 }
4991 
4992 /**
4993  * Returns true if an audio volume has been applied to the referenced node,
4994  * false otherwise. It is still possible that volume at this node might have
4995  * been scaled by an ancestor node.
4996  */
4997 bool NodePath::
4999  nassertr_always(!is_empty(), false);
5000  return node()->has_attrib(AudioVolumeAttrib::get_class_slot());
5001 }
5002 
5003 /**
5004  * Completely removes any audio volume from the referenced node. This is
5005  * preferable to simply setting the audio volume to identity, as it also
5006  * removes the overhead associated with having an audio volume at all.
5007  */
5008 void NodePath::
5010  nassertv_always(!is_empty());
5011  node()->clear_attrib(AudioVolumeAttrib::get_class_slot());
5012 }
5013 
5014 /**
5015  * Sets the audio volume component of the transform
5016  */
5017 void NodePath::
5018 set_audio_volume(PN_stdfloat volume, int priority) {
5019  nassertv_always(!is_empty());
5020 
5021  const RenderAttrib *attrib =
5022  node()->get_attrib(AudioVolumeAttrib::get_class_slot());
5023  if (attrib != nullptr) {
5024  priority = max(priority,
5025  node()->get_state()->get_override(AudioVolumeAttrib::get_class_slot()));
5026  CPT(AudioVolumeAttrib) ava = DCAST(AudioVolumeAttrib, attrib);
5027 
5028  // Modify the existing AudioVolumeAttrib to add the indicated volume.
5029  node()->set_attrib(ava->set_volume(volume), priority);
5030 
5031  } else {
5032  // Create a new AudioVolumeAttrib for this node.
5033  node()->set_attrib(AudioVolumeAttrib::make(volume), priority);
5034  }
5035 }
5036 
5037 /**
5038  * Disables any audio volume attribute inherited from above. This is not the
5039  * same thing as clear_audio_volume(), which undoes any previous
5040  * set_audio_volume() operation on this node; rather, this actively disables
5041  * any set_audio_volume() that might be inherited from a parent node.
5042  *
5043  * It is legal to specify a new volume on the same node with a subsequent call
5044  * to set_audio_volume(); this new scale will apply to lower nodes.
5045  */
5046 void NodePath::
5047 set_audio_volume_off(int priority) {
5048  nassertv_always(!is_empty());
5049  node()->set_attrib(AudioVolumeAttrib::make_off(), priority);
5050 }
5051 
5052 /**
5053  * Returns the complete audio volume that has been applied to this node via a
5054  * previous call to set_audio_volume(), or 1. (identity) if no volume has been
5055  * applied to this particular node.
5056  */
5057 PN_stdfloat NodePath::
5059  const RenderAttrib *attrib =
5060  node()->get_attrib(AudioVolumeAttrib::get_class_slot());
5061  if (attrib != nullptr) {
5062  const AudioVolumeAttrib *ava = DCAST(AudioVolumeAttrib, attrib);
5063  return ava->get_volume();
5064  }
5065 
5066  return 1.0f;
5067 }
5068 
5069 /**
5070  * Returns the complete audio volume for this node taking highers nodes in the
5071  * graph into account.
5072  */
5073 PN_stdfloat NodePath::
5075  CPT(RenderState) net_state = get_net_state();
5076  const RenderAttrib *attrib = net_state->get_attrib(AudioVolumeAttrib::get_class_slot());
5077  if (attrib != nullptr) {
5078  const AudioVolumeAttrib *ava = DCAST(AudioVolumeAttrib, attrib);
5079  if (ava != nullptr) {
5080  return ava->get_volume();
5081  }
5082  }
5083 
5084  return 1.0f;
5085 }
5086 
5087 /**
5088  * Returns the NodePath at or above the referenced node that is hidden to the
5089  * indicated camera(s), or an empty NodePath if no ancestor of the referenced
5090  * node is hidden (and the node should be visible).
5091  */
5093 get_hidden_ancestor(DrawMask camera_mask, Thread *current_thread) const {
5094  int pipeline_stage = current_thread->get_pipeline_stage();
5095 
5096  NodePathComponent *comp;
5097  for (comp = _head;
5098  comp != nullptr;
5099  comp = comp->get_next(pipeline_stage, current_thread)) {
5100  PandaNode *node = comp->get_node();
5101  if (node->is_overall_hidden() ||
5102  ((node->get_draw_show_mask() | ~node->get_draw_control_mask()) & camera_mask).is_zero()) {
5103  NodePath result;
5104  result._head = comp;
5105  return result;
5106  }
5107  }
5108 
5109  return not_found();
5110 }
5111 
5112 /**
5113  * Removes the referenced node (and the entire subgraph below this node) from
5114  * the scene graph in any normal sense. The node will no longer be visible
5115  * and is not tested for collisions; furthermore, no normal scene graph
5116  * traversal will visit the node. The node's bounding volume no longer
5117  * contributes to its parent's bounding volume.
5118  *
5119  * A stashed node cannot be located by a normal find() operation (although a
5120  * special find string can still retrieve it).
5121  */
5122 void NodePath::
5123 stash(int sort, Thread *current_thread) {
5124  nassertv_always(!is_singleton() && !is_empty());
5125  nassertv(verify_complete());
5126 
5127  int pipeline_stage = current_thread->get_pipeline_stage();
5128  bool reparented = PandaNode::reparent(_head->get_next(pipeline_stage, current_thread),
5129  _head, sort, true, pipeline_stage,
5130  current_thread);
5131  nassertv(reparented);
5132 }
5133 
5134 /**
5135  * Undoes the effect of a previous stash() on this node: makes the referenced
5136  * node (and the entire subgraph below this node) once again part of the scene
5137  * graph.
5138  */
5139 void NodePath::
5140 unstash(int sort, Thread *current_thread) {
5141  nassertv_always(!is_singleton() && !is_empty());
5142  nassertv(verify_complete());
5143 
5144  int pipeline_stage = current_thread->get_pipeline_stage();
5145  bool reparented = PandaNode::reparent(_head->get_next(pipeline_stage, current_thread),
5146  _head, sort, false, pipeline_stage,
5147  current_thread);
5148  nassertv(reparented);
5149 }
5150 
5151 /**
5152  * Unstashes this node and all stashed child nodes.
5153  */
5154 void NodePath::
5155 unstash_all(Thread *current_thread) {
5156  NodePathCollection stashed_descendents = find_all_matches("**/@@*");
5157  stashed_descendents.unstash();
5158  unstash(0, current_thread);
5159 }
5160 
5161 /**
5162  * Returns the NodePath at or above the referenced node that is stashed, or an
5163  * empty NodePath if no ancestor of the referenced node is stashed (and the
5164  * node should be visible).
5165  */
5167 get_stashed_ancestor(Thread *current_thread) const {
5168  NodePathComponent *comp = _head;
5169  if (comp != nullptr) {
5170  int pipeline_stage = current_thread->get_pipeline_stage();
5171  NodePathComponent *next = comp->get_next(pipeline_stage, current_thread);
5172 
5173  while (next != nullptr) {
5174  PandaNode *node = comp->get_node();
5175  PandaNode *parent_node = next->get_node();
5176 
5177  if (parent_node->find_stashed(node) >= 0) {
5178  NodePath result;
5179  result._head = comp;
5180  return result;
5181  }
5182 
5183  comp = next;
5184  next = next->get_next(pipeline_stage, current_thread);
5185  }
5186  }
5187 
5188  return not_found();
5189 }
5190 
5191 /**
5192  * Returns true if the two paths are equivalent; that is, if they contain the
5193  * same list of nodes in the same order.
5194  */
5195 bool NodePath::
5196 operator == (const WeakNodePath &other) const {
5197  return (other == *this);
5198 }
5199 
5200 /**
5201  * Returns true if the two paths are not equivalent.
5202  */
5203 bool NodePath::
5204 operator != (const WeakNodePath &other) const {
5205  return (other != *this);
5206 }
5207 
5208 /**
5209  * Returns true if this NodePath sorts before the other one, false otherwise.
5210  * The sorting order of two nonequivalent NodePaths is consistent but
5211  * undefined, and is useful only for storing NodePaths in a sorted container
5212  * like an STL set.
5213  */
5214 bool NodePath::
5215 operator < (const WeakNodePath &other) const {
5216  return other.compare_to(*this) > 0;
5217 }
5218 
5219 /**
5220  * Returns a number less than zero if this NodePath sorts before the other
5221  * one, greater than zero if it sorts after, or zero if they are equivalent.
5222  *
5223  * Two NodePaths are considered equivalent if they consist of exactly the same
5224  * list of nodes in the same order. Otherwise, they are different; different
5225  * NodePaths will be ranked in a consistent but undefined ordering; the
5226  * ordering is useful only for placing the NodePaths in a sorted container
5227  * like an STL set.
5228  */
5229 int NodePath::
5230 compare_to(const WeakNodePath &other) const {
5231  return -other.compare_to(*this);
5232 }
5233 
5234 /**
5235  * Returns true if all of the nodes described in the NodePath are connected,
5236  * or false otherwise.
5237  */
5238 bool NodePath::
5239 verify_complete(Thread *current_thread) const {
5240  if (is_empty()) {
5241  return true;
5242  }
5243 
5244 #ifdef HAVE_THREADS
5245  if (Thread::is_true_threads()) {
5246  // In a threaded environment, we can't reliably test this, since a sub-
5247  // thread may be mucking with the NodePath's ancestry as we try to
5248  // validate it. NodePaths are inherently not thread-safe, but generally
5249  // that's not an issue.
5250  return true;
5251  }
5252 #endif // HAVE_THREADS
5253 
5254  PStatTimer timer(_verify_complete_pcollector);
5255 
5256  const NodePathComponent *comp = _head;
5257  nassertr(comp != nullptr, false);
5258 
5259  int pipeline_stage = current_thread->get_pipeline_stage();
5260 
5261  PandaNode *node = comp->get_node();
5262  nassertr(node != nullptr, false);
5263  int length = comp->get_length(pipeline_stage, current_thread);
5264 
5265  comp = comp->get_next(pipeline_stage, current_thread);
5266  length--;
5267  while (comp != nullptr) {
5268  PandaNode *next_node = comp->get_node();
5269  nassertr(next_node != nullptr, false);
5270 
5271  if (node->find_parent(next_node) < 0) {
5272  pgraph_cat.warning()
5273  << *this << " is incomplete; " << *node << " is not a child of "
5274  << *next_node << "\n";
5275  return false;
5276  }
5277 
5278  if (comp->get_length(pipeline_stage, current_thread) != length) {
5279  pgraph_cat.warning()
5280  << *this << " is incomplete; length at " << *next_node
5281  << " indicates " << comp->get_length(pipeline_stage, current_thread)
5282  << " while length at " << *node << " indicates " << length << "\n";
5283  return false;
5284  }
5285 
5286  node = next_node;
5287  comp = comp->get_next(pipeline_stage, current_thread);
5288  length--;
5289  }
5290 
5291  return true;
5292 }
5293 
5294 /**
5295  * Walks through the scene graph beginning at the bottom node, and internally
5296  * adjusts any GeomVertexFormats for optimal rendering on the indicated GSG.
5297  * If this step is not done prior to rendering, the formats will be optimized
5298  * at render time instead, for a small cost.
5299  *
5300  * It is not normally necessary to do this on a model loaded directly from
5301  * disk, since the loader will do this by default.
5302  */
5303 void NodePath::
5305  nassertv_always(!is_empty());
5306 
5307  CPT(RenderState) state = RenderState::make_empty();
5308  if (has_parent()) {
5309  state = get_parent().get_net_state();
5310  }
5311 
5312  SceneGraphReducer gr(gsg);
5313  gr.premunge(node(), state);
5314 }
5315 
5316 /**
5317  * Walks through the scene graph beginning at the bottom node, and does
5318  * whatever initialization is required to render the scene properly with the
5319  * indicated GSG. It is not strictly necessary to call this, since the GSG
5320  * will initialize itself when the scene is rendered, but this may take some
5321  * of the overhead away from that process.
5322  *
5323  * In particular, this will ensure that textures and vertex buffers within the
5324  * scene are loaded into graphics memory.
5325  */
5326 void NodePath::
5328  nassertv_always(!is_empty());
5329 
5330  node()->prepare_scene(gsg, get_net_state());
5331 }
5332 
5333 /**
5334  * Causes the bounding volume of the bottom node and all of its descendants
5335  * (that is, the bounding volume associated with the the bottom arc) to be
5336  * rendered, if possible. The rendering method is less than optimal; this is
5337  * intended primarily for debugging.
5338  */
5339 void NodePath::
5341  nassertv_always(!is_empty());
5342  node()->set_effect(ShowBoundsEffect::make(false));
5343 }
5344 
5345 /**
5346  * Similar to show_bounds(), this draws a bounding box representing the
5347  * "tight" bounds of this node and all of its descendants. The bounding box
5348  * is recomputed every frame by reexamining all of the vertices; this is far
5349  * from efficient, but this is intended for debugging.
5350  */
5351 void NodePath::
5353  nassertv_always(!is_empty());
5354  node()->set_effect(ShowBoundsEffect::make(true));
5355 }
5356 
5357 /**
5358  * Stops the rendering of the bounding volume begun with show_bounds().
5359  */
5360 void NodePath::
5362  nassertv_always(!is_empty());
5363  node()->clear_effect(ShowBoundsEffect::get_class_type());
5364 }
5365 
5366 /**
5367  * Returns a newly-allocated bounding volume containing the bottom node and
5368  * all of its descendants. This is the bounding volume on the bottom arc,
5369  * converted to the local coordinate space of the node.
5370  */
5371 PT(BoundingVolume) NodePath::
5372 get_bounds(Thread *current_thread) const {
5373  nassertr_always(!is_empty(), new BoundingSphere);
5374  return node()->get_bounds(current_thread)->make_copy();
5375 }
5376 
5377 /**
5378  * Forces the recomputing of all the bounding volumes at every node in the
5379  * subgraph beginning at this node and below.
5380  *
5381  * This should not normally need to be called, since the bounding volumes are
5382  * supposed to be recomputed automatically when necessary. It may be useful
5383  * when debugging, to verify that the bounding volumes have not become
5384  * inadvertently stale; it may also be useful to force animated characters to
5385  * update their bounding volumes (which does not presently happen
5386  * automatically).
5387  */
5388 void NodePath::
5389 force_recompute_bounds() {
5390  nassertv_always(!is_empty());
5391  r_force_recompute_bounds(node());
5392 }
5393 
5394 /**
5395  * Writes a description of the bounding volume containing the bottom node and
5396  * all of its descendants to the indicated output stream.
5397  */
5398 void NodePath::
5399 write_bounds(ostream &out) const {
5400  get_bounds()->write(out);
5401 }
5402 
5403 /**
5404  * Calculates the minimum and maximum vertices of all Geoms at this NodePath's
5405  * bottom node and below. This is a tight bounding box; it will generally be
5406  * tighter than the bounding volume returned by get_bounds() (but it is more
5407  * expensive to compute).
5408  *
5409  * The bounding box is computed relative to the parent node's coordinate
5410  * system by default. You can optionally specify a different NodePath to
5411  * compute the bounds relative to. Note that the box is always axis-aligned
5412  * against the given NodePath's coordinate system, so you might get a
5413  * differently sized box depending on which node you pass.
5414  *
5415  * The return value is true if any points are within the bounding volume, or
5416  * false if none are.
5417  */
5418 bool NodePath::
5419 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
5420  const NodePath &other, Thread *current_thread) const {
5421  min_point.set(0.0f, 0.0f, 0.0f);
5422  max_point.set(0.0f, 0.0f, 0.0f);
5423  nassertr_always(!is_empty(), false);
5424 
5425  CPT(TransformState) transform = TransformState::make_identity();
5426  if (!other.is_empty()) {
5427  transform = get_transform(other)->compose(get_transform()->get_inverse());
5428  }
5429 
5430  bool found_any = false;
5431  node()->calc_tight_bounds(min_point, max_point, found_any,
5432  move(transform), current_thread);
5433 
5434  return found_any;
5435 }
5436 
5437 /**
5438  * Analyzes the geometry below this node and reports the number of vertices,
5439  * triangles, etc. This is the same information reported by the bam-info
5440  * program.
5441  */
5442 /*
5443 NB: Had to remove this function to avoid circular dependency when
5444 moving SceneGraphAnalyzer into pgraphnodes, attempting to reduce size
5445 of pgraph. This function is now defined as a Python extension
5446 function instead.
5447 
5448 void NodePath::
5449 analyze() const {
5450  nassertv_always(!is_empty());
5451  SceneGraphAnalyzer sga;
5452  sga.add_node(node());
5453 
5454  if (sga.get_num_lod_nodes() == 0) {
5455  sga.write(nout);
5456 
5457  } else {
5458  nout << "At highest LOD:\n";
5459  SceneGraphAnalyzer sga2;
5460  sga2.set_lod_mode(SceneGraphAnalyzer::LM_highest);
5461  sga2.add_node(node());
5462  sga2.write(nout);
5463 
5464  nout << "\nAt lowest LOD:\n";
5465  sga2.clear();
5466  sga2.set_lod_mode(SceneGraphAnalyzer::LM_lowest);
5467  sga2.add_node(node());
5468  sga2.write(nout);
5469 
5470  nout << "\nAll nodes:\n";
5471  sga.write(nout);
5472  }
5473 }
5474 */
5475 
5476 /**
5477  * Lightly flattens out the hierarchy below this node by applying transforms,
5478  * colors, and texture matrices from the nodes onto the vertices, but does not
5479  * remove any nodes.
5480  *
5481  * This can result in improved rendering performance because there will be
5482  * fewer transforms in the resulting scene graph, but the number of nodes will
5483  * remain the same.
5484  *
5485  * In particular, any NodePaths that reference nodes within this hierarchy
5486  * will not be damaged. However, since this operation will remove transforms
5487  * from the scene graph, it may be dangerous to apply to nodes where you
5488  * expect to dynamically modify the transform, or where you expect the
5489  * geometry to remain in a particular local coordinate system.
5490  *
5491  * The return value is always 0, since flatten_light does not remove any
5492  * nodes.
5493  */
5494 int NodePath::
5496  nassertr_always(!is_empty(), 0);
5497  SceneGraphReducer gr;
5498  gr.apply_attribs(node());
5499 
5500  return 0;
5501 }
5502 
5503 /**
5504  * A more thorough flattening than flatten_light(), this first applies all the
5505  * transforms, colors, and texture matrices from the nodes onto the vertices,
5506  * and then removes unneeded grouping nodes--nodes that have exactly one
5507  * child, for instance, but have no special properties in themselves.
5508  *
5509  * This results in improved performance over flatten_light() because the
5510  * number of nodes in the scene graph is reduced.
5511  *
5512  * The return value is the number of nodes removed.
5513  */
5514 int NodePath::
5516  nassertr_always(!is_empty(), 0);
5517  SceneGraphReducer gr;
5518  gr.apply_attribs(node());
5519  int num_removed = gr.flatten(node(), 0);
5520 
5521  if (flatten_geoms) {
5523  gr.collect_vertex_data(node());
5524  gr.unify(node(), true);
5525  }
5526 
5527  return num_removed;
5528 }
5529 
5530 /**
5531  * The strongest possible flattening. This first applies all of the
5532  * transforms to the vertices, as in flatten_medium(), but then it will
5533  * combine sibling nodes together when possible, in addition to removing
5534  * unnecessary parent-child nodes. This can result in substantially fewer
5535  * nodes, but any nicely-grouped hierachical bounding volumes may be lost.
5536  *
5537  * It is generally a good idea to apply this kind of flattening only to nodes
5538  * that will be culled largely as a single unit, like a car. Applying this to
5539  * an entire scene may result in overall poorer performance because of less-
5540  * effective culling.
5541  */
5542 int NodePath::
5544  nassertr_always(!is_empty(), 0);
5545  SceneGraphReducer gr;
5546  gr.apply_attribs(node());
5547  int num_removed = gr.flatten(node(), ~0);
5548 
5549  if (flatten_geoms) {
5551  gr.collect_vertex_data(node(), ~(SceneGraphReducer::CVD_format | SceneGraphReducer::CVD_name | SceneGraphReducer::CVD_animation_type));
5552  gr.unify(node(), false);
5553  }
5554 
5555  return num_removed;
5556 }
5557 
5558 /**
5559  * Removes textures from Geoms at this node and below by applying the texture
5560  * colors to the vertices. This is primarily useful to simplify a low-LOD
5561  * model. The texture colors are replaced by flat colors that approximate the
5562  * original textures.
5563  *
5564  * Only the bottommost texture on each Geom is used (if there is more than
5565  * one), and it is applied as if it were M_modulate, and WM_repeat, regardless
5566  * of its actual settings. If the texture has a simple_ram_image, this may be
5567  * used if the main image isn't resident.
5568  *
5569  * After this call, there will be no texturing specified at this level and
5570  * below. Of course, there might still be texturing inherited from above.
5571  */
5572 void NodePath::
5574  nassertv_always(!is_empty());
5575  SceneGraphReducer gr;
5576  gr.apply_attribs(node(), SceneGraphReducer::TT_apply_texture_color | SceneGraphReducer::TT_tex_matrix | SceneGraphReducer::TT_other);
5577 }
5578 
5579 /**
5580  * Returns the lowest ancestor of this node that contains a tag definition
5581  * with the indicated key, if any, or an empty NodePath if no ancestor of this
5582  * node contains this tag definition. See set_tag().
5583  */
5585 find_net_tag(const string &key) const {
5586  if (is_empty()) {
5587  return NodePath::not_found();
5588  }
5589  if (has_tag(key)) {
5590  return *this;
5591  }
5592  return get_parent().find_net_tag(key);
5593 }
5594 
5595 /**
5596  * Writes the contents of this node and below out to a bam file with the
5597  * indicated filename. This file may then be read in again, as is, at some
5598  * later point. Returns true if successful, false on some kind of error.
5599  */
5600 bool NodePath::
5601 write_bam_file(const Filename &filename) const {
5602  nassertr_always(!is_empty(), false);
5603 
5604  BamFile bam_file;
5605 
5606  bool okflag = false;
5607 
5608  if (bam_file.open_write(filename)) {
5609  // Tell the BamWriter which node is the root node, for making NodePaths
5610  // relative to when writing them out to the file.
5611  bam_file.get_writer()->set_root_node(node());
5612 
5613  if (bam_file.write_object(node())) {
5614  okflag = true;
5615  }
5616  bam_file.close();
5617  }
5618  return okflag;
5619 }
5620 
5621 /**
5622  * Writes the contents of this node and below out to the indicated stream.
5623  */
5624 bool NodePath::
5625 write_bam_stream(ostream &out) const {
5626  nassertr_always(!is_empty(), false);
5627 
5628  BamFile bam_file;
5629 
5630  bool okflag = false;
5631 
5632  if (bam_file.open_write(out)) {
5633  // Tell the BamWriter which node is the root node, for making NodePaths
5634  // relative to when writing them out to the file.
5635  bam_file.get_writer()->set_root_node(node());
5636 
5637  if (bam_file.write_object(node())) {
5638  okflag = true;
5639  }
5640  bam_file.close();
5641  }
5642  return okflag;
5643 }
5644 
5645 /**
5646  * Converts the NodePath object into a single stream of data using a
5647  * BamWriter, and stores that data in the indicated string. Returns true on
5648  * success, false on failure.
5649  *
5650  * If the BamWriter is NULL, this behaves the same way as
5651  * NodePath::write_bam_stream() and PandaNode::encode_to_bam_stream(), in the
5652  * sense that it only writes this node and all nodes below it.
5653  *
5654  * However, if the BamWriter is not NULL, it behaves very differently. In
5655  * this case, it encodes the *entire graph* of all nodes connected to the
5656  * NodePath, including all parent nodes and siblings. This is necessary for
5657  * correct streaming of related NodePaths and restoration of instances, etc.,
5658  * but it does mean you must detach() a node before writing it if you want to
5659  * limit the nodes that get written.
5660  *
5661  * This method is used by __reduce__ to handle streaming of NodePaths to a
5662  * pickle file. The BamWriter case is used by the direct.stdpy.pickle module,
5663  * while the saner, non-BamWriter case is used when the standard pickle module
5664  * calls this function.
5665  */
5666 bool NodePath::
5667 encode_to_bam_stream(vector_uchar &data, BamWriter *writer) const {
5668  data.clear();
5669  ostringstream stream;
5670 
5671  DatagramBuffer buffer;
5672  BamWriter local_writer;
5673  bool used_local_writer = false;
5674  if (writer == nullptr) {
5675  // Create our own writer.
5676 
5677  if (!buffer.write_header(_bam_header)) {
5678  return false;
5679  }
5680  writer = &local_writer;
5681  used_local_writer = true;
5682  }
5683 
5684  writer->set_target(&buffer);
5685 
5686  int num_nodes = get_num_nodes();
5687  if (used_local_writer && num_nodes > 1) {
5688  // In this case--no BamWriter--we only write the bottom node.
5689  num_nodes = 1;
5690  }
5691 
5692  // Tell the BamWriter which node is the root node, for making NodePaths
5693  // relative to when writing them out to the file.
5694  if (!is_empty()) {
5695  writer->set_root_node(node());
5696  }
5697 
5698  // Write an initial Datagram to represent the error type and number of
5699  // nodes.
5700  Datagram dg;
5701  dg.add_uint8(_error_type);
5702  dg.add_int32(num_nodes);
5703 
5704  if (!buffer.put_datagram(dg)) {
5705  writer->set_target(nullptr);
5706  return false;
5707  }
5708 
5709  // Now write the nodes, one at a time.
5710  for (int i = 0; i < num_nodes; ++i) {
5711  PandaNode *node = get_node(num_nodes - i - 1);
5712  nassertr(node != nullptr, false);
5713  if (!writer->write_object(node)) {
5714  writer->set_target(nullptr);
5715  return false;
5716  }
5717  }
5718  writer->set_target(nullptr);
5719 
5720  buffer.swap_data(data);
5721  return true;
5722 }
5723 
5724 /**
5725  * Reads the string created by a previous call to encode_to_bam_stream(), and
5726  * extracts and returns the NodePath on that string. Returns NULL on error.
5727  */
5729 decode_from_bam_stream(vector_uchar data, BamReader *reader) {
5730  NodePath result;
5731 
5732  DatagramBuffer buffer(move(data));
5733 
5734  BamReader local_reader;
5735  if (reader == nullptr) {
5736  // Create a local reader.
5737 
5738  string head;
5739  if (!buffer.read_header(head, _bam_header.size())) {
5740  return NodePath::fail();
5741  }
5742 
5743  if (head != _bam_header) {
5744  return NodePath::fail();
5745  }
5746 
5747  reader = &local_reader;
5748  }
5749 
5750  reader->set_source(&buffer);
5751 
5752  // One initial datagram to encode the error type, and the number of nodes.
5753  Datagram dg;
5754  if (!buffer.get_datagram(dg)) {
5755  return NodePath::fail();
5756  }
5757 
5758  DatagramIterator dgi(dg);
5759  ErrorType error_type = (ErrorType)dgi.get_uint8();
5760  int num_nodes = dgi.get_int32();
5761  if (num_nodes == 0) {
5762  // An empty NodePath.
5763  result._error_type = error_type;
5764 
5765  } else {
5766  // A real NodePath. Ignore error_type.
5767  for (int i = 0; i < num_nodes; ++i) {
5768  TypedWritable *object = reader->read_object();
5769 
5770  if (object == nullptr ||
5771  !object->is_of_type(PandaNode::get_class_type())) {
5772  reader->set_source(nullptr);
5773  return NodePath::fail();
5774  }
5775 
5776  if (!reader->resolve()) {
5777  reader->set_source(nullptr);
5778  return NodePath::fail();
5779  }
5780 
5781  PandaNode *node = DCAST(PandaNode, object);
5782  result = NodePath(result, node);
5783  }
5784  }
5785 
5786  reader->set_source(nullptr);
5787 
5788  return result;
5789 }
5790 
5791 /**
5792  * Walks up from both NodePaths to find the first node that both have in
5793  * common, if any. Fills a_count and b_count with the number of nodes below
5794  * the common node in each path.
5795  *
5796  * The return value is the NodePathComponent of the node they have in common,
5797  * or NULL if they have nothing in common.
5798  */
5799 NodePathComponent *NodePath::
5800 find_common_ancestor(const NodePath &a, const NodePath &b,
5801  int &a_count, int &b_count, Thread *current_thread) {
5802  nassertr(!a.is_empty() && !b.is_empty(), nullptr);
5803  NodePathComponent *ac = a._head;
5804  NodePathComponent *bc = b._head;
5805  a_count = 0;
5806  b_count = 0;
5807 
5808  int pipeline_stage = current_thread->get_pipeline_stage();
5809 
5810  // Shorten up the longer one until they are the same length.
5811  while (ac->get_length(pipeline_stage, current_thread) > bc->get_length(pipeline_stage, current_thread)) {
5812  nassertr(ac != nullptr, nullptr);
5813  ac = ac->get_next(pipeline_stage, current_thread);
5814  a_count++;
5815  }
5816  while (bc->get_length(pipeline_stage, current_thread) > ac->get_length(pipeline_stage, current_thread)) {
5817  nassertr(bc != nullptr, nullptr);
5818  bc = bc->get_next(pipeline_stage, current_thread);
5819  b_count++;
5820  }
5821 
5822  // Now shorten them both up until we reach the same component.
5823  while (ac != bc) {
5824  // These shouldn't go to NULL unless they both go there together.
5825  nassertr(ac != nullptr, nullptr);
5826  nassertr(bc != nullptr, nullptr);
5827  ac = ac->get_next(pipeline_stage, current_thread);
5828  a_count++;
5829  bc = bc->get_next(pipeline_stage, current_thread);
5830  b_count++;
5831  }
5832 
5833  return ac;
5834 }
5835 
5836 /**
5837  * Recursively determines the net state changes to the indicated component
5838  * node from the root of the graph.
5839  */
5840 CPT(RenderState) NodePath::
5841 r_get_net_state(NodePathComponent *comp, Thread *current_thread) const {
5842  if (comp == nullptr) {
5843  return RenderState::make_empty();
5844  } else {
5845  CPT(RenderState) state = comp->get_node()->get_state(current_thread);
5846  int pipeline_stage = current_thread->get_pipeline_stage();
5847  return r_get_net_state(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(state);
5848  }
5849 }
5850 
5851 /**
5852  * Recursively determines the net state changes to the indicated component
5853  * node from the nth node above it. If n exceeds the length of the path, this
5854  * returns the net transform from the root of the graph.
5855  */
5856 CPT(RenderState) NodePath::
5857 r_get_partial_state(NodePathComponent *comp, int n,
5858  Thread *current_thread) const {
5859  if (n == 0 || comp == nullptr) {
5860  return RenderState::make_empty();
5861  } else {
5862  CPT(RenderState) state = comp->get_node()->get_state(current_thread);
5863  int pipeline_stage = current_thread->get_pipeline_stage();
5864  return r_get_partial_state(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(state);
5865  }
5866 }
5867 
5868 /**
5869  * Recursively determines the net transform to the indicated component node
5870  * from the root of the graph.
5871  */
5872 CPT(TransformState) NodePath::
5873 r_get_net_transform(NodePathComponent *comp, Thread *current_thread) const {
5874  if (comp == nullptr) {
5875  return TransformState::make_identity();
5876  } else {
5877  PandaNode *node = comp->get_node();
5878  int pipeline_stage = current_thread->get_pipeline_stage();
5879  CPT(TransformState) net_transform = r_get_net_transform(comp->get_next(pipeline_stage, current_thread), current_thread);
5880 
5881  PandaNode::CDReader node_cdata(node->_cycler, current_thread);
5882  if (!node_cdata->_effects->has_adjust_transform()) {
5883  if (node_cdata->_transform->is_identity()) {
5884  return net_transform;
5885  } else {
5886  return net_transform->compose(node_cdata->_transform);
5887  }
5888  } else {
5889  CPT(TransformState) transform = node_cdata->_transform.p();
5890  node_cdata->_effects->adjust_transform(net_transform, transform, node);
5891  return net_transform->compose(transform);
5892  }
5893  }
5894 }
5895 
5896 /**
5897  * Recursively determines the net transform to the indicated component node
5898  * from the nth node above it. If n exceeds the length of the path, this
5899  * returns the net transform from the root of the graph.
5900  *
5901  * If any node in the path had a net_transform effect applied, returns NULL--
5902  * in this case the partial transform cannot be easily determined.
5903  */
5904 CPT(TransformState) NodePath::
5905 r_get_partial_transform(NodePathComponent *comp, int n,
5906  Thread *current_thread) const {
5907  if (n == 0 || comp == nullptr) {
5908  return TransformState::make_identity();
5909  } else {
5910  PandaNode *node = comp->get_node();
5911  PandaNode::CDReader node_cdata(node->_cycler, current_thread);
5912  if (node_cdata->_effects->has_adjust_transform()) {
5913  return nullptr;
5914  }
5915  int pipeline_stage = current_thread->get_pipeline_stage();
5916  CPT(TransformState) partial = r_get_partial_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread);
5917  if (partial == nullptr) {
5918  return nullptr;
5919  }
5920  if (node_cdata->_transform->is_identity()) {
5921  return partial;
5922  } else {
5923  return partial->compose(node_cdata->_transform);
5924  }
5925  }
5926 }
5927 
5928 /**
5929  * Recursively determines the net "previous" transform to the indicated
5930  * component node from the root of the graph.
5931  */
5932 CPT(TransformState) NodePath::
5933 r_get_net_prev_transform(NodePathComponent *comp, Thread *current_thread) const {
5934  if (comp == nullptr) {
5935  return TransformState::make_identity();
5936  } else {
5937  CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread);
5938  int pipeline_stage = current_thread->get_pipeline_stage();
5939  return r_get_net_prev_transform(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(transform);
5940  }
5941 }
5942 
5943 /**
5944  * Recursively determines the net "previous" transform to the indicated
5945  * component node from the nth node above it. If n exceeds the length of the
5946  * path, this returns the net previous transform from the root of the graph.
5947  */
5948 CPT(TransformState) NodePath::
5949 r_get_partial_prev_transform(NodePathComponent *comp, int n, Thread *current_thread) const {
5950  if (n == 0 || comp == nullptr) {
5951  return TransformState::make_identity();
5952  } else {
5953  CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread);
5954  int pipeline_stage = current_thread->get_pipeline_stage();
5955  return r_get_partial_prev_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(transform);
5956  }
5957 }
5958 
5959 /**
5960  * Finds up to max_matches matches against the given path string from this
5961  * node and deeper. The max_matches count indicates the maximum number of
5962  * matches to return, or -1 not to limit the number returned.
5963  */
5964 void NodePath::
5965 find_matches(NodePathCollection &result, const string &path,
5966  int max_matches) const {
5967  if (is_empty()) {
5968  pgraph_cat.warning()
5969  << "Attempt to extend an empty NodePath by '" << path
5970  << "'.\n";
5971  return;
5972  }
5973  FindApproxPath approx_path;
5974  if (approx_path.add_string(path)) {
5975  find_matches(result, approx_path, max_matches);
5976  }
5977 }
5978 
5979 /**
5980  * Finds up to max_matches matches against the given approx_path from this
5981  * node and deeper. The max_matches count indicates the maximum number of
5982  * matches to return, or -1 not to limit the number returned.
5983  */
5984 void NodePath::
5985 find_matches(NodePathCollection &result, FindApproxPath &approx_path,
5986  int max_matches) const {
5987  if (is_empty()) {
5988  pgraph_cat.warning()
5989  << "Attempt to extend an empty NodePath by: " << approx_path << ".\n";
5990  return;
5991  }
5992 
5993  // We start with just one entry on the level.
5994  FindApproxLevelEntry *level =
5995  new FindApproxLevelEntry(WorkingNodePath(*this), approx_path);
5996  nassertv(level->_node_path.is_valid());
5997 
5998  find_matches(result, level, max_matches);
5999 }
6000 
6001 /**
6002  * The fundamental implementation of find_matches(), given a starting level (a
6003  * linked list of FindApproxLevelEntry objects).
6004  */
6005 void NodePath::
6006 find_matches(NodePathCollection &result, FindApproxLevelEntry *level,
6007  int max_matches) const {
6008 
6009  int num_levels_remaining = _max_search_depth;
6010 
6011  FindApproxLevelEntry *deleted_entries = nullptr;
6012 
6013  while (num_levels_remaining > 0 && level != nullptr) {
6014  if (pgraph_cat.is_spam()) {
6015  pgraph_cat.spam()
6016  << "find_matches pass: " << result << ", "
6017  << max_matches << ", " << num_levels_remaining << "\n";
6018  level->write_level(pgraph_cat.spam(false), 4);
6019  }
6020 
6021  num_levels_remaining--;
6022 
6023  FindApproxLevelEntry *next_level = nullptr;
6024 
6025  // For each node in the current level, build up the set of possible
6026  // matches in the next level.
6027  FindApproxLevelEntry *entry = level;
6028  while (entry != nullptr) {
6029  if (entry->consider_node(result, next_level, max_matches, 0)) {
6030  // If we found the requisite number of matches, we can stop. Delete
6031  // all remaining entries and return immediately.
6032 
6033  while (entry != nullptr) {
6034  FindApproxLevelEntry *next = entry->_next;
6035  delete entry;
6036  entry = next;
6037  }
6038  while (next_level != nullptr) {
6039  FindApproxLevelEntry *next = next_level->_next;
6040  delete next_level;
6041  next_level = next;
6042  }
6043  while (deleted_entries != nullptr) {
6044  FindApproxLevelEntry *next = deleted_entries->_next;
6045  delete deleted_entries;
6046  deleted_entries = next;
6047  }
6048  return;
6049  }
6050 
6051  // Move the entry to the delete chain so we can delete it before we
6052  // return from this method. (We can't delete it immediately, because
6053  // there might be WorkingNodePaths in the next_level that reference the
6054  // WorkingNodePath object within the entry.)
6055  FindApproxLevelEntry *next = entry->_next;
6056  entry->_next = deleted_entries;
6057  deleted_entries = entry;
6058 
6059  entry = next;
6060  }
6061 
6062  // Make sure the remaining entries from this level are added to the delete
6063  // chain.
6064  while (entry != nullptr) {
6065  FindApproxLevelEntry *next = entry->_next;
6066  entry->_next = deleted_entries;
6067  deleted_entries = entry;
6068 
6069  entry = next;
6070  }
6071 
6072  level = next_level;
6073  }
6074 
6075  // Now it's safe to delete all entries on the delete chain.
6076  while (deleted_entries != nullptr) {
6077  FindApproxLevelEntry *next = deleted_entries->_next;
6078  delete deleted_entries;
6079  deleted_entries = next;
6080  }
6081 }
6082 
6083 /**
6084  * The recursive implementation of clear_model_nodes(). This walks through
6085  * the subgraph defined by the indicated node and below.
6086  */
6087 int NodePath::
6088 r_clear_model_nodes(PandaNode *node) {
6089  int count = 0;
6090 
6091  if (node->is_of_type(ModelNode::get_class_type())) {
6092  ModelNode *mnode;
6093  DCAST_INTO_R(mnode, node, count);
6094  mnode->set_preserve_transform(ModelNode::PT_drop_node);
6095  ++count;
6096  }
6097 
6099  int num_children = cr.get_num_children();
6100  for (int i = 0; i < num_children; i++) {
6101  count += r_clear_model_nodes(cr.get_child(i));
6102  }
6103 
6104  return count;
6105 }
6106 
6107 /**
6108  * The recursive implementation of adjust_all_priorities(). This walks
6109  * through the subgraph defined by the indicated node and below.
6110  */
6111 void NodePath::
6112 r_adjust_all_priorities(PandaNode *node, int adjustment) {
6113  node->set_state(node->get_state()->adjust_all_priorities(adjustment));
6114  if (node->is_geom_node()) {
6115  GeomNode *gnode;
6116  DCAST_INTO_V(gnode, node);
6117 
6118  int num_geoms = gnode->get_num_geoms();
6119  for (int i = 0; i < num_geoms; i++) {
6120  gnode->set_geom_state(i, gnode->get_geom_state(i)->adjust_all_priorities(adjustment));
6121  }
6122  }
6123 
6125  int num_children = cr.get_num_children();
6126  for (int i = 0; i < num_children; i++) {
6127  r_adjust_all_priorities(cr.get_child(i), adjustment);
6128  }
6129 }
6130 
6131 /**
6132  *
6133  */
6134 void NodePath::
6135 r_force_recompute_bounds(PandaNode *node) {
6136  if (node->is_geom_node()) {
6137  GeomNode *gnode;
6138  DCAST_INTO_V(gnode, node);
6139 
6140  int num_geoms = gnode->get_num_geoms();
6141  for (int i = 0; i < num_geoms; i++) {
6142  const Geom *geom = gnode->get_geom(i);
6143  geom->mark_bounds_stale();
6144  }
6145  }
6146 
6147  node->mark_bounds_stale();
6148 
6149  // Now consider children.
6151  int num_children = cr.get_num_children();
6152  for (int i = 0; i < num_children; i++) {
6153  r_force_recompute_bounds(cr.get_child(i));
6154  }
6155 }
6156 
6157 /**
6158  * Recursively applies the indicated collide mask to the nodes at and below
6159  * this node.
6160  */
6161 void NodePath::
6162 r_set_collide_mask(PandaNode *node,
6163  CollideMask and_mask, CollideMask or_mask,
6164  TypeHandle node_type) {
6165  if (node->is_of_type(node_type)) {
6166  CollideMask into_collide_mask = node->get_into_collide_mask();
6167  into_collide_mask = (into_collide_mask & and_mask) | or_mask;
6168  node->set_into_collide_mask(into_collide_mask);
6169  }
6170 
6172  int num_children = cr.get_num_children();
6173  for (int i = 0; i < num_children; i++) {
6174  r_set_collide_mask(cr.get_child(i), and_mask, or_mask, node_type);
6175  }
6176 }
6177 
6178 /**
6179  *
6180  */
6181 bool NodePath::
6182 r_has_vertex_column(PandaNode *node, const InternalName *name) const {
6183  if (node->is_geom_node()) {
6184  GeomNode *gnode;
6185  DCAST_INTO_R(gnode, node, false);
6186 
6187  int num_geoms = gnode->get_num_geoms();
6188  for (int i = 0; i < num_geoms; i++) {
6189  const Geom *geom = gnode->get_geom(i);
6190  CPT(GeomVertexData) vdata = geom->get_vertex_data();
6191  if (vdata->has_column(name)) {
6192  return true;
6193  }
6194  }
6195  }
6196 
6197  // Now consider children.
6199  int num_children = cr.get_num_children();
6200  for (int i = 0; i < num_children; i++) {
6201  PandaNode *child = cr.get_child(i);
6202  if (r_has_vertex_column(child, name)) {
6203  return true;
6204  }
6205  }
6206 
6207  return false;
6208 }
6209 
6210 /**
6211  *
6212  */
6213 void NodePath::
6214 r_find_all_vertex_columns(PandaNode *node,
6215  NodePath::InternalNames &vertex_columns) const {
6216  if (node->is_geom_node()) {
6217  GeomNode *gnode;
6218  DCAST_INTO_V(gnode, node);
6219 
6220  int num_geoms = gnode->get_num_geoms();
6221  for (int i = 0; i < num_geoms; ++i) {
6222  const Geom *geom = gnode->get_geom(i);
6223  const GeomVertexFormat *format = geom->get_vertex_data()->get_format();
6224  int num_arrays = format->get_num_arrays();
6225  for (int j = 0; j < num_arrays; ++j) {
6226  const GeomVertexArrayFormat *array = format->get_array(j);
6227  int num_columns = array->get_num_columns();
6228  for (int k = 0; k < num_columns; ++k) {
6229  const GeomVertexColumn *column = array->get_column(k);
6230  vertex_columns.insert(column->get_name());
6231  }
6232  }
6233  }
6234  }
6235 
6236  // Now consider children.
6238  int num_children = cr.get_num_children();
6239  for (int i = 0; i < num_children; i++) {
6240  PandaNode *child = cr.get_child(i);
6241  r_find_all_vertex_columns(child, vertex_columns);
6242  }
6243 }
6244 
6245 /**
6246  *
6247  */
6248 Texture *NodePath::
6249 r_find_texture(PandaNode *node, const RenderState *state,
6250  const GlobPattern &glob) const {
6251  if (node->is_geom_node()) {
6252  GeomNode *gnode;
6253  DCAST_INTO_R(gnode, node, nullptr);
6254 
6255  int num_geoms = gnode->get_num_geoms();
6256  for (int i = 0; i < num_geoms; i++) {
6257  CPT(RenderState) geom_state =
6258  state->compose(gnode->get_geom_state(i));
6259 
6260  // Look for a TextureAttrib on the state.
6261  const RenderAttrib *attrib =
6262  geom_state->get_attrib(TextureAttrib::get_class_slot());
6263  if (attrib != nullptr) {
6264  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6265  for (int i = 0; i < ta->get_num_on_stages(); i++) {
6266  Texture *texture = ta->get_on_texture(ta->get_on_stage(i));
6267  if (texture != nullptr) {
6268  if (glob.matches(texture->get_name())) {
6269  return texture;
6270  }
6271  }
6272  }
6273  }
6274  }
6275  }
6276 
6277  // Now consider children.
6279  int num_children = cr.get_num_children();
6280  for (int i = 0; i < num_children; i++) {
6281  PandaNode *child = cr.get_child(i);
6282  CPT(RenderState) next_state = state->compose(child->get_state());
6283 
6284  Texture *result = r_find_texture(child, next_state, glob);
6285  if (result != nullptr) {
6286  return result;
6287  }
6288  }
6289 
6290  return nullptr;
6291 }
6292 
6293 /**
6294  *
6295  */
6296 void NodePath::
6297 r_find_all_textures(PandaNode *node, const RenderState *state,
6298  NodePath::Textures &textures) const {
6299  if (node->is_geom_node()) {
6300  GeomNode *gnode;
6301  DCAST_INTO_V(gnode, node);
6302 
6303  int num_geoms = gnode->get_num_geoms();
6304  for (int i = 0; i < num_geoms; i++) {
6305  CPT(RenderState) geom_state =
6306  state->compose(gnode->get_geom_state(i));
6307 
6308  // Look for a TextureAttrib on the state.
6309  const RenderAttrib *attrib =
6310  geom_state->get_attrib(TextureAttrib::get_class_slot());
6311  if (attrib != nullptr) {
6312  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6313  for (int i = 0; i < ta->get_num_on_stages(); i++) {
6314  Texture *texture = ta->get_on_texture(ta->get_on_stage(i));
6315  if (texture != nullptr) {
6316  textures.insert(texture);
6317  }
6318  }
6319  }
6320  }
6321  }
6322 
6323  // Now consider children.
6325  int num_children = cr.get_num_children();
6326  for (int i = 0; i < num_children; i++) {
6327  PandaNode *child = cr.get_child(i);
6328  CPT(RenderState) next_state = state->compose(child->get_state());
6329  r_find_all_textures(child, next_state, textures);
6330  }
6331 }
6332 
6333 /**
6334  *
6335  */
6336 Texture * NodePath::
6337 r_find_texture(PandaNode *node, TextureStage *stage) const {
6338  // Look for a TextureAttrib on the node.
6339  const RenderAttrib *attrib =
6340  node->get_attrib(TextureAttrib::get_class_slot());
6341  if (attrib != nullptr) {
6342  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6343  if (ta->has_on_stage(stage)) {
6344  return ta->get_on_texture(stage);
6345  }
6346  }
6347 
6348  if (node->is_geom_node()) {
6349  GeomNode *gnode;
6350  DCAST_INTO_R(gnode, node, nullptr);
6351 
6352  int num_geoms = gnode->get_num_geoms();
6353  for (int i = 0; i < num_geoms; i++) {
6354  CPT(RenderState) geom_state = gnode->get_geom_state(i);
6355 
6356  // Look for a TextureAttrib on the state.
6357  const RenderAttrib *attrib =
6358  geom_state->get_attrib(TextureAttrib::get_class_slot());
6359  if (attrib != nullptr) {
6360  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6361  if (ta->has_on_stage(stage)) {
6362  return ta->get_on_texture(stage);
6363  }
6364  }
6365  }
6366  }
6367 
6368  // Now consider children.
6370  int num_children = cr.get_num_children();
6371  for (int i = 0; i < num_children; i++) {
6372  PandaNode *child = cr.get_child(i);
6373 
6374  Texture *result = r_find_texture(child, stage);
6375  if (result != nullptr) {
6376  return result;
6377  }
6378  }
6379 
6380  return nullptr;
6381 }
6382 
6383 /**
6384  *
6385  */
6386 void NodePath::
6387 r_find_all_textures(PandaNode *node, TextureStage *stage,
6388  NodePath::Textures &textures) const {
6389  // Look for a TextureAttrib on the node.
6390  const RenderAttrib *attrib =
6391  node->get_attrib(TextureAttrib::get_class_slot());
6392  if (attrib != nullptr) {
6393  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6394  if (ta->has_on_stage(stage)) {
6395  textures.insert(ta->get_on_texture(stage));
6396  }
6397  }
6398 
6399  if (node->is_geom_node()) {
6400  GeomNode *gnode;
6401  DCAST_INTO_V(gnode, node);
6402 
6403  int num_geoms = gnode->get_num_geoms();
6404  for (int i = 0; i < num_geoms; i++) {
6405  CPT(RenderState) geom_state = gnode->get_geom_state(i);
6406 
6407  // Look for a TextureAttrib on the state.
6408  const RenderAttrib *attrib =
6409  geom_state->get_attrib(TextureAttrib::get_class_slot());
6410  if (attrib != nullptr) {
6411  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6412  if (ta->has_on_stage(stage)) {
6413  textures.insert(ta->get_on_texture(stage));
6414  }
6415  }
6416  }
6417  }
6418 
6419  // Now consider children.
6421  int num_children = cr.get_num_children();
6422  for (int i = 0; i < num_children; i++) {
6423  PandaNode *child = cr.get_child(i);
6424  r_find_all_textures(child, stage, textures);
6425  }
6426 }
6427 
6428 /**
6429  *
6430  */
6431 TextureStage * NodePath::
6432 r_find_texture_stage(PandaNode *node, const RenderState *state,
6433  const GlobPattern &glob) const {
6434  if (node->is_geom_node()) {
6435  GeomNode *gnode;
6436  DCAST_INTO_R(gnode, node, nullptr);
6437 
6438  int num_geoms = gnode->get_num_geoms();
6439  for (int i = 0; i < num_geoms; i++) {
6440  CPT(RenderState) geom_state =
6441  state->compose(gnode->get_geom_state(i));
6442 
6443  // Look for a TextureAttrib on the state.
6444  const RenderAttrib *attrib =
6445  geom_state->get_attrib(TextureAttrib::get_class_slot());
6446  if (attrib != nullptr) {
6447  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6448  for (int i = 0; i < ta->get_num_on_stages(); i++) {
6449  TextureStage *texture_stage = ta->get_on_stage(i);
6450  if (texture_stage != nullptr) {
6451  if (glob.matches(texture_stage->get_name())) {
6452  return texture_stage;
6453  }
6454  }
6455  }
6456  }
6457  }
6458  }
6459 
6460  // Now consider children.
6462  int num_children = cr.get_num_children();
6463  for (int i = 0; i < num_children; i++) {
6464  PandaNode *child = cr.get_child(i);
6465  CPT(RenderState) next_state = state->compose(child->get_state());
6466 
6467  TextureStage *result = r_find_texture_stage(child, next_state, glob);
6468  if (result != nullptr) {
6469  return result;
6470  }
6471  }
6472 
6473  return nullptr;
6474 }
6475 
6476 /**
6477  *
6478  */
6479 void NodePath::
6480 r_find_all_texture_stages(PandaNode *node, const RenderState *state,
6481  NodePath::TextureStages &texture_stages) const {
6482  if (node->is_geom_node()) {
6483  GeomNode *gnode;
6484  DCAST_INTO_V(gnode, node);
6485 
6486  int num_geoms = gnode->get_num_geoms();
6487  for (int i = 0; i < num_geoms; i++) {
6488  CPT(RenderState) geom_state =
6489  state->compose(gnode->get_geom_state(i));
6490 
6491  // Look for a TextureAttrib on the state.
6492  const RenderAttrib *attrib =
6493  geom_state->get_attrib(TextureAttrib::get_class_slot());
6494  if (attrib != nullptr) {
6495  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6496  for (int i = 0; i < ta->get_num_on_stages(); i++) {
6497  TextureStage *texture_stage = ta->get_on_stage(i);
6498  if (texture_stage != nullptr) {
6499  texture_stages.insert(texture_stage);
6500  }
6501  }
6502  }
6503  }
6504  }
6505 
6506  // Now consider children.
6508  int num_children = cr.get_num_children();
6509  for (int i = 0; i < num_children; i++) {
6510  PandaNode *child = cr.get_child(i);
6511  CPT(RenderState) next_state = state->compose(child->get_state());
6512  r_find_all_texture_stages(child, next_state, texture_stages);
6513  }
6514 }
6515 
6516 /**
6517  *
6518  */
6519 void NodePath::
6520 r_unify_texture_stages(PandaNode *node, TextureStage *stage) {
6521  // Look for a TextureAttrib on the state.
6522  const RenderAttrib *attrib =
6523  node->get_attrib(TextureAttrib::get_class_slot());
6524  if (attrib != nullptr) {
6525  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6526  CPT(RenderAttrib) new_attrib = ta->unify_texture_stages(stage);
6527  if (new_attrib != ta) {
6528  node->set_attrib(new_attrib);
6529  }
6530  }
6531 
6532  if (node->is_geom_node()) {
6533  GeomNode *gnode;
6534  DCAST_INTO_V(gnode, node);
6535 
6536  int num_geoms = gnode->get_num_geoms();
6537  for (int i = 0; i < num_geoms; i++) {
6538  CPT(RenderState) state = gnode->get_geom_state(i);
6539 
6540  // Look for a TextureAttrib on the state.
6541  const RenderAttrib *attrib =
6542  state->get_attrib(TextureAttrib::get_class_slot());
6543  if (attrib != nullptr) {
6544  const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
6545  CPT(RenderAttrib) new_attrib = ta->unify_texture_stages(stage);
6546  if (new_attrib != ta) {
6547  CPT(RenderState) new_state = state->add_attrib(new_attrib);
6548  gnode->set_geom_state(i, new_state);
6549  }
6550  }
6551  }
6552  }
6553 
6554  // Now consider children.
6556  int num_children = cr.get_num_children();
6557  for (int i = 0; i < num_children; i++) {
6558  PandaNode *child = cr.get_child(i);
6559  r_unify_texture_stages(child, stage);
6560  }
6561 }
6562 
6563 /**
6564  *
6565  */
6566 Material *NodePath::
6567 r_find_material(PandaNode *node, const RenderState *state,
6568  const GlobPattern &glob) const {
6569  if (node->is_geom_node()) {
6570  GeomNode *gnode;
6571  DCAST_INTO_R(gnode, node, nullptr);
6572 
6573  int num_geoms = gnode->get_num_geoms();
6574  for (int i = 0; i < num_geoms; i++) {
6575  CPT(RenderState) geom_state =
6576  state->compose(gnode->get_geom_state(i));
6577 
6578  // Look for a MaterialAttrib on the state.
6579  const RenderAttrib *attrib =
6580  geom_state->get_attrib(MaterialAttrib::get_class_slot());
6581  if (attrib != nullptr) {
6582  const MaterialAttrib *ta = DCAST(MaterialAttrib, attrib);
6583  if (!ta->is_off()) {
6584  Material *material = ta->get_material();
6585  if (material != nullptr) {
6586  if (glob.matches(material->get_name())) {
6587  return material;
6588  }
6589  }
6590  }
6591  }
6592  }
6593  }
6594 
6595  // Now consider children.
6597  int num_children = cr.get_num_children();
6598  for (int i = 0; i < num_children; i++) {
6599  PandaNode *child = cr.get_child(i);
6600  CPT(RenderState) next_state = state->compose(child->get_state());
6601 
6602  Material *result = r_find_material(child, next_state, glob);
6603  if (result != nullptr) {
6604  return result;
6605  }
6606  }
6607 
6608  return nullptr;
6609 }
6610 
6611 /**
6612  *
6613  */
6614 void NodePath::
6615 r_find_all_materials(PandaNode *node, const RenderState *state,
6616  NodePath::Materials &materials) const {
6617  if (node->is_geom_node()) {
6618  GeomNode *gnode;
6619  DCAST_INTO_V(gnode, node);
6620 
6621  int num_geoms = gnode->get_num_geoms();
6622  for (int i = 0; i < num_geoms; i++) {
6623  CPT(RenderState) geom_state =
6624  state->compose(gnode->get_geom_state(i));
6625 
6626  // Look for a MaterialAttrib on the state.
6627  const RenderAttrib *attrib =
6628  geom_state->get_attrib(MaterialAttrib::get_class_slot());
6629  if (attrib != nullptr) {
6630  const MaterialAttrib *ta = DCAST(MaterialAttrib, attrib);
6631  if (!ta->is_off()) {
6632  Material *material = ta->get_material();
6633  if (material != nullptr) {
6634  materials.insert(material);
6635  }
6636  }
6637  }
6638  }
6639  }
6640 
6641  // Now consider children.
6643  int num_children = cr.get_num_children();
6644  for (int i = 0; i < num_children; i++) {
6645  PandaNode *child = cr.get_child(i);
6646  CPT(RenderState) next_state = state->compose(child->get_state());
6647  r_find_all_materials(child, next_state, materials);
6648  }
6649 }
6650 
6651 /**
6652  *
6653  */
6654 void NodePath::
6655 r_replace_material(PandaNode *node, Material *mat,
6656  const MaterialAttrib *new_attrib) {
6657  // Consider the state of the node itself.
6658  {
6659  CPT(RenderState) node_state = node->get_state();
6660  const MaterialAttrib *ma;
6661  if (node_state->get_attrib(ma)) {
6662  if (mat == ma->get_material()) {
6663  node->set_state(node_state->set_attrib(new_attrib));
6664  }
6665  }
6666  }
6667 
6668  // If this is a GeomNode, consider the state of any of its Geoms.
6669  if (node->is_geom_node()) {
6670  GeomNode *gnode;
6671  DCAST_INTO_V(gnode, node);
6672 
6673  int num_geoms = gnode->get_num_geoms();
6674  for (int i = 0; i < num_geoms; i++) {
6675  CPT(RenderState) geom_state = gnode->get_geom_state(i);
6676 
6677  // Look for a MaterialAttrib on the state.
6678  const MaterialAttrib *ma;
6679  if (geom_state->get_attrib(ma)) {
6680  if (mat == ma->get_material()) {
6681  // Replace it
6682  gnode->set_geom_state(i, geom_state->set_attrib(new_attrib));
6683  }
6684  }
6685  }
6686  }
6687 
6688  // Now consider children.
6690  size_t num_children = cr.get_num_children();
6691  for (size_t i = 0; i < num_children; ++i) {
6692  PandaNode *child = cr.get_child(i);
6693  r_replace_material(child, mat, new_attrib);
6694  }
6695 }
6696 
6697 /**
6698  * Writes the contents of this object to the datagram for shipping out to a
6699  * Bam file.
6700  */
6701 void NodePath::
6702 write_datagram(BamWriter *manager, Datagram &dg) const {
6703  if (is_empty()) {
6704  manager->write_pointer(dg, nullptr);
6705  return;
6706  }
6707 
6708  PandaNode *root = DCAST(PandaNode, manager->get_root_node());
6709 
6710  // We have no root node to measure from.
6711  if (root == nullptr || root == node()) {
6712  manager->write_pointer(dg, node());
6713  manager->write_pointer(dg, nullptr);
6714  return;
6715  }
6716 
6717  Thread *current_thread = Thread::get_current_thread();
6718  int pipeline_stage = current_thread->get_pipeline_stage();
6719 
6720  // Record the chain of nodes from the root to this node.
6721  pvector<PandaNode *> path;
6722  NodePathComponent *comp = _head;
6723  while (comp != nullptr) {
6724  PandaNode *node = comp->get_node();
6725  path.push_back(node);
6726 
6727  if (node == root) {
6728  break;
6729  }
6730 
6731  comp = comp->get_next(pipeline_stage, current_thread);
6732  }
6733 
6734  if (comp == nullptr) {
6735  // We did not encounter the root node. Not much we can do.
6736  manager->write_pointer(dg, node());
6737  manager->write_pointer(dg, nullptr);
6738  return;
6739  }
6740 
6741  // Write out the nodes in reverse order, for fast reconstructing.
6742  for (int i = path.size() - 1; i >= 0; --i) {
6743  manager->write_pointer(dg, path[i]);
6744  }
6745  manager->write_pointer(dg, nullptr);
6746 }
6747 
6748 /**
6749  * Receives an array of pointers, one for each time manager->read_pointer()
6750  * was called in fillin(). Returns the number of pointers processed.
6751  */
6752 int NodePath::
6754  int pi = 0;
6755  PT(PandaNode) node = DCAST(PandaNode, p_list[pi++]);
6756  if (node.is_null()) {
6757  // An empty NodePath.
6758  _head = nullptr;
6759  return pi;
6760  }
6761 
6762  Thread *current_thread = Thread::get_current_thread();
6763  int pipeline_stage = current_thread->get_pipeline_stage();
6764 
6765  // Take an arbitrary path to the root of the NodePath. This probably won't
6766  // be ambiguous, as this is usually the root of the model or scene we are
6767  // currently loading.
6768  PT(NodePathComponent) comp = node->get_generic_component(false, pipeline_stage, current_thread);
6769  nassertd(!comp.is_null()) {
6770  while (p_list[pi++]) {}
6771  return pi;
6772  }
6773 
6774  // Build up the chain of NodePathComponents leading up to this node.
6775  while (p_list[pi] != nullptr) {
6776  PT(PandaNode) node = DCAST(PandaNode, p_list[pi++]);
6777 
6778  LightReMutexHolder holder(node->_paths_lock);
6779 
6780  // First, walk through the list of NodePathComponents we already have on
6781  // the child, looking for one that already exists, referencing the
6782  // indicated parent component.
6783  PandaNode::Paths::const_iterator it;
6784  for (it = node->_paths.begin(); it != node->_paths.end(); ++it) {
6785  if ((*it)->get_next(pipeline_stage, current_thread) == comp) {
6786  // If we already have such a component, use that.
6787  comp = (*it);
6788  break;
6789  }
6790  }
6791 
6792  if (it == node->_paths.end()) {
6793  // We don't already have a NodePathComponent referring to this parent-
6794  // child relationship. Create a new one. Note that we can't verify
6795  // that they are actually related because we may not have completed the
6796  // node's pointers yet, so we trust that the .bam is right.
6797  comp = new NodePathComponent(node, comp, pipeline_stage, current_thread);
6798  node->_paths.insert(comp);
6799  }
6800  }
6801  // One more for the final NULL node.
6802  ++pi;
6803 
6804  _head = comp;
6805  return pi;
6806 }
6807 
6808 /**
6809  * This internal function is called by make_from_bam to read in all of the
6810  * relevant data from the BamFile for the new NodePath.
6811  */
6812 void NodePath::
6814  while(manager->read_pointer(scan)) {};
6815 }
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
void unstash(int sort=0, Thread *current_thread=Thread::get_current_thread())
Undoes the effect of a previous stash() on this node: makes the referenced node (and the entire subgr...
Definition: nodePath.cxx:5140
The principle public interface to reading and writing Bam disk files.
Definition: bamFile.h:41
get_mode
Returns the specified antialias mode.
std::string get_bin_name() const
Returns the name of the bin that this particular node was assigned to via set_bin(),...
Definition: nodePath.cxx:2832
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear_audio_volume()
Completely removes any audio volume from the referenced node.
Definition: nodePath.cxx:5009
get_instance_count
Returns the number of geometry instances.
Definition: shaderAttrib.h:127
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.h:75
Texture * get_texture() const
Returns the base-level texture that has been set on this particular node, or NULL if no texture has b...
Definition: nodePath.cxx:3110
void clear_depth_write()
Completely removes any depth-write adjustment that may have been set on this node via set_depth_write...
Definition: nodePath.cxx:4564
TransparencyAttrib::Mode get_transparency() const
Returns the transparent rendering that has been specifically set on this node via set_transparency(),...
Definition: nodePath.cxx:4875
get_num_columns
Returns the number of different columns in the array.
void add_match_pointer(PandaNode *pointer, int flags)
Adds a component that must match a particular node exactly, by pointer.
void clear_two_sided()
Completely removes any two-sided adjustment that may have been set on this node via set_two_sided().
Definition: nodePath.cxx:4449
RenderAttrib::TexGenMode get_tex_gen(TextureStage *stage) const
Returns the texture coordinate generation mode for the given stage, or M_off if there is no explicit ...
Definition: nodePath.cxx:3636
void set_audio_volume(PN_stdfloat volume, int priority=0)
Sets the audio volume component of the transform.
Definition: nodePath.cxx:5018
NodePathCollection find_all_matches(const std::string &path) const
Returns the complete set of all NodePaths that begin with this NodePath and can be extended by path.
Definition: nodePath.cxx:354
virtual Light * as_light()
Cross-casts the node to a Light pointer, if it is one of the four kinds of Light nodes,...
Definition: pandaNode.cxx:2101
bool write_object(const TypedWritable *object)
Writes the indicated object to the Bam file.
Definition: bamFile.cxx:226
void set_depth_test(bool depth_test, int priority=0)
Specifically sets or disables the testing of the depth buffer on this particular node.
Definition: nodePath.cxx:4491
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
void do_billboard_axis(const NodePath &camera, PN_stdfloat offset)
Performs a billboard-type rotate to the indicated camera node, one time only, and leaves the object r...
Definition: nodePath.cxx:4659
void add_texture_stage(TextureStage *node_texture_stage)
Adds a new TextureStage to the collection.
The abstract interface to all kinds of lights.
Definition: light.h:38
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition: nodePath.cxx:314
bool has_vertex_column(const InternalName *name) const
Returns true if there are at least some vertices at this node and below that contain a reference to t...
Definition: nodePath.cxx:3789
void clear_material()
Completely removes any material adjustment that may have been set via set_material() from this partic...
Definition: nodePath.cxx:4123
NodePath find_net_tag(const std::string &key) const
Returns the lowest ancestor of this node that contains a tag definition with the indicated key,...
Definition: nodePath.cxx:5585
bool get_render_mode_perspective() const
Returns the flag that has been set on this node via set_render_mode_perspective(),...
Definition: nodePath.cxx:4412
void set_color_scale_off(int priority=0)
Disables any color scale attribute inherited from above.
Definition: nodePath.cxx:2111
This is a const pointer to an InternalName, and should be used in lieu of a CPT(InternalName) in func...
Definition: internalName.h:193
get_stashed_children
Returns the set of all child nodes of the referenced node that have been stashed.
Definition: nodePath.h:236
set_root_node
Sets the root node of the part of the scene graph we are currently writing out.
Definition: bamWriter.h:96
int collect_vertex_data(PandaNode *root, int collect_bits=~0)
Collects all different GeomVertexData blocks that have compatible formats at this node and below into...
Indicates a coordinate-system transform on vertices.
void add_path(const NodePath &node_path)
Adds a new NodePath to the collection.
void set_hpr_scale(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r, PN_stdfloat sx, PN_stdfloat sy, PN_stdfloat sz)
Sets the rotation and scale components of the transform, leaving translation untouched.
Definition: nodePath.I:737
void clear_clip_plane()
Completely removes any clip planes that may have been set via set_clip_plane() or set_clip_plane_off(...
Definition: nodePath.cxx:2520
get_mode
Returns the transparency mode.
get_name
Returns the name of this texture stage.
Definition: textureStage.h:188
void do_billboard_point_eye(const NodePath &camera, PN_stdfloat offset)
Performs a billboard-type rotate to the indicated camera node, one time only, and leaves the object r...
Definition: nodePath.cxx:4689
bool has_off_plane(const NodePath &plane) const
Returns true if the indicated plane is disabled by the attrib, false otherwise.
vector_uchar encode_to_bam_stream() const
Converts the NodePath object into a single stream of data using a BamWriter, and returns that data as...
Definition: nodePath.I:2105
bool has_components() const
Returns true if the transform can be described by separate pos, hpr, and scale components.
Enables or disables writing to the depth buffer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_occluder(const NodePath &occluder) const
Returns true if the indicated occluder has been specifically applied to this particular node.
Definition: nodePath.cxx:2685
int flatten_light()
Analyzes the geometry below this node and reports the number of vertices, triangles,...
Definition: nodePath.cxx:5495
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_render_mode_filled(int priority=0)
Sets up the geometry at this level and below (unless overridden) to render in filled (i....
Definition: nodePath.cxx:4287
NodePath instance_under_node(const NodePath &other, const std::string &name, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Behaves like instance_to(), but implicitly creates a new node to instance the geometry under,...
Definition: nodePath.cxx:518
bool has_fog_off() const
Returns true if a fog has been specifically disabled on this particular node via set_fog_off(),...
Definition: nodePath.cxx:4239
void set_pos_hpr(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Sets the translation and rotation component of the transform, leaving scale untouched.
Definition: nodePath.I:728
LVector3 get_relative_vector(const NodePath &other, const LVecBase3 &vec) const
Given that the indicated vector is in the coordinate system of the other node, returns the same vecto...
Definition: nodePath.cxx:1903
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
int get_instance_count() const
Returns the geometry instance count, or 0 if disabled.
Definition: nodePath.cxx:3332
const InternalName * get_top() const
Returns the oldest ancestor in the InternalName's chain, not counting the root.
void clear_logic_op()
Completely removes any logical operation that may have been set on this node via set_logic_op().
Definition: nodePath.cxx:4905
bool operator !=(const NodePath &other) const
Returns true if the two paths are not equivalent.
Definition: nodePath.I:1929
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath()
This constructs an empty NodePath with no nodes.
Definition: nodePath.I:18
void set_depth_write(bool depth_write, int priority=0)
Specifically sets or disables the writing to the depth buffer on this particular node.
Definition: nodePath.cxx:4548
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_depth_offset() const
Returns true if a depth-offset adjustment has been explicitly set on this particular node via set_dep...
Definition: nodePath.cxx:4631
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_state
Sets the complete RenderState that will be applied to all nodes at this level and below.
Definition: pandaNode.h:173
bool has_color() const
Returns true if a color has been applied to the given node, false otherwise.
Definition: nodePath.cxx:1996
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
void clear_bin()
Completely removes any bin adjustment that may have been set via set_bin() from this particular node.
Definition: nodePath.cxx:2811
bool resolve()
This may be called at any time during processing of the Bam file to resolve all the known pointers so...
Definition: bamReader.cxx:325
int get_depth_offset() const
Returns the depth offset value if it has been specified using set_depth_offset, or 0 if not.
Definition: nodePath.cxx:4641
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
void write_level(std::ostream &out, int indent_level) const
Writes the entire level (a linked list of entries beginning at this entry).
Indicates which, if any, material should be applied to geometry.
PandaNode * get_child(size_t n) const
Returns the nth child of the node.
Definition: pandaNode.I:962
This class is local to this package only; it doesn't get exported.
Enables or disables writing to the depth buffer.
void set_alpha_scale(PN_stdfloat scale, int priority=0)
Sets the alpha scale component of the transform without (much) affecting the color scale.
Definition: nodePath.cxx:2122
std::string get_net_basename(int n) const
Returns the basename of this name prefixed by the indicated number of ancestors.
LColor get_color() const
Returns the color that has been assigned to the node, or black if no color has been assigned.
Definition: nodePath.cxx:2006
Specifies whether or how to enable antialiasing, if supported by the backend renderer.
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
Definition: textureAttrib.h:55
const LVecBase4 & get_color_scale() const
Returns the complete color scale vector that has been applied to this node via a previous call to set...
Definition: nodePath.cxx:2174
void set_clip_plane(const NodePath &clip_plane, int priority=0)
Adds the indicated clipping plane to the list of planes that apply to geometry at this node and below...
Definition: nodePath.cxx:2441
NodePath get_stashed_ancestor(Thread *current_thread=Thread::get_current_thread()) const
Returns the NodePath at or above the referenced node that is stashed, or an empty NodePath if no ance...
Definition: nodePath.cxx:5167
get_on_stage
Returns the nth stage turned on by the attribute, sorted in render order.
Definition: textureAttrib.h:55
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_audio_volume() const
Returns true if an audio volume has been applied to the referenced node, false otherwise.
Definition: nodePath.cxx:4998
void unstash()
Unstashes all NodePaths in the collection.
is_overall_hidden
Returns true if the node has been hidden to all cameras by clearing its overall bit.
Definition: pandaNode.h:248
bool is_empty() const
Returns true if there are no NodePaths in the collection, false otherwise.
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:224
This controls the enabling of transparency.
bool has_all_off() const
Returns true if this attrib disables all planes (although it may also enable some).
int compare_to(const NodePath &other) const
Returns a number less than zero if this NodePath sorts before the other one, greater than zero if it ...
Definition: nodePath.I:1955
MaterialCollection find_all_materials() const
Returns a list of a materials applied to geometry at this node and below.
Definition: nodePath.cxx:4056
This defines a bounding sphere, consisting of a center and a radius.
void set_audio_volume_off(int priority=0)
Disables any audio volume attribute inherited from above.
Definition: nodePath.cxx:5047
void clear_billboard()
Removes any billboard effect from the node.
Definition: nodePath.cxx:4787
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_num_nodes
Returns the number of nodes in the path.
Definition: nodePath.h:206
get_fog
If the FogAttrib is not an 'off' FogAttrib, returns the fog that is associated.
Definition: fogAttrib.h:38
void set_bin(const std::string &bin_name, int draw_order, int priority=0)
Assigns the geometry at this level and below to the named rendering bin.
Definition: nodePath.cxx:2801
void set_instance_count(int instance_count)
Sets the geometry instance count, or 0 if geometry instancing should be disabled.
Definition: nodePath.cxx:3367
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_scale(PN_stdfloat scale)
Sets the scale component of the transform, leaving translation and rotation untouched.
Definition: nodePath.I:675
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_bin_name
Returns the name of the bin this attribute specifies.
Definition: cullBinAttrib.h:39
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
get_on_sampler
Returns the sampler associated with the indicated stage, or the one associated with its texture if no...
Definition: textureAttrib.h:72
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
NodePath get_hidden_ancestor(DrawMask camera_mask=PandaNode::get_overall_bit(), Thread *current_thread=Thread::get_current_thread()) const
Returns the NodePath at or above the referenced node that is hidden to the indicated camera(s),...
Definition: nodePath.cxx:5093
get_color
If the type is T_flat or T_off, this returns the color that will be applied to geometry.
Definition: colorAttrib.h:47
This class is local to this package only; it doesn't get exported.
An interface for simplifying ("flattening") scene graphs by eliminating unneeded nodes and collapsing...
void clear_tex_gen()
Removes the texture coordinate generation mode from all texture stages on this node.
Definition: nodePath.cxx:3585
bool has_on_occluder(const NodePath &occluder) const
Returns true if the indicated occluder is enabled by the effect, false otherwise.
Definition: shader.h:49
void apply_texture_colors()
Removes textures from Geoms at this node and below by applying the texture colors to the vertices.
Definition: nodePath.cxx:5573
NodePathCollection find_all_paths_to(PandaNode *node) const
Returns the set of all NodePaths that extend from this NodePath down to the indicated node.
Definition: nodePath.cxx:367
bool calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, const NodePath &other=NodePath(), Thread *current_thread=Thread::get_current_thread()) const
Calculates the minimum and maximum vertices of all Geoms at this NodePath's bottom node and below.
Definition: nodePath.cxx:5419
void hide_bounds()
Stops the rendering of the bounding volume begun with show_bounds().
Definition: nodePath.cxx:5361
void set_render_mode(RenderModeAttrib::Mode mode, PN_stdfloat thickness, int priority=0)
Sets up the geometry at this level and below (unless overridden) to render in the specified mode and ...
Definition: nodePath.cxx:4346
void show_bounds()
Causes the bounding volume of the bottom node and all of its descendants (that is,...
Definition: nodePath.cxx:5340
void set_quat_scale(const LQuaternion &quat, const LVecBase3 &scale)
Sets the rotation and scale components of the transform, leaving translation untouched.
Definition: nodePath.cxx:1228
void compose_color_scale(const LVecBase4 &scale, int priority=0)
multiplies the color scale component of the transform, with previous color scale leaving translation ...
Definition: nodePath.cxx:2050
get_parent
Returns the NodePath to the parent of the referenced node: that is, this NodePath,...
Definition: nodePath.h:244
bool has_fog() const
Returns true if a fog has been applied to this particular node via set_fog(), false otherwise.
Definition: nodePath.cxx:4220
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
This functions similarly to a LightAttrib.
void set_pos_quat(const LVecBase3 &pos, const LQuaternion &quat)
Sets the translation and rotation component of the transform, leaving scale untouched.
Definition: nodePath.cxx:1201
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_material(Material *node_material)
Adds a new Material to the collection.
bool has_billboard() const
Returns true if there is any billboard effect on the node.
Definition: nodePath.cxx:4796
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
This is a small container class that can hold any one of the value types that can be passed as input ...
Definition: shaderInput.h:40
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class for a number of special render effects that may be set on scene graph nodes to...
Definition: renderEffect.h:48
NodePath instance_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Adds the referenced node of the NodePath as a child of the referenced node of the indicated other Nod...
Definition: nodePath.cxx:471
void clear_depth_test()
Completely removes any depth-test adjustment that may have been set on this node via set_depth_test()...
Definition: nodePath.cxx:4507
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a=1.0, int priority=0)
Applies a scene-graph color to the referenced node.
Definition: nodePath.cxx:1952
void set_logic_op(LogicOpAttrib::Operation op, int priority=0)
Specifically sets or disables a logical operation on this particular node.
Definition: nodePath.cxx:4893
get_quat
Returns the rotation component of the transform as a quaternion.
void add_texture(Texture *texture)
Adds a new Texture to the collection.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_node
Returns the nth node of the path, where 0 is the referenced (bottom) node and get_num_nodes() - 1 is ...
Definition: nodePath.h:206
bool has_material() const
Returns true if a material has been applied to this particular node via set_material(),...
Definition: nodePath.cxx:4133
PandaNode * get_node() const
Returns the node referenced by this component.
void unify_texture_stages(TextureStage *stage)
Searches through all TextureStages at this node and below.
Definition: nodePath.cxx:4011
void clear_transparency()
Completely removes any transparency adjustment that may have been set on this node via set_transparen...
Definition: nodePath.cxx:4849
int32_t get_int32()
Extracts a signed 32-bit integer.
get_shear
Returns the shear component of the transform.
PN_stdfloat get_render_mode_thickness() const
Returns the render mode thickness that has been specifically set on this node via set_render_mode(),...
Definition: nodePath.cxx:4395
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a class designed to support low-overhead traversals of the complete scene graph,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_pipeline_stage
Returns the Pipeline stage number associated with this thread.
Definition: thread.h:105
void set_compass(const NodePath &reference=NodePath())
Puts a compass effect on the node, so that it will retain a fixed rotation relative to the reference ...
Definition: nodePath.cxx:4807
bool has_tex_projector(TextureStage *stage) const
Returns true if this node has a TexProjectorEffect for the indicated stage, false otherwise.
Definition: nodePath.cxx:3716
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_mode
Returns the depth write mode.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
RenderModeAttrib::Mode get_render_mode() const
Returns the render mode that has been specifically set on this node via set_render_mode(),...
Definition: nodePath.cxx:4378
void set_material(Material *tex, int priority=0)
Sets the geometry at this level and below to render using the indicated material.
Definition: nodePath.cxx:4100
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_antialias() const
Returns true if an antialias setting has been explicitly mode on this particular node via set_antiali...
Definition: nodePath.cxx:4970
void clear_scissor()
Removes the scissor region that was defined at this node level by a previous call to set_scissor().
Definition: nodePath.cxx:2767
void close()
Closes the input or output stream.
Definition: bamFile.cxx:243
get_name
Returns the complete name represented by the InternalName and all of its parents.
Definition: internalName.h:61
get_mat
Returns the matrix that describes the transform.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
This defines how a single column is interleaved within a vertex array stored within a Geom.
void set_pos_quat_scale_shear(const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale, const LVecBase3 &shear)
Completely replaces the transform with new translation, rotation, scale, and shear components.
Definition: nodePath.cxx:1280
TextureCollection find_all_textures() const
Returns a list of a textures applied to geometry at this node and below.
Definition: nodePath.cxx:3918
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
void reverse_ls() const
Lists the hierarchy at and above the referenced node.
Definition: nodePath.I:419
bool add_string(const std::string &str_path)
Adds a sequence of components separated by slashes, followed optionally by a semicolon and a sequence...
void set_transform(const TransformState *transform, Thread *current_thread=Thread::get_current_thread())
Changes the complete transform object on this node.
Definition: nodePath.I:565
A PolylightEffect can be used on a node to define a LightGroup for that node.
void set_texture(Texture *tex, int priority=0)
Adds the indicated texture to the list of textures that will be rendered on the default texture stage...
Definition: nodePath.cxx:2871
bool write_bam_stream(std::ostream &out) const
Writes the contents of this node and below out to the indicated stream.
Definition: nodePath.cxx:5625
static NodePath decode_from_bam_stream(vector_uchar data, BamReader *reader=nullptr)
Reads the string created by a previous call to encode_to_bam_stream(), and extracts and returns the N...
Definition: nodePath.cxx:5729
LVector3 get_pos_delta() const
Returns the delta vector from this node's position in the previous frame (according to set_prev_trans...
Definition: nodePath.cxx:1007
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:31
Indicates which faces should be culled based on their vertex ordering.
bool is_stashed() const
Returns true if the referenced node is stashed either directly, or because some ancestor is stashed.
Definition: nodePath.I:1877
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_light_off() const
Returns true if all Lights have been specifically disabled on this particular node.
Definition: nodePath.cxx:2394
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
get_draw_control_mask
Returns the set of bits in draw_show_mask that are considered meaningful.
Definition: pandaNode.h:255
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void prepare_scene(GraphicsStateGuardianBase *gsg)
Walks through the scene graph beginning at the bottom node, and does whatever initialization is requi...
Definition: nodePath.cxx:5327
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
void set_render_mode_thickness(PN_stdfloat thickness, int priority=0)
Sets up the point geometry at this level and below to render as thick points (that is,...
Definition: nodePath.cxx:4334
void set_fog(Fog *fog, int priority=0)
Sets the geometry at this level and below to render using the indicated fog.
Definition: nodePath.cxx:4185
bool has_light(const NodePath &light) const
Returns true if the indicated Light or PolylightNode has been specifically enabled on this particular...
Definition: nodePath.cxx:2360
void clear_light()
Completely removes any lighting operations that may have been set via set_light() or set_light_off() ...
Definition: nodePath.cxx:2306
PT(Material) NodePath
Returns the material that has been set on this particular node, or NULL if no material has been set.
Definition: nodePath.cxx:4153
Applies a Fog to the geometry at and below this node.
Definition: fogAttrib.h:25
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void reset_prev_transform(Thread *current_thread=Thread::get_current_thread())
Resets the transform that represents this node's "previous" position to the same as the current trans...
Definition: pandaNode.cxx:1153
get_scale
Returns the scale to be applied to colors.
bool operator==(const NodePath &other) const
Returns true if the two paths are equivalent; that is, if they contain the same list of nodes in the ...
Definition: nodePath.I:1921
bool has_texture() const
Returns true if a texture has been applied to this particular node via set_texture(),...
Definition: nodePath.cxx:3038
void clear_depth_offset()
Completely removes any depth-offset adjustment that may have been set on this node via set_depth_offs...
Definition: nodePath.cxx:4620
int get_key() const
Returns an integer that is guaranteed to be the same for all NodePaths that represent the same node i...
Definition: nodePath.I:245
A lightweight class that represents a single element that may be timed and/or counted via stats.
bool has_tex_transform(TextureStage *stage) const
Returns true if there is an explicit texture matrix on the current node for the given stage.
Definition: nodePath.cxx:3440
bool consider_node(NodePathCollection &result, FindApproxLevelEntry *&next_level, int max_matches, int increment) const
Considers the node represented by the entry for matching the find path.
InternalNameCollection find_all_vertex_columns() const
Returns a list of all vertex array columns stored on some geometry found at this node level and below...
Definition: nodePath.cxx:3799
void set_light_off(int priority=0)
Sets the geometry at this level and below to render using no lights at all.
Definition: nodePath.cxx:2255
get_draw_show_mask
Returns the hide/show bits of this particular node.
Definition: pandaNode.h:256
void set_occluder(const NodePath &occluder)
Adds the indicated occluder to the list of occluders that apply to geometry at this node and below.
Definition: nodePath.cxx:2622
void reparent_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread())
Removes the referenced node of the NodePath from its current parent and attaches it to the referenced...
Definition: nodePath.cxx:391
void output(std::ostream &out) const
Writes a sensible description of the NodePath to the indicated output stream.
Definition: nodePath.cxx:660
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_fog_off(int priority=0)
Sets the geometry at this level and below to render using no fog.
Definition: nodePath.cxx:4197
bool operator<(const NodePath &other) const
Returns true if this NodePath sorts before the other one, false otherwise.
Definition: nodePath.I:1940
bool is_top_node(int pipeline_stage, Thread *current_thread) const
Returns true if this component represents the top node in the path.
get_offset
Returns the depth offset represented by this attrib.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool verify_complete(Thread *current_thread=Thread::get_current_thread()) const
Returns true if all of the nodes described in the NodePath are connected, or false otherwise.
Definition: nodePath.cxx:5239
This node is placed at key points within the scene graph to indicate the roots of "models": subtrees ...
Definition: modelNode.h:31
void heads_up(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Behaves like look_at(), but with a strong preference to keeping the up vector oriented in the indicat...
Definition: nodePath.I:798
void add_match_many(int flags)
Adds a component that will match a chain of zero or more consecutive nodes.
bool has_logic_op() const
Returns true if a logical operation has been explicitly set on this particular node via set_logic_op(...
Definition: nodePath.cxx:4917
int get_bin_draw_order() const
Returns the drawing order associated with the bin that this particular node was assigned to via set_b...
Definition: nodePath.cxx:2850
static NodePath fail()
Creates a NodePath with the ET_fail error type set.
Definition: nodePath.I:149
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Fog * get_fog() const
Returns the fog that has been set on this particular node, or NULL if no fog has been set.
Definition: nodePath.cxx:4258
void set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top)
Sets up a scissor region on the nodes rendered at this level and below.
Definition: nodePath.cxx:2709
void unstash_all(Thread *current_thread=Thread::get_current_thread())
Unstashes this node and all stashed child nodes.
Definition: nodePath.cxx:5155
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
void set_pos_quat_scale(const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale)
Replaces the translation, rotation, and scale components, implicitly setting shear to 0.
Definition: nodePath.cxx:1254
has_on_stage
Returns true if the indicated stage is turned on by the attrib, false otherwise.
Definition: textureAttrib.h:69
bool has_effect(TypeHandle type) const
Returns true if there is a render effect of the indicated type defined on this node,...
Definition: nodePath.I:510
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:563
void unify(PandaNode *root, bool preserve_order)
Calls unify() on every GeomNode at this level and below.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_quat(const LQuaternion &quat)
Sets the rotation component of the transform, leaving translation and scale untouched.
Definition: nodePath.cxx:1070
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This functions similarly to a LightAttrib or ClipPlaneAttrib.
size_t get_num_children() const
Returns the number of children of the node.
Definition: pandaNode.I:953
get_path
Returns the nth NodePath in the collection.
Specifies how atmospheric fog effects are applied to geometry.
Definition: fog.h:41
bool has_color_scale() const
Returns true if a color scale has been applied to the referenced node, false otherwise.
Definition: nodePath.cxx:2029
set_source
Changes the source of future datagrams for this BamReader.
Definition: bamReader.h:151
unsigned short get_antialias() const
Returns the antialias setting that has been specifically set on this node via set_antialias(),...
Definition: nodePath.cxx:4980
bool has_on_light(const NodePath &light) const
Returns true if the indicated light is turned on by the attrib, false otherwise.
Definition: lightAttrib.I:54
void show_tight_bounds()
Similar to show_bounds(), this draws a bounding box representing the "tight" bounds of this node and ...
Definition: nodePath.cxx:5352
void clear_render_mode()
Completely removes any render mode adjustment that may have been set on this node via set_render_mode...
Definition: nodePath.cxx:4357
NodePathComponent * get_next(int pipeline_stage, Thread *current_thread) const
Returns the next component in the path.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Texture * find_texture(const std::string &name) const
Returns the first texture found applied to geometry at this node or below that matches the indicated ...
Definition: nodePath.cxx:3897
get_pos
Returns the pos component of the transform.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_antialias(unsigned short mode, int priority=0)
Specifies the antialiasing type that should be applied at this node and below.
Definition: nodePath.cxx:4948
int find_parent(PandaNode *node, Thread *current_thread=Thread::get_current_thread()) const
Returns the index of the indicated parent node, if it is a parent, or -1 if it is not.
Definition: pandaNode.I:44
int find_child(PandaNode *node, Thread *current_thread=Thread::get_current_thread()) const
Returns the index of the indicated child node, if it is a child, or -1 if it is not.
Definition: pandaNode.I:90
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_singleton(Thread *current_thread=Thread::get_current_thread()) const
Returns true if the NodePath contains exactly one node.
Definition: nodePath.I:196
get_num_arrays
Returns the number of individual arrays required by the format.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
LogicOpAttrib::Operation get_logic_op() const
Returns the logical operation that has been specifically set on this node via set_logic_op(),...
Definition: nodePath.cxx:4931
void set_billboard_point_eye(PN_stdfloat offset=0.0, bool fixed_depth=false)
Puts a billboard transition on the node such that it will rotate in three dimensions about the origin...
Definition: nodePath.I:1755
bool has_on_plane(const NodePath &plane) const
Returns true if the indicated plane is enabled by the attrib, false otherwise.
get_hpr
Returns the rotation component of the transform as a trio of Euler angles.
void set_all_color_scale(PN_stdfloat scale, int priority=0)
Scales all the color components of the object by the same amount, darkening the object,...
Definition: nodePath.cxx:2148
get_writer
Returns the BamWriter in charge of performing the write operations.
Definition: bamFile.h:82
bool write_bam_file(const Filename &filename) const
Writes the contents of this node and below out to a bam file with the indicated filename.
Definition: nodePath.cxx:5601
get_children
Returns the set of all child nodes of the referenced node.
Definition: nodePath.h:235
bool has_hpr() const
Returns true if the transform's rotation component can be extracted out separately and described as a...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void premunge(PandaNode *root, const RenderState *initial_state)
Walks the scene graph rooted at this node and below, and uses the indicated GSG to premunge every Geo...
Texture * get_texture() const
If the TextureAttrib is not an 'off' TextureAttrib, returns the base-level texture that is associated...
Definition: textureAttrib.I:61
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:784
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
Definition: bamWriter.cxx:204
void clear_color_scale()
Completely removes any color scale from the referenced node.
Definition: nodePath.cxx:2040
void set_effect(const RenderEffect *effect)
Adds the indicated render effect to the scene graph on this node.
Definition: pandaNode.cxx:1005
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath copy_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Functions like instance_to(), except a deep copy is made of the referenced node and all of its descen...
Definition: nodePath.cxx:535
void premunge_scene(GraphicsStateGuardianBase *gsg=nullptr)
Walks through the scene graph beginning at the bottom node, and internally adjusts any GeomVertexForm...
Definition: nodePath.cxx:5304
virtual bool put_datagram(const Datagram &data) override
Writes the given datagram to the file.
LVecBase3 get_hpr() const
Retrieves the rotation component of the transform.
Definition: nodePath.cxx:1058
This class is a wrapper around a NodePath that, unlike the actual NodePath class, doesn't hold a refe...
Definition: weakNodePath.h:32
const SamplerState & get_texture_sampler() const
Returns the sampler state that has been given for the base-level texture that has been set on this pa...
Definition: nodePath.cxx:3148
void clear_color()
Completely removes any color adjustment from the node.
Definition: nodePath.cxx:1986
void set_fluid_pos(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the translation component, without changing the "previous" position, so that the collision syste...
Definition: nodePath.I:627
void set_effect(const RenderEffect *effect)
Adds the indicated render effect to the scene graph on this node.
Definition: nodePath.I:490
get_mode
Returns the depth write mode.
int get_child_sort(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the sort index of the nth child node of this node (that is, the number that was passed to add...
Definition: pandaNode.I:78
bool has_all_off() const
Returns true if this attrib turns off all stages (although it may also turn some on).
set_into_collide_mask
Sets the "into" CollideMask.
Definition: pandaNode.h:264
bool has_clip_plane_off() const
Returns true if all clipping planes have been specifically disabled on this particular node.
Definition: nodePath.cxx:2581
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int flatten(PandaNode *root, int combine_siblings_bits)
Simplifies the graph by removing unnecessary nodes and nodes.
get_sort
Returns the sort value of the referenced node within its parent; that is, the sort number passed on t...
Definition: nodePath.h:245
bool get_depth_write() const
Returns true if depth-write rendering has been specifically set on this node via set_depth_write(),...
Definition: nodePath.cxx:4587
void clear_texture()
Completely removes any texture adjustment that may have been set via set_texture() or set_texture_off...
Definition: nodePath.cxx:3002
void set_tex_transform(TextureStage *stage, const TransformState *transform)
Sets the texture matrix on the current node to the indicated transform for the given stage.
Definition: nodePath.cxx:3387
virtual PandaNode * make_copy() const
Returns a newly-allocated PandaNode that is a shallow copy of this one.
Definition: pandaNode.cxx:487
get_actual_mode
Returns the actual culling mode, without considering the effects of the reverse flag.
Defines the way an object appears in the presence of lighting.
Definition: material.h:43
Similar to MutexHolder, but for a light reentrant mutex.
LQuaternion get_quat() const
Retrieves the rotation component of the transform.
Definition: nodePath.cxx:1080
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
LPoint3 get_pos() const
Retrieves the translation component of the transform.
Definition: nodePath.cxx:992
void set_attrib(const RenderAttrib *attrib, int override=0)
Adds the indicated render attribute to the scene graph on this node.
Definition: pandaNode.cxx:944
Applies a scale to colors in the scene graph and on vertices.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
void set_light(const NodePath &light, int priority=0)
Adds the indicated Light or PolylightNode to the list of lights that illuminate geometry at this node...
Definition: nodePath.cxx:2194
void set_color_off(int priority=0)
Sets the geometry at this level and below to render using the geometry color.
Definition: nodePath.cxx:1975
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Assigns geometry to a particular bin by name.
Definition: cullBinAttrib.h:27
static NodePath removed()
Creates a NodePath with the ET_removed error type set.
Definition: nodePath.I:139
Represents a set of settings that indicate how a texture is sampled.
Definition: samplerState.h:36
void set_render_mode_filled_wireframe(const LColor &wireframe_color, int priority=0)
Sets up the geometry at this level and below (unless overridden) to render in filled,...
Definition: nodePath.cxx:4300
bool has_scissor() const
Returns true if a scissor region was defined at this node by a previous call to set_scissor().
Definition: nodePath.cxx:2779
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class defines the physical layout of the vertex data stored within a Geom.
This class can be used to write a series of datagrams into a memory buffer.
CPT(RenderState) NodePath
Returns the state changes that must be made to transition to the render state of this node from the r...
Definition: nodePath.cxx:697
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_stashed
Returns the nth stashed child of this node.
Definition: pandaNode.h:148
void project_texture(TextureStage *stage, Texture *tex, const NodePath &projector)
A convenience function to enable projective texturing at this node level and below,...
Definition: nodePath.cxx:3773
Material * find_material(const std::string &name) const
Returns the first material found applied to geometry at this node or below that matches the indicated...
Definition: nodePath.cxx:4046
Applies a transform matrix to UV's before they are rendered.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear_antialias()
Completely removes any antialias setting that may have been set on this node via set_antialias().
Definition: nodePath.cxx:4959
void add_name(const InternalName *name)
Adds a new InternalName to the collection.
get_material
If the MaterialAttrib is not an 'off' MaterialAttrib, returns the material that is associated.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_billboard_axis(PN_stdfloat offset=0.0)
Puts a billboard transition on the node such that it will rotate in two dimensions around the up axis...
Definition: nodePath.I:1745
void set_texture_off(int priority=0)
Sets the geometry at this level and below to render using no texture, on any stage.
Definition: nodePath.cxx:2961
void set_shear(PN_stdfloat shxy, PN_stdfloat shxz, PN_stdfloat shyz)
Sets the shear component of the transform, leaving translation, rotation, and scale untouched.
Definition: nodePath.I:704
static NodePath not_found()
Creates a NodePath with the ET_not_found error type set.
Definition: nodePath.I:129
get_scale
Returns the scale component of the transform.
void apply_attribs(PandaNode *node, int attrib_types=~(TT_clip_plane|TT_cull_face|TT_apply_texture_color))
Walks the scene graph, accumulating attribs of the indicated types, applying them to the vertices,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
int flatten_strong()
The strongest possible flattening.
Definition: nodePath.cxx:5543
void set_hpr(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Sets the rotation component of the transform, leaving translation and scale untouched.
Definition: nodePath.I:651
bool has_texture_off() const
Returns true if texturing has been specifically disabled on this particular node via set_texture_off(...
Definition: nodePath.cxx:3069
is_valid
Returns true if the WorkingNodePath object appears to be a valid NodePath reference,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static const SamplerState & get_default()
Returns a reference to the global default immutable SamplerState object.
Definition: samplerState.I:36
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
static const ShaderInput & get_blank()
Returns a static ShaderInput object with name NULL, priority zero, type INVALID, and all value-fields...
Definition: shaderInput.cxx:23
bool has_off_stage(TextureStage *stage) const
Returns true if the indicated stage is turned off by the attrib, false otherwise.
void set_render_mode_wireframe(int priority=0)
Sets up the geometry at this level and below (unless overridden) to render in wireframe mode.
Definition: nodePath.cxx:4275
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
get_operation
Returns the logic operation specified by this attribute.
Definition: logicOpAttrib.h:61
This effect automatically applies a computed texture matrix to the specified texture stage,...
bool has_tex_gen(TextureStage *stage) const
Returns true if there is a mode for automatic texture coordinate generation on the current node for t...
Definition: nodePath.cxx:3618
void set_pos_hpr_scale_shear(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale, const LVecBase3 &shear)
Completely replaces the transform with new translation, rotation, scale, and shear components.
Definition: nodePath.cxx:1267
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
get_mode
Returns the render mode.
get_root_node
Returns the root node of the part of the scene graph we are currently writing out.
Definition: bamWriter.h:96
void stash_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread())
Similar to reparent_to(), but the node is added to its new parent's stashed list, so that the result ...
Definition: nodePath.cxx:412
A thread; that is, a lightweight process.
Definition: thread.h:46
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:591
bool read_header(std::string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
int get_length(int pipeline_stage, Thread *current_thread) const
Returns the length of the path to this node.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_wireframe_color
Returns the color that is used in M_filled_wireframe mode to distinguish the wireframe from the rest ...
bool has_all_off() const
Returns true if this attrib turns off all lights (although it may also turn some on).
Definition: lightAttrib.I:99
void set_material_off(int priority=0)
Sets the geometry at this level and below to render using no material.
Definition: nodePath.cxx:4113
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
This is a special kind of attribute that instructs the graphics driver to apply an offset or bias to ...
int get_stashed_sort(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the sort index of the nth stashed node of this node (that is, the number that was passed to a...
Definition: pandaNode.I:166
void wrt_reparent_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread())
This functions identically to reparent_to(), except the transform on this node is also adjusted so th...
Definition: nodePath.cxx:433
int find_ancestor(const std::string &basename) const
Returns the index of the ancestor with the indicated basename, or -1 if no ancestor has that basename...
void clear_effect(TypeHandle type)
Removes the render effect of the given type from this node.
Definition: nodePath.I:519
LVecBase3 get_scale() const
Retrieves the scale component of the transform.
Definition: nodePath.cxx:1128
void set_depth_offset(int bias, int priority=0)
This instructs the graphics driver to apply an offset or bias to the generated depth values for rende...
Definition: nodePath.cxx:4609
This describes the structure of a single array within a Geom data.
bool is_off() const
Returns true if the MaterialAttrib is an 'off' MaterialAttrib, indicating that it should disable the ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_thickness
Returns the line width or point thickness.
NodePath get_top(Thread *current_thread=Thread::get_current_thread()) const
Returns a singleton NodePath that represents the top of the path, or empty NodePath if this path is e...
Definition: nodePath.cxx:209
void set_mat(const LMatrix4 &mat)
Directly sets an arbitrary 4x4 transform matrix.
Definition: nodePath.cxx:1292
TextureStageCollection find_all_texture_stages() const
Returns a list of a TextureStages applied to geometry at this node and below.
Definition: nodePath.cxx:3990
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_perspective
Returns the perspective flag.
Specifies how polygons are to be drawn.
void clear_occluder()
Completely removes any occluders that may have been set via set_occluder() from this particular node.
Definition: nodePath.cxx:2648
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:27
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
void set_render_mode_perspective(bool perspective, int priority=0)
Sets up the point geometry at this level and below to render as perspective sprites (that is,...
Definition: nodePath.cxx:4318
NodePath find_path_to(PandaNode *node) const
Searches for the indicated node below this node and returns the shortest NodePath that connects them.
Definition: nodePath.cxx:332
PN_stdfloat get_audio_volume() const
Returns the complete audio volume that has been applied to this node via a previous call to set_audio...
Definition: nodePath.cxx:5058
const LMatrix4 & get_mat() const
Returns the transform matrix that has been applied to the referenced node, or the identity matrix if ...
Definition: nodePath.I:776
int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: nodePath.cxx:6753
If enabled, specifies that a custom logical operation be performed instead of any color blending.
Definition: logicOpAttrib.h:30
bool has_depth_test() const
Returns true if a depth-test adjustment has been explicitly set on this particular node via set_depth...
Definition: nodePath.cxx:4518
bool has_off_light(const NodePath &light) const
Returns true if the indicated light is turned off by the attrib, false otherwise.
Definition: lightAttrib.I:89
void stash(int sort=0, Thread *current_thread=Thread::get_current_thread())
Removes the referenced node (and the entire subgraph below this node) from the scene graph in any nor...
Definition: nodePath.cxx:5123
void set_pos_hpr_scale(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat h, PN_stdfloat p, PN_stdfloat r, PN_stdfloat sx, PN_stdfloat sy, PN_stdfloat sz)
Completely replaces the transform with new translation, rotation, and scale components.
Definition: nodePath.I:746
bool has_two_sided() const
Returns true if a two-sided adjustment has been explicitly set on this particular node via set_two_si...
Definition: nodePath.cxx:4460
void set_transparency(TransparencyAttrib::Mode mode, int priority=0)
Specifically sets or disables transparent rendering mode on this particular node.
Definition: nodePath.cxx:4836
A class to retrieve the individual data elements previously stored in a Datagram.
const InternalName * get_name() const
Returns the name of this particular data field, e.g.
bool get_depth_test() const
Returns true if depth-test rendering has been specifically set on this node via set_depth_test(),...
Definition: nodePath.cxx:4530
LPoint3 get_relative_point(const NodePath &other, const LVecBase3 &point) const
Given that the indicated point is in the coordinate system of the other node, returns the same point ...
Definition: nodePath.cxx:1892
void clear_effect(TypeHandle type)
Removes the render effect of the given type from this node.
Definition: pandaNode.cxx:1022
void set_geom_state(int n, const RenderState *state)
Changes the RenderState associated with the nth geom of the node.
Definition: geomNode.I:102
virtual bool get_datagram(Datagram &data) override
Reads the next datagram from the file.
bool open_write(const Filename &bam_filename, bool report_errors=true)
Attempts to open the indicated file for writing.
Definition: bamFile.cxx:190
set_target
Changes the destination of future datagrams written by the BamWriter.
Definition: bamWriter.h:91
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
void set_preserve_transform(PreserveTransform preserve_transform)
Sets the preserve_transform flag.
Definition: modelNode.I:52
has_parent
Returns true if the referenced node has a parent; i.e.
Definition: nodePath.h:244
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:758
void set_tex_projector(TextureStage *stage, const NodePath &from, const NodePath &to, int lens_index=0)
Establishes a TexProjectorEffect on this node, which can be used to establish projective texturing (b...
Definition: nodePath.cxx:3661
void clear_tex_transform()
Removes all texture matrices from the current node.
Definition: nodePath.cxx:3408
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureStage * find_texture_stage(const std::string &name) const
Returns the first TextureStage found applied to geometry at this node or below that matches the indic...
Definition: nodePath.cxx:3979
bool is_off() const
Returns true if the FogAttrib is an 'off' FogAttrib, indicating that it should disable fog.
Definition: fogAttrib.I:26
int find_stashed(PandaNode *node, Thread *current_thread=Thread::get_current_thread()) const
Returns the index of the indicated stashed node, if it is a stashed child, or -1 if it is not.
Definition: pandaNode.I:178
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2068
NodePath get_tex_projector_from(TextureStage *stage) const
Returns the "from" node associated with the TexProjectorEffect on the indicated stage.
Definition: nodePath.cxx:3735
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:35
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Computes texture coordinates for geometry automatically based on vertex position and/or normal.
Definition: texGenAttrib.h:32
get_draw_order
Returns the draw order this attribute specifies.
Definition: cullBinAttrib.h:40
void clear_fog()
Completely removes any fog adjustment that may have been set via set_fog() or set_fog_off() from this...
Definition: nodePath.cxx:4208
LVecBase3 get_shear() const
Retrieves the shear component of the transform.
Definition: nodePath.cxx:1176
void write_bounds(std::ostream &out) const
Writes a description of the bounding volume containing the bottom node and all of its descendants to ...
Definition: nodePath.cxx:5399
get_color_type
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.h:46
int flatten_medium()
A more thorough flattening than flatten_light(), this first applies all the transforms,...
Definition: nodePath.cxx:5515
bool get_two_sided() const
Returns true if two-sided rendering has been specifically set on this node via set_two_sided(),...
Definition: nodePath.cxx:4473
void set_two_sided(bool two_sided, int priority=0)
Specifically sets or disables two-sided rendering mode on this particular node.
Definition: nodePath.cxx:4430
void detach_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from its parent, but does not immediately delete it.
Definition: nodePath.cxx:631
NodePath get_tex_projector_to(TextureStage *stage) const
Returns the "to" node associated with the TexProjectorEffect on the indicated stage.
Definition: nodePath.cxx:3754
bool has_compass() const
Returns true if there is any compass effect on the node.
Definition: nodePath.cxx:4825
bool has_depth_write() const
Returns true if a depth-write adjustment has been explicitly set on this particular node via set_dept...
Definition: nodePath.cxx:4575
Applies a scale to audio volume for positional sounds in the scene graph.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
void set_state(const RenderState *state, Thread *current_thread=Thread::get_current_thread())
Changes the complete state object on this node.
Definition: nodePath.I:427
void do_billboard_point_world(const NodePath &camera, PN_stdfloat offset)
Performs a billboard-type rotate to the indicated camera node, one time only, and leaves the object r...
Definition: nodePath.cxx:4718
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_array
Returns the description of the nth array used by the format.
InternalNameCollection find_all_texcoords() const
Returns a list of all texture coordinate sets used by any geometry at this node level and below.
Definition: nodePath.cxx:3841
bool has_tag(const std::string &key) const
Returns true if a value has been defined on this node for the particular key (even if that value is t...
Definition: nodePath.I:2027
bool has_transparency() const
Returns true if a transparent-rendering adjustment has been explicitly set on this particular node vi...
Definition: nodePath.cxx:4861
get_column
Returns the specification with the indicated name, or NULL if the name is not used.
void clear_tex_projector()
Removes the TexProjectorEffect for all stages from this node.
Definition: nodePath.cxx:3706
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_color_scale(const LVecBase4 &scale, int priority=0)
Sets the color scale component of the transform, leaving translation and rotation untouched.
Definition: nodePath.cxx:2080
get_into_collide_mask
Returns the "into" collide mask for this node.
Definition: pandaNode.h:264
get_ancestor
Returns the nth ancestor of the path, where 0 is the NodePath itself and get_num_nodes() - 1 is get_t...
Definition: nodePath.h:209
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_clip_plane_off(int priority=0)
Sets the geometry at this level and below to render using no clip_planes at all.
Definition: nodePath.cxx:2475
bool has_clip_plane(const NodePath &clip_plane) const
Returns true if the indicated clipping plane has been specifically applied to this particular node.
Definition: nodePath.cxx:2559
get_volume
Returns the volume to be applied to sounds.
bool has_render_mode() const
Returns true if a render mode has been explicitly set on this particular node via set_render_mode() (...
Definition: nodePath.cxx:4368
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
void clear_compass()
Removes any compass effect from the node.
Definition: nodePath.cxx:4816
get_num_stashed
Returns the number of stashed nodes this node has.
Definition: pandaNode.h:148
void write_datagram(BamWriter *manager, Datagram &dg) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: nodePath.cxx:6702
int make_compatible_state(PandaNode *root)
Searches for GeomNodes that contain multiple Geoms that differ only in their ColorAttribs.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Indicates which set of lights should be considered "on" to illuminate geometry at this level and belo...
Definition: lightAttrib.h:30
void set_billboard_point_world(PN_stdfloat offset=0.0)
Puts a billboard transition on the node such that it will rotate in three dimensions about the origin...
Definition: nodePath.I:1764
get_on_texture
Returns the texture associated with the indicated stage, or NULL if no texture is associated.
Definition: textureAttrib.h:69
const TransformState * get_prev_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the transform that has been set as this node's "previous" position.
Definition: nodePath.cxx:846
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
const RenderState * get_state(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete state object set on this node.
Definition: nodePath.cxx:686
bool has_bin() const
Returns true if the node has been assigned to the a particular rendering bin via set_bin(),...
Definition: nodePath.cxx:2821
PN_stdfloat get_net_audio_volume() const
Returns the complete audio volume for this node taking highers nodes in the graph into account.
Definition: nodePath.cxx:5074
This is one component of a NodePath.
void swap_data(vector_uchar &other)
Swaps the data in the internal buffer with that of the other buffer.
This is a set of zero or more NodePaths.
void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is called by make_from_bam to read in all of the relevant data from the BamFil...
Definition: nodePath.cxx:6813
void look_at(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the transform on this NodePath so that it rotates to face the indicated point in space.
Definition: nodePath.I:789
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.