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