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