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