Panda3D
Loading...
Searching...
No Matches
pandaNode.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 pandaNode.cxx
10 * @author drose
11 * @date 2002-02-20
12 */
13
14#include "pandaNode.h"
15#include "config_pgraph.h"
16#include "nodePathComponent.h"
17#include "bamReader.h"
18#include "bamWriter.h"
19#include "indent.h"
21#include "sceneGraphReducer.h"
22#include "accumulatedAttribs.h"
23#include "clipPlaneAttrib.h"
24#include "boundingSphere.h"
25#include "boundingBox.h"
26#include "pStatTimer.h"
27#include "config_mathutil.h"
28#include "lightReMutexHolder.h"
30
31using std::ostream;
32using std::ostringstream;
33using std::string;
34
35// This category is just temporary for debugging convenience.
36NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
37NotifyCategoryDef(drawmask, "");
38
39TypeHandle PandaNode::BamReaderAuxDataDown::_type_handle;
40
41PandaNode::SceneRootFunc *PandaNode::_scene_root_func;
42
43PandaNodeChain PandaNode::_dirty_prev_transforms("_dirty_prev_transforms");
44DrawMask PandaNode::_overall_bit = DrawMask::bit(31);
45
46PStatCollector PandaNode::_reset_prev_pcollector("App:Collisions:Reset");
47PStatCollector PandaNode::_update_bounds_pcollector("*:Bounds");
48
49TypeHandle PandaNode::_type_handle;
50TypeHandle PandaNode::CData::_type_handle;
51TypeHandle PandaNodePipelineReader::_type_handle;
52
53/*
54 * There are two different interfaces here for making and breaking parent-
55 * child connections: the fundamental PandaNode interface, via add_child() and
56 * remove_child() (and related functions), and the NodePath support interface,
57 * via attach(), detach(), and reparent(). They both do essentially the same
58 * thing, but with slightly different inputs. The PandaNode interfaces try to
59 * guess which NodePaths should be updated as a result of the scene graph
60 * change, while the NodePath interfaces already know. The NodePath support
61 * interface functions are strictly called from within the NodePath class, and
62 * are used to implement NodePath::reparent_to() and NodePath::remove_node(),
63 * etc. The fundamental interface, on the other hand, is intended to be
64 * called directly by the user. The fundamental interface has a slightly
65 * lower overhead because it does not need to create a NodePathComponent chain
66 * where one does not already exist; however, the NodePath support interface
67 * is more useful when the NodePath already does exist, because it ensures
68 * that the particular NodePath calling it is kept appropriately up-to-date.
69 */
70
71
72/**
73 *
74 */
75PandaNode::
76PandaNode(const string &name) :
77 Namable(name),
78 _paths_lock("PandaNode::_paths_lock"),
79 _dirty_prev_transform(false)
80{
81 if (pgraph_cat.is_debug()) {
82 pgraph_cat.debug()
83 << "Constructing " << (void *)this << ", " << get_name() << "\n";
84 }
85
86#ifdef DO_MEMORY_USAGE
87 MemoryUsage::update_type(this, this);
88#endif
89}
90
91/**
92 *
93 */
94PandaNode::
95~PandaNode() {
96 if (pgraph_cat.is_debug()) {
97 pgraph_cat.debug()
98 << "Destructing " << (void *)this << ", " << get_name() << "\n";
99 }
100
101 if (_dirty_prev_transform) {
102 // Need to have this held before we grab any other locks.
103 LightMutexHolder holder(_dirty_prev_transforms._lock);
104 do_clear_dirty_prev_transform();
105 }
106
107 // We shouldn't have any parents left by the time we destruct, or there's a
108 // refcount fault somewhere.
109
110 // Actually, that's not necessarily true anymore, since we might be updating
111 // a node dynamically via the bam reader, which doesn't necessarily keep
112 // related pairs of nodes in sync with each other.
113 /*
114#ifndef NDEBUG
115 {
116 CDReader cdata(_cycler);
117 nassertv(cdata->get_up()->empty());
118 }
119#endif // NDEBUG
120 */
121
123}
124
125/**
126 * Do not call the copy constructor directly; instead, use make_copy() or
127 * copy_subgraph() to make a copy of a node.
128 */
129PandaNode::
130PandaNode(const PandaNode &copy) :
132 Namable(copy),
133 _paths_lock("PandaNode::_paths_lock"),
134 _dirty_prev_transform(false),
135 _python_tag_data(copy._python_tag_data),
136 _unexpected_change_flags(0)
137{
138 if (pgraph_cat.is_debug()) {
139 pgraph_cat.debug()
140 << "Copying " << (void *)this << ", " << get_name() << "\n";
141 }
142#ifdef DO_MEMORY_USAGE
143 MemoryUsage::update_type(this, this);
144#endif
145
146 // Need to have this held before we grab any other locks.
147 LightMutexHolder holder(_dirty_prev_transforms._lock);
148
149 // Copy the other node's state.
150 {
151 CDReader copy_cdata(copy._cycler);
152 CDWriter cdata(_cycler, true);
153 cdata->_state = copy_cdata->_state;
154 cdata->_transform = copy_cdata->_transform;
155 cdata->_prev_transform = copy_cdata->_prev_transform;
156 if (cdata->_transform != cdata->_prev_transform) {
157 do_set_dirty_prev_transform();
158 }
159
160 cdata->_effects = copy_cdata->_effects;
161 cdata->_tag_data = copy_cdata->_tag_data;
162 cdata->_draw_control_mask = copy_cdata->_draw_control_mask;
163 cdata->_draw_show_mask = copy_cdata->_draw_show_mask;
164 cdata->_into_collide_mask = copy_cdata->_into_collide_mask;
165 cdata->_bounds_type = copy_cdata->_bounds_type;
166 cdata->_user_bounds = copy_cdata->_user_bounds;
167 cdata->_internal_bounds = nullptr;
168 cdata->_internal_bounds_computed = UpdateSeq::initial();
169 cdata->_internal_bounds_mark = UpdateSeq::initial();
170 ++cdata->_internal_bounds_mark;
171 cdata->_final_bounds = copy_cdata->_final_bounds;
172 cdata->_fancy_bits = copy_cdata->_fancy_bits;
173 }
174}
175
176/**
177 * This is similar to make_copy(), but it makes a copy for the specific
178 * purpose of flatten. Typically, this will be a new PandaNode with a new
179 * pointer, but all of the internal data will always be shared with the
180 * original; whereas the new node returned by make_copy() might not share the
181 * internal data.
182 */
184dupe_for_flatten() const {
185 return make_copy();
186}
187
188/**
189 * Returns true if it is generally safe to flatten out this particular kind of
190 * PandaNode by duplicating instances (by calling dupe_for_flatten()), false
191 * otherwise (for instance, a Camera cannot be safely flattened, because the
192 * Camera pointer itself is meaningful).
193 */
195safe_to_flatten() const {
196 return true;
197}
198
199/**
200 * Returns true if it is generally safe to transform this particular kind of
201 * PandaNode by calling the xform() method, false otherwise.
202 */
204safe_to_transform() const {
205 return true;
206}
207
208/**
209 * Returns true if it is safe to automatically adjust the transform on this
210 * kind of node. Usually, this is only a bad idea if the user expects to find
211 * a particular transform on the node.
212 *
213 * ModelNodes with the preserve_transform flag set are presently the only
214 * kinds of nodes that should not have their transform even adjusted.
215 */
218 return true;
219}
220
221/**
222 * Returns true if it is generally safe to combine this particular kind of
223 * PandaNode with other kinds of PandaNodes of compatible type, adding
224 * children or whatever. For instance, an LODNode should not be combined with
225 * any other PandaNode, because its set of children is meaningful.
226 */
228safe_to_combine() const {
229 return true;
230}
231
232/**
233 * Returns true if it is generally safe to combine the children of this
234 * PandaNode with each other. For instance, an LODNode's children should not
235 * be combined with each other, because the set of children is meaningful.
236 */
239 return true;
240}
241
242/**
243 * Returns true if a flatten operation may safely continue past this node, or
244 * false if nodes below this node may not be molested.
245 */
247safe_to_flatten_below() const {
248 return true;
249}
250
251/**
252 * Returns true if the node's name has extrinsic meaning and must be preserved
253 * across a flatten operation, false otherwise.
254 */
256preserve_name() const {
257 return false;
258}
259
260/**
261 * Returns the union of all attributes from SceneGraphReducer::AttribTypes
262 * that may not safely be applied to the vertices of this node. If this is
263 * nonzero, these attributes must be dropped at this node as a state change.
264 *
265 * This is a generalization of safe_to_transform().
266 */
269 return 0;
270}
271
272/**
273 * Applies whatever attributes are specified in the AccumulatedAttribs object
274 * (and by the attrib_types bitmask) to the vertices on this node, if
275 * appropriate. If this node uses geom arrays like a GeomNode, the supplied
276 * GeomTransformer may be used to unify shared arrays across multiple
277 * different nodes.
278 *
279 * This is a generalization of xform().
280 */
282apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
283 GeomTransformer &transformer) {
284 if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
285 const LMatrix4 &mat = attribs._transform->get_mat();
286 xform(mat);
287
288 Thread *current_thread = Thread::get_current_thread();
289 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
290 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
291 cdata->_effects = cdata->_effects->xform(mat);
292 cdata->set_fancy_bit(FB_effects, !cdata->_effects->is_empty());
293 }
294 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
295 }
297}
298
299/**
300 * Transforms the contents of this PandaNode by the indicated matrix, if it
301 * means anything to do so. For most kinds of PandaNodes, this does nothing.
302 */
304xform(const LMatrix4 &) {
305}
306
307/**
308 * Collapses this PandaNode with the other PandaNode, if possible, and returns
309 * a pointer to the combined PandaNode, or NULL if the two PandaNodes cannot
310 * safely be combined.
311 *
312 * The return value may be this, other, or a new PandaNode altogether.
313 *
314 * This function is called from GraphReducer::flatten(), and need not deal
315 * with children; its job is just to decide whether to collapse the two
316 * PandaNodes and what the collapsed PandaNode should look like.
317 */
319combine_with(PandaNode *other) {
320 // An unadorned PandaNode always combines with any other PandaNodes by
321 // yielding completely. However, if we are actually some fancy PandaNode
322 // type that derives from PandaNode but didn't redefine this function, we
323 // should refuse to combine.
324 if (is_exact_type(get_class_type())) {
325 // No, we're an ordinary PandaNode.
326 return other;
327
328 } else if (other->is_exact_type(get_class_type())) {
329 // We're not an ordinary PandaNode, but the other one is.
330 return this;
331 }
332
333 // We're something other than an ordinary PandaNode. Don't combine.
334 return nullptr;
335}
336
337/**
338 * This is used to support NodePath::calc_tight_bounds(). It is not intended
339 * to be called directly, and it has nothing to do with the normal Panda
340 * bounding-volume computation.
341 *
342 * If the node contains any geometry, this updates min_point and max_point to
343 * enclose its bounding box. found_any is to be set true if the node has any
344 * geometry at all, or left alone if it has none. This method may be called
345 * over several nodes, so it may enter with min_point, max_point, and
346 * found_any already set.
347 *
348 * This function is recursive, and the return value is the transform after it
349 * has been modified by this node's transform.
350 */
351CPT(TransformState) PandaNode::
352calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
353 const TransformState *transform, Thread *current_thread) const {
354 CPT(TransformState) next_transform = transform->compose(get_transform());
355
356 Children cr = get_children(current_thread);
357 int num_children = cr.get_num_children();
358 for (int i = 0; i < num_children; i++) {
359 cr.get_child(i)->calc_tight_bounds(min_point, max_point,
360 found_any, next_transform,
361 current_thread);
362 }
363
364 return next_transform;
365}
366
367/**
368 * This function will be called during the cull traversal to perform any
369 * additional operations that should be performed at cull time. This may
370 * include additional manipulation of render state or additional
371 * visible/invisible decisions, or any other arbitrary operation.
372 *
373 * Note that this function will *not* be called unless set_cull_callback() is
374 * called in the constructor of the derived class. It is necessary to call
375 * set_cull_callback() to indicated that we require cull_callback() to be
376 * called.
377 *
378 * By the time this function is called, the node has already passed the
379 * bounding-volume test for the viewing frustum, and the node's transform and
380 * state have already been applied to the indicated CullTraverserData object.
381 *
382 * The return value is true if this node should be visible, or false if it
383 * should be culled.
384 */
385bool PandaNode::
386cull_callback(CullTraverser *, CullTraverserData &) {
387 return true;
388}
389
390/**
391 * Should be overridden by derived classes to return true if this kind of node
392 * has some restrictions on the set of children that should be rendered. Node
393 * with this property include LODNodes, SwitchNodes, and SequenceNodes.
394 *
395 * If this function returns true, get_first_visible_child() and
396 * get_next_visible_child() will be called to walk through the list of
397 * children during cull, instead of iterating through the entire list. This
398 * method is called after cull_callback(), so cull_callback() may be
399 * responsible for the decisions as to which children are visible at the
400 * moment.
401 */
404 return false;
405}
406
407/**
408 * Returns the index number of the first visible child of this node, or a
409 * number >= get_num_children() if there are no visible children of this node.
410 * This is called during the cull traversal, but only if
411 * has_selective_visibility() has already returned true. See
412 * has_selective_visibility().
413 */
416 return 0;
417}
418
419/**
420 * Returns the index number of the next visible child of this node following
421 * the indicated child, or a number >= get_num_children() if there are no more
422 * visible children of this node. See has_selective_visibility() and
423 * get_first_visible_child().
424 */
426get_next_visible_child(int n) const {
427 return n + 1;
428}
429
430/**
431 * Should be overridden by derived classes to return true if this kind of node
432 * has the special property that just one of its children is visible at any
433 * given time, and furthermore that the particular visible child can be
434 * determined without reference to any external information (such as a
435 * camera). At present, only SequenceNodes and SwitchNodes fall into this
436 * category.
437 *
438 * If this function returns true, get_visible_child() can be called to return
439 * the index of the currently-visible child.
440 */
443 return false;
444}
445
446/**
447 * Returns the index number of the currently visible child of this node. This
448 * is only meaningful if has_single_child_visibility() has returned true.
449 */
451get_visible_child() const {
452 return 0;
453}
454
455/**
456 * Returns true if there is some value to visiting this particular node during
457 * the cull traversal for any camera, false otherwise. This will be used to
458 * optimize the result of get_net_draw_show_mask(), so that any subtrees that
459 * contain only nodes for which is_renderable() is false need not be visited.
460 */
462is_renderable() const {
463 return false;
464}
465
466/**
467 * Adds the node's contents to the CullResult we are building up during the
468 * cull traversal, so that it will be drawn at render time. For most nodes
469 * other than GeomNodes, this is a do-nothing operation.
470 */
474
475/**
476 * Returns a newly-allocated PandaNode that is a shallow copy of this one. It
477 * will be a different pointer, but its internal data may or may not be shared
478 * with that of the original PandaNode. No children will be copied.
479 */
481make_copy() const {
482 return new PandaNode(*this);
483}
484
485/**
486 * Allocates and returns a complete copy of this PandaNode and the entire
487 * scene graph rooted at this PandaNode. Some data may still be shared from
488 * the original (e.g. vertex index tables), but nothing that will impede
489 * normal use of the PandaNode.
490 */
491PT(PandaNode) PandaNode::
492copy_subgraph(Thread *current_thread) const {
493 InstanceMap inst_map;
494 return r_copy_subgraph(inst_map, current_thread);
495}
496
497/**
498 * Returns the number of nodes at and below this level.
499 */
500int PandaNode::
501count_num_descendants() const {
502 int count = 1;
503 Children children = get_children();
504 int num_children = children.get_num_children();
505
506 for (int i = 0; i < num_children; ++i) {
507 PandaNode *child = children.get_child(i);
508 count += child->count_num_descendants();
509 }
510
511 return count;
512}
513
514/**
515 * Adds a new child to the node. The child is added in the relative position
516 * indicated by sort; if all children have the same sort index, the child is
517 * added at the end.
518 *
519 * If the same child is added to a node more than once, the previous instance
520 * is first removed.
521 */
522void PandaNode::
523add_child(PandaNode *child_node, int sort, Thread *current_thread) {
524 nassertv(child_node != nullptr);
525
526 if (!verify_child_no_cycles(child_node)) {
527 // Whoops, adding this child node would introduce a cycle in the scene
528 // graph.
529 return;
530 }
531
532 // Ensure the child_node is not deleted while we do this.
533 PT(PandaNode) keep_child = child_node;
534 remove_child(child_node);
535
536 // Apply this operation to the current stage as well as to all upstream
537 // stages.
538 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
539 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
540 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
541
542 cdata->modify_down()->insert(DownConnection(child_node, sort));
543 cdata_child->modify_up()->insert(UpConnection(this));
544 }
545 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
546
547 OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
548 new_connection(this, child_node, pipeline_stage, current_thread);
549 }
550 CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
551
552 force_bounds_stale();
553
554 children_changed();
555 child_node->parents_changed();
557 child_node->mark_bam_modified();
558}
559
560/**
561 * Removes the nth child from the node.
562 */
564remove_child(int child_index, Thread *current_thread) {
565 int pipeline_stage = current_thread->get_pipeline_stage();
566 nassertv(pipeline_stage == 0);
567
568 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
569 PT(Down) down = cdata->modify_down();
570 nassertv(child_index >= 0 && child_index < (int)down->size());
571
572 PT(PandaNode) child_node = (*down)[child_index].get_child();
573 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
574 current_thread);
575 PT(Up) up = cdata_child->modify_up();
576
577 down->erase(down->begin() + child_index);
578 int num_erased = up->erase(UpConnection(this));
579 nassertv(num_erased == 1);
580
581 sever_connection(this, child_node, pipeline_stage, current_thread);
582 force_bounds_stale(pipeline_stage, current_thread);
583
584 children_changed();
585 child_node->parents_changed();
587 child_node->mark_bam_modified();
588}
589
590/**
591 * Removes the indicated child from the node. Returns true if the child was
592 * removed, false if it was not already a child of the node. This will also
593 * successfully remove the child if it had been stashed.
594 */
596remove_child(PandaNode *child_node, Thread *current_thread) {
597 nassertr(child_node != nullptr, false);
598
599 // Make sure the child node is not destructed during the execution of this
600 // method.
601 PT(PandaNode) keep_child = child_node;
602
603 // We have to do this for each upstream pipeline stage.
604 bool any_removed = false;
605
606 OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
607 if (stage_remove_child(child_node, pipeline_stage, current_thread)) {
608 any_removed = true;
609
610 sever_connection(this, child_node, pipeline_stage, current_thread);
611 force_bounds_stale(pipeline_stage, current_thread);
612 }
613 }
614 CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
615
616 if (any_removed) {
617 // Call callback hooks.
618 children_changed();
619 child_node->parents_changed();
620 }
621
622 return any_removed;
623}
624
625/**
626 * Searches for the orig_child node in the node's list of children, and
627 * replaces it with the new_child instead. Returns true if the replacement is
628 * made, or false if the node is not a child or if there is some other
629 * problem.
630 */
632replace_child(PandaNode *orig_child, PandaNode *new_child,
633 Thread *current_thread) {
634 nassertr(orig_child != nullptr, false);
635 nassertr(new_child != nullptr, false);
636
637 if (orig_child == new_child) {
638 // Trivial no-op.
639 return true;
640 }
641
642 if (!verify_child_no_cycles(new_child)) {
643 // Whoops, adding this child node would introduce a cycle in the scene
644 // graph.
645 return false;
646 }
647
648 // Make sure the orig_child node is not destructed during the execution of
649 // this method.
650 PT(PandaNode) keep_orig_child = orig_child;
651
652 // We have to do this for each upstream pipeline stage.
653 bool any_replaced = false;
654
655 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
656 if (stage_replace_child(orig_child, new_child, pipeline_stage, current_thread)) {
657 any_replaced = true;
658 }
659 }
660 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
661
662 if (any_replaced) {
663 children_changed();
664 orig_child->parents_changed();
665 new_child->parents_changed();
666 }
667
668 return any_replaced;
669}
670
671
672/**
673 * Stashes the indicated child node. This removes the child from the list of
674 * active children and puts it on a special list of stashed children. This
675 * child node no longer contributes to the bounding volume of the PandaNode,
676 * and is not visited in normal traversals. It is invisible and uncollidable.
677 * The child may later be restored by calling unstash_child().
678 *
679 * This can only be called from the top pipeline stage (i.e. from App).
680 */
682stash_child(int child_index, Thread *current_thread) {
683 int pipeline_stage = current_thread->get_pipeline_stage();
684 nassertv(pipeline_stage == 0);
685 nassertv(child_index >= 0 && child_index < get_num_children());
686
687 // Save a reference count for ourselves.
688 PT(PandaNode) self = this;
689
690 PT(PandaNode) child_node = get_child(child_index);
691 int sort = get_child_sort(child_index);
692
693 remove_child(child_index);
694
695 {
696 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
697 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
698
699 cdata->modify_stashed()->insert(DownConnection(child_node, sort));
700 cdata_child->modify_up()->insert(UpConnection(this));
701 }
702
703 new_connection(this, child_node, pipeline_stage, current_thread);
704 force_bounds_stale(pipeline_stage, current_thread);
705
706 children_changed();
707 child_node->parents_changed();
709 child_node->mark_bam_modified();
710}
711
712/**
713 * Returns the indicated stashed node to normal child status. This removes
714 * the child from the list of stashed children and puts it on the normal list
715 * of active children. This child node once again contributes to the bounding
716 * volume of the PandaNode, and will be visited in normal traversals. It is
717 * visible and collidable.
718 *
719 * This can only be called from the top pipeline stage (i.e. from App).
720 */
722unstash_child(int stashed_index, Thread *current_thread) {
723 int pipeline_stage = current_thread->get_pipeline_stage();
724 nassertv(pipeline_stage == 0);
725 nassertv(stashed_index >= 0 && stashed_index < get_num_stashed());
726
727 // Save a reference count for ourselves. I don't think this should be
728 // necessary, but there are occasional crashes in stash() during furniture
729 // moving mode. Perhaps this will eliminate those crashes.
730 PT(PandaNode) self = this;
731
732 PT(PandaNode) child_node = get_stashed(stashed_index);
733 int sort = get_stashed_sort(stashed_index);
734
735 remove_stashed(stashed_index);
736
737 {
738 CDWriter cdata(_cycler);
739 CDWriter cdata_child(child_node->_cycler);
740
741 cdata->modify_down()->insert(DownConnection(child_node, sort));
742 cdata_child->modify_up()->insert(UpConnection(this));
743 }
744
745 new_connection(this, child_node, pipeline_stage, current_thread);
746
747 force_bounds_stale();
748 children_changed();
749 child_node->parents_changed();
751 child_node->mark_bam_modified();
752}
753
754/**
755 * Adds a new child to the node, directly as a stashed child. The child is
756 * not added in the normal sense, but will be revealed if unstash_child() is
757 * called on it later.
758 *
759 * If the same child is added to a node more than once, the previous instance
760 * is first removed.
761 *
762 * This can only be called from the top pipeline stage (i.e. from App).
763 */
765add_stashed(PandaNode *child_node, int sort, Thread *current_thread) {
766 int pipeline_stage = current_thread->get_pipeline_stage();
767 nassertv(pipeline_stage == 0);
768
769 if (!verify_child_no_cycles(child_node)) {
770 // Whoops, adding this child node would introduce a cycle in the scene
771 // graph.
772 return;
773 }
774
775 // Ensure the child_node is not deleted while we do this.
776 PT(PandaNode) keep_child = child_node;
777 remove_child(child_node);
778
779 {
780 CDWriter cdata(_cycler);
781 CDWriter cdata_child(child_node->_cycler);
782
783 cdata->modify_stashed()->insert(DownConnection(child_node, sort));
784 cdata_child->modify_up()->insert(UpConnection(this));
785 }
786
787 new_connection(this, child_node, pipeline_stage, current_thread);
788
789 // Call callback hooks.
790 children_changed();
791 child_node->parents_changed();
793 child_node->mark_bam_modified();
794}
795
796/**
797 * Removes the nth stashed child from the node.
798 */
800remove_stashed(int child_index, Thread *current_thread) {
801 int pipeline_stage = current_thread->get_pipeline_stage();
802 nassertv(pipeline_stage == 0);
803
804 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
805 Down &stashed = *cdata->modify_stashed();
806 nassertv(child_index >= 0 && child_index < (int)stashed.size());
807
808 PT(PandaNode) child_node = stashed[child_index].get_child();
809 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
810
811 stashed.erase(stashed.begin() + child_index);
812 int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
813 nassertv(num_erased == 1);
814
815 sever_connection(this, child_node, pipeline_stage, current_thread);
816 force_bounds_stale(pipeline_stage, current_thread);
817
818 children_changed();
819 child_node->parents_changed();
821 child_node->mark_bam_modified();
822}
823
824/**
825 * Removes all the children from the node at once, including stashed children.
826 *
827 * This can only be called from the top pipeline stage (i.e. from App).
828 */
830remove_all_children(Thread *current_thread) {
831 // We have to do this for each upstream pipeline stage.
832 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
833 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
834 PT(Down) down = cdata->modify_down();
835 Down::iterator di;
836 for (di = down->begin(); di != down->end(); ++di) {
837 PT(PandaNode) child_node = (*di).get_child();
838 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
839 current_thread);
840 cdata_child->modify_up()->erase(UpConnection(this));
841
842 sever_connection(this, child_node, pipeline_stage, current_thread);
843 child_node->parents_changed();
844 child_node->mark_bam_modified();
845 }
846 down->clear();
847
848 Down &stashed = *cdata->modify_stashed();
849 for (di = stashed.begin(); di != stashed.end(); ++di) {
850 PT(PandaNode) child_node = (*di).get_child();
851 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
852 current_thread);
853 cdata_child->modify_up()->erase(UpConnection(this));
854
855 sever_connection(this, child_node, pipeline_stage, current_thread);
856 child_node->parents_changed();
857 child_node->mark_bam_modified();
858 }
859 stashed.clear();
860 }
861 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
862
863 force_bounds_stale();
864 children_changed();
866}
867
868/**
869 * Moves all the children from the other node onto this node.
870 *
871 * Any NodePaths to child nodes of the other node are truncated, rather than
872 * moved to the new parent.
873 */
875steal_children(PandaNode *other, Thread *current_thread) {
876 if (other == this) {
877 // Trivial.
878 return;
879 }
880
881 // We do this through the high-level interface for convenience. This could
882 // begin to be a problem if we have a node with hundreds of children to
883 // copy; this could break down the ov_set.insert() method, which is an
884 // O(n^2) operation. If this happens, we should rewrite this to do a
885 // simpler add_child() operation that involves push_back() instead of
886 // insert(), and then sort the down list at the end.
887
888 int num_children = other->get_num_children();
889 int i;
890 for (i = 0; i < num_children; i++) {
891 PandaNode *child_node = other->get_child(i);
892 int sort = other->get_child_sort(i);
893 add_child(child_node, sort, current_thread);
894 }
895 int num_stashed = other->get_num_stashed();
896 for (i = 0; i < num_stashed; i++) {
897 PandaNode *child_node = other->get_stashed(i);
898 int sort = other->get_stashed_sort(i);
899 add_stashed(child_node, sort, current_thread);
900 }
901
902 other->remove_all_children(current_thread);
903}
904
905/**
906 * Makes another instance of all the children of the other node, copying them
907 * to this node.
908 */
910copy_children(PandaNode *other, Thread *current_thread) {
911 if (other == this) {
912 // Trivial.
913 return;
914 }
915 Children children = other->get_children(current_thread);
916 Stashed stashed = other->get_stashed(current_thread);
917 int num_children = children.get_num_children();
918 int i;
919 for (i = 0; i < num_children; i++) {
920 PandaNode *child_node = children.get_child(i);
921 int sort = children.get_child_sort(i);
922 add_child(child_node, sort, current_thread);
923 }
924 int num_stashed = stashed.get_num_stashed();
925 for (i = 0; i < num_stashed; i++) {
926 PandaNode *child_node = stashed.get_stashed(i);
927 int sort = stashed.get_stashed_sort(i);
928 add_stashed(child_node, sort, current_thread);
929 }
930}
931
932/**
933 * Adds the indicated render attribute to the scene graph on this node. This
934 * attribute will now apply to this node and everything below. If there was
935 * already an attribute of the same type, it is replaced.
936 */
938set_attrib(const RenderAttrib *attrib, int override) {
939 // Apply this operation to the current stage as well as to all upstream
940 // stages.
941 bool any_changed = false;
942 Thread *current_thread = Thread::get_current_thread();
943 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
944 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
945
946 CPT(RenderState) new_state = cdata->_state->set_attrib(attrib, override);
947 if (cdata->_state != new_state) {
948 cdata->_state = new_state;
949 cdata->set_fancy_bit(FB_state, true);
950 any_changed = true;
951 }
952 }
953 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
954
955 // Maybe we changed a ClipPlaneAttrib.
956 if (any_changed) {
957 mark_bounds_stale(current_thread);
958 state_changed();
960 }
961}
962
963/**
964 * Removes the render attribute of the given type from this node. This node,
965 * and the subgraph below, will now inherit the indicated render attribute
966 * from the nodes above this one.
967 */
968void PandaNode::
969clear_attrib(int slot) {
970 bool any_changed = false;
971
972 Thread *current_thread = Thread::get_current_thread();
973 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
974 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
975
976 CPT(RenderState) new_state = cdata->_state->remove_attrib(slot);
977 if (cdata->_state != new_state) {
978 cdata->_state = new_state;
979 cdata->set_fancy_bit(FB_state, !new_state->is_empty());
980 any_changed = true;
981 }
982 }
983 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
984
985 // We mark the bounds stale when the state changes, in case we have changed
986 // a ClipPlaneAttrib.
987 if (any_changed) {
988 mark_bounds_stale(current_thread);
989 state_changed();
991 }
992}
993
994/**
995 * Adds the indicated render effect to the scene graph on this node. If there
996 * was already an effect of the same type, it is replaced.
997 */
999set_effect(const RenderEffect *effect) {
1000 // Apply this operation to the current stage as well as to all upstream
1001 // stages.
1002 Thread *current_thread = Thread::get_current_thread();
1003 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1004 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1005 cdata->_effects = cdata->_effects->add_effect(effect);
1006 cdata->set_fancy_bit(FB_effects, true);
1007 }
1008 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1010}
1011
1012/**
1013 * Removes the render effect of the given type from this node.
1014 */
1017 Thread *current_thread = Thread::get_current_thread();
1018 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1019 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1020 cdata->_effects = cdata->_effects->remove_effect(type);
1021 cdata->set_fancy_bit(FB_effects, !cdata->_effects->is_empty());
1022 }
1023 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1025}
1026
1027/**
1028 * Sets the complete RenderState that will be applied to all nodes at this
1029 * level and below. (The actual state that will be applied to lower nodes is
1030 * based on the composition of RenderStates from above this node as well).
1031 * This completely replaces whatever has been set on this node via repeated
1032 * calls to set_attrib().
1033 */
1034void PandaNode::
1035set_state(const RenderState *state, Thread *current_thread) {
1036 // Apply this operation to the current stage as well as to all upstream
1037 // stages.
1038 bool any_changed = false;
1039 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1040 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1041 if (cdata->_state != state) {
1042 cdata->_state = state;
1043 cdata->set_fancy_bit(FB_state, !state->is_empty());
1044 any_changed = true;
1045 }
1046 }
1047 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1048
1049 // Maybe we have changed a ClipPlaneAttrib.
1050 if (any_changed) {
1051 mark_bounds_stale(current_thread);
1052 state_changed();
1054 }
1055}
1056
1057/**
1058 * Sets the complete RenderEffects that will be applied this node. This
1059 * completely replaces whatever has been set on this node via repeated calls
1060 * to set_attrib().
1061 */
1062void PandaNode::
1063set_effects(const RenderEffects *effects, Thread *current_thread) {
1064 // Apply this operation to the current stage as well as to all upstream
1065 // stages.
1066 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1067 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1068 cdata->_effects = effects;
1069 cdata->set_fancy_bit(FB_effects, !effects->is_empty());
1070 }
1071 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1073}
1074
1075/**
1076 * Sets the transform that will be applied to this node and below. This
1077 * defines a new coordinate space at this point in the scene graph and below.
1078 */
1079void PandaNode::
1080set_transform(const TransformState *transform, Thread *current_thread) {
1081 nassertv(!transform->is_invalid());
1082
1083 // Need to have this held before we grab any other locks.
1084 LightMutexHolder holder(_dirty_prev_transforms._lock);
1085
1086 // Apply this operation to the current stage as well as to all upstream
1087 // stages.
1088 bool any_changed = false;
1089 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1090 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1091 if (cdata->_transform != transform) {
1092 cdata->_transform = transform;
1093 cdata->set_fancy_bit(FB_transform, !transform->is_identity());
1094 any_changed = true;
1095
1096 if (pipeline_stage == 0) {
1097 if (cdata->_transform != cdata->_prev_transform) {
1098 do_set_dirty_prev_transform();
1099 }
1100 }
1101 }
1102 }
1103 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1104
1105 if (any_changed) {
1106 mark_bounds_stale(current_thread);
1107 transform_changed();
1109 }
1110}
1111
1112/**
1113 * Sets the transform that represents this node's "previous" position, one
1114 * frame ago, for the purposes of detecting motion for accurate collision
1115 * calculations.
1116 */
1118set_prev_transform(const TransformState *transform, Thread *current_thread) {
1119 nassertv(!transform->is_invalid());
1120
1121 // Need to have this held before we grab any other locks.
1122 LightMutexHolder holder(_dirty_prev_transforms._lock);
1123
1124 // Apply this operation to the current stage as well as to all upstream
1125 // stages.
1126 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1127 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1128 cdata->_prev_transform = transform;
1129 if (pipeline_stage == 0) {
1130 if (cdata->_transform != cdata->_prev_transform) {
1131 do_set_dirty_prev_transform();
1132 } else {
1133 do_clear_dirty_prev_transform();
1134 }
1135 }
1136 }
1137 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1139}
1140
1141/**
1142 * Resets the transform that represents this node's "previous" position to the
1143 * same as the current transform. This is not the same thing as clearing it
1144 * to identity.
1145 */
1147reset_prev_transform(Thread *current_thread) {
1148 // Need to have this held before we grab any other locks.
1149 LightMutexHolder holder(_dirty_prev_transforms._lock);
1150 do_clear_dirty_prev_transform();
1151
1152 // Apply this operation to the current stage as well as to all upstream
1153 // stages.
1154
1155 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1156 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1157 cdata->_prev_transform = cdata->_transform;
1158 }
1159 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1161}
1162
1163/**
1164 * Visits all nodes in the world with the _dirty_prev_transform flag--that is,
1165 * all nodes whose _prev_transform is different from the _transform in
1166 * pipeline stage 0--and resets the _prev_transform to be the same as
1167 * _transform.
1168 */
1170reset_all_prev_transform(Thread *current_thread) {
1171 nassertv(current_thread->get_pipeline_stage() == 0);
1172
1173 PStatTimer timer(_reset_prev_pcollector, current_thread);
1174 LightMutexHolder holder(_dirty_prev_transforms._lock);
1175
1176 LinkedListNode *list_node = _dirty_prev_transforms._next;
1177 while (list_node != &_dirty_prev_transforms) {
1178 PandaNode *panda_node = (PandaNode *)list_node;
1179 nassertv(panda_node->_dirty_prev_transform);
1180 panda_node->_dirty_prev_transform = false;
1181
1182 CDStageWriter cdata(panda_node->_cycler, 0, current_thread);
1183 cdata->_prev_transform = cdata->_transform;
1184
1185 list_node = panda_node->_next;
1186#ifndef NDEBUG
1187 panda_node->_prev = nullptr;
1188 panda_node->_next = nullptr;
1189#endif // NDEBUG
1190 panda_node->mark_bam_modified();
1191 }
1192
1193 _dirty_prev_transforms._prev = &_dirty_prev_transforms;
1194 _dirty_prev_transforms._next = &_dirty_prev_transforms;
1195}
1196
1197/**
1198 * Associates a user-defined value with a user-defined key which is stored on
1199 * the node. This value has no meaning to Panda; but it is stored
1200 * indefinitely on the node until it is requested again.
1201 *
1202 * Each unique key stores a different string value. There is no effective
1203 * limit on the number of different keys that may be stored or on the length
1204 * of any one key's value.
1205 */
1206void PandaNode::
1207set_tag(const string &key, const string &value, Thread *current_thread) {
1208 // Apply this operation to the current stage as well as to all upstream
1209 // stages.
1210 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1211 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1212 cdata->_tag_data.store(key, value);
1213 cdata->set_fancy_bit(FB_tag, true);
1214 }
1215 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1217}
1218
1219/**
1220 * Removes the value defined for this key on this particular node. After a
1221 * call to clear_tag(), has_tag() will return false for the indicated key.
1222 */
1223void PandaNode::
1224clear_tag(const string &key, Thread *current_thread) {
1225 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1226 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1227 cdata->_tag_data.remove(key);
1228 cdata->set_fancy_bit(FB_tag, !cdata->_tag_data.is_empty());
1229 }
1230 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1232}
1233
1234/**
1235 * Copies all of the tags stored on the other node onto this node. If a
1236 * particular tag exists on both nodes, the contents of this node's value is
1237 * replaced by that of the other.
1238 */
1240copy_tags(PandaNode *other) {
1241 if (other == this) {
1242 // Trivial.
1243 return;
1244 }
1245
1246 // Apply this operation to the current stage as well as to all upstream
1247 // stages.
1248 Thread *current_thread = Thread::get_current_thread();
1249 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1250 CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
1251 CDStageReader cdatar(other->_cycler, pipeline_stage, current_thread);
1252
1253 for (size_t n = 0; n < cdatar->_tag_data.size(); ++n) {
1254 cdataw->_tag_data.store(cdatar->_tag_data.get_key(n), cdatar->_tag_data.get_data(n));
1255 }
1256 cdataw->set_fancy_bit(FB_tag, !cdataw->_tag_data.is_empty());
1257 }
1258 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1259
1260 // It's okay to copy the tags by pointer, because get_python_tags does a
1261 // copy-on-write.
1262 _python_tag_data = other->_python_tag_data;
1263
1265}
1266
1267/**
1268 * Writes a list of all the tag keys assigned to the node to the indicated
1269 * stream. Writes one instance of the separator following each key (but does
1270 * not write a terminal separator). The value associated with each key is not
1271 * written.
1272 *
1273 * This is mainly for the benefit of the realtime user, to see the list of all
1274 * of the associated tag keys.
1275 */
1277list_tags(ostream &out, const string &separator) const {
1278 CDReader cdata(_cycler);
1279 for (size_t n = 0; n < cdata->_tag_data.size(); ++n) {
1280 if (n > 0) {
1281 out << separator;
1282 }
1283 out << cdata->_tag_data.get_key(n);
1284 }
1285
1286 // We used to list the Python tags here. That's a bit awkward, though,
1287 // since that means calling up into Python code to print the keys. If
1288 // someone finds it useful, we can implement it in an extension method.
1289}
1290
1291/**
1292 * Fills the given vector up with the list of tags on this PandaNode.
1293 *
1294 * It is the user's responsibility to ensure that the keys vector is empty
1295 * before making this call; otherwise, the new keys will be appended to it.
1296 */
1298get_tag_keys(vector_string &keys) const {
1299 CDReader cdata(_cycler);
1300 for (size_t n = 0; n < cdata->_tag_data.size(); ++n) {
1301 keys.push_back(cdata->_tag_data.get_key(n));
1302 }
1303}
1304
1305/**
1306 * Returns a number less than 0, 0, or greater than 0, to indicate the
1307 * similarity of tags between this node and the other one. If this returns 0,
1308 * the tags are identical. If it returns other than 0, then the tags are
1309 * different; and the nodes may be sorted into a consistent (but arbitrary)
1310 * ordering based on this number.
1311 */
1313compare_tags(const PandaNode *other) const {
1314 CDReader cdata(_cycler);
1315 CDReader cdata_other(other->_cycler);
1316
1317 const TagData &a_data = cdata->_tag_data;
1318 const TagData &b_data = cdata_other->_tag_data;
1319
1320 size_t ai = 0;
1321 size_t bi = 0;
1322 while (ai < a_data.size() && bi < b_data.size()) {
1323 int cmp = strcmp(a_data.get_key(ai).c_str(), b_data.get_key(bi).c_str());
1324 if (cmp != 0) {
1325 return cmp;
1326 }
1327
1328 cmp = strcmp(a_data.get_data(ai).c_str(), b_data.get_data(bi).c_str());
1329 if (cmp != 0) {
1330 return cmp;
1331 }
1332
1333 ++ai;
1334 ++bi;
1335 }
1336 if (ai < a_data.size()) {
1337 // list A is longer.
1338 return 1;
1339 }
1340 if (bi < b_data.size()) {
1341 // list B is longer.
1342 return -1;
1343 }
1344
1345 // We compare these by pointer, since it's problematic to call up into
1346 // Python from arbitrary C++ code.
1347 if (_python_tag_data != other->_python_tag_data) {
1348 return (_python_tag_data < other->_python_tag_data) ? -1 : 1;
1349 }
1350
1351 return 0;
1352}
1353
1354/**
1355 * Copies the TransformState, RenderState, RenderEffects, tags, Python tags,
1356 * and the show/hide state from the other node onto this one. Typically this
1357 * is used to prepare a node to replace another node in the scene graph (also
1358 * see replace_node()).
1359 */
1362 if (other == this) {
1363 // Trivial.
1364 return;
1365 }
1366
1367 // Need to have this held before we grab any other locks.
1368 LightMutexHolder holder(_dirty_prev_transforms._lock);
1369
1370 bool any_transform_changed = false;
1371 bool any_state_changed = false;
1372 bool any_draw_mask_changed = false;
1373 Thread *current_thread = Thread::get_current_thread();
1374 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1375 CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
1376 CDStageReader cdatar(other->_cycler, pipeline_stage, current_thread);
1377
1378 if (cdataw->_transform != cdatar->_transform) {
1379 any_transform_changed = true;
1380 }
1381 if (cdataw->_state != cdatar->_state) {
1382 any_state_changed = true;
1383 }
1384 if (cdataw->_draw_control_mask != cdatar->_draw_control_mask ||
1385 cdataw->_draw_show_mask != cdatar->_draw_show_mask) {
1386 any_draw_mask_changed = true;
1387 }
1388
1389 cdataw->_transform = cdatar->_transform;
1390 cdataw->_prev_transform = cdatar->_prev_transform;
1391 cdataw->_state = cdatar->_state;
1392 cdataw->_effects = cdatar->_effects;
1393 cdataw->_draw_control_mask = cdatar->_draw_control_mask;
1394 cdataw->_draw_show_mask = cdatar->_draw_show_mask;
1395
1396 // The collide mask becomes the union of the two masks. This is important
1397 // to preserve properties such as the default GeomNode bitmask.
1398 cdataw->_into_collide_mask |= cdatar->_into_collide_mask;
1399
1400 for (size_t n = 0; n < cdatar->_tag_data.size(); ++n) {
1401 cdataw->_tag_data.store(cdatar->_tag_data.get_key(n), cdatar->_tag_data.get_data(n));
1402 }
1403
1404 static const int change_bits = (FB_transform | FB_state | FB_effects |
1405 FB_tag | FB_draw_mask);
1406 cdataw->_fancy_bits =
1407 (cdataw->_fancy_bits & ~change_bits) |
1408 (cdatar->_fancy_bits & change_bits);
1409
1410 if (pipeline_stage == 0) {
1411 if (cdataw->_transform != cdataw->_prev_transform) {
1412 do_set_dirty_prev_transform();
1413 }
1414 }
1415 }
1416 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1417
1418 // It's okay to copy the tags by pointer, because get_python_tags does a
1419 // copy-on-write.
1420 _python_tag_data = other->_python_tag_data;
1421
1422 if (any_transform_changed || any_state_changed || any_draw_mask_changed) {
1423 mark_bounds_stale(current_thread);
1424
1425 if (any_transform_changed) {
1426 transform_changed();
1427 }
1428 if (any_state_changed) {
1429 state_changed();
1430 }
1431 if (any_draw_mask_changed) {
1432 draw_mask_changed();
1433 }
1435 }
1436}
1437
1438/**
1439 * Inserts this node into the scene graph in place of the other one, and
1440 * removes the other node. All scene graph attributes (TransformState,
1441 * RenderState, etc.) are copied to this node.
1442 *
1443 * All children are moved to this node, and removed from the old node. The
1444 * new node is left in the same place in the old node's parent's list of
1445 * children.
1446 *
1447 * Even NodePaths that reference the old node are updated in-place to
1448 * reference the new node instead.
1449 *
1450 * This method is intended to be used to replace a node of a given type in the
1451 * scene graph with a node of a different type.
1452 */
1454replace_node(PandaNode *other) {
1455 // nassertv(Thread::get_current_pipeline_stage() == 0);
1456
1457 if (other == this) {
1458 // Trivial.
1459 return;
1460 }
1461
1462 // Make sure the other node is not destructed during the execution of this
1463 // method.
1464 PT(PandaNode) keep_other = other;
1465
1466 // Get all the important scene graph properties.
1467 copy_all_properties(other);
1468
1469 // Fix up the NodePaths.
1470 {
1471 LightReMutexHolder holder1(other->_paths_lock);
1472 LightReMutexHolder holder2(_paths_lock);
1473 Paths::iterator pi;
1474 for (pi = other->_paths.begin(); pi != other->_paths.end(); ++pi) {
1475 (*pi)->_node = this;
1476 _paths.insert(*pi);
1477 }
1478 other->_paths.clear();
1479 }
1480
1481 // Get the children.
1482 steal_children(other);
1483
1484 // Switch the parents.
1485 Thread *current_thread = Thread::get_current_thread();
1486 Parents other_parents = other->get_parents();
1487 for (size_t i = 0; i < other_parents.get_num_parents(); ++i) {
1488 PandaNode *parent = other_parents.get_parent(i);
1489 if (find_parent(parent) != -1) {
1490 // This node was already a child of this parent; don't change it.
1491 parent->remove_child(other);
1492 } else {
1493 // This node was not yet a child of this parent; now it is.
1494 parent->replace_child(other, this, current_thread);
1495 }
1496 }
1497}
1498
1499/**
1500 * Sets one or more of the PandaNode::UnexpectedChange bits on, indicating
1501 * that the corresponding property should not change again on this node. Once
1502 * one of these bits has been set, if the property changes, an assertion
1503 * failure will be raised, which is designed to assist the developer in
1504 * identifying the troublesome code that modified the property unexpectedly.
1505 *
1506 * The input parameter is the union of bits that are to be set. To clear
1507 * these bits later, use clear_unexpected_change().
1508 *
1509 * Since this is a developer debugging tool only, this function does nothing
1510 * in a production (NDEBUG) build.
1511 */
1513set_unexpected_change(unsigned int flags) {
1514#ifndef NDEBUG
1515 _unexpected_change_flags |= flags;
1516#endif // !NDEBUG
1517}
1518
1519/**
1520 * Returns nonzero if any of the bits in the input parameter are set on this
1521 * node, or zero if none of them are set. More specifically, this returns the
1522 * particular set of bits (masked by the input parameter) that have been set
1523 * on this node. See set_unexpected_change().
1524 *
1525 * Since this is a developer debugging tool only, this function always returns
1526 * zero in a production (NDEBUG) build.
1527 */
1528unsigned int PandaNode::
1529get_unexpected_change(unsigned int flags) const {
1530#ifndef NDEBUG
1531 return _unexpected_change_flags & flags;
1532#else
1533 return 0;
1534#endif // !NDEBUG
1535}
1536
1537/**
1538 * Sets one or more of the PandaNode::UnexpectedChange bits off, indicating
1539 * that the corresponding property may once again change on this node. See
1540 * set_unexpected_change().
1541 *
1542 * The input parameter is the union of bits that are to be cleared.
1543 *
1544 * Since this is a developer debugging tool only, this function does nothing
1545 * in a production (NDEBUG) build.
1546 */
1548clear_unexpected_change(unsigned int flags) {
1549#ifndef NDEBUG
1550 _unexpected_change_flags &= ~flags;
1551#endif // !NDEBUG
1552}
1553
1554/**
1555 * Adjusts the hide/show bits of this particular node.
1556 *
1557 * These three parameters can be used to adjust the _draw_control_mask and
1558 * _draw_show_mask independently, which work together to provide per-camera
1559 * visibility for the node and its descendents.
1560 *
1561 * _draw_control_mask indicates the bits in _draw_show_mask that are
1562 * significant. Each different bit corresponds to a different camera (and
1563 * these bits are assigned via Camera::set_camera_mask()).
1564 *
1565 * Where _draw_control_mask has a 1 bit, a 1 bit in _draw_show_mask indicates
1566 * the node is visible to that camera, and a 0 bit indicates the node is
1567 * hidden to that camera. Where _draw_control_mask is 0, the node is hidden
1568 * only if a parent node is hidden.
1569 *
1570 * The meaning of the three parameters is as follows:
1571 *
1572 * * Wherever show_mask is 1, _draw_show_mask and _draw_control_mask will be
1573 * set 1. Thus, show_mask indicates the set of cameras to which the node
1574 * should be shown.
1575 *
1576 * * Wherever hide_mask is 1, _draw_show_mask will be set 0 and
1577 * _draw_control_mask will be set 1. Thus, hide_mask indicates the set of
1578 * cameras from which the node should be hidden.
1579 *
1580 * * Wherever clear_mask is 1, _draw_control_mask will be set 0. Thus,
1581 * clear_mask indicates the set of cameras from which the hidden state should
1582 * be inherited from a parent.
1583 */
1585adjust_draw_mask(DrawMask show_mask, DrawMask hide_mask, DrawMask clear_mask) {
1586 bool any_changed = false;
1587
1588 Thread *current_thread = Thread::get_current_thread();
1589 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1590 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1591
1592 DrawMask draw_control_mask = (cdata->_draw_control_mask | show_mask | hide_mask) & ~clear_mask;
1593 DrawMask draw_show_mask = (cdata->_draw_show_mask | show_mask) & ~hide_mask;
1594 // The uncontrolled bits are implicitly on.
1595 draw_show_mask |= ~draw_control_mask;
1596
1597 if (cdata->_draw_control_mask != draw_control_mask ||
1598 cdata->_draw_show_mask != draw_show_mask) {
1599 cdata->_draw_control_mask = draw_control_mask;
1600 cdata->_draw_show_mask = draw_show_mask;
1601 any_changed = true;
1602 }
1603 cdata->set_fancy_bit(FB_draw_mask, !draw_control_mask.is_zero());
1604 }
1605 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1606
1607 if (any_changed) {
1608 mark_bounds_stale(current_thread);
1609 draw_mask_changed();
1611 }
1612}
1613
1614/**
1615 * Returns the set of bits in get_net_draw_show_mask() that have been
1616 * explicitly set via adjust_draw_mask(), rather than implicitly inherited.
1617 *
1618 * A 1 bit in any position of this mask indicates that (a) this node has
1619 * renderable children, and (b) some child of this node has made an explicit
1620 * hide() or show_through() call for the corresponding bit.
1621 */
1624 Thread *current_thread = Thread::get_current_thread();
1625 int pipeline_stage = current_thread->get_pipeline_stage();
1626 CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
1627 if (cdata->_last_update != cdata->_next_update) {
1628 // The cache is stale; it needs to be rebuilt.
1629 PStatTimer timer(_update_bounds_pcollector);
1630 CDStageWriter cdataw =
1631 ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
1632 return cdataw->_net_draw_control_mask;
1633 }
1634 return cdata->_net_draw_control_mask;
1635}
1636
1637/**
1638 * Returns the union of all draw_show_mask values--of renderable nodes only--
1639 * at this level and below. If any bit in this mask is 0, there is no reason
1640 * to traverse below this node for a camera with the corresponding
1641 * camera_mask.
1642 *
1643 * The bits in this mask that do not correspond to a 1 bit in the
1644 * net_draw_control_mask are meaningless (and will be set to 1). For bits
1645 * that *do* correspond to a 1 bit in the net_draw_control_mask, a 1 bit
1646 * indicates that at least one child should be visible, while a 0 bit
1647 * indicates that all children are hidden.
1648 */
1650get_net_draw_show_mask() const {
1651 Thread *current_thread = Thread::get_current_thread();
1652 int pipeline_stage = current_thread->get_pipeline_stage();
1653 CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
1654 if (cdata->_last_update != cdata->_next_update) {
1655 // The cache is stale; it needs to be rebuilt.
1656 PStatTimer timer(_update_bounds_pcollector);
1657 CDStageWriter cdataw =
1658 ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
1659 return cdataw->_net_draw_show_mask;
1660 }
1661 return cdata->_net_draw_show_mask;
1662}
1663
1664/**
1665 * Sets the "into" CollideMask.
1666 *
1667 * This specifies the set of bits that must be shared with a CollisionNode's
1668 * "from" CollideMask in order for the CollisionNode to detect a collision
1669 * with this particular node.
1670 *
1671 * The actual CollideMask that will be set is masked by the return value from
1672 * get_legal_collide_mask(). Thus, the into_collide_mask cannot be set to
1673 * anything other than nonzero except for those types of nodes that can be
1674 * collided into, such as CollisionNodes and GeomNodes.
1675 */
1676void PandaNode::
1678 mask &= get_legal_collide_mask();
1679
1680 bool any_changed = false;
1681 Thread *current_thread = Thread::get_current_thread();
1682 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1683 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1684 if (cdata->_into_collide_mask != mask) {
1685 cdata->_into_collide_mask = mask;
1686 any_changed = true;
1687 }
1688 }
1689 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1690
1691 if (any_changed) {
1692 mark_bounds_stale(current_thread);
1694 }
1695}
1696
1697/**
1698 * Returns the subset of CollideMask bits that may be set for this particular
1699 * type of PandaNode. For most nodes, this is 0; it doesn't make sense to set
1700 * a CollideMask for most kinds of nodes.
1701 *
1702 * For nodes that can be collided with, such as GeomNode and CollisionNode,
1703 * this returns all bits on.
1704 */
1706get_legal_collide_mask() const {
1707 return CollideMask::all_off();
1708}
1709
1710/**
1711 * Returns the union of all into_collide_mask() values set at CollisionNodes
1712 * at this level and below.
1713 */
1715get_net_collide_mask(Thread *current_thread) const {
1716 int pipeline_stage = current_thread->get_pipeline_stage();
1717 CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
1718 if (cdata->_last_update != cdata->_next_update) {
1719 // The cache is stale; it needs to be rebuilt.
1720 PStatTimer timer(_update_bounds_pcollector);
1721 CDStageWriter cdataw =
1722 ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
1723 return cdataw->_net_collide_mask;
1724 }
1725 return cdata->_net_collide_mask;
1726}
1727
1728/**
1729 * Returns a ClipPlaneAttrib which represents the union of all of the clip
1730 * planes that have been turned *off* at this level and below.
1731 */
1732CPT(RenderAttrib) PandaNode::
1733get_off_clip_planes(Thread *current_thread) const {
1734 int pipeline_stage = current_thread->get_pipeline_stage();
1735 CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
1736 if (cdata->_last_update != cdata->_next_update) {
1737 // The cache is stale; it needs to be rebuilt.
1738 PStatTimer timer(_update_bounds_pcollector);
1739 CDStageWriter cdataw =
1740 ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
1741 return cdataw->_off_clip_planes;
1742 }
1743 return cdata->_off_clip_planes;
1744}
1745
1746/**
1747 * Walks through the scene graph beginning at this node, and does whatever
1748 * initialization is required to render the scene properly with the indicated
1749 * GSG. It is not strictly necessary to call this, since the GSG will
1750 * initialize itself when the scene is rendered, but this may take some of the
1751 * overhead away from that process.
1752 *
1753 * In particular, this will ensure that textures and vertex buffers within the
1754 * scene are loaded into graphics memory.
1755 */
1756void PandaNode::
1757prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state) {
1758 GeomTransformer transformer;
1759 Thread *current_thread = Thread::get_current_thread();
1760 r_prepare_scene(gsg, node_state, transformer, current_thread);
1761}
1762
1763/**
1764 * Returns true if this particular node is known to be the render root of some
1765 * active DisplayRegion associated with the global GraphicsEngine, false
1766 * otherwise.
1767 */
1769is_scene_root() const {
1770 // This function pointer has to be filled in when the global GraphicsEngine
1771 // is created, because we can't link with the GraphicsEngine functions
1772 // directly.
1773 if (_scene_root_func != nullptr) {
1774 return (*_scene_root_func)(this);
1775 }
1776 return false;
1777}
1778
1779/**
1780 * Returns true if this particular node is in a live scene graph: that is, it
1781 * is a child or descendent of a node that is itself a scene root. If this is
1782 * true, this node may potentially be traversed by the render traverser.
1783 * Stashed nodes don't count for this purpose, but hidden nodes do.
1784 */
1786is_under_scene_root() const {
1787 if (is_scene_root()) {
1788 return true;
1789 }
1790
1791 Parents parents = get_parents();
1792 for (size_t i = 0; i < parents.get_num_parents(); ++i) {
1793 PandaNode *parent = parents.get_parent(i);
1794 if (parent->find_stashed((PandaNode *)this) == -1) {
1795 if (parent->is_under_scene_root()) {
1796 return true;
1797 }
1798 }
1799 }
1800 return false;
1801}
1802
1803/**
1804 *
1805 */
1806void PandaNode::
1807output(ostream &out) const {
1808 out << get_type() << " " << get_name();
1809}
1810
1811/**
1812 *
1813 */
1814void PandaNode::
1815write(ostream &out, int indent_level) const {
1816 indent(out, indent_level) << *this;
1817 if (has_tags()) {
1818 out << " [";
1819 list_tags(out, " ");
1820 out << "]";
1821 }
1822 CPT(TransformState) transform = get_transform();
1823 if (!transform->is_identity()) {
1824 out << " " << *transform;
1825 }
1826 CPT(RenderState) state = get_state();
1827 if (!state->is_empty()) {
1828 out << " " << *state;
1829 }
1830 CPT(RenderEffects) effects = get_effects();
1831 if (!effects->is_empty()) {
1832 out << " " << *effects;
1833 }
1834 DrawMask draw_control_mask = get_draw_control_mask();
1835 if (!draw_control_mask.is_zero()) {
1836 DrawMask draw_show_mask = get_draw_show_mask();
1837 if (!(draw_control_mask & _overall_bit).is_zero()) {
1838 if (!(draw_show_mask & _overall_bit).is_zero()) {
1839 out << " (show_through)";
1840 } else {
1841 out << " (hidden)";
1842 }
1843 }
1844 if (!(draw_control_mask & ~_overall_bit).is_zero()) {
1845 draw_control_mask &= ~_overall_bit;
1846 if (!(draw_show_mask & draw_control_mask).is_zero()) {
1847 out << " (per-camera show_through)";
1848 }
1849 if (!(~draw_show_mask & draw_control_mask).is_zero()) {
1850 out << " (per-camera hidden)";
1851 }
1852 }
1853 }
1854 out << "\n";
1855}
1856
1857/**
1858 * Specifies the desired type of bounding volume that will be created for this
1859 * node. This is normally BoundingVolume::BT_default, which means to set the
1860 * type according to the config variable "bounds-type".
1861 *
1862 * If this is BT_sphere or BT_box, a BoundingSphere or BoundingBox is
1863 * explicitly created. If it is BT_best, the appropriate type to best enclose
1864 * the node's children is created.
1865 *
1866 * This affects the bounding volume returned by get_bounds(), which is not
1867 * exactly the same bounding volume modified by set_bounds(), because a new
1868 * bounding volume has to be created that includes this node and all of its
1869 * children.
1870 */
1871void PandaNode::
1872set_bounds_type(BoundingVolume::BoundsType bounds_type) {
1873 Thread *current_thread = Thread::get_current_thread();
1874 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1875 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1876 cdata->_bounds_type = bounds_type;
1877 mark_bounds_stale(pipeline_stage, current_thread);
1878
1879 // GeomNodes, CollisionNodes, and PGItems all have an internal bounds that
1880 // may need to be updated when the bounds_type changes.
1881 mark_internal_bounds_stale(pipeline_stage, current_thread);
1883 }
1884 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1885}
1886
1887/**
1888 * Returns the bounding volume type set with set_bounds_type().
1889 */
1890BoundingVolume::BoundsType PandaNode::
1891get_bounds_type() const {
1892 CDReader cdata(_cycler);
1893 return cdata->_bounds_type;
1894}
1895
1896/**
1897 * Resets the bounding volume so that it is the indicated volume. When it is
1898 * explicitly set, the bounding volume will no longer be automatically
1899 * computed according to the contents of the node itself, for nodes like
1900 * GeomNodes and TextNodes that contain substance (but the bounding volume
1901 * will still be automatically expanded to include its children).
1902 *
1903 * Call clear_bounds() if you would like to return the bounding volume to its
1904 * default behavior later.
1905 */
1907set_bounds(const BoundingVolume *volume) {
1908 Thread *current_thread = Thread::get_current_thread();
1909 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
1910 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
1911 if (volume == nullptr) {
1912 cdata->_user_bounds = nullptr;
1913 } else {
1914 cdata->_user_bounds = volume->make_copy();
1915 }
1916 mark_bounds_stale(pipeline_stage, current_thread);
1918 }
1919 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
1920}
1921
1922/**
1923 * @deprecated Use set_bounds() instead.
1924 */
1926set_bound(const BoundingVolume *volume) {
1927 pgraph_cat.warning()
1928 << "Deprecated PandaNode::set_bound() called. Use set_bounds() instead.\n";
1929 set_bounds(volume);
1930}
1931
1932/**
1933 * Returns the external bounding volume of this node: a bounding volume that
1934 * contains the user bounding volume, the internal bounding volume, and all of
1935 * the children's bounding volumes.
1936 */
1937CPT(BoundingVolume) PandaNode::
1938get_bounds(Thread *current_thread) const {
1939 int pipeline_stage = current_thread->get_pipeline_stage();
1940 CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
1941 if (cdata->_last_bounds_update != cdata->_next_update) {
1942 // The cache is stale; it needs to be rebuilt.
1943 CPT(BoundingVolume) result;
1944 {
1945 PStatTimer timer(_update_bounds_pcollector);
1946 CDStageWriter cdataw =
1947 ((PandaNode *)this)->update_cached(true, pipeline_stage, cdata);
1948 result = cdataw->_external_bounds;
1949 }
1950 return result;
1951 }
1952 return cdata->_external_bounds;
1953}
1954
1955/**
1956 * This flavor of get_bounds() return the external bounding volume, and also
1957 * fills in seq with the bounding volume's current sequence number. When this
1958 * sequence number changes, it indicates that the bounding volume might have
1959 * changed, e.g. because some nested child's bounding volume has changed.
1960 *
1961 * Although this might occasionally increment without changing the bounding
1962 * volume, the bounding volume will never change without incrementing this
1963 * counter, so as long as this counter remains unchanged you can be confident
1964 * the bounding volume is also unchanged.
1965 */
1966CPT(BoundingVolume) PandaNode::
1967get_bounds(UpdateSeq &seq, Thread *current_thread) const {
1968 int pipeline_stage = current_thread->get_pipeline_stage();
1969 CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
1970 if (cdata->_last_bounds_update != cdata->_next_update) {
1971 // The cache is stale; it needs to be rebuilt.
1972 CPT(BoundingVolume) result;
1973 {
1974 PStatTimer timer(_update_bounds_pcollector);
1975 CDStageWriter cdataw =
1976 ((PandaNode *)this)->update_cached(true, pipeline_stage, cdata);
1977 result = cdataw->_external_bounds;
1978 seq = cdataw->_last_bounds_update;
1979 }
1980 return result;
1981 }
1982 seq = cdata->_last_bounds_update;
1983 return cdata->_external_bounds;
1984}
1985
1986/**
1987 * Returns the total number of vertices that will be rendered by this node and
1988 * all of its descendents.
1989 *
1990 * This is not necessarily an accurate count of vertices that will actually be
1991 * rendered, since this will include all vertices of all LOD's, and it will
1992 * also include hidden nodes. It may also omit or only approximate certain
1993 * kinds of dynamic geometry. However, it will not include stashed nodes.
1994 */
1995int PandaNode::
1996get_nested_vertices(Thread *current_thread) const {
1997 int pipeline_stage = current_thread->get_pipeline_stage();
1998 CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
1999 if (cdata->_last_bounds_update != cdata->_next_update) {
2000 // The cache is stale; it needs to be rebuilt.
2001 int result;
2002 {
2003 PStatTimer timer(_update_bounds_pcollector);
2004 CDStageWriter cdataw =
2005 ((PandaNode *)this)->update_cached(true, pipeline_stage, cdata);
2006 result = cdataw->_nested_vertices;
2007 }
2008 return result;
2009 }
2010 return cdata->_nested_vertices;
2011}
2012
2013/**
2014 * Indicates that the bounding volume, or something that influences the
2015 * bounding volume (or any of the other things stored in CData, like
2016 * net_collide_mask), may have changed for this node, and that it must be
2017 * recomputed.
2018 *
2019 * With no parameters, this means to iterate through all stages including and
2020 * upstream of the current pipeline stage.
2021 *
2022 * This method is intended for internal use; usually it is not necessary for a
2023 * user to call this directly. It will be called automatically by derived
2024 * classes when appropriate.
2025 */
2026void PandaNode::
2027mark_bounds_stale(Thread *current_thread) const {
2028 OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
2029 mark_bounds_stale(pipeline_stage, current_thread);
2030 }
2031 CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
2032}
2033
2034/**
2035 * Should be called by a derived class to mark the internal bounding volume
2036 * stale, so that compute_internal_bounds() will be called when the bounding
2037 * volume is next requested.
2038 *
2039 * With no parameters, this means to iterate through all stages including and
2040 * upstream of the current pipeline stage.
2041 *
2042 * It is normally not necessary to call this method directly; each node should
2043 * be responsible for calling it when its internals have changed.
2044 */
2045void PandaNode::
2046mark_internal_bounds_stale(Thread *current_thread) {
2047 OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
2048 mark_internal_bounds_stale(pipeline_stage, current_thread);
2049 }
2050 CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
2051}
2052
2053/**
2054 * A simple downcast check. Returns true if this kind of node happens to
2055 * inherit from GeomNode, false otherwise.
2056 *
2057 * This is provided as a a faster alternative to calling
2058 * is_of_type(GeomNode::get_class_type()), since this test is so important to
2059 * rendering.
2060 */
2062is_geom_node() const {
2063 return false;
2064}
2065
2066/**
2067 * A simple downcast check. Returns true if this kind of node happens to
2068 * inherit from LODNode, false otherwise.
2069 *
2070 * This is provided as a a faster alternative to calling
2071 * is_of_type(LODNode::get_class_type()).
2072 */
2074is_lod_node() const {
2075 return false;
2076}
2077
2078/**
2079 * A simple downcast check. Returns true if this kind of node happens to
2080 * inherit from CollisionNode, false otherwise.
2081 *
2082 * This is provided as a a faster alternative to calling
2083 * is_of_type(CollisionNode::get_class_type()).
2084 */
2086is_collision_node() const {
2087 return false;
2088}
2089
2090/**
2091 * Cross-casts the node to a Light pointer, if it is one of the four kinds of
2092 * Light nodes, or returns NULL if it is not.
2093 */
2095as_light() {
2096 return nullptr;
2097}
2098
2099/**
2100 * Returns true if this is an AmbientLight, false if it is not a light, or it
2101 * is some other kind of light.
2102 */
2104is_ambient_light() const {
2105 return false;
2106}
2107
2108/**
2109 * Reads the bytes created by a previous call to encode_to_bam_stream(), and
2110 * extracts and returns the single object on those bytes. Returns NULL on
2111 * error.
2112 *
2113 * This method is intended to replace decode_raw_from_bam_stream() when you
2114 * know the stream in question returns an object of type PandaNode, allowing
2115 * for easier reference count management. Note that the caller is still
2116 * responsible for maintaining the reference count on the return value.
2117 */
2118PT(PandaNode) PandaNode::
2119decode_from_bam_stream(vector_uchar data, BamReader *reader) {
2120 TypedWritable *object;
2121 ReferenceCount *ref_ptr;
2122
2123 if (TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, std::move(data), reader)) {
2124 return DCAST(PandaNode, object);
2125 } else {
2126 return nullptr;
2127 }
2128}
2129
2130/**
2131 * Returns the node's internal bounding volume. This is the bounding volume
2132 * around the node alone, without including children.
2133 */
2134CPT(BoundingVolume) PandaNode::
2135get_internal_bounds(int pipeline_stage, Thread *current_thread) const {
2136 while (true) {
2137 UpdateSeq mark;
2138 {
2139 CDStageReader cdata(_cycler, pipeline_stage, current_thread);
2140 if (cdata->_user_bounds != nullptr) {
2141 return cdata->_user_bounds;
2142 }
2143
2144 if (cdata->_internal_bounds_mark == cdata->_internal_bounds_computed) {
2145 return cdata->_internal_bounds;
2146 }
2147
2148 mark = cdata->_internal_bounds_mark;
2149 }
2150
2151 // First, call compute_internal_bounds without acquiring the lock. This
2152 // avoids a deadlock condition.
2153 CPT(BoundingVolume) internal_bounds;
2154 int internal_vertices;
2155 compute_internal_bounds(internal_bounds, internal_vertices,
2156 pipeline_stage, current_thread);
2157 nassertr(!internal_bounds.is_null(), nullptr);
2158
2159 // Now, acquire the lock, and apply the above-computed bounds.
2160 CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage);
2161 if (cdataw->_internal_bounds_mark == mark) {
2162 cdataw->_internal_bounds_computed = mark;
2163 cdataw->_internal_bounds = internal_bounds;
2164 cdataw->_internal_vertices = internal_vertices;
2165 ((PandaNode *)this)->mark_bam_modified();
2166 return cdataw->_internal_bounds;
2167 }
2168
2169 // Dang, someone in another thread incremented _internal_bounds_mark while
2170 // we weren't holding the lock. That means we need to go back and do it
2171 // again.
2172 }
2173}
2174
2175/**
2176 * Returns the total number of vertices that will be rendered by this
2177 * particular node alone, not accounting for its children.
2178 *
2179 * This may not include all vertices for certain dynamic effects.
2180 */
2181int PandaNode::
2182get_internal_vertices(int pipeline_stage, Thread *current_thread) const {
2183 while (true) {
2184 UpdateSeq mark;
2185 {
2186 CDStageReader cdata(_cycler, pipeline_stage, current_thread);
2187 if (cdata->_internal_bounds_mark == cdata->_internal_bounds_computed) {
2188 return cdata->_internal_vertices;
2189 }
2190
2191 mark = cdata->_internal_bounds_mark;
2192 }
2193
2194 // First, call compute_internal_bounds without acquiring the lock. This
2195 // avoids a deadlock condition.
2196 CPT(BoundingVolume) internal_bounds;
2197 int internal_vertices;
2198 compute_internal_bounds(internal_bounds, internal_vertices,
2199 pipeline_stage, current_thread);
2200 nassertr(!internal_bounds.is_null(), 0);
2201
2202 // Now, acquire the lock, and apply the above-computed bounds.
2203 CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage);
2204 if (cdataw->_internal_bounds_mark == mark) {
2205 cdataw->_internal_bounds_computed = mark;
2206 cdataw->_internal_bounds = internal_bounds;
2207 cdataw->_internal_vertices = internal_vertices;
2208 ((PandaNode *)this)->mark_bam_modified();
2209 return cdataw->_internal_vertices;
2210 }
2211
2212 // Dang, someone in another thread incremented _internal_bounds_mark while
2213 // we weren't holding the lock. That means we need to go back and do it
2214 // again.
2215 }
2216}
2217
2218/**
2219 * This is provided as an alternate way for a node to set its own internal
2220 * bounds, rather than overloading compute_internal_bounds(). If this method
2221 * is called, the internal bounding volume will immediately be set to the
2222 * indicated pointer.
2223 */
2224void PandaNode::
2225set_internal_bounds(const BoundingVolume *volume) {
2226 Thread *current_thread = Thread::get_current_thread();
2227 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
2228 CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
2229 cdataw->_internal_bounds = volume;
2230 cdataw->_internal_bounds_computed = cdataw->_internal_bounds_mark;
2231 }
2232 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
2233 mark_bounds_stale(current_thread);
2235}
2236
2237/**
2238 * Similar to mark_bounds_stale(), except that the parents of this node marked
2239 * stale even if this node was already considered stale.
2240 *
2241 * With no parameters, this means to iterate through all stages including and
2242 * upstream of the current pipeline stage.
2243 */
2244void PandaNode::
2245force_bounds_stale(Thread *current_thread) {
2246 OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
2247 force_bounds_stale(pipeline_stage, current_thread);
2248 }
2249 CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
2250}
2251
2252/**
2253 * Similar to mark_bounds_stale(), except that the parents of this node marked
2254 * stale even if this node was already considered stale.
2255 */
2256void PandaNode::
2257force_bounds_stale(int pipeline_stage, Thread *current_thread) {
2258 {
2259 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
2260 ++cdata->_next_update;
2262
2263 // It is important that we allow this lock to be dropped before we
2264 // continue up the graph; otherwise, we risk deadlock from another thread
2265 // walking down the graph.
2266 }
2267
2268 // It is similarly important that we use get_parents() here to copy the
2269 // parents list, instead of keeping the lock open while we walk through the
2270 // parents list directly on the node.
2271 Parents parents;
2272 {
2273 CDStageReader cdata(_cycler, pipeline_stage, current_thread);
2274 parents = Parents(cdata);
2275 }
2276 int num_parents = parents.get_num_parents();
2277 for (int i = 0; i < num_parents; ++i) {
2278 PandaNode *parent = parents.get_parent(i);
2279 parent->mark_bounds_stale(pipeline_stage, current_thread);
2280 }
2281}
2282
2283/**
2284 * Recursively calls Geom::mark_bounds_stale() on every Geom at this node and
2285 * below.
2286 */
2287void PandaNode::
2288r_mark_geom_bounds_stale(Thread *current_thread) {
2289 Children children = get_children(current_thread);
2290
2291 size_t i;
2292 for (i = 0; i < children.get_num_children(); i++) {
2293 PandaNode *child = children.get_child(i);
2294 child->r_mark_geom_bounds_stale(current_thread);
2295 }
2296
2297 Stashed stashed = get_stashed(current_thread);
2298 for (i = 0; i < stashed.get_num_stashed(); i++) {
2299 PandaNode *child = stashed.get_stashed(i);
2300 child->r_mark_geom_bounds_stale(current_thread);
2301 }
2302}
2303
2304/**
2305 * Returns a newly-allocated BoundingVolume that represents the internal
2306 * contents of the node. Should be overridden by PandaNode classes that
2307 * contain something internally.
2308 */
2309void PandaNode::
2310compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
2311 int &internal_vertices,
2312 int pipeline_stage,
2313 Thread *current_thread) const {
2314 internal_bounds = new BoundingSphere;
2315 internal_vertices = 0;
2316}
2317
2318/**
2319 * Called after a scene graph update that either adds or remove parents from
2320 * this node, this just provides a hook for derived PandaNode objects that
2321 * need to update themselves based on the set of parents the node has.
2322 */
2323void PandaNode::
2324parents_changed() {
2325 nassertv((_unexpected_change_flags & UC_parents) == 0);
2326}
2327
2328/**
2329 * Called after a scene graph update that either adds or remove children from
2330 * this node, this just provides a hook for derived PandaNode objects that
2331 * need to update themselves based on the set of children the node has.
2332 */
2333void PandaNode::
2334children_changed() {
2335 nassertv((_unexpected_change_flags & UC_children) == 0);
2336}
2337
2338/**
2339 * Called after the node's transform has been changed for any reason, this
2340 * just provides a hook so derived classes can do something special in this
2341 * case.
2342 */
2343void PandaNode::
2344transform_changed() {
2345 nassertv((_unexpected_change_flags & UC_transform) == 0);
2346}
2347
2348/**
2349 * Called after the node's RenderState has been changed for any reason, this
2350 * just provides a hook so derived classes can do something special in this
2351 * case.
2352 */
2353void PandaNode::
2354state_changed() {
2355 nassertv((_unexpected_change_flags & UC_state) == 0);
2356}
2357
2358/**
2359 * Called after the node's DrawMask has been changed for any reason, this just
2360 * provides a hook so derived classes can do something special in this case.
2361 */
2362void PandaNode::
2363draw_mask_changed() {
2364 nassertv((_unexpected_change_flags & UC_draw_mask) == 0);
2365}
2366
2367/**
2368 * This is the recursive implementation of copy_subgraph(). It returns a copy
2369 * of the entire subgraph rooted at this node.
2370 *
2371 * Note that it includes the parameter inst_map, which is a map type, and is
2372 * not (and cannot be) exported from PANDA.DLL. Thus, any derivative of
2373 * PandaNode that is not also a member of PANDA.DLL *cannot* access this map.
2374 */
2375PT(PandaNode) PandaNode::
2376r_copy_subgraph(PandaNode::InstanceMap &inst_map, Thread *current_thread) const {
2377 PT(PandaNode) copy = make_copy();
2378 nassertr(copy != nullptr, nullptr);
2379 if (copy->get_type() != get_type()) {
2380 pgraph_cat.warning()
2381 << "Don't know how to copy nodes of type " << get_type() << "\n";
2382
2383 if (no_unsupported_copy) {
2384 nassert_raise("unsupported copy");
2385 return nullptr;
2386 }
2387 }
2388
2389 copy->r_copy_children(this, inst_map, current_thread);
2390 return copy;
2391}
2392
2393/**
2394 * This is called by r_copy_subgraph(); the copy has already been made of this
2395 * particular node (and this is the copy); this function's job is to copy all
2396 * of the children from the original.
2397 *
2398 * Note that it includes the parameter inst_map, which is a map type, and is
2399 * not (and cannot be) exported from PANDA.DLL. Thus, any derivative of
2400 * PandaNode that is not also a member of PANDA.DLL *cannot* access this map,
2401 * and probably should not even override this function.
2402 */
2403void PandaNode::
2404r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
2405 Thread *current_thread) {
2406 CDReader from_cdata(from->_cycler, current_thread);
2407 CPT(Down) from_down = from_cdata->get_down();
2408 Down::const_iterator di;
2409 for (di = from_down->begin(); di != from_down->end(); ++di) {
2410 int sort = (*di).get_sort();
2411 PandaNode *source_child = (*di).get_child();
2412 PT(PandaNode) dest_child;
2413
2414 // Check to see if we have already copied this child. If we have, use the
2415 // copy. In this way, a subgraph that contains instances will be
2416 // correctly duplicated into another subgraph that also contains its own
2417 // instances.
2418 InstanceMap::const_iterator ci;
2419 ci = inst_map.find(source_child);
2420 if (ci != inst_map.end()) {
2421 dest_child = (*ci).second;
2422 } else {
2423 dest_child = source_child->r_copy_subgraph(inst_map, current_thread);
2424 inst_map[source_child] = dest_child;
2425 }
2426
2427 quick_add_new_child(dest_child, sort, current_thread);
2428 }
2429}
2430
2431/**
2432 * The recursive implementation of prepare_scene(). Don't call this directly;
2433 * call PandaNode::prepare_scene() or NodePath::prepare_scene() instead.
2434 */
2437 GeomTransformer &transformer, Thread *current_thread) {
2438 Children children = get_children(current_thread);
2439 // We must call get_num_children() each time through the loop, in case we're
2440 // running SIMPLE_THREADS and we get interrupted.
2441 size_t i;
2442 for (i = 0; i < children.get_num_children(); i++) {
2443 PandaNode *child = children.get_child(i);
2444 CPT(RenderState) child_state = node_state->compose(child->get_state());
2445 child->r_prepare_scene(gsg, child_state, transformer, current_thread);
2446 }
2447
2448 Stashed stashed = get_stashed(current_thread);
2449 for (i = 0; i < stashed.get_num_stashed(); i++) {
2450 PandaNode *child = stashed.get_stashed(i);
2451 CPT(RenderState) child_state = node_state->compose(child->get_state());
2452 child->r_prepare_scene(gsg, child_state, transformer, current_thread);
2453 }
2454}
2455
2456/**
2457 * Intended to be called in the constructor by any subclass that defines
2458 * cull_callback(), this sets up the flags to indicate that the cullback needs
2459 * to be called.
2460 */
2461void PandaNode::
2462set_cull_callback() {
2463 Thread *current_thread = Thread::get_current_thread();
2464 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
2465 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
2466 cdata->set_fancy_bit(FB_cull_callback, true);
2467 }
2468 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
2470}
2471
2472/**
2473 * disables the call back
2474 */
2475void PandaNode::
2476disable_cull_callback() {
2477 Thread *current_thread = Thread::get_current_thread();
2478 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
2479 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
2480 cdata->set_fancy_bit(FB_cull_callback, false);
2481 }
2482 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
2484}
2485
2486/**
2487 * The private implementation of remove_child(), for a particular pipeline
2488 * stage.
2489 */
2490bool PandaNode::
2491stage_remove_child(PandaNode *child_node, int pipeline_stage,
2492 Thread *current_thread) {
2493 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
2494
2495 // First, look for the parent in the child's up list, to ensure the child is
2496 // known.
2497 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
2498 current_thread);
2499 int parent_index = child_node->do_find_parent(this, cdata_child);
2500 if (parent_index < 0) {
2501 // Nope, no relation.
2502 return false;
2503 }
2504
2505 PT(Down) down = cdata->modify_down();
2506 int child_index = do_find_child(child_node, down);
2507 if (child_index >= 0) {
2508 // The child exists; remove it.
2509 down->erase(down->begin() + child_index);
2510 int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
2511 nassertr(num_erased == 1, false);
2512 return true;
2513 }
2514
2515 PT(Down) stashed = cdata->modify_stashed();
2516 int stashed_index = do_find_child(child_node, stashed);
2517 if (stashed_index >= 0) {
2518 // The child has been stashed; remove it.
2519 stashed->erase(stashed->begin() + stashed_index);
2520 int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
2521 nassertr(num_erased == 1, false);
2522 return true;
2523 }
2524
2525 // Never heard of this child. This shouldn't be possible, because the
2526 // parent was in the child's up list, above. Must be some internal error.
2527 nassertr(false, false);
2528 return false;
2529}
2530
2531/**
2532 * The private implementation of replace_child(), for a particular pipeline
2533 * stage.
2534 */
2535bool PandaNode::
2536stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
2537 int pipeline_stage, Thread *current_thread) {
2538 {
2539 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
2540 CDStageWriter cdata_orig_child(orig_child->_cycler, pipeline_stage, current_thread);
2541 CDStageWriter cdata_new_child(new_child->_cycler, pipeline_stage, current_thread);
2542
2543 // First, look for the parent in the child's up list, to ensure the child
2544 // is known.
2545 int parent_index = orig_child->do_find_parent(this, cdata_orig_child);
2546 if (parent_index < 0) {
2547 // Nope, no relation.
2548 return false;
2549 }
2550
2551 if (orig_child == new_child) {
2552 // Trivial no-op.
2553 return true;
2554 }
2555
2556 // Don't let orig_child be destructed yet.
2557 PT(PandaNode) keep_orig_child = orig_child;
2558
2559 // If we already have new_child as a child, remove it first.
2560 if (stage_remove_child(new_child, pipeline_stage, current_thread)) {
2561 sever_connection(this, new_child, pipeline_stage, current_thread);
2562 }
2563
2564 PT(Down) down = cdata->modify_down();
2565 int child_index = do_find_child(orig_child, down);
2566 if (child_index >= 0) {
2567 // The child exists; replace it.
2568 DownConnection &dc = (*down)[child_index];
2569 nassertr(dc.get_child() == orig_child, false);
2570 dc.set_child(new_child);
2571
2572 } else {
2573 PT(Down) stashed = cdata->modify_stashed();
2574 int stashed_index = do_find_child(orig_child, stashed);
2575 if (stashed_index >= 0) {
2576 // The child has been stashed; remove it.
2577 DownConnection &dc = (*stashed)[stashed_index];
2578 nassertr(dc.get_child() == orig_child, false);
2579 dc.set_child(new_child);
2580
2581 } else {
2582 // Never heard of this child. This shouldn't be possible, because the
2583 // parent was in the child's up list, above. Must be some internal
2584 // error.
2585 nassertr(false, false);
2586 return false;
2587 }
2588 }
2589
2590 // Now adjust the bookkeeping on both children.
2591 cdata_new_child->modify_up()->insert(UpConnection(this));
2592 int num_erased = cdata_orig_child->modify_up()->erase(UpConnection(this));
2593 nassertr(num_erased == 1, false);
2594 }
2595
2596 sever_connection(this, orig_child, pipeline_stage, current_thread);
2597 new_connection(this, new_child, pipeline_stage, current_thread);
2598
2599 force_bounds_stale(pipeline_stage, current_thread);
2600 orig_child->parents_changed();
2601 new_child->parents_changed();
2603 orig_child->mark_bam_modified();
2604 new_child->mark_bam_modified();
2605
2606 return true;
2607}
2608
2609/**
2610 * Similar to add_child(), but performs fewer checks. The purpose of this
2611 * method is to add a child node that was newly constructed, to a parent node
2612 * that was newly constructed, so we know we have to make fewer sanity checks.
2613 * This is a private method; do not call it directly.
2614 */
2615void PandaNode::
2616quick_add_new_child(PandaNode *child_node, int sort, Thread *current_thread) {
2617 // Apply this operation to the current stage as well as to all upstream
2618 // stages.
2619 OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
2620 CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
2621 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
2622
2623 cdata->modify_down()->insert(DownConnection(child_node, sort));
2624 cdata_child->modify_up()->insert(UpConnection(this));
2625 }
2626 CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
2627}
2628
2629/**
2630 * Raises an assertion when a graph cycle attempt is detected (and aborted).
2631 */
2632void PandaNode::
2633report_cycle(PandaNode *child_node) {
2634 ostringstream strm;
2635 strm << "Detected attempt to create a cycle in the scene graph: "
2636 << NodePath::any_path(this) << " : " << *child_node;
2637 nassert_raise(strm.str());
2638}
2639
2640/**
2641 * Returns true if the indicated node is this node, or any ancestor of this
2642 * node; or false if it is not in this node's ancestry.
2643 */
2644bool PandaNode::
2645find_node_above(PandaNode *node) {
2646 if (node == this) {
2647 return true;
2648 }
2649
2650 Parents parents = get_parents();
2651 for (size_t i = 0; i < parents.get_num_parents(); ++i) {
2652 PandaNode *parent = parents.get_parent(i);
2653 if (parent->find_node_above(node)) {
2654 return true;
2655 }
2656 }
2657
2658 return false;
2659}
2660
2661/**
2662 * Creates a new parent-child relationship, and returns the new
2663 * NodePathComponent. If the child was already attached to the indicated
2664 * parent, repositions it and returns the original NodePathComponent.
2665 *
2666 * This operation is automatically propagated back up to pipeline 0, from the
2667 * specified pipeline stage.
2668 */
2669PT(NodePathComponent) PandaNode::
2670attach(NodePathComponent *parent, PandaNode *child_node, int sort,
2671 int pipeline_stage, Thread *current_thread) {
2672 if (parent == nullptr) {
2673 // Attaching to NULL means to create a new "instance" with no attachments,
2674 // and no questions asked.
2675 PT(NodePathComponent) child =
2676 new NodePathComponent(child_node, nullptr,
2677 pipeline_stage, current_thread);
2678 LightReMutexHolder holder(child_node->_paths_lock);
2679 child_node->_paths.insert(child);
2680 return child;
2681 }
2682
2683 // See if the child was already attached to the parent. If it was, we'll
2684 // use that same NodePathComponent.
2685 PT(NodePathComponent) child = get_component(parent, child_node, pipeline_stage, current_thread);
2686
2687 if (child == nullptr) {
2688 // The child was not already attached to the parent, so get a new
2689 // component.
2690 child = get_top_component(child_node, true, pipeline_stage, current_thread);
2691 }
2692
2693 reparent(parent, child, sort, false, pipeline_stage, current_thread);
2694
2695 return child;
2696}
2697
2698/**
2699 * Breaks a parent-child relationship.
2700 *
2701 * This operation is automatically propagated back up to pipeline 0, from the
2702 * specified pipeline stage.
2703 */
2704void PandaNode::
2705detach(NodePathComponent *child, int pipeline_stage, Thread *current_thread) {
2706 nassertv(child != nullptr);
2707
2708 for (int pipeline_stage_i = pipeline_stage;
2709 pipeline_stage_i >= 0;
2710 --pipeline_stage_i) {
2711 detach_one_stage(child, pipeline_stage_i, current_thread);
2712 }
2713
2714 child->get_node()->parents_changed();
2715}
2716
2717/**
2718 * Breaks a parent-child relationship.
2719 *
2720 * This operation is not automatically propagated upstream. It is applied to
2721 * the indicated pipeline stage only.
2722 */
2723void PandaNode::
2724detach_one_stage(NodePathComponent *child, int pipeline_stage,
2725 Thread *current_thread) {
2726 nassertv(child != nullptr);
2727 if (child->is_top_node(pipeline_stage, current_thread)) {
2728 return;
2729 }
2730
2731 PT(PandaNode) child_node = child->get_node();
2732 PT(PandaNode) parent_node = child->get_next(pipeline_stage, current_thread)->get_node();
2733
2734 CDStageWriter cdata_parent(parent_node->_cycler, pipeline_stage, current_thread);
2735 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
2736 int parent_index = child_node->do_find_parent(parent_node, cdata_child);
2737 if (parent_index >= 0) {
2738 // Now look for the child and break the actual connection.
2739
2740 // First, look for and remove the parent node from the child's up list.
2741 int num_erased = cdata_child->modify_up()->erase(UpConnection(parent_node));
2742 nassertv(num_erased == 1);
2743
2744 // Now, look for and remove the child node from the parent's down list.
2745 // We also check in the stashed list, in case the child node has been
2746 // stashed.
2747 Down::iterator di;
2748 bool found = false;
2749 PT(Down) down = cdata_parent->modify_down();
2750 for (di = down->begin(); di != down->end(); ++di) {
2751 if ((*di).get_child() == child_node) {
2752 down->erase(di);
2753 found = true;
2754 break;
2755 }
2756 }
2757 if (!found) {
2758 PT(Down) stashed = cdata_parent->modify_stashed();
2759 for (di = stashed->begin(); di != stashed->end(); ++di) {
2760 if ((*di).get_child() == child_node) {
2761 stashed->erase(di);
2762 found = true;
2763 break;
2764 }
2765 }
2766 }
2767 nassertv(found);
2768 }
2769
2770 // Finally, break the NodePathComponent connection.
2771 sever_connection(parent_node, child_node, pipeline_stage, current_thread);
2772
2773 parent_node->force_bounds_stale(pipeline_stage, current_thread);
2774 parent_node->children_changed();
2775 parent_node->mark_bam_modified();
2776}
2777
2778/**
2779 * Switches a node from one parent to another. Returns true if the new
2780 * connection is allowed, or false if it conflicts with another instance (that
2781 * is, another instance of the child is already attached to the indicated
2782 * parent).
2783 *
2784 * This operation is automatically propagated back up to pipeline 0, from the
2785 * specified pipeline stage.
2786 */
2787bool PandaNode::
2788reparent(NodePathComponent *new_parent, NodePathComponent *child, int sort,
2789 bool as_stashed, int pipeline_stage, Thread *current_thread) {
2790 bool any_ok = false;
2791
2792 if (new_parent != nullptr &&
2793 !new_parent->get_node()->verify_child_no_cycles(child->get_node())) {
2794 // Whoops, adding this child node would introduce a cycle in the scene
2795 // graph.
2796 return false;
2797 }
2798
2799 for (int pipeline_stage_i = pipeline_stage;
2800 pipeline_stage_i >= 0;
2801 --pipeline_stage_i) {
2802 if (reparent_one_stage(new_parent, child, sort, as_stashed,
2803 pipeline_stage_i, current_thread)) {
2804 any_ok = true;
2805 }
2806 }
2807
2808 if (new_parent != nullptr) {
2809 new_parent->get_node()->children_changed();
2810 new_parent->get_node()->mark_bam_modified();
2811 }
2812 child->get_node()->parents_changed();
2813 child->get_node()->mark_bam_modified();
2814
2815 return any_ok;
2816}
2817
2818/**
2819 * Switches a node from one parent to another. Returns true if the new
2820 * connection is allowed, or false if it conflicts with another instance (that
2821 * is, another instance of the child is already attached to the indicated
2822 * parent).
2823 *
2824 * This operation is not automatically propagated upstream. It is applied to
2825 * the indicated pipeline stage only.
2826 */
2827bool PandaNode::
2828reparent_one_stage(NodePathComponent *new_parent, NodePathComponent *child,
2829 int sort, bool as_stashed, int pipeline_stage,
2830 Thread *current_thread) {
2831 nassertr(child != nullptr, false);
2832
2833 // Keep a reference count to the new parent, since detaching the child might
2834 // lose the count.
2835 PT(NodePathComponent) keep_parent = new_parent;
2836
2837 if (!child->is_top_node(pipeline_stage, current_thread)) {
2838 detach(child, pipeline_stage, current_thread);
2839 }
2840
2841 if (new_parent != nullptr) {
2842 PandaNode *child_node = child->get_node();
2843 PandaNode *parent_node = new_parent->get_node();
2844
2845 {
2846 CDStageReader cdata_child(child_node->_cycler, pipeline_stage, current_thread);
2847 int parent_index = child_node->do_find_parent(parent_node, cdata_child);
2848
2849 if (parent_index >= 0) {
2850 // Whoops, there's already another instance of the child there.
2851 return false;
2852 }
2853 }
2854
2855 // Redirect the connection to the indicated new parent.
2856 child->set_next(new_parent, pipeline_stage, current_thread);
2857
2858 // Now reattach the child node at the indicated sort position.
2859 {
2860 CDStageWriter cdata_parent(parent_node->_cycler, pipeline_stage, current_thread);
2861 CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
2862
2863 if (as_stashed) {
2864 cdata_parent->modify_stashed()->insert(DownConnection(child_node, sort));
2865 } else {
2866 cdata_parent->modify_down()->insert(DownConnection(child_node, sort));
2867 }
2868 cdata_child->modify_up()->insert(UpConnection(parent_node));
2869
2870#ifndef NDEBUG
2871 // The NodePathComponent should already be in the set.
2872 {
2873 LightReMutexHolder holder(child_node->_paths_lock);
2874 nassertr(child_node->_paths.find(child) != child_node->_paths.end(), false);
2875 }
2876#endif // NDEBUG
2877 }
2878
2879 child_node->fix_path_lengths(pipeline_stage, current_thread);
2880 parent_node->force_bounds_stale(pipeline_stage, current_thread);
2881 }
2882
2883 return true;
2884}
2885
2886/**
2887 * Returns the NodePathComponent based on the indicated child of the given
2888 * parent, or NULL if there is no such parent-child relationship.
2889 */
2890PT(NodePathComponent) PandaNode::
2891get_component(NodePathComponent *parent, PandaNode *child_node,
2892 int pipeline_stage, Thread *current_thread) {
2893 nassertr(parent != nullptr, nullptr);
2894 PandaNode *parent_node = parent->get_node();
2895
2896 LightReMutexHolder holder(child_node->_paths_lock);
2897
2898 // First, walk through the list of NodePathComponents we already have on the
2899 // child, looking for one that already exists, referencing the indicated
2900 // parent component.
2901 for (NodePathComponent *child : child_node->_paths) {
2902 if (child->get_next(pipeline_stage, current_thread) == parent) {
2903 // If we already have such a component, just return it.
2904 // But before we do, we have to make sure it's not in the middle of being
2905 // destructed.
2906#ifdef HAVE_THREADS
2907 if (child->ref_if_nonzero()) {
2908 PT(NodePathComponent) result;
2909 result.cheat() = child;
2910 return result;
2911 }
2912#else
2913 // If we're not building with threading, increment as normal.
2914 return child;
2915#endif
2916 }
2917 }
2918
2919 // We don't already have a NodePathComponent referring to this parent-child
2920 // relationship. Are they actually related?
2921 CDStageReader cdata_child(child_node->_cycler, pipeline_stage, current_thread);
2922 int parent_index = child_node->do_find_parent(parent_node, cdata_child);
2923
2924 if (parent_index >= 0) {
2925 // They are. Create and return a new one.
2926 PT(NodePathComponent) child =
2927 new NodePathComponent(child_node, parent, pipeline_stage, current_thread);
2928 child_node->_paths.insert(child);
2929 return child;
2930 } else {
2931 // They aren't related. Return NULL.
2932 return nullptr;
2933 }
2934}
2935
2936/**
2937 * Returns a NodePathComponent referencing the indicated node as a singleton.
2938 * It is invalid to call this for a node that has parents, unless you are
2939 * about to create a new instance (and immediately reconnect the
2940 * NodePathComponent elsewhere).
2941 *
2942 * If force is true, this will always return something, even if it needs to
2943 * create a new top component; otherwise, if force is false, it will return
2944 * NULL if there is not already a top component available.
2945 */
2946PT(NodePathComponent) PandaNode::
2947get_top_component(PandaNode *child_node, bool force, int pipeline_stage,
2948 Thread *current_thread) {
2949 LightReMutexHolder holder(child_node->_paths_lock);
2950
2951 // Walk through the list of NodePathComponents we already have on the child,
2952 // looking for one that already exists as a top node.
2953 for (NodePathComponent *child : child_node->_paths) {
2954 if (child->is_top_node(pipeline_stage, current_thread)) {
2955 // If we already have such a component, just return it.
2956 // But before we do, we have to make sure it's not in the middle of being
2957 // destructed.
2958#ifdef HAVE_THREADS
2959 if (child->ref_if_nonzero()) {
2960 PT(NodePathComponent) result;
2961 result.cheat() = child;
2962 return result;
2963 }
2964#else
2965 // If we're not building with threading, increment as normal.
2966 return child;
2967#endif
2968 }
2969 }
2970
2971 if (!force) {
2972 // If we don't care to force the point, return NULL to indicate there's
2973 // not already a top component.
2974 return nullptr;
2975 }
2976
2977 // We don't already have such a NodePathComponent; create and return a new
2978 // one.
2979 PT(NodePathComponent) child =
2980 new NodePathComponent(child_node, nullptr,
2981 pipeline_stage, current_thread);
2982 child_node->_paths.insert(child);
2983
2984 return child;
2985}
2986
2987/**
2988 * Returns a NodePathComponent referencing this node as a path from the root.
2989 *
2990 * Unless accept_ambiguity is true, it is only valid to call this if there is
2991 * an unambiguous path from the root; otherwise, a warning will be issued and
2992 * one path will be chosen arbitrarily.
2993 */
2994PT(NodePathComponent) PandaNode::
2995get_generic_component(bool accept_ambiguity, int pipeline_stage,
2996 Thread *current_thread) {
2997 bool ambiguity_detected = false;
2998 PT(NodePathComponent) result =
2999 r_get_generic_component(accept_ambiguity, ambiguity_detected,
3000 pipeline_stage, current_thread);
3001
3002 if (!accept_ambiguity && ambiguity_detected) {
3003 pgraph_cat.warning()
3004 << "Chose: " << *result << "\n";
3005 nassertr(!unambiguous_graph, result);
3006 }
3007
3008 return result;
3009}
3010
3011/**
3012 * The recursive implementation of get_generic_component, this simply sets the
3013 * flag when the ambiguity is detected (so we can report the bottom node that
3014 * started the ambiguous search).
3015 */
3016PT(NodePathComponent) PandaNode::
3017r_get_generic_component(bool accept_ambiguity, bool &ambiguity_detected,
3018 int pipeline_stage, Thread *current_thread) {
3019 PT(PandaNode) parent_node;
3020
3021 {
3022 CDStageReader cdata(_cycler, pipeline_stage, current_thread);
3023
3024 int num_parents = cdata->get_up()->size();
3025 if (num_parents == 0) {
3026 // No parents; no ambiguity. This is the root.
3027 return get_top_component(this, true, pipeline_stage, current_thread);
3028 }
3029
3030 PT(NodePathComponent) result;
3031 if (num_parents == 1) {
3032 // Only one parent; no ambiguity.
3033 PT(NodePathComponent) parent =
3034 get_parent(0)->r_get_generic_component(accept_ambiguity, ambiguity_detected, pipeline_stage, current_thread);
3035 return get_component(parent, this, pipeline_stage, current_thread);
3036 }
3037
3038 // Oops, multiple parents; the NodePath is ambiguous.
3039 if (!accept_ambiguity) {
3040 pgraph_cat.warning()
3041 << *this << " has " << num_parents
3042 << " parents; choosing arbitrary path to root.\n";
3043 }
3044 ambiguity_detected = true;
3045 CPT(Up) up = cdata->get_up();
3046 parent_node = (*up)[0].get_parent();
3047 }
3048
3049 // Now that the lock is released, it's safe to recurse.
3050 PT(NodePathComponent) parent =
3051 parent_node->r_get_generic_component(accept_ambiguity, ambiguity_detected, pipeline_stage, current_thread);
3052 return get_component(parent, this, pipeline_stage, current_thread);
3053}
3054
3055/**
3056 * Removes a NodePathComponent from the set prior to its deletion. This
3057 * should only be called by the NodePathComponent destructor.
3058 */
3059void PandaNode::
3060delete_component(NodePathComponent *component) {
3061 LightReMutexHolder holder(_paths_lock);
3062 int num_erased = _paths.erase(component);
3063 nassertv(num_erased == 1);
3064}
3065
3066/**
3067 * This is called internally when a parent-child connection is broken to
3068 * update the NodePathComponents that reflected this connection.
3069 *
3070 * It severs any NodePathComponents on the child node that reference the
3071 * indicated parent node. These components remain unattached; there may
3072 * therefore be multiple "instances" of a node that all have no parent, even
3073 * while there are other instances that do have parents.
3074 *
3075 * This operation is not automatically propagated upstream. It is applied to
3076 * the indicated pipeline stage only.
3077 */
3078void PandaNode::
3079sever_connection(PandaNode *parent_node, PandaNode *child_node,
3080 int pipeline_stage, Thread *current_thread) {
3081 {
3082 LightReMutexHolder holder(child_node->_paths_lock);
3083 Paths::iterator pi;
3084 for (pi = child_node->_paths.begin(); pi != child_node->_paths.end(); ++pi) {
3085 if (!(*pi)->is_top_node(pipeline_stage, current_thread) &&
3086 (*pi)->get_next(pipeline_stage, current_thread)->get_node() == parent_node) {
3087 // Sever the component here.
3088 (*pi)->set_top_node(pipeline_stage, current_thread);
3089 }
3090 }
3091 }
3092 child_node->fix_path_lengths(pipeline_stage, current_thread);
3093}
3094
3095/**
3096 * This is called internally when a parent-child connection is established to
3097 * update the NodePathComponents that might be involved.
3098 *
3099 * It adjusts any NodePathComponents the child has that reference the child as
3100 * a top node. Any other components we can leave alone, because we are making
3101 * a new instance of the child.
3102 *
3103 * This operation is not automatically propagated upstream. It is applied to
3104 * the indicated pipeline stage only.
3105 */
3106void PandaNode::
3107new_connection(PandaNode *parent_node, PandaNode *child_node,
3108 int pipeline_stage, Thread *current_thread) {
3109 {
3110 LightReMutexHolder holder(child_node->_paths_lock);
3111 Paths::iterator pi;
3112 for (pi = child_node->_paths.begin(); pi != child_node->_paths.end(); ++pi) {
3113 if ((*pi)->is_top_node(pipeline_stage, current_thread)) {
3114 (*pi)->set_next(parent_node->get_generic_component(false, pipeline_stage, current_thread), pipeline_stage, current_thread);
3115 }
3116 }
3117 }
3118 child_node->fix_path_lengths(pipeline_stage, current_thread);
3119}
3120
3121/**
3122 * Recursively fixes the _length member of each NodePathComponent at this
3123 * level and below, after an add or delete child operation that might have
3124 * messed these up.
3125 *
3126 * This operation is not automatically propagated upstream. It is applied to
3127 * the indicated pipeline stage only.
3128 */
3129void PandaNode::
3130fix_path_lengths(int pipeline_stage, Thread *current_thread) {
3131 LightReMutexHolder holder(_paths_lock);
3132
3133 bool any_wrong = false;
3134
3135 Paths::const_iterator pi;
3136 for (pi = _paths.begin(); pi != _paths.end(); ++pi) {
3137 if ((*pi)->fix_length(pipeline_stage, current_thread)) {
3138 any_wrong = true;
3139 }
3140 }
3141
3142 // If any paths were updated, we have to recurse on all of our children,
3143 // since any one of those paths might be shared by any of our child nodes.
3144 // Don't hold any locks while we recurse.
3145 if (any_wrong) {
3146 Children children;
3147 Stashed stashed;
3148 {
3149 CDStageReader cdata(_cycler, pipeline_stage, current_thread);
3150 children = Children(cdata);
3151 stashed = Stashed(cdata);
3152 }
3153
3154 int num_children = children.get_num_children();
3155 int i;
3156 for (i = 0; i < num_children; ++i) {
3157 PandaNode *child_node = children.get_child(i);
3158 child_node->fix_path_lengths(pipeline_stage, current_thread);
3159 }
3160 int num_stashed = stashed.get_num_stashed();
3161 for (i = 0; i < num_stashed; ++i) {
3162 PandaNode *child_node = stashed.get_stashed(i);
3163 child_node->fix_path_lengths(pipeline_stage, current_thread);
3164 }
3165 }
3166}
3167
3168/**
3169 * The recursive implementation of ls().
3170 */
3171void PandaNode::
3172r_list_descendants(ostream &out, int indent_level) const {
3173 write(out, indent_level);
3174
3175 Children children = get_children();
3176 int num_children = children.get_num_children();
3177
3178 for (int i = 0; i < num_children; ++i) {
3179 PandaNode *child = children.get_child(i);
3180 child->r_list_descendants(out, indent_level + 2);
3181 }
3182
3183 // Also report the number of stashed nodes at this level.
3184 int num_stashed = get_num_stashed();
3185 if (num_stashed != 0) {
3186 indent(out, indent_level) << "(" << num_stashed << " stashed)\n";
3187 }
3188}
3189
3190/**
3191 * The private implementation of find_child().
3192 */
3193int PandaNode::
3194do_find_child(PandaNode *node, const PandaNode::Down *down) const {
3195 nassertr(node != nullptr, -1);
3196
3197 // We have to search for the child by brute force, since we don't know what
3198 // sort index it was added as.
3199 Down::const_iterator di;
3200 for (di = down->begin(); di != down->end(); ++di) {
3201 if ((*di).get_child() == node) {
3202 return di - down->begin();
3203 }
3204 }
3205
3206 return -1;
3207}
3208
3209/**
3210 * Updates the cached values of the node that are dependent on its children,
3211 * such as the external bounding volume, the _net_collide_mask, and the
3212 * _off_clip_planes.
3213 *
3214 * If update_bounds is false, it will not update the bounding volume or vertex
3215 * count.
3216 *
3217 * The old value should be passed in; it will be released. The new value is
3218 * returned.
3219 */
3220PandaNode::CDStageWriter PandaNode::
3221update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
3222 // We might need to try this a couple of times, in case someone else steps
3223 // on our result.
3224 if (drawmask_cat.is_debug()) {
3225 drawmask_cat.debug(false)
3226 << *this << "::update_cached() {\n";
3227 }
3228 Thread *current_thread = cdata.get_current_thread();
3229
3230 do {
3231 // Grab the last_update counter.
3232 UpdateSeq last_update = cdata->_last_update;
3233 UpdateSeq next_update = cdata->_next_update;
3234 UpdateSeq last_bounds_update = cdata->_last_bounds_update;
3235 nassertr(last_update != next_update ||
3236 (update_bounds && last_bounds_update != next_update),
3237 CDStageWriter(_cycler, pipeline_stage, cdata));
3238
3239 // Start with a clean slate.
3240 CollideMask net_collide_mask = cdata->_into_collide_mask;
3241 DrawMask net_draw_control_mask, net_draw_show_mask;
3242 bool renderable = is_renderable();
3243
3244 if (renderable) {
3245 // If this node is itself renderable, it contributes to the net draw
3246 // mask.
3247 net_draw_control_mask = cdata->_draw_control_mask;
3248 net_draw_show_mask = cdata->_draw_show_mask;
3249 }
3250
3251 if (drawmask_cat.is_debug()) {
3252 drawmask_cat.debug(false)
3253 << "net_draw_control_mask = " << net_draw_control_mask
3254 << "\nnet_draw_show_mask = " << net_draw_show_mask
3255 << "\n";
3256 }
3257 CPT(RenderAttrib) off_clip_planes = cdata->_state->get_attrib(ClipPlaneAttrib::get_class_slot());
3258 if (off_clip_planes == nullptr) {
3259 off_clip_planes = ClipPlaneAttrib::make();
3260 }
3261
3262 // Also get the list of the node's children.
3263 Children children(cdata);
3264
3265 // Now that we've got all the data we need from the node, we can release
3266 // the lock.
3267 _cycler.release_read_stage(pipeline_stage, cdata.take_pointer());
3268
3269 int num_children = children.get_num_children();
3270
3271 // We need to keep references to the bounding volumes, since in a threaded
3272 // environment the pointers might go away while we're working (since we're
3273 // not holding a lock on our set of children right now). But we also need
3274 // the regular pointers, to pass to BoundingVolume::around().
3275 const BoundingVolume **child_volumes;
3276#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
3277 pvector<CPT(BoundingVolume) > child_volumes_ref;
3278 if (update_bounds) {
3279 child_volumes_ref.reserve(num_children + 1);
3280 }
3281#endif
3282 int child_volumes_i = 0;
3283
3284 bool all_box = true;
3285 CPT(BoundingVolume) internal_bounds = nullptr;
3286
3287 if (update_bounds) {
3288 child_volumes = (const BoundingVolume **)alloca(sizeof(BoundingVolume *) * (num_children + 1));
3289 internal_bounds = get_internal_bounds(pipeline_stage, current_thread);
3290
3291 if (!internal_bounds->is_empty()) {
3292#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
3293 child_volumes_ref.push_back(internal_bounds);
3294#endif
3295 nassertr(child_volumes_i < num_children + 1, CDStageWriter(_cycler, pipeline_stage, cdata));
3296 child_volumes[child_volumes_i++] = internal_bounds;
3297 if (internal_bounds->as_bounding_box() == nullptr) {
3298 all_box = false;
3299 }
3300 }
3301 }
3302
3303 // Now expand those contents to include all of our children.
3304 int child_vertices = 0;
3305
3306 for (int i = 0; i < num_children; ++i) {
3307 PandaNode *child = children.get_child(i);
3308
3309 const ClipPlaneAttrib *orig_cp = DCAST(ClipPlaneAttrib, off_clip_planes);
3310
3311 CDLockedStageReader child_cdata(child->_cycler, pipeline_stage, current_thread);
3312
3313 UpdateSeq last_child_update = update_bounds
3314 ? child_cdata->_last_bounds_update
3315 : child_cdata->_last_update;
3316
3317 if (last_child_update != child_cdata->_next_update) {
3318 // Child needs update.
3319 CDStageWriter child_cdataw = child->update_cached(update_bounds, pipeline_stage, child_cdata);
3320
3321 net_collide_mask |= child_cdataw->_net_collide_mask;
3322
3323 if (drawmask_cat.is_debug()) {
3324 drawmask_cat.debug(false)
3325 << "\nchild update " << *child << ":\n";
3326 }
3327
3328 DrawMask child_control_mask = child_cdataw->_net_draw_control_mask;
3329 DrawMask child_show_mask = child_cdataw->_net_draw_show_mask;
3330 if (!(child_control_mask | child_show_mask).is_zero()) {
3331 // This child includes a renderable node or subtree. Thus, we
3332 // should propagate its draw masks.
3333 renderable = true;
3334
3335 // For each bit position in the masks, we have assigned the
3336 // following semantic meaning. The number on the left represents
3337 // the pairing of the corresponding bit from the control mask and
3338 // from the show mask:
3339
3340 // 00 : not a renderable node (control 0, show 0) 01 : a normally
3341 // visible node (control 0, show 1) 10 : a hidden node
3342 // (control 1, show 0) 11 : a show-through node (control 1, show
3343 // 1)
3344
3345 // Now, when we accumulate these masks, we want to do so according
3346 // to the following table, for each bit position:
3347
3348 // 00 01 10 11 (child) --------------------- 00 | 00 01
3349 // 10 11 01 | 01 01 01* 11 10 | 10 01* 10 11 11 | 11
3350 // 11 11 11 (parent)
3351
3352 // This table is almost the same as the union of both masks, with
3353 // one exception, marked with a * in the above table: if one is 10
3354 // and the other is 01--that is, one is hidden and the other is
3355 // normally visible--then the result should be 01, normally visible.
3356 // This is because we only want to propagate the hidden bit upwards
3357 // if *all* renderable nodes are hidden.
3358
3359 // Get the set of exception bits for which the above rule applies.
3360 // These are the bits for which both bits have flipped, but which
3361 // were not the same in the original.
3362 DrawMask exception_mask = (net_draw_control_mask ^ child_control_mask) & (net_draw_show_mask ^ child_show_mask);
3363 exception_mask &= (net_draw_control_mask ^ net_draw_show_mask);
3364
3365 if (drawmask_cat.is_debug()) {
3366 drawmask_cat.debug(false)
3367 << "exception_mask = " << exception_mask << "\n";
3368 }
3369
3370 // Now compute the union, applying the above exception.
3371 net_draw_control_mask |= child_control_mask;
3372 net_draw_show_mask |= child_show_mask;
3373
3374 net_draw_control_mask &= ~exception_mask;
3375 net_draw_show_mask |= exception_mask;
3376 }
3377
3378 if (drawmask_cat.is_debug()) {
3379 drawmask_cat.debug(false)
3380 << "child_control_mask = " << child_control_mask
3381 << "\nchild_show_mask = " << child_show_mask
3382 << "\nnet_draw_control_mask = " << net_draw_control_mask
3383 << "\nnet_draw_show_mask = " << net_draw_show_mask
3384 << "\n";
3385 }
3386
3387 off_clip_planes = orig_cp->compose_off(child_cdataw->_off_clip_planes);
3388
3389 if (update_bounds) {
3390 if (!child_cdataw->_external_bounds->is_empty()) {
3391#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
3392 child_volumes_ref.push_back(child_cdataw->_external_bounds);
3393#endif
3394 nassertr(child_volumes_i < num_children + 1, CDStageWriter(_cycler, pipeline_stage, cdata));
3395 child_volumes[child_volumes_i++] = child_cdataw->_external_bounds;
3396 if (child_cdataw->_external_bounds->as_bounding_box() == nullptr) {
3397 all_box = false;
3398 }
3399 }
3400 child_vertices += child_cdataw->_nested_vertices;
3401 }
3402
3403 } else {
3404 // Child is good.
3405 net_collide_mask |= child_cdata->_net_collide_mask;
3406
3407 // See comments in similar block above.
3408 if (drawmask_cat.is_debug()) {
3409 drawmask_cat.debug(false)
3410 << "\nchild fresh " << *child << ":\n";
3411 }
3412 DrawMask child_control_mask = child_cdata->_net_draw_control_mask;
3413 DrawMask child_show_mask = child_cdata->_net_draw_show_mask;
3414 if (!(child_control_mask | child_show_mask).is_zero()) {
3415 renderable = true;
3416
3417 DrawMask exception_mask = (net_draw_control_mask ^ child_control_mask) & (net_draw_show_mask ^ child_show_mask);
3418 exception_mask &= (net_draw_control_mask ^ net_draw_show_mask);
3419
3420 if (drawmask_cat.is_debug()) {
3421 drawmask_cat.debug(false)
3422 << "exception_mask = " << exception_mask << "\n";
3423 }
3424
3425 // Now compute the union, applying the above exception.
3426 net_draw_control_mask |= child_control_mask;
3427 net_draw_show_mask |= child_show_mask;
3428
3429 net_draw_control_mask &= ~exception_mask;
3430 net_draw_show_mask |= exception_mask;
3431 }
3432
3433 if (drawmask_cat.is_debug()) {
3434 drawmask_cat.debug(false)
3435 << "child_control_mask = " << child_control_mask
3436 << "\nchild_show_mask = " << child_show_mask
3437 << "\nnet_draw_control_mask = " << net_draw_control_mask
3438 << "\nnet_draw_show_mask = " << net_draw_show_mask
3439 << "\n";
3440 }
3441
3442 off_clip_planes = orig_cp->compose_off(child_cdata->_off_clip_planes);
3443
3444 if (update_bounds) {
3445 if (!child_cdata->_external_bounds->is_empty()) {
3446#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
3447 child_volumes_ref.push_back(child_cdata->_external_bounds);
3448#endif
3449 nassertr(child_volumes_i < num_children + 1, CDStageWriter(_cycler, pipeline_stage, cdata));
3450 child_volumes[child_volumes_i++] = child_cdata->_external_bounds;
3451 if (child_cdata->_external_bounds->as_bounding_box() == nullptr) {
3452 all_box = false;
3453 }
3454 }
3455 child_vertices += child_cdata->_nested_vertices;
3456 }
3457 }
3458 }
3459
3460 {
3461 // Now grab the write lock on this node.
3462 CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
3463 if (last_update == cdataw->_last_update &&
3464 next_update == cdataw->_next_update) {
3465 // Great, no one has monkeyed with these while we were computing the
3466 // cache. Safe to store the computed values and return.
3467 cdataw->_net_collide_mask = net_collide_mask;
3468
3469 if (renderable) {
3470 // Any explicit draw control mask on this node trumps anything
3471 // inherited from below, except a show-through.
3472 DrawMask draw_control_mask = cdataw->_draw_control_mask;
3473 DrawMask draw_show_mask = cdataw->_draw_show_mask;
3474
3475 DrawMask show_through_mask = net_draw_control_mask & net_draw_show_mask;
3476
3477 net_draw_control_mask |= draw_control_mask;
3478 net_draw_show_mask = (net_draw_show_mask & ~draw_control_mask) | (draw_show_mask & draw_control_mask);
3479
3480 net_draw_show_mask |= show_through_mask;
3481
3482 // There are renderable nodes below, so the implicit draw bits are
3483 // all on.
3484 cdataw->_net_draw_control_mask = net_draw_control_mask;
3485 cdataw->_net_draw_show_mask = net_draw_show_mask | ~net_draw_control_mask;
3486 if (drawmask_cat.is_debug()) {
3487 drawmask_cat.debug(false)
3488 << "renderable, set mask " << cdataw->_net_draw_show_mask << "\n";
3489 }
3490 } else {
3491 // There are no renderable nodes below, so the implicit draw bits
3492 // are all off. Also, we don't care about the draw mask on this
3493 // particular node (since nothing below it is renderable anyway).
3494 cdataw->_net_draw_control_mask = net_draw_control_mask;
3495 cdataw->_net_draw_show_mask = net_draw_show_mask;
3496 if (drawmask_cat.is_debug()) {
3497 drawmask_cat.debug(false)
3498 << "not renderable, set mask " << cdataw->_net_draw_show_mask << "\n";
3499 }
3500 }
3501
3502 cdataw->_off_clip_planes = off_clip_planes;
3503
3504 if (update_bounds) {
3505 cdataw->_nested_vertices = cdataw->_internal_vertices + child_vertices;
3506
3507 CPT(TransformState) transform = get_transform(current_thread);
3509
3510 BoundingVolume::BoundsType btype = cdataw->_bounds_type;
3511 if (btype == BoundingVolume::BT_default) {
3512 btype = bounds_type;
3513 }
3514
3515 if (btype == BoundingVolume::BT_box ||
3516 (btype != BoundingVolume::BT_sphere && all_box && transform->is_identity())) {
3517 // If all of the child volumes are a BoundingBox, and we have no
3518 // transform, then our volume is also a BoundingBox.
3519
3520 gbv = new BoundingBox;
3521 } else {
3522 // Otherwise, it's a sphere.
3523 gbv = new BoundingSphere;
3524 }
3525
3526 if (child_volumes_i > 0) {
3527 const BoundingVolume **child_begin = &child_volumes[0];
3528 const BoundingVolume **child_end = child_begin + child_volumes_i;
3529 ((BoundingVolume *)gbv)->around(child_begin, child_end);
3530
3531 // If we have a transform, apply it to the bounding volume we just
3532 // computed.
3533 if (!transform->is_identity()) {
3534 gbv->xform(transform->get_mat());
3535 }
3536 }
3537
3538 cdataw->_external_bounds = gbv;
3539 cdataw->_last_bounds_update = next_update;
3540 }
3541
3542 cdataw->_last_update = next_update;
3543
3544 if (drawmask_cat.is_debug()) {
3545 drawmask_cat.debug(false)
3546 << "} " << *this << "::update_cached();\n";
3547 }
3548
3549 nassertr(cdataw->_last_update == cdataw->_next_update, cdataw);
3550
3551 // Even though implicit bounding volume is not (yet?) part of the bam
3552 // stream.
3554 return cdataw;
3555 }
3556
3557 if (cdataw->_last_update == cdataw->_next_update &&
3558 (!update_bounds || cdataw->_last_bounds_update == cdataw->_next_update)) {
3559 // Someone else has computed the cache for us. OK.
3560 return cdataw;
3561 }
3562 }
3563
3564 // We need to go around again. Release the write lock, and grab the read
3565 // lock back.
3566 cdata = CDLockedStageReader(_cycler, pipeline_stage, current_thread);
3567
3568 if (cdata->_last_update == cdata->_next_update &&
3569 (!update_bounds || cdata->_last_bounds_update == cdata->_next_update)) {
3570 // Someone else has computed the cache for us while we were diddling
3571 // with the locks. OK.
3572 return CDStageWriter(_cycler, pipeline_stage, cdata);
3573 }
3574
3575 } while (true);
3576}
3577
3578/**
3579 * This is used by the GraphicsEngine to hook in a pointer to the
3580 * scene_root_func(), the function to determine whether the node is an active
3581 * scene root. This back-pointer is necessary because we can't make calls
3582 * directly into GraphicsEngine, which is in the display module.
3583 */
3585set_scene_root_func(SceneRootFunc *func) {
3586 _scene_root_func = func;
3587}
3588
3589/**
3590 * Tells the BamReader how to create objects of type PandaNode.
3591 */
3594 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
3595}
3596
3597/**
3598 * Writes the contents of this object to the datagram for shipping out to a
3599 * Bam file.
3600 */
3602write_datagram(BamWriter *manager, Datagram &dg) {
3603 TypedWritable::write_datagram(manager, dg);
3604 dg.add_string(get_name());
3605
3606 manager->write_cdata(dg, _cycler);
3607}
3608
3609/**
3610 * Called by the BamWriter when this object has not itself been modified
3611 * recently, but it should check its nested objects for updates.
3612 */
3614update_bam_nested(BamWriter *manager) {
3615 CDReader cdata(_cycler);
3616 cdata->update_bam_nested(manager);
3617}
3618
3619/**
3620 * This method is provided for the benefit of classes (like MouseRecorder)
3621 * that inherit from PandaMode and also RecorderBase. It's not virtual at
3622 * this level since it doesn't need to be (it's called up from the derived
3623 * class).
3624 *
3625 * This method acts very like write_datagram, but it writes the node as
3626 * appropriate for writing a RecorderBase object as described in the beginning
3627 * of a session file, meaning it doesn't need to write things such as
3628 * children. It balances with fillin_recorder().
3629 */
3632 dg.add_string(get_name());
3633}
3634
3635/**
3636 * This function is called by the BamReader's factory when a new object of
3637 * type PandaNode is encountered in the Bam file. It should create the
3638 * PandaNode and extract its information from the file.
3639 */
3640TypedWritable *PandaNode::
3641make_from_bam(const FactoryParams &params) {
3642 PandaNode *node = new PandaNode("");
3643 DatagramIterator scan;
3644 BamReader *manager;
3645
3646 parse_params(params, scan, manager);
3647 node->fillin(scan, manager);
3648
3649 return node;
3650}
3651
3652/**
3653 * This internal function is called by make_from_bam to read in all of the
3654 * relevant data from the BamFile for the new PandaNode.
3655 */
3656void PandaNode::
3657fillin(DatagramIterator &scan, BamReader *manager) {
3658 TypedWritable::fillin(scan, manager);
3659
3661
3662 string name = scan.get_string();
3663 set_name(name);
3664
3665 manager->read_cdata(scan, _cycler);
3666}
3667
3668/**
3669 * This internal function is called by make_recorder (in classes derived from
3670 * RecorderBase, such as MouseRecorder) to read in all of the relevant data
3671 * from the session file. It balances with write_recorder().
3672 */
3673void PandaNode::
3674fillin_recorder(DatagramIterator &scan, BamReader *) {
3675 string name = scan.get_string();
3676 set_name(name);
3677}
3678
3679/**
3680 *
3681 */
3682PandaNode::CData::
3683CData() :
3684 _state(RenderState::make_empty()),
3685 _transform(TransformState::make_identity()),
3686 _prev_transform(TransformState::make_identity()),
3687
3688 _effects(RenderEffects::make_empty()),
3689 _draw_control_mask(DrawMask::all_off()),
3690 _draw_show_mask(DrawMask::all_on()),
3691 _into_collide_mask(CollideMask::all_off()),
3692 _bounds_type(BoundingVolume::BT_default),
3693 _user_bounds(nullptr),
3694 _final_bounds(false),
3695 _fancy_bits(0),
3696
3697 _net_collide_mask(CollideMask::all_off()),
3698 _net_draw_control_mask(DrawMask::all_off()),
3699 _net_draw_show_mask(DrawMask::all_off()),
3700
3701 _down(new PandaNode::Down(PandaNode::get_class_type())),
3702 _stashed(new PandaNode::Down(PandaNode::get_class_type())),
3703 _up(new PandaNode::Up(PandaNode::get_class_type()))
3704{
3705 ++_next_update;
3706}
3707
3708/**
3709 *
3710 */
3711PandaNode::CData::
3712CData(const PandaNode::CData &copy) :
3713 BoundsData(copy),
3714 _state(copy._state),
3715 _transform(copy._transform),
3716 _prev_transform(copy._prev_transform),
3717
3718 _effects(copy._effects),
3719 _tag_data(copy._tag_data),
3720 _draw_control_mask(copy._draw_control_mask),
3721 _draw_show_mask(copy._draw_show_mask),
3722 _into_collide_mask(copy._into_collide_mask),
3723 _bounds_type(copy._bounds_type),
3724 _user_bounds(copy._user_bounds),
3725 _final_bounds(copy._final_bounds),
3726 _fancy_bits(copy._fancy_bits),
3727
3728 _net_collide_mask(copy._net_collide_mask),
3729 _net_draw_control_mask(copy._net_draw_control_mask),
3730 _net_draw_show_mask(copy._net_draw_show_mask),
3731 _off_clip_planes(copy._off_clip_planes),
3732 _nested_vertices(copy._nested_vertices),
3733 _external_bounds(copy._external_bounds),
3734 _last_update(copy._last_update),
3735 _next_update(copy._next_update),
3736 _last_bounds_update(copy._last_bounds_update),
3737
3738 _down(copy._down),
3739 _stashed(copy._stashed),
3740 _up(copy._up)
3741{
3742 // Note that this copy constructor is not used by the PandaNode copy
3743 // constructor! Any elements that must be copied between nodes should also
3744 // be explicitly copied there.
3745}
3746
3747/**
3748 *
3749 */
3750PandaNode::CData::
3751~CData() {
3752}
3753
3754/**
3755 *
3756 */
3757CycleData *PandaNode::CData::
3758make_copy() const {
3759 return new CData(*this);
3760}
3761
3762/**
3763 * Writes the contents of this object to the datagram for shipping out to a
3764 * Bam file.
3765 */
3766void PandaNode::CData::
3767write_datagram(BamWriter *manager, Datagram &dg) const {
3768 manager->write_pointer(dg, _state);
3769 manager->write_pointer(dg, _transform);
3770
3771
3772 manager->write_pointer(dg, _effects);
3773
3774 dg.add_uint32(_draw_control_mask.get_word());
3775 dg.add_uint32(_draw_show_mask.get_word());
3776 dg.add_uint32(_into_collide_mask.get_word());
3777 dg.add_uint8(_bounds_type);
3778
3779 dg.add_uint32(_tag_data.size());
3780 for (size_t n = 0; n < _tag_data.size(); ++n) {
3781 dg.add_string(_tag_data.get_key(n));
3782 dg.add_string(_tag_data.get_data(n));
3783 }
3784
3785 write_up_list(*get_up(), manager, dg);
3786 write_down_list(*get_down(), manager, dg);
3787 write_down_list(*get_stashed(), manager, dg);
3788}
3789
3790/**
3791 * Called by the BamWriter when this object has not itself been modified
3792 * recently, but it should check its nested objects for updates.
3793 */
3794void PandaNode::CData::
3795update_bam_nested(BamWriter *manager) const {
3796 // No need to check the state pointers for updates, since they're all
3797 // immutable objects. manager->consider_update(_state);
3798 // manager->consider_update(_transform); manager->consider_update(_effects);
3799
3800 update_up_list(*get_up(), manager);
3801 update_down_list(*get_down(), manager);
3802 update_down_list(*get_stashed(), manager);
3803}
3804
3805/**
3806 * Receives an array of pointers, one for each time manager->read_pointer()
3807 * was called in fillin(). Returns the number of pointers processed.
3808 */
3809int PandaNode::CData::
3810complete_pointers(TypedWritable **p_list, BamReader *manager) {
3811 int pi = CycleData::complete_pointers(p_list, manager);
3812
3813 // Get the state and transform pointers.
3814 RenderState *state;
3815 DCAST_INTO_R(state, p_list[pi++], pi);
3816 _state = state;
3817
3818 TransformState *transform;
3819 DCAST_INTO_R(transform, p_list[pi++], pi);
3820 _prev_transform = _transform = transform;
3821
3822/*
3823 * Finalize these pointers now to decrement their artificially-held reference
3824 * counts. We do this now, rather than later, in case some other object
3825 * reassigns them a little later on during initialization, before they can
3826 * finalize themselves normally (for instance, the character may change the
3827 * node's transform). If that happens, the pointer may discover that no one
3828 * else holds its reference count when it finalizes, which will constitute a
3829 * memory leak (see the comments in TransformState::finalize(), etc.).
3830 */
3831 manager->finalize_now((RenderState *)_state.p());
3832 manager->finalize_now((TransformState *)_transform.p());
3833
3834
3835
3836 // Get the effects pointer.
3837 RenderEffects *effects;
3838 DCAST_INTO_R(effects, p_list[pi++], pi);
3839 _effects = effects;
3840
3841/*
3842 * Finalize these pointers now to decrement their artificially-held reference
3843 * counts. We do this now, rather than later, in case some other object
3844 * reassigns them a little later on during initialization, before they can
3845 * finalize themselves normally (for instance, the character may change the
3846 * node's transform). If that happens, the pointer may discover that no one
3847 * else holds its reference count when it finalizes, which will constitute a
3848 * memory leak (see the comments in TransformState::finalize(), etc.).
3849 */
3850 manager->finalize_now((RenderEffects *)_effects.p());
3851
3852
3853
3854 // Get the parent and child pointers.
3855 pi += complete_up_list(*modify_up(), "up", p_list + pi, manager);
3856 pi += complete_down_list(*modify_down(), "down", p_list + pi, manager);
3857 pi += complete_down_list(*modify_stashed(), "stashed", p_list + pi, manager);
3858
3859 // Since the _effects and _states members have been finalized by now, this
3860 // should be safe.
3861 set_fancy_bit(FB_transform, !_transform->is_identity());
3862 set_fancy_bit(FB_state, !_state->is_empty());
3863 set_fancy_bit(FB_effects, !_effects->is_empty());
3864 set_fancy_bit(FB_tag, !_tag_data.is_empty());
3865
3866 // Mark the bounds stale.
3867 ++_next_update;
3868
3869 nassertr(!_transform->is_invalid(), pi);
3870 nassertr(!_prev_transform->is_invalid(), pi);
3871
3872 return pi;
3873}
3874
3875/**
3876 * This internal function is called by make_from_bam to read in all of the
3877 * relevant data from the BamFile for the new PandaNode.
3878 */
3879void PandaNode::CData::
3880fillin(DatagramIterator &scan, BamReader *manager) {
3881 // Read the state and transform pointers.
3882 manager->read_pointer(scan);
3883 manager->read_pointer(scan);
3884
3885 // Read the effects pointer.
3886 manager->read_pointer(scan);
3887
3888 if (manager->get_file_minor_ver() < 2) {
3889 DrawMask draw_mask;
3890 draw_mask.set_word(scan.get_uint32());
3891
3892 if (draw_mask == DrawMask::all_off()) {
3893 // Hidden.
3894 _draw_control_mask = _overall_bit;
3895 _draw_show_mask = ~_overall_bit;
3896
3897 } else if (draw_mask == DrawMask::all_on()) {
3898 // Normally visible.
3899 _draw_control_mask = DrawMask::all_off();
3900 _draw_show_mask = DrawMask::all_on();
3901
3902 } else {
3903 // Some per-camera combination.
3904 draw_mask &= ~_overall_bit;
3905 _draw_control_mask = ~draw_mask;
3906 _draw_show_mask = draw_mask;
3907 }
3908
3909 } else {
3910 _draw_control_mask.set_word(scan.get_uint32());
3911 _draw_show_mask.set_word(scan.get_uint32());
3912 }
3913
3914 _into_collide_mask.set_word(scan.get_uint32());
3915
3916 _bounds_type = BoundingVolume::BT_default;
3917 if (manager->get_file_minor_ver() >= 19) {
3918 _bounds_type = (BoundingVolume::BoundsType)scan.get_uint8();
3919 }
3920
3921 // Read in the tag list.
3922 int num_tags = scan.get_uint32();
3923 for (int i = 0; i < num_tags; i++) {
3924 string key = scan.get_string();
3925 string value = scan.get_string();
3926 _tag_data.store(key, value);
3927 }
3928
3929
3930 fillin_up_list(*modify_up(), "up", scan, manager);
3931 fillin_down_list(*modify_down(), "down", scan, manager);
3932 fillin_down_list(*modify_stashed(), "stashed", scan, manager);
3933}
3934
3935/**
3936 * Writes the indicated list of parent node pointers to the datagram.
3937 */
3938void PandaNode::CData::
3939write_up_list(const PandaNode::Up &up_list,
3940 BamWriter *manager, Datagram &dg) const {
3941/*
3942 * When we write a PandaNode, we write out its complete list of child node
3943 * pointers, but we only write out the parent node pointers that have already
3944 * been added to the bam file by a previous write operation. This is a bit of
3945 * trickery that allows us to write out just a subgraph (instead of the
3946 * complete graph) when we write out an arbitrary node in the graph, yet also
3947 * allows us to keep nodes completely in sync when we use the bam format for
3948 * streaming scene graph operations over the network.
3949 */
3950
3951 int num_parents = 0;
3952 Up::const_iterator ui;
3953 for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
3954 PandaNode *parent_node = (*ui).get_parent();
3955 if (manager->has_object(parent_node)) {
3956 num_parents++;
3957 }
3958 }
3959 nassertv(num_parents == (int)(uint16_t)num_parents);
3960 dg.add_uint16(num_parents);
3961 for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
3962 PandaNode *parent_node = (*ui).get_parent();
3963 if (manager->has_object(parent_node)) {
3964 manager->write_pointer(dg, parent_node);
3965 }
3966 }
3967}
3968
3969/**
3970 * Writes the indicated list of child node pointers to the datagram.
3971 */
3972void PandaNode::CData::
3973write_down_list(const PandaNode::Down &down_list,
3974 BamWriter *manager, Datagram &dg) const {
3975 int num_children = down_list.size();
3976 nassertv(num_children == (int)(uint16_t)num_children);
3977 dg.add_uint16(num_children);
3978
3979 // Should we smarten up the writing of the sort number? Most of the time
3980 // these will all be zero.
3981 Down::const_iterator di;
3982 for (di = down_list.begin(); di != down_list.end(); ++di) {
3983 PandaNode *child_node = (*di).get_child();
3984 int sort = (*di).get_sort();
3985 manager->write_pointer(dg, child_node);
3986 dg.add_int32(sort);
3987 }
3988}
3989
3990/**
3991 * Calls consider_update on each node of the indicated up list.
3992 */
3993void PandaNode::CData::
3994update_up_list(const PandaNode::Up &up_list, BamWriter *manager) const {
3995 Up::const_iterator ui;
3996 for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
3997 PandaNode *parent_node = (*ui).get_parent();
3998 if (manager->has_object(parent_node)) {
3999 manager->consider_update(parent_node);
4000 }
4001 }
4002}
4003
4004/**
4005 * Calls consider_update on each node of the indicated up list.
4006 */
4007void PandaNode::CData::
4008update_down_list(const PandaNode::Down &down_list, BamWriter *manager) const {
4009 Down::const_iterator di;
4010 for (di = down_list.begin(); di != down_list.end(); ++di) {
4011 PandaNode *child_node = (*di).get_child();
4012 manager->consider_update(child_node);
4013 }
4014}
4015
4016/**
4017 * Calls complete_pointers() on the list of parent node pointers.
4018 */
4019int PandaNode::CData::
4020complete_up_list(PandaNode::Up &up_list, const string &tag,
4021 TypedWritable **p_list, BamReader *manager) {
4022 int pi = 0;
4023
4024 int num_parents = manager->get_int_tag(tag);
4025 Up new_up_list(PandaNode::get_class_type());
4026 new_up_list.reserve(num_parents);
4027 for (int i = 0; i < num_parents; i++) {
4028 PandaNode *parent_node = DCAST(PandaNode, p_list[pi++]);
4029 UpConnection connection(parent_node);
4030 new_up_list.push_back(connection);
4031 }
4032
4033 // Now we should sort the list, since the sorting is based on pointer order,
4034 // which might be different from one session to the next.
4035 new_up_list.sort();
4036
4037 // Make it permanent.
4038 up_list.swap(new_up_list);
4039 new_up_list.clear();
4040
4041 return pi;
4042}
4043
4044/**
4045 * Calls complete_pointers() on the list of child node pointers.
4046 */
4047int PandaNode::CData::
4048complete_down_list(PandaNode::Down &down_list, const string &tag,
4049 TypedWritable **p_list, BamReader *manager) {
4050 int pi = 0;
4051
4052 BamReaderAuxDataDown *aux;
4053 DCAST_INTO_R(aux, manager->get_aux_tag(tag), pi);
4054
4055 Down &new_down_list = aux->_down_list;
4056 for (Down::iterator di = new_down_list.begin();
4057 di != new_down_list.end();
4058 ++di) {
4059 PandaNode *child_node = DCAST(PandaNode, p_list[pi++]);
4060 (*di).set_child(child_node);
4061 }
4062
4063 // Unlike the up list, we should *not* sort the down list. The down list is
4064 // stored in a specific order, not related to pointer order; and this order
4065 // should be preserved from one session to the next.
4066
4067 // Make it permanent.
4068 down_list.swap(new_down_list);
4069 new_down_list.clear();
4070
4071 return pi;
4072}
4073
4074/**
4075 * Reads the indicated list parent node pointers from the datagram (or at
4076 * least calls read_pointer() for each one).
4077 */
4078void PandaNode::CData::
4079fillin_up_list(PandaNode::Up &up_list, const string &tag,
4080 DatagramIterator &scan, BamReader *manager) {
4081 int num_parents = scan.get_uint16();
4082 manager->set_int_tag(tag, num_parents);
4083 manager->read_pointers(scan, num_parents);
4084}
4085
4086/**
4087 * Reads the indicated list child node pointers from the datagram (or at least
4088 * calls read_pointer() for each one).
4089 */
4090void PandaNode::CData::
4091fillin_down_list(PandaNode::Down &down_list, const string &tag,
4092 DatagramIterator &scan, BamReader *manager) {
4093 int num_children = scan.get_uint16();
4094
4095 // Create a temporary down_list, with the right number of elements, but a
4096 // NULL value for each pointer (we'll fill in the pointers later). We need
4097 // to do this to associate the sort values with their pointers.
4098 Down new_down_list(PandaNode::get_class_type());
4099 new_down_list.reserve(num_children);
4100 for (int i = 0; i < num_children; i++) {
4101 manager->read_pointer(scan);
4102 int sort = scan.get_int32();
4103 DownConnection connection(nullptr, sort);
4104 new_down_list.push_back(connection);
4105 }
4106
4107 // Now store the temporary down_list in the BamReader, so we can get it
4108 // during the call to complete_down_list().
4109 PT(BamReaderAuxDataDown) aux = new BamReaderAuxDataDown;
4110 aux->_down_list.swap(new_down_list);
4111 manager->set_aux_tag(tag, aux);
4112}
4113
4114/**
4115 * Ensures that the draw masks etc. are properly computed on this node. If
4116 * update_bounds is true, also checks the bounding volume.
4117 */
4119check_cached(bool update_bounds) const {
4120 UpdateSeq last_update = update_bounds
4121 ? _cdata->_last_bounds_update
4122 : _cdata->_last_update;
4123
4124 if (last_update != _cdata->_next_update) {
4125 // The cache is stale; it needs to be rebuilt.
4126
4127 // We'll need to get a fresh read pointer, since another thread might
4128 // already have modified the pointer on the object since we queried it.
4129#ifdef DO_PIPELINING
4130 node_unref_delete((CycleData *)_cdata);
4131#endif // DO_PIPELINING
4132 ((PandaNodePipelineReader *)this)->_cdata = nullptr;
4133 int pipeline_stage = _current_thread->get_pipeline_stage();
4134 PandaNode::CDLockedStageReader fresh_cdata(_node->_cycler, pipeline_stage, _current_thread);
4135 if (fresh_cdata->_last_update == fresh_cdata->_next_update &&
4136 (!update_bounds || fresh_cdata->_last_bounds_update == fresh_cdata->_next_update)) {
4137 // What luck, some other thread has already freshened the cache for us.
4138 // Save the new pointer, and let the lock release itself.
4139 if (_cdata != (const PandaNode::CData *)fresh_cdata) {
4140 ((PandaNodePipelineReader *)this)->_cdata = fresh_cdata;
4141#ifdef DO_PIPELINING
4142 _cdata->node_ref();
4143#endif // DO_PIPELINING
4144 }
4145
4146 } else {
4147 // No, the cache is still stale. We have to do the work of freshening
4148 // it.
4149 PStatTimer timer(PandaNode::_update_bounds_pcollector);
4150 PandaNode::CDStageWriter cdataw = ((PandaNode *)_node)->update_cached(update_bounds, pipeline_stage, fresh_cdata);
4151 nassertv(cdataw->_last_update == cdataw->_next_update);
4152 // As above, we save the new pointer, and then let the lock release
4153 // itself.
4154 if (_cdata != (const PandaNode::CData *)cdataw) {
4155 ((PandaNodePipelineReader *)this)->_cdata = cdataw;
4156#ifdef DO_PIPELINING
4157 _cdata->node_ref();
4158#endif // DO_PIPELINING
4159 }
4160 }
4161 }
4162
4163 nassertv(_cdata->_last_update == _cdata->_next_update);
4164 nassertv(!update_bounds || _cdata->_last_bounds_update == _cdata->_next_update);
4165}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition bamReader.I:275
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition bamReader.h:110
void read_pointers(DatagramIterator &scan, int count)
A convenience function to read a contiguous list of pointers.
int get_int_tag(const std::string &tag) const
Returns the value previously set via set_int_tag().
BamReaderAuxData * get_aux_tag(const std::string &tag) const
Returns the value previously set via set_aux_tag().
void finalize_now(TypedWritable *whom)
Forces the finalization of a particular object.
void set_aux_tag(const std::string &tag, BamReaderAuxData *value)
Allows the creating object to store a temporary data value on the BamReader.
void set_int_tag(const std::string &tag, int value)
Allows the creating object to store a temporary data value on the BamReader.
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition bamReader.I:83
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
bool has_object(const TypedWritable *obj) const
Returns true if the object has previously been written (or at least requested to be written) to the b...
void consider_update(const TypedWritable *obj)
Should be called from TypedWritable::update_bam_nested() to recursively check the entire hiererachy o...
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
static BitMask< uint32_t, nbits > bit(int index)
Returns a BitMask with only the indicated bit on.
Definition bitMask.I:70
static BitMask< uint32_t, nbits > all_on()
Returns a BitMask whose bits are all on.
Definition bitMask.I:32
static BitMask< uint32_t, nbits > all_off()
Returns a BitMask whose bits are all off.
Definition bitMask.I:43
void set_word(WordType value)
Sets the entire BitMask to the value indicated by the given word.
Definition bitMask.I:255
bool is_zero() const
Returns true if the entire bitmask is zero, false otherwise.
Definition bitMask.I:153
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Definition boundingBox.h:29
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...
bool around(const BoundingVolume **first, const BoundingVolume **last)
Resets the volume to enclose only the volumes indicated.
This functions similarly to a LightAttrib.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
This class is similar to CycleDataLockedReader, except it allows reading from a particular stage of t...
Thread * get_current_thread() const
Returns the Thread pointer of the currently-executing thread, as passed to the constructor of this ob...
const CycleDataType * take_pointer()
This is intended to be called only from CycleDataStageWriter when it elevates the pointer from read t...
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
This class is similar to CycleDataReader, except it allows reading from a particular stage of the pip...
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
A single page of data maintained by a PipelineCycler.
Definition cycleData.h:50
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition cycleData.cxx:48
A class to retrieve the individual data elements previously stored in a Datagram.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
std::string get_string()
Extracts a variable-length string.
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_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition datagram.I:94
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
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition datagram.I:85
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition datagram.I:219
An instance of this class is passed to the Factory when requesting it to do its business and construc...
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition factory.I:73
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Similar to MutexHolder, but for a light mutex.
Similar to MutexHolder, but for a light reentrant mutex.
The abstract interface to all kinds of lights.
Definition light.h:38
This just stores the pointers to implement a doubly-linked list of some kind of object.
static void update_type(ReferenceCount *ptr, TypeHandle type)
Associates the indicated type with the given pointer.
Definition memoryUsage.I:55
A base class for all things which can have a name.
Definition namable.h:26
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.
static NodePath any_path(PandaNode *node, Thread *current_thread=Thread::get_current_thread())
Returns a new NodePath that represents any arbitrary path from the root to the indicated node.
Definition nodePath.I:62
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
This class maintains a linked list of PandaNodes.
Encapsulates the data from a PandaNode, pre-fetched for one stage of the pipeline.
Definition pandaNode.h:840
void check_cached(bool update_bounds) const
Ensures that the draw masks etc.
int get_child_sort(size_t n) 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:973
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
PandaNode * get_parent(size_t n) const
Returns the nth parent of the node.
Definition pandaNode.I:1122
size_t get_num_parents() const
Returns the number of parents of the node.
Definition pandaNode.I:1113
PandaNode * get_stashed(size_t n) const
Returns the nth stashed child of the node.
Definition pandaNode.I:1042
int get_stashed_sort(size_t n) 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:1053
size_t get_num_stashed() const
Returns the number of stashed children of the node.
Definition pandaNode.I:1033
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
void copy_children(PandaNode *other, Thread *current_thread=Thread::get_current_thread())
Makes another instance of all the children of the other node, copying them to this node.
void write_recorder(BamWriter *manager, Datagram &dg)
This method is provided for the benefit of classes (like MouseRecorder) that inherit from PandaMode a...
DrawMask get_net_draw_control_mask() const
Returns the set of bits in get_net_draw_show_mask() that have been explicitly set via adjust_draw_mas...
virtual bool preserve_name() const
Returns true if the node's name has extrinsic meaning and must be preserved across a flatten operatio...
bool replace_child(PandaNode *orig_child, PandaNode *new_child, Thread *current_thread=Thread::get_current_thread())
Searches for the orig_child node in the node's list of children, and replaces it with the new_child i...
get_draw_control_mask
Returns the set of bits in draw_show_mask that are considered meaningful.
Definition pandaNode.h:255
virtual bool is_geom_node() const
A simple downcast check.
void get_tag_keys(vector_string &keys) const
Fills the given vector up with the list of tags on this PandaNode.
virtual PandaNode * combine_with(PandaNode *other)
Collapses this PandaNode with the other PandaNode, if possible, and returns a pointer to the combined...
virtual int get_next_visible_child(int n) const
Returns the index number of the next visible child of this node following the indicated child,...
bool unstash_child(PandaNode *child_node, Thread *current_thread=Thread::get_current_thread())
Returns the indicated stashed node to normal child status.
Definition pandaNode.I:128
virtual bool has_single_child_visibility() const
Should be overridden by derived classes to return true if this kind of node has the special property ...
virtual bool safe_to_combine() const
Returns true if it is generally safe to combine this particular kind of PandaNode with other kinds of...
virtual int get_unsafe_to_apply_attribs() const
Returns the union of all attributes from SceneGraphReducer::AttribTypes that may not safely be applie...
virtual int get_visible_child() const
Returns the index number of the currently visible child of this node.
set_tag
Associates a user-defined value with a user-defined key which is stored on the node.
Definition pandaNode.h:207
virtual bool safe_to_transform() const
Returns true if it is generally safe to transform this particular kind of PandaNode by calling the xf...
void clear_unexpected_change(unsigned int flags)
Sets one or more of the PandaNode::UnexpectedChange bits off, indicating that the corresponding prope...
DrawMask get_net_draw_show_mask() const
Returns the union of all draw_show_mask values–of renderable nodes only– at this level and below.
void reset_prev_transform(Thread *current_thread=Thread::get_current_thread())
Resets the transform that represents this node's "previous" position to the same as the current trans...
void clear_effect(TypeHandle type)
Removes the render effect of the given type from this node.
virtual void update_bam_nested(BamWriter *manager)
Called by the BamWriter when this object has not itself been modified recently, but it should check i...
CollideMask get_net_collide_mask(Thread *current_thread=Thread::get_current_thread()) const
Returns the union of all into_collide_mask() values set at CollisionNodes at this level and below.
void remove_all_children(Thread *current_thread=Thread::get_current_thread())
Removes all the children from the node at once, including stashed children.
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
virtual bool is_renderable() const
Returns true if there is some value to visiting this particular node during the cull traversal for an...
static void reset_all_prev_transform(Thread *current_thread=Thread::get_current_thread())
Visits all nodes in the world with the _dirty_prev_transform flag–that is, all nodes whose _prev_tran...
virtual bool safe_to_combine_children() const
Returns true if it is generally safe to combine the children of this PandaNode with each other.
virtual bool safe_to_modify_transform() const
Returns true if it is safe to automatically adjust the transform on this kind of node.
void remove_stashed(int child_index, Thread *current_thread=Thread::get_current_thread())
Removes the nth stashed child from the node.
get_legal_collide_mask
Returns the subset of CollideMask bits that may be set for this particular type of PandaNode.
Definition pandaNode.h:265
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
void set_bounds(const BoundingVolume *volume)
Resets the bounding volume so that it is the indicated volume.
set_effects
Sets the complete RenderEffects that will be applied this node.
Definition pandaNode.h:178
void remove_child(int child_index, Thread *current_thread=Thread::get_current_thread())
Removes the nth child from the node.
static void register_with_read_factory()
Tells the BamReader how to create objects of type PandaNode.
get_parents
Returns an object that can be used to walk through the list of parents of the node,...
Definition pandaNode.h:784
get_num_stashed
Returns the number of stashed nodes this node has.
Definition pandaNode.h:148
void adjust_draw_mask(DrawMask show_mask, DrawMask hide_mask, DrawMask clear_mask)
Adjusts the hide/show bits of this particular node.
get_draw_show_mask
Returns the hide/show bits of this particular node.
Definition pandaNode.h:256
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this PandaNode by the indicated matrix, if it means anything to do so.
bool is_under_scene_root() const
Returns true if this particular node is in a live scene graph: that is, it is a child or descendent o...
virtual int get_first_visible_child() const
Returns the index number of the first visible child of this node, or a number >= get_num_children() i...
get_child
Returns the nth child node of this node.
Definition pandaNode.h:124
void copy_all_properties(PandaNode *other)
Copies the TransformState, RenderState, RenderEffects, tags, Python tags, and the show/hide state fro...
virtual bool safe_to_flatten_below() const
Returns true if a flatten operation may safely continue past this node, or false if nodes below this ...
virtual PandaNode * dupe_for_flatten() const
This is similar to make_copy(), but it makes a copy for the specific purpose of flatten.
set_state
Sets the complete RenderState that will be applied to all nodes at this level and below.
Definition pandaNode.h:173
virtual bool has_selective_visibility() const
Should be overridden by derived classes to return true if this kind of node has some restrictions on ...
set_transform
Sets the transform that will be applied to this node and below.
Definition pandaNode.h:183
void set_effect(const RenderEffect *effect)
Adds the indicated render effect to the scene graph on this node.
virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer)
Applies whatever attributes are specified in the AccumulatedAttribs object (and by the attrib_types b...
bool is_scene_root() const
Returns true if this particular node is known to be the render root of some active DisplayRegion asso...
virtual bool is_lod_node() const
A simple downcast check.
bool stash_child(PandaNode *child_node, Thread *current_thread=Thread::get_current_thread())
Stashes the indicated child node.
Definition pandaNode.I:107
void add_stashed(PandaNode *child_node, int sort=0, Thread *current_thread=Thread::get_current_thread())
Adds a new child to the node, directly as a stashed child.
virtual bool is_collision_node() const
A simple downcast check.
void list_tags(std::ostream &out, const std::string &separator="\n") const
Writes a list of all the tag keys assigned to the node to the indicated stream.
unsigned int get_unexpected_change(unsigned int flags) const
Returns nonzero if any of the bits in the input parameter are set on this node, or zero if none of th...
static void set_scene_root_func(SceneRootFunc *func)
This is used by the GraphicsEngine to hook in a pointer to the scene_root_func(), the function to det...
clear_tag
Removes the value defined for this key on this particular node.
Definition pandaNode.h:207
void copy_tags(PandaNode *other)
Copies all of the tags stored on the other node onto this node.
void set_prev_transform(const TransformState *transform, Thread *current_thread=Thread::get_current_thread())
Sets the transform that represents this node's "previous" position, one frame ago,...
void set_bound(const BoundingVolume *volume)
virtual bool is_ambient_light() const
Returns true if this is an AmbientLight, false if it is not a light, or it is some other kind of ligh...
int find_parent(PandaNode *node, Thread *current_thread=Thread::get_current_thread()) const
Returns the index of the indicated parent node, if it is a parent, or -1 if it is not.
Definition pandaNode.I:44
virtual PandaNode * make_copy() const
Returns a newly-allocated PandaNode that is a shallow copy of this one.
int get_child_sort(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the sort index of the nth child node of this node (that is, the number that was passed to add...
Definition pandaNode.I:78
void set_attrib(const RenderAttrib *attrib, int override=0)
Adds the indicated render attribute to the scene graph on this node.
virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data)
Adds the node's contents to the CullResult we are building up during the cull traversal,...
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
void set_unexpected_change(unsigned int flags)
Sets one or more of the PandaNode::UnexpectedChange bits on, indicating that the corresponding proper...
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition pandaNode.h:782
get_bounds_type
Returns the bounding volume type set with set_bounds_type().
Definition pandaNode.h:293
void replace_node(PandaNode *other)
Inserts this node into the scene graph in place of the other one, and removes the other node.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
void steal_children(PandaNode *other, Thread *current_thread=Thread::get_current_thread())
Moves all the children from the other node onto this node.
int compare_tags(const PandaNode *other) const
Returns a number less than 0, 0, or greater than 0, to indicate the similarity of tags between this n...
set_bounds_type
Specifies the desired type of bounding volume that will be created for this node.
Definition pandaNode.h:293
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,...
get_parent
Returns the nth parent node of this node.
Definition pandaNode.h:118
virtual bool safe_to_flatten() const
Returns true if it is generally safe to flatten out this particular kind of PandaNode by duplicating ...
get_num_children
Returns the number of child nodes this node has.
Definition pandaNode.h:124
A base class for all things that want to be reference-counted.
bool ref_if_nonzero() const
Atomically increases the reference count of this object if it is not zero.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
This is the base class for a number of special render effects that may be set on scene graph nodes to...
This represents a unique collection of RenderEffect objects that correspond to a particular renderabl...
bool is_empty() const
Returns true if the state is empty, false otherwise.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
bool is_empty() const
Returns true if the state is empty, false otherwise.
Definition renderState.I:27
const Key & get_key(size_t n) const
Returns the key in the nth entry of the table.
constexpr size_t size() const
Returns the total number of entries in the table.
const Value & get_data(size_t n) const
Returns the data in the nth entry of the table.
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
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
Indicates a coordinate-system transform on vertices.
bool is_invalid() const
Returns true if the transform represents an invalid matrix, for instance the result of inverting a si...
get_mat
Returns the matrix that describes the transform.
bool is_identity() const
Returns true if the transform represents the identity matrix, false otherwise.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition typedObject.I:38
A base class for things which need to inherit from both TypedWritable and from ReferenceCount.
Base class for objects that can be written to and read from Bam files.
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
static bool decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, vector_uchar data, BamReader *reader=nullptr)
Reads the bytes created by a previous call to encode_to_bam_stream(), and extracts the single object ...
void mark_bam_modified()
Increments the bam_modified counter, so that this object will be invalidated and retransmitted on any...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This is a sequence number that increments monotonically.
Definition updateSeq.h:37
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.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void node_unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void release_read_stage(int pipeline_stage, const CycleData *pointer) const
Releases a pointer previously obtained via a call to read_stage().