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