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