diff --git a/contrib/src/speedtree/speedTreeNode.cxx b/contrib/src/speedtree/speedTreeNode.cxx index 198160b584..ac0884e641 100644 --- a/contrib/src/speedtree/speedTreeNode.cxx +++ b/contrib/src/speedtree/speedTreeNode.cxx @@ -1002,17 +1002,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool SpeedTreeNode:: -is_renderable() const { - return true; -} - /** * Adds the node's contents to the CullResult we are building up during the * cull traversal, so that it will be drawn at render time. For most nodes @@ -1162,6 +1151,7 @@ set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode eMode) const { void SpeedTreeNode:: init_node() { PandaNode::set_cull_callback(); + PandaNode::set_renderable(); _is_valid = false; _needs_repopulate = false; diff --git a/contrib/src/speedtree/speedTreeNode.h b/contrib/src/speedtree/speedTreeNode.h index 3c95bbce76..9c3f0ca886 100644 --- a/contrib/src/speedtree/speedTreeNode.h +++ b/contrib/src/speedtree/speedTreeNode.h @@ -157,7 +157,6 @@ public: GeomTransformer &transformer); virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); void prepare_scene(GraphicsStateGuardianBase *gsgbase, const RenderState *net_state); diff --git a/panda/src/bullet/bulletDebugNode.cxx b/panda/src/bullet/bulletDebugNode.cxx index 8b758c59ca..7bc26ae1c1 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -48,6 +48,7 @@ BulletDebugNode(const char *name) : PandaNode(name) { set_bounds(bounds); set_final(true); set_overall_hidden(true); + set_renderable(); } /** @@ -151,17 +152,6 @@ draw_mask_changed() { } } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool BulletDebugNode:: -is_renderable() const { - return true; -} - /** * Adds the node's contents to the CullResult we are building up during the * cull traversal, so that it will be drawn at render time. For most nodes diff --git a/panda/src/bullet/bulletDebugNode.h b/panda/src/bullet/bulletDebugNode.h index 720e26fbd3..471c059f99 100644 --- a/panda/src/bullet/bulletDebugNode.h +++ b/panda/src/bullet/bulletDebugNode.h @@ -55,7 +55,6 @@ public: virtual bool safe_to_combine_children() const; virtual bool safe_to_flatten_below() const; - virtual bool is_renderable() const; virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); private: diff --git a/panda/src/collide/collisionLevelState.I b/panda/src/collide/collisionLevelState.I index db618f99e7..1001f6d08d 100644 --- a/panda/src/collide/collisionLevelState.I +++ b/panda/src/collide/collisionLevelState.I @@ -30,9 +30,9 @@ CollisionLevelState(const NodePath &node_path) : */ template INLINE CollisionLevelState:: -CollisionLevelState(const CollisionLevelState &parent, PandaNode *child) : +CollisionLevelState(const CollisionLevelState &parent, const PandaNode::DownConnection &child, MaskType mask) : CollisionLevelStateBase(parent, child), - _current(parent._current) + _current(mask) { } #endif // CPPPARSER @@ -101,21 +101,21 @@ prepare_collider(const ColliderDef &def, const NodePath &root) { template bool CollisionLevelState:: any_in_bounds() { -#ifndef NDEBUG +#ifdef NDEBUG + const bool is_spam = false; +#else + const bool is_spam = collide_cat.is_spam(); +#endif int indent_level = 0; - if (collide_cat.is_spam()) { + if (is_spam) { indent_level = _node_path.get_num_nodes() * 2; collide_cat.spam(); indent(collide_cat.spam(false), indent_level) << "Considering " << _node_path.get_node_path() << "\n"; } -#endif // NDEBUG - PandaNode *pnode = node(); - - CPT(BoundingVolume) node_bv = pnode->get_bounds(); - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - const GeometricBoundingVolume *node_gbv = (const GeometricBoundingVolume *)node_bv.p(); + if (_node_gbv != nullptr) { + PandaNode *pnode = node(); CollideMask this_mask = pnode->get_net_collide_mask(); int num_colliders = get_num_colliders(); @@ -131,13 +131,11 @@ any_in_bounds() { // Also don't test a node with itself, or with any of its // descendants. if (pnode == cnode) { -#ifndef NDEBUG - if (collide_cat.is_spam()) { + if (is_spam) { indent(collide_cat.spam(false), indent_level) << "Not comparing " << c << " to " << _node_path << " (same node)\n"; } -#endif // NDEBUG } else { // There are bits in common, and it's not the same instance, so go @@ -148,16 +146,14 @@ any_in_bounds() { is_in = true; // If there's no bounding volume, we're implicitly in. if (col_gbv != nullptr) { - is_in = (node_gbv->contains(col_gbv) != 0); + is_in = (_node_gbv->contains(col_gbv) != 0); _node_volume_pcollector.add_level(1); -#ifndef NDEBUG - if (collide_cat.is_spam()) { + if (is_spam) { indent(collide_cat.spam(false), indent_level) << "Comparing " << c << ": " << *col_gbv - << " to " << *node_gbv << ", is_in = " << is_in << "\n"; + << " to " << *_node_gbv << ", is_in = " << is_in << "\n"; } -#endif // NDEBUG } } } @@ -171,8 +167,7 @@ any_in_bounds() { } } -#ifndef NDEBUG - if (collide_cat.is_spam()) { + if (is_spam) { int num_active_colliders = 0; int num_colliders = get_num_colliders(); for (int c = 0; c < num_colliders; c++) { @@ -201,11 +196,121 @@ any_in_bounds() { collide_cat.spam(false) << "\n"; } -#endif // NDEBUG return has_any_collider(); } #endif // CPPPARSER +#ifndef CPPPARSER +/** + * Checks the bounding volume of the given child of the current node against + * each of our colliders. Returns a mask indicating which colliders are inside + * of the bounding volume. + */ +template +MaskType CollisionLevelState:: +get_child_mask(const PandaNode::DownConnection &child) const { + PandaNode *pnode = child.get_child(); +#ifdef NDEBUG + const bool is_spam = false; +#else + const bool is_spam = collide_cat.is_spam(); +#endif + int indent_level = 0; + if (is_spam) { + indent_level = (_node_path.get_num_nodes() + 1) * 2; + collide_cat.spam(); + indent(collide_cat.spam(false), indent_level) + << "Considering " << _node_path << "/" << pnode->get_name() << "\n"; + } + + MaskType mask = _current; + + const GeometricBoundingVolume *node_gbv = child.get_bounds(); + if (node_gbv != nullptr) { + CollideMask node_mask = child.get_net_collide_mask(); + + int num_colliders = get_num_colliders(); + for (int c = 0; c < num_colliders; c++) { + if (mask.get_bit(c)) { + CollisionNode *cnode = get_collider_node(c); + bool is_in = false; + + // Don't even bother testing the bounding volume if there are no + // collide bits in common between our collider and this node. + CollideMask from_mask = cnode->get_from_collide_mask() & _include_mask; + if (!(from_mask & node_mask).is_zero()) { + // Also don't test a node with itself, or with any of its + // descendants. + if (pnode == cnode) { + if (is_spam) { + indent(collide_cat.spam(false), indent_level) + << "Not comparing " << c << " to " << _node_path << "/" + << pnode->get_name() << " (same node)\n"; + } + + } else { + // There are bits in common, and it's not the same instance, so go + // ahead and try the bounding volume. + const GeometricBoundingVolume *col_gbv = + get_local_bound(c); + + is_in = true; // If there's no bounding volume, we're implicitly in. + + if (col_gbv != nullptr) { + is_in = (node_gbv->contains(col_gbv) != 0); + _node_volume_pcollector.add_level(1); + + if (is_spam) { + indent(collide_cat.spam(false), indent_level) + << "Comparing " << c << ": " << *col_gbv + << " to " << *node_gbv << ", is_in = " << is_in << "\n"; + } + } + } + } + + if (!is_in) { + // This collider cannot intersect with any geometry at this node or + // below. + mask.clear_bit(c); + } + } + } + } + + if (is_spam) { + int num_active_colliders = 0; + int num_colliders = get_num_colliders(); + for (int c = 0; c < num_colliders; c++) { + if (mask.get_bit(c)) { + num_active_colliders++; + } + } + + collide_cat.spam(); + indent(collide_cat.spam(false), indent_level) + << _node_path.get_node_path() << "/" << pnode->get_name() << " has " + << num_active_colliders << " interested colliders"; + if (num_colliders != 0) { + collide_cat.spam(false) + << " ("; + for (int c = 0; c < num_colliders; c++) { + if (mask.get_bit(c)) { + CollisionNode *cnode = get_collider_node(c); + collide_cat.spam(false) + << " " << c << ". " << cnode->get_name(); + } + } + collide_cat.spam(false) + << " )"; + } + collide_cat.spam(false) + << "\n"; + } + return mask; +} +#endif // CPPPARSER + #ifndef CPPPARSER /** * Applies the inverse transform from the current node, if any, onto all the diff --git a/panda/src/collide/collisionLevelState.h b/panda/src/collide/collisionLevelState.h index f06a025f1e..851b908a8c 100644 --- a/panda/src/collide/collisionLevelState.h +++ b/panda/src/collide/collisionLevelState.h @@ -37,7 +37,8 @@ public: #ifndef CPPPARSER INLINE CollisionLevelState(const NodePath &node_path); INLINE CollisionLevelState(const CollisionLevelState &parent, - PandaNode *child); + const PandaNode::DownConnection &child, + MaskType mask); INLINE CollisionLevelState(const CollisionLevelState ©); INLINE void operator = (const CollisionLevelState ©); @@ -45,6 +46,7 @@ public: INLINE void prepare_collider(const ColliderDef &def, const NodePath &root); bool any_in_bounds(); + MaskType get_child_mask(const PandaNode::DownConnection &child) const; bool apply_transform(); INLINE static bool has_max_colliders(); diff --git a/panda/src/collide/collisionLevelStateBase.I b/panda/src/collide/collisionLevelStateBase.I index 20923a9c22..5b649838f7 100644 --- a/panda/src/collide/collisionLevelStateBase.I +++ b/panda/src/collide/collisionLevelStateBase.I @@ -18,7 +18,8 @@ INLINE CollisionLevelStateBase:: CollisionLevelStateBase(const NodePath &node_path) : _node_path(node_path), _colliders(get_class_type()), - _include_mask(CollideMask::all_on()) + _include_mask(CollideMask::all_on()), + _node_gbv(node_path.node()->get_bounds()->as_geometric_bounding_volume()) { } @@ -30,7 +31,21 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, PandaNode *child) _node_path(parent._node_path, child), _colliders(parent._colliders), _include_mask(parent._include_mask), - _local_bounds(parent._local_bounds) + _local_bounds(parent._local_bounds), + _node_gbv(child->get_bounds()->as_geometric_bounding_volume()) +{ +} + +/** + * This constructor goes to the next child node in the traversal. + */ +INLINE CollisionLevelStateBase:: +CollisionLevelStateBase(const CollisionLevelStateBase &parent, const PandaNode::DownConnection &child) : + _node_path(parent._node_path, child.get_child()), + _colliders(parent._colliders), + _include_mask(parent._include_mask), + _local_bounds(parent._local_bounds), + _node_gbv(child.get_bounds()) { } @@ -43,7 +58,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase ©) : _colliders(copy._colliders), _include_mask(copy._include_mask), _local_bounds(copy._local_bounds), - _parent_bounds(copy._parent_bounds) + _parent_bounds(copy._parent_bounds), + _node_gbv(copy._node_gbv) { } @@ -57,6 +73,7 @@ operator = (const CollisionLevelStateBase ©) { _include_mask = copy._include_mask; _local_bounds = copy._local_bounds; _parent_bounds = copy._parent_bounds; + _node_gbv = copy._node_gbv; } /** @@ -113,6 +130,14 @@ get_collider_node_path(int n) const { return _colliders[n]._node_path; } +/** + * Returns the bounding volume of the current node. + */ +INLINE const GeometricBoundingVolume *CollisionLevelStateBase:: +get_node_bound() const { + return _node_gbv; +} + /** * Returns the bounding volume of the indicated collider, transformed into the * current node's transform space. diff --git a/panda/src/collide/collisionLevelStateBase.h b/panda/src/collide/collisionLevelStateBase.h index 4c2abccb58..f84d492965 100644 --- a/panda/src/collide/collisionLevelStateBase.h +++ b/panda/src/collide/collisionLevelStateBase.h @@ -52,6 +52,8 @@ public: INLINE CollisionLevelStateBase(const NodePath &node_path); INLINE CollisionLevelStateBase(const CollisionLevelStateBase &parent, PandaNode *child); + INLINE CollisionLevelStateBase(const CollisionLevelStateBase &parent, + const PandaNode::DownConnection &child); INLINE CollisionLevelStateBase(const CollisionLevelStateBase ©); INLINE void operator = (const CollisionLevelStateBase ©); @@ -67,6 +69,7 @@ public: INLINE const CollisionSolid *get_collider(int n) const; INLINE CollisionNode *get_collider_node(int n) const; INLINE NodePath get_collider_node_path(int n) const; + INLINE const GeometricBoundingVolume *get_node_bound() const; INLINE const GeometricBoundingVolume *get_local_bound(int n) const; INLINE const GeometricBoundingVolume *get_parent_bound(int n) const; @@ -80,6 +83,8 @@ protected: Colliders _colliders; CollideMask _include_mask; + const GeometricBoundingVolume *_node_gbv = nullptr; + typedef PTA(CPT(GeometricBoundingVolume)) BoundingVolumes; BoundingVolumes _local_bounds; BoundingVolumes _parent_bounds; diff --git a/panda/src/collide/collisionNode.cxx b/panda/src/collide/collisionNode.cxx index 1196ba20c7..2df893ef18 100644 --- a/panda/src/collide/collisionNode.cxx +++ b/panda/src/collide/collisionNode.cxx @@ -43,6 +43,7 @@ CollisionNode(const std::string &name) : _collider_sort(0) { set_cull_callback(); + set_renderable(); // CollisionNodes are hidden by default. set_overall_hidden(true); @@ -186,11 +187,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { CPT(CollisionSolid) solid = (*si).get_read_pointer(); PT(PandaNode) node = solid->get_viz(trav, data, false); if (node != nullptr) { - CullTraverserData next_data(data, node); - // We don't want to inherit the render state from above for these guys. - next_data._state = RenderState::make_empty(); - trav->traverse(next_data); + trav->traverse_down(data, node, data._net_transform, RenderState::make_empty()); } } @@ -208,12 +206,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { CPT(CollisionSolid) solid = (*si).get_read_pointer(); PT(PandaNode) node = solid->get_viz(trav, data, false); if (node != nullptr) { - CullTraverserData next_data(data, node); - - next_data._net_transform = - next_data._net_transform->compose(transform); - next_data._state = get_last_pos_state(); - trav->traverse(next_data); + trav->traverse_down(data, node, + data._net_transform->compose(transform), get_last_pos_state()); } } } @@ -223,17 +217,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool CollisionNode:: -is_renderable() const { - return true; -} - /** * A simple downcast check. Returns true if this kind of node happens to * inherit from CollisionNode, false otherwise. diff --git a/panda/src/collide/collisionNode.h b/panda/src/collide/collisionNode.h index 8dd46f6a28..fee71fce78 100644 --- a/panda/src/collide/collisionNode.h +++ b/panda/src/collide/collisionNode.h @@ -43,7 +43,6 @@ public: virtual CollideMask get_legal_collide_mask() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual bool is_collision_node() const; virtual void output(std::ostream &out) const; diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx index 2feb236241..23459d88f3 100644 --- a/panda/src/collide/collisionTraverser.cxx +++ b/panda/src/collide/collisionTraverser.cxx @@ -288,7 +288,9 @@ traverse(const NodePath &root) { #ifdef DO_PSTATS PStatTimer pass_timer(get_pass_collector(pass)); #endif - r_traverse_single(level_states[pass], pass); + if (level_states[pass].any_in_bounds()) { + r_traverse_single(level_states[pass], pass); + } } } } @@ -562,22 +564,13 @@ prepare_colliders_single(CollisionTraverser::LevelStatesSingle &level_states, */ void CollisionTraverser:: r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { - if (!level_state.any_in_bounds()) { - return; - } if (!level_state.apply_transform()) { return; } PandaNode *node = level_state.node(); if (node->is_collision_node()) { - CollisionNode *cnode; - DCAST_INTO_V(cnode, node); - CPT(BoundingVolume) node_bv = cnode->get_bounds(); - const GeometricBoundingVolume *node_gbv = nullptr; - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - DCAST_INTO_V(node_gbv, node_bv); - } + CollisionNode *cnode = (CollisionNode *)node; CollisionEntry entry; entry._into_node = cnode; @@ -589,13 +582,14 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { int num_colliders = level_state.get_num_colliders(); for (int c = 0; c < num_colliders; ++c) { if (level_state.has_collider(c)) { - entry._from_node = level_state.get_collider_node(c); + CollisionNode *from_node = level_state.get_collider_node(c); - if ((entry._from_node->get_from_collide_mask() & + if ((from_node->get_from_collide_mask() & cnode->get_into_collide_mask()) != 0) { #ifdef DO_PSTATS // PStatTimer collide_timer(_solid_collide_collectors[pass]); #endif + entry._from_node = from_node; entry._from_node_path = level_state.get_collider_node_path(c); entry._from = level_state.get_collider(c); @@ -603,7 +597,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { entry, level_state.get_parent_bound(c), level_state.get_local_bound(c), - node_gbv); + level_state.get_node_bound()); } } } @@ -616,13 +610,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { } #endif - GeomNode *gnode; - DCAST_INTO_V(gnode, node); - CPT(BoundingVolume) node_bv = gnode->get_bounds(); - const GeometricBoundingVolume *node_gbv = nullptr; - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - DCAST_INTO_V(node_gbv, node_bv); - } + GeomNode *gnode = (GeomNode *)node; CollisionEntry entry; entry._into_node = gnode; @@ -648,7 +636,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { entry, level_state.get_parent_bound(c), level_state.get_local_bound(c), - node_gbv); + level_state.get_node_bound()); } } } @@ -658,9 +646,14 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { // If it's a switch node or sequence node, visit just the one visible // child. int index = node->get_visible_child(); - if (index >= 0 && index < node->get_num_children()) { - CollisionLevelStateSingle next_state(level_state, node->get_child(index)); - r_traverse_single(next_state, pass); + PandaNode::Children children = node->get_children(); + if (index >= 0 && index < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(index); + CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateSingle next_state(level_state, child, mask); + r_traverse_single(next_state, pass); + } } } else if (node->is_lod_node()) { @@ -673,12 +666,16 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelStateSingle next_state(level_state, children.get_child(i)); - if (i != index) { - next_state.set_include_mask(next_state.get_include_mask() & - ~GeomNode::get_default_collide_mask()); + const PandaNode::DownConnection &child = children.get_child_connection(i); + CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateSingle next_state(level_state, child, mask); + if (i != index) { + next_state.set_include_mask(next_state.get_include_mask() & + ~GeomNode::get_default_collide_mask()); + } + r_traverse_single(next_state, pass); } - r_traverse_single(next_state, pass); } } else { @@ -686,8 +683,12 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelStateSingle next_state(level_state, children.get_child(i)); - r_traverse_single(next_state, pass); + const PandaNode::DownConnection &child = children.get_child_connection(i); + CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateSingle next_state(level_state, child, mask); + r_traverse_single(next_state, pass); + } } } } @@ -773,22 +774,13 @@ prepare_colliders_double(CollisionTraverser::LevelStatesDouble &level_states, */ void CollisionTraverser:: r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { - if (!level_state.any_in_bounds()) { - return; - } if (!level_state.apply_transform()) { return; } PandaNode *node = level_state.node(); if (node->is_collision_node()) { - CollisionNode *cnode; - DCAST_INTO_V(cnode, node); - CPT(BoundingVolume) node_bv = cnode->get_bounds(); - const GeometricBoundingVolume *node_gbv = nullptr; - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - DCAST_INTO_V(node_gbv, node_bv); - } + CollisionNode *cnode = (CollisionNode *)node; CollisionEntry entry; entry._into_node = cnode; @@ -814,7 +806,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { entry, level_state.get_parent_bound(c), level_state.get_local_bound(c), - node_gbv); + level_state.get_node_bound()); } } } @@ -827,13 +819,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { } #endif - GeomNode *gnode; - DCAST_INTO_V(gnode, node); - CPT(BoundingVolume) node_bv = gnode->get_bounds(); - const GeometricBoundingVolume *node_gbv = nullptr; - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - DCAST_INTO_V(node_gbv, node_bv); - } + GeomNode *gnode = (GeomNode *)node; CollisionEntry entry; entry._into_node = gnode; @@ -859,7 +845,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { entry, level_state.get_parent_bound(c), level_state.get_local_bound(c), - node_gbv); + level_state.get_node_bound()); } } } @@ -869,9 +855,14 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { // If it's a switch node or sequence node, visit just the one visible // child. int index = node->get_visible_child(); - if (index >= 0 && index < node->get_num_children()) { - CollisionLevelStateDouble next_state(level_state, node->get_child(index)); - r_traverse_double(next_state, pass); + PandaNode::Children children = node->get_children(); + if (index >= 0 && index < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(index); + CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateDouble next_state(level_state, child, mask); + r_traverse_double(next_state, pass); + } } } else if (node->is_lod_node()) { @@ -880,16 +871,20 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { // visit all other levels without GeomNode::get_default_collide_mask(), // allowing only collision with CollisionNodes and special geometry under // higher levels of detail. - int index = DCAST(LODNode, node)->get_lowest_switch(); + int index = ((LODNode *)node)->get_lowest_switch(); PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelStateDouble next_state(level_state, children.get_child(i)); - if (i != index) { - next_state.set_include_mask(next_state.get_include_mask() & - ~GeomNode::get_default_collide_mask()); + const PandaNode::DownConnection &child = children.get_child_connection(i); + CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateDouble next_state(level_state, child, mask); + if (i != index) { + next_state.set_include_mask(next_state.get_include_mask() & + ~GeomNode::get_default_collide_mask()); + } + r_traverse_double(next_state, pass); } - r_traverse_double(next_state, pass); } } else { @@ -897,8 +892,12 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelStateDouble next_state(level_state, children.get_child(i)); - r_traverse_double(next_state, pass); + const PandaNode::DownConnection &child = children.get_child_connection(i); + CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateDouble next_state(level_state, child, mask); + r_traverse_double(next_state, pass); + } } } } @@ -984,22 +983,13 @@ prepare_colliders_quad(CollisionTraverser::LevelStatesQuad &level_states, */ void CollisionTraverser:: r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { - if (!level_state.any_in_bounds()) { - return; - } if (!level_state.apply_transform()) { return; } PandaNode *node = level_state.node(); if (node->is_collision_node()) { - CollisionNode *cnode; - DCAST_INTO_V(cnode, node); - CPT(BoundingVolume) node_bv = cnode->get_bounds(); - const GeometricBoundingVolume *node_gbv = nullptr; - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - DCAST_INTO_V(node_gbv, node_bv); - } + CollisionNode *cnode = (CollisionNode *)node; CollisionEntry entry; entry._into_node = cnode; @@ -1025,7 +1015,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { entry, level_state.get_parent_bound(c), level_state.get_local_bound(c), - node_gbv); + level_state.get_node_bound()); } } } @@ -1038,13 +1028,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { } #endif - GeomNode *gnode; - DCAST_INTO_V(gnode, node); - CPT(BoundingVolume) node_bv = gnode->get_bounds(); - const GeometricBoundingVolume *node_gbv = nullptr; - if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - DCAST_INTO_V(node_gbv, node_bv); - } + GeomNode *gnode = (GeomNode *)node; CollisionEntry entry; entry._into_node = gnode; @@ -1070,7 +1054,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { entry, level_state.get_parent_bound(c), level_state.get_local_bound(c), - node_gbv); + level_state.get_node_bound()); } } } @@ -1080,9 +1064,14 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { // If it's a switch node or sequence node, visit just the one visible // child. int index = node->get_visible_child(); - if (index >= 0 && index < node->get_num_children()) { - CollisionLevelStateQuad next_state(level_state, node->get_child(index)); - r_traverse_quad(next_state, pass); + PandaNode::Children children = node->get_children(); + if (index >= 0 && index < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(index); + CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateQuad next_state(level_state, child, mask); + r_traverse_quad(next_state, pass); + } } } else if (node->is_lod_node()) { @@ -1091,16 +1080,20 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { // visit all other levels without GeomNode::get_default_collide_mask(), // allowing only collision with CollisionNodes and special geometry under // higher levels of detail. - int index = DCAST(LODNode, node)->get_lowest_switch(); + int index = ((LODNode *)node)->get_lowest_switch(); PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelStateQuad next_state(level_state, children.get_child(i)); - if (i != index) { - next_state.set_include_mask(next_state.get_include_mask() & - ~GeomNode::get_default_collide_mask()); + const PandaNode::DownConnection &child = children.get_child_connection(i); + CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateQuad next_state(level_state, child, mask); + if (i != index) { + next_state.set_include_mask(next_state.get_include_mask() & + ~GeomNode::get_default_collide_mask()); + } + r_traverse_quad(next_state, pass); } - r_traverse_quad(next_state, pass); } } else { @@ -1108,8 +1101,12 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { PandaNode::Children children = node->get_children(); int num_children = children.get_num_children(); for (int i = 0; i < num_children; ++i) { - CollisionLevelStateQuad next_state(level_state, children.get_child(i)); - r_traverse_quad(next_state, pass); + const PandaNode::DownConnection &child = children.get_child_connection(i); + CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child); + if (!mask.is_zero()) { + CollisionLevelStateQuad next_state(level_state, child, mask); + r_traverse_quad(next_state, pass); + } } } } @@ -1162,10 +1159,7 @@ compare_collider_to_node(CollisionEntry &entry, // CollisionNodes. We are already filtering out tests for a // CollisionNode into itself. CPT(BoundingVolume) solid_bv = entry._into->get_bounds(); - const GeometricBoundingVolume *solid_gbv = nullptr; - if (solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { - solid_gbv = (const GeometricBoundingVolume *)solid_bv.p(); - } + const GeometricBoundingVolume *solid_gbv = solid_bv->as_geometric_bounding_volume(); compare_collider_to_solid(entry, from_node_gbv, solid_gbv); } @@ -1198,14 +1192,13 @@ compare_collider_to_geom_node(CollisionEntry &entry, if (geom != nullptr) { CPT(BoundingVolume) geom_bv = geom->get_bounds(); const GeometricBoundingVolume *geom_gbv = nullptr; - if (num_geoms > 1 && - geom_bv->is_of_type(GeometricBoundingVolume::get_class_type())) { + if (num_geoms > 1) { // Only bother to test against each geom's bounding volume if we // have more than one geom in the node, as a slight optimization. // (If the node contains just one geom, then the node's bounding // volume, which we just tested, is the same as the geom's bounding // volume.) - DCAST_INTO_V(geom_gbv, geom_bv); + geom_gbv = geom_bv->as_geometric_bounding_volume(); } compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv); diff --git a/panda/src/collide/collisionVisualizer.cxx b/panda/src/collide/collisionVisualizer.cxx index 44fefce973..d8e70d11df 100644 --- a/panda/src/collide/collisionVisualizer.cxx +++ b/panda/src/collide/collisionVisualizer.cxx @@ -43,6 +43,7 @@ TypeHandle CollisionVisualizer::_type_handle; CollisionVisualizer:: CollisionVisualizer(const std::string &name) : PandaNode(name), _lock("CollisionVisualizer") { set_cull_callback(); + set_renderable(); // We always want to render the CollisionVisualizer node itself (even if it // doesn't appear to have any geometry within it). @@ -62,6 +63,7 @@ CollisionVisualizer(const CollisionVisualizer ©) : _normal_scale(copy._normal_scale) { set_cull_callback(); + set_renderable(); // We always want to render the CollisionVisualizer node itself (even if it // doesn't appear to have any geometry within it). @@ -145,12 +147,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { bool was_detected = (solid_info._detected_count > 0); PT(PandaNode) node = solid->get_viz(trav, xform_data, !was_detected); if (node != nullptr) { - CullTraverserData next_data(xform_data, node); - // We don't want to inherit the render state from above for these // guys. - next_data._state = get_viz_state(); - trav->traverse(next_data); + trav->traverse_down(xform_data, node, xform_data._net_transform, get_viz_state()); } } @@ -251,18 +250,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool CollisionVisualizer:: -is_renderable() const { - return true; -} - - /** * Writes a brief description of the node to the indicated output stream. * This is invoked by the << operator. It may be overridden in derived diff --git a/panda/src/collide/collisionVisualizer.h b/panda/src/collide/collisionVisualizer.h index cc245e101b..d289b74c0c 100644 --- a/panda/src/collide/collisionVisualizer.h +++ b/panda/src/collide/collisionVisualizer.h @@ -50,7 +50,6 @@ public: // from parent class PandaNode. virtual PandaNode *make_copy() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual void output(std::ostream &out) const; // from parent class CollisionRecorder. diff --git a/panda/src/grutil/pipeOcclusionCullTraverser.cxx b/panda/src/grutil/pipeOcclusionCullTraverser.cxx index 2a7f27a878..62828319b3 100644 --- a/panda/src/grutil/pipeOcclusionCullTraverser.cxx +++ b/panda/src/grutil/pipeOcclusionCullTraverser.cxx @@ -290,7 +290,7 @@ bool PipeOcclusionCullTraverser:: is_in_view(CullTraverserData &data) { _next_query = nullptr; - if (!CullTraverser::is_in_view(data)) { + if (!data.is_in_view(get_camera_mask())) { return false; } if (!_live) { diff --git a/panda/src/grutil/rigidBodyCombiner.cxx b/panda/src/grutil/rigidBodyCombiner.cxx index fb2c3959ab..76ec13a538 100644 --- a/panda/src/grutil/rigidBodyCombiner.cxx +++ b/panda/src/grutil/rigidBodyCombiner.cxx @@ -145,8 +145,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { } // Render the internal scene only--this is the optimized scene. - CullTraverserData next_data(data, _internal_root); - trav->traverse(next_data); + trav->traverse_down(data, _internal_root); // Do not directly render the nodes beneath this node. return false; diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index 4683ee6ba7..4a2281a941 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -107,6 +107,7 @@ ShaderTerrainMesh::ShaderTerrainMesh() : _update_enabled(true), _heightfield_tex(nullptr) { + set_renderable(); } /** @@ -440,14 +441,7 @@ void ShaderTerrainMesh::do_create_chunk_geom() { } /** - * @copydoc PandaNode::is_renderable() - */ -bool ShaderTerrainMesh::is_renderable() const { - return true; -} - -/** - * @copydoc PandaNode::is_renderable() + * @copydoc PandaNode::safe_to_flatten() */ bool ShaderTerrainMesh::safe_to_flatten() const { return false; diff --git a/panda/src/grutil/shaderTerrainMesh.h b/panda/src/grutil/shaderTerrainMesh.h index 9b08e12c0d..a0b8541e85 100644 --- a/panda/src/grutil/shaderTerrainMesh.h +++ b/panda/src/grutil/shaderTerrainMesh.h @@ -85,7 +85,6 @@ PUBLISHED: public: // Methods derived from PandaNode - virtual bool is_renderable() const; virtual bool safe_to_flatten() const; virtual bool safe_to_combine() const; virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); diff --git a/panda/src/parametrics/ropeNode.cxx b/panda/src/parametrics/ropeNode.cxx index 4daf80e8ea..5d49c2dda3 100644 --- a/panda/src/parametrics/ropeNode.cxx +++ b/panda/src/parametrics/ropeNode.cxx @@ -71,6 +71,7 @@ RopeNode(const std::string &name) : PandaNode(name) { set_cull_callback(); + set_renderable(); } /** @@ -162,17 +163,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool RopeNode:: -is_renderable() const { - return true; -} - /** * */ diff --git a/panda/src/parametrics/ropeNode.h b/panda/src/parametrics/ropeNode.h index e4647e304d..4cb8542dc0 100644 --- a/panda/src/parametrics/ropeNode.h +++ b/panda/src/parametrics/ropeNode.h @@ -45,7 +45,6 @@ public: virtual bool safe_to_transform() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; PUBLISHED: enum RenderMode { diff --git a/panda/src/parametrics/sheetNode.cxx b/panda/src/parametrics/sheetNode.cxx index 734082f577..620a35512f 100644 --- a/panda/src/parametrics/sheetNode.cxx +++ b/panda/src/parametrics/sheetNode.cxx @@ -70,6 +70,7 @@ SheetNode(const std::string &name) : PandaNode(name) { set_cull_callback(); + set_renderable(); } /** @@ -140,17 +141,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool SheetNode:: -is_renderable() const { - return true; -} - /** * */ diff --git a/panda/src/parametrics/sheetNode.h b/panda/src/parametrics/sheetNode.h index 97a8d80a71..1d27d840a8 100644 --- a/panda/src/parametrics/sheetNode.h +++ b/panda/src/parametrics/sheetNode.h @@ -43,7 +43,6 @@ public: virtual bool safe_to_transform() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; PUBLISHED: INLINE void set_surface(NurbsSurfaceEvaluator *surface); diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index f1bec0168e..59551133aa 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -30,7 +30,7 @@ NotifyCategoryDecl(pgraph, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH); NotifyCategoryDecl(loader, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH); NotifyCategoryDecl(portal, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH); -extern ConfigVariableBool fake_view_frustum_cull; +extern EXPCL_PANDA_PGRAPH ConfigVariableBool fake_view_frustum_cull; extern ConfigVariableBool clip_plane_cull; extern ConfigVariableBool allow_portal_cull; extern ConfigVariableBool debug_portal_cull; diff --git a/panda/src/pgraph/cullPlanes.cxx b/panda/src/pgraph/cullPlanes.cxx index fafbae6cfa..7cfd67f30b 100644 --- a/panda/src/pgraph/cullPlanes.cxx +++ b/panda/src/pgraph/cullPlanes.cxx @@ -79,26 +79,33 @@ xform(const LMatrix4 &mat) const { * will be added to the state, unless those ClipPlanes are also listed in * off_attrib. */ -CPT(CullPlanes) CullPlanes:: -apply_state(const CullTraverser *trav, const CullTraverserData *data, +void CullPlanes:: +apply_state(CPT(CullPlanes) &planes, + const CullTraverser *trav, const CullTraverserData *data, const ClipPlaneAttrib *net_attrib, const ClipPlaneAttrib *off_attrib, - const OccluderEffect *node_effect) const { + const OccluderEffect *node_effect) { if (net_attrib == nullptr && node_effect == nullptr) { - return this; + return; } PT(CullPlanes) new_planes; - if (get_ref_count() == 1) { - new_planes = (CullPlanes *)this; - } else { - new_planes = new CullPlanes(*this); + if (planes != nullptr) { + if (planes->get_ref_count() == 1) { + new_planes = (CullPlanes *)planes.p(); + } else { + new_planes = new CullPlanes(*planes); + } } CPT(TransformState) net_transform = nullptr; if (net_attrib != nullptr) { int num_on_planes = net_attrib->get_num_on_planes(); + if (num_on_planes > 0 && new_planes.is_null()) { + new_planes = new CullPlanes; + } + for (int i = 0; i < num_on_planes; ++i) { NodePath clip_plane = net_attrib->get_on_plane(i); Planes::const_iterator pi = new_planes->_planes.find(clip_plane); @@ -128,13 +135,17 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data, const Lens *lens = scene->get_lens(); int num_on_occluders = node_effect->get_num_on_occluders(); + if (num_on_occluders > 0 && new_planes.is_null()) { + new_planes = new CullPlanes; + } + for (int i = 0; i < num_on_occluders; ++i) { NodePath occluder = node_effect->get_on_occluder(i); Occluders::const_iterator oi = new_planes->_occluders.find(occluder); if (oi == new_planes->_occluders.end()) { // Here's a new occluder; consider adding it to the list. OccluderNode *occluder_node = DCAST(OccluderNode, occluder.node()); - nassertr(occluder_node->get_num_vertices() == 4, new_planes); + nassertv(occluder_node->get_num_vertices() == 4); CPT(TransformState) occluder_transform = occluder.get_transform(scene->get_cull_center()); @@ -247,7 +258,7 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data, // existing occluder volumes. bool is_enclosed = false; Occluders::const_iterator oi; - for (oi = _occluders.begin(); oi != _occluders.end(); ++oi) { + for (oi = new_planes->_occluders.begin(); oi != new_planes->_occluders.end(); ++oi) { int occluder_result = (*oi).second->contains(occluder_gbv); if ((occluder_result & BoundingVolume::IF_all) != 0) { is_enclosed = true; @@ -292,14 +303,14 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data, if (show_occluder_volumes) { // Draw the frustum for visualization. - nassertr(net_transform != nullptr, new_planes); + nassertv(net_transform != nullptr); trav->draw_bounding_volume(frustum, data->get_internal_transform(trav)); } } } } - return new_planes; + planes = std::move(new_planes); } /** diff --git a/panda/src/pgraph/cullPlanes.h b/panda/src/pgraph/cullPlanes.h index 9480854853..8688e30054 100644 --- a/panda/src/pgraph/cullPlanes.h +++ b/panda/src/pgraph/cullPlanes.h @@ -53,11 +53,12 @@ public: static CPT(CullPlanes) make_empty(); CPT(CullPlanes) xform(const LMatrix4 &mat) const; - CPT(CullPlanes) apply_state(const CullTraverser *trav, - const CullTraverserData *data, - const ClipPlaneAttrib *net_attrib, - const ClipPlaneAttrib *off_attrib, - const OccluderEffect *node_effect) const; + static void apply_state(CPT(CullPlanes) &planes, + const CullTraverser *trav, + const CullTraverserData *data, + const ClipPlaneAttrib *net_attrib, + const ClipPlaneAttrib *off_attrib, + const OccluderEffect *node_effect); CPT(CullPlanes) do_cull(int &result, CPT(RenderState) &state, const GeometricBoundingVolume *node_gbv) const; diff --git a/panda/src/pgraph/cullTraverser.I b/panda/src/pgraph/cullTraverser.I index 98bc781b50..7b243bc7c7 100644 --- a/panda/src/pgraph/cullTraverser.I +++ b/panda/src/pgraph/cullTraverser.I @@ -183,6 +183,14 @@ get_effective_incomplete_render() const { return _effective_incomplete_render; } +/** + * Returns true if fake view frustum culling is active. + */ +INLINE bool CullTraverser:: +get_fake_view_frustum_cull() const { + return _fake_view_frustum_cull; +} + /** * Flushes the PStatCollectors used during traversal. */ @@ -195,53 +203,106 @@ flush_level() { } /** - * This is implemented inline to reduce recursion. + * Calls traverse_down on each child. */ INLINE void CullTraverser:: -do_traverse(CullTraverserData &data) { - if (is_in_view(data)) { - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << "\n" << data.get_node_path() - << " " << data._draw_mask << "\n"; - } - - PandaNodePipelineReader *node_reader = data.node_reader(); - int fancy_bits = node_reader->get_fancy_bits(); - - if (fancy_bits == 0 && data._cull_planes->is_empty()) { - // Nothing interesting in this node; just move on. - - } else { - // Something in this node is worth taking a closer look. - const RenderEffects *node_effects = node_reader->get_effects(); - if (node_effects->has_show_bounds()) { - // If we should show the bounding volume for this node, make it up - // now. - show_bounds(data, node_effects->has_show_tight_bounds()); - } - - data.apply_transform_and_state(this); - - const FogAttrib *fog = (const FogAttrib *) - node_reader->get_state()->get_attrib(FogAttrib::get_class_slot()); - - if (fog != nullptr && fog->get_fog() != nullptr) { - // If we just introduced a FogAttrib here, call adjust_to_camera() - // now. This maybe isn't the perfect time to call it, but it's good - // enough; and at this time we have all the information we need for - // it. - fog->get_fog()->adjust_to_camera(get_camera_transform()); - } - - if (fancy_bits & PandaNode::FB_cull_callback) { - PandaNode *node = data.node(); - if (!node->cull_callback(this, data)) { - return; - } - } - } - - traverse_below(data); +traverse_below(CullTraverserData &data) { + PandaNodePipelineReader *node_reader = data.node_reader(); + PandaNode::Children children = node_reader->get_children(); + node_reader->release(); + int num_children = children.get_num_children(); + for (int i = 0; i < num_children; ++i) { + traverse_down(data, children.get_child_connection(i), data._state); } } + +/** + * Traverses a child of the given node/data. + */ +INLINE void CullTraverser:: +traverse_down(const CullTraverserData &data, PandaNode *node) { + traverse_down(data, node, data._net_transform, data._state); +} + +/** + * Traverses down into the given node/data, overriding the current transform + * and state. Note that the state and transform on the given node are still + * applied. + */ +INLINE void CullTraverser:: +traverse_down(const CullTraverserData &data, PandaNode *node, + const TransformState *net_transform, const RenderState *state) { + PandaNodePipelineReader node_reader(node, data._node_reader.get_current_thread()); + + int result = data.is_child_in_view(node_reader, _camera_mask); + if (result == BoundingVolume::IF_no_intersection) { +#ifdef NDEBUG + return; +#else + if (UNLIKELY(_fake_view_frustum_cull)) { + do_fake_cull(data, node, net_transform, state); + } + return; +#endif + } + + GeometricBoundingVolume *view_frustum = nullptr; + if ((result & BoundingVolume::IF_all) == 0 && !node_reader.is_final()) { + view_frustum = data._view_frustum; + } + + CullTraverserData next_data(data, std::move(node_reader), net_transform, state, view_frustum); + if (data._cull_planes != nullptr) { + const GeometricBoundingVolume *node_gbv = node_reader.get_bounds()->as_geometric_bounding_volume(); + if (!next_data.apply_cull_planes(data._cull_planes, node_gbv)) { + return; + } + } + + do_traverse(next_data); +} + +/** + * Traverses down into the given node, as though it were a child node of the + * current node. + */ +INLINE void CullTraverser:: +traverse_down(const CullTraverserData &data, const PandaNode::DownConnection &child) { + traverse_down(data, child, data._state); +} + +/** + * Traverses down into the given node, as though it were a child node of the + * current node, except that the current state is ignored and replaced with the + * given state. The state on the child node is still applied. + */ +INLINE void CullTraverser:: +traverse_down(const CullTraverserData &data, const PandaNode::DownConnection &child, const RenderState *state) { + int result = data.is_child_in_view(child, _camera_mask); + if (result == BoundingVolume::IF_no_intersection) { +#ifdef NDEBUG + return; +#else + if (UNLIKELY(_fake_view_frustum_cull)) { + do_fake_cull(data, child.get_child(), data._net_transform, state); + } + return; +#endif + } + + PandaNodePipelineReader node_reader(child.get_child(), data._node_reader.get_current_thread()); + + GeometricBoundingVolume *view_frustum = nullptr; + if ((result & BoundingVolume::IF_all) == 0 && !node_reader.is_final()) { + view_frustum = data._view_frustum; + } + + CullTraverserData next_data(data, std::move(node_reader), data._net_transform, state, view_frustum); + + if (data._cull_planes != nullptr && + !next_data.apply_cull_planes(data._cull_planes, child.get_bounds())) { + return; + } + + do_traverse(next_data); +} diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx index eee63e026b..1183707240 100644 --- a/panda/src/pgraph/cullTraverser.cxx +++ b/panda/src/pgraph/cullTraverser.cxx @@ -17,6 +17,7 @@ #include "transformState.h" #include "renderState.h" #include "colorAttrib.h" +#include "textureAttrib.h" #include "renderModeAttrib.h" #include "cullFaceAttrib.h" #include "depthOffsetAttrib.h" @@ -48,14 +49,15 @@ TypeHandle CullTraverser::_type_handle; CullTraverser:: CullTraverser() : _gsg(nullptr), - _current_thread(Thread::get_current_thread()) + _current_thread(Thread::get_current_thread()), + _camera_mask(DrawMask::all_on()), + _fake_view_frustum_cull(false), + _has_tag_state_key(false), + _initial_state(RenderState::make_empty()), + _cull_handler(nullptr), + _portal_clipper(nullptr), + _effective_incomplete_render(false) { - _camera_mask = DrawMask::all_on(); - _has_tag_state_key = false; - _initial_state = RenderState::make_empty(); - _cull_handler = nullptr; - _portal_clipper = nullptr; - _effective_incomplete_render = true; } /** @@ -67,6 +69,7 @@ CullTraverser(const CullTraverser ©) : _current_thread(copy._current_thread), _scene_setup(copy._scene_setup), _camera_mask(copy._camera_mask), + _fake_view_frustum_cull(copy._fake_view_frustum_cull), _has_tag_state_key(copy._has_tag_state_key), _tag_state_key(copy._tag_state_key), _initial_state(copy._initial_state), @@ -99,6 +102,10 @@ set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, _effective_incomplete_render = _gsg->get_incomplete_render() && dr_incomplete_render; _view_frustum = scene_setup->get_view_frustum(); + +#ifndef NDEBUG + _fake_view_frustum_cull = fake_view_frustum_cull; +#endif } /** @@ -132,7 +139,9 @@ traverse(const NodePath &root) { _initial_state, _view_frustum, _current_thread); - traverse(data); + if (data.is_in_view(_camera_mask)) { + do_traverse(data); + } // Finally add the lines to be drawn if (debug_portal_cull) { @@ -143,45 +152,68 @@ traverse(const NodePath &root) { NodePath cull_center = _scene_setup->get_cull_center(); CPT(TransformState) transform = cull_center.get_transform(root); - CullTraverserData my_data(data, portal_viewer._previous); - my_data._net_transform = my_data._net_transform->compose(transform); - traverse(my_data); + CullTraverserData my_data(data, portal_viewer._previous, + data._net_transform->compose(transform), + data._state, data._view_frustum); + if (my_data.is_in_view(_camera_mask)) { + do_traverse(my_data); + } } else { CullTraverserData data(root, TransformState::make_identity(), _initial_state, _view_frustum, _current_thread); - do_traverse(data); + if (data.is_in_view(_camera_mask)) { + do_traverse(data); + } } } /** - * Traverses from the next node with the given data, which has been - * constructed with the node but has not yet been converted into the node's - * space. + * Internal method called by traverse() and traverse_down(). Traverses the + * given node, assuming it has already been checked with is_in_view(). */ void CullTraverser:: -traverse(CullTraverserData &data) { - do_traverse(data); -} +do_traverse(CullTraverserData &data) { +#ifndef NDEBUG + if (UNLIKELY(pgraph_cat.is_spam())) { + pgraph_cat.spam() + << "\n" << data.get_node_path() + << " " << data._draw_mask << "\n"; + } +#endif -/** - * Traverses all the children of the indicated node, with the given data, - * which has been converted into the node's space. - */ -void CullTraverser:: -traverse_below(CullTraverserData &data) { - _nodes_pcollector.add_level(1); - PandaNodePipelineReader *node_reader = data.node_reader(); PandaNode *node = data.node(); + PandaNodePipelineReader *node_reader = data.node_reader(); + int fancy_bits = node_reader->get_fancy_bits(); - if (!data.is_this_node_hidden(_camera_mask)) { + if ((fancy_bits & ~PandaNode::FB_renderable) == 0 && data._cull_planes == nullptr) { + // Nothing interesting in this node; just move on. + } + else { + // Something in this node is worth taking a closer look. + if (fancy_bits & PandaNode::FB_show_bounds) { + // If we should show the bounding volume for this node, make it up + // now. + show_bounds(data, (fancy_bits & PandaNode::FB_show_tight_bounds) != 0); + } + + data.apply_transform_and_state(this); + + if (fancy_bits & PandaNode::FB_cull_callback) { + if (!node->cull_callback(this, data)) { + return; + } + } + } + + if ((fancy_bits & PandaNode::FB_renderable) != 0 && + !data.is_this_node_hidden(_camera_mask)) { node->add_for_draw(this, data); // Check for a decal effect. - const RenderEffects *node_effects = node_reader->get_effects(); - if (node_effects->has_decal()) { + if (fancy_bits & PandaNode::FB_decal) { // If we *are* implementing decals with DepthOffsetAttribs, apply it // now, so that each child of this node gets offset by a tiny amount. data._state = data._state->compose(get_depth_offset_state()); @@ -195,22 +227,15 @@ traverse_below(CullTraverserData &data) { } } + _nodes_pcollector.add_level(1); + // Now visit all the node's children. PandaNode::Children children = node_reader->get_children(); node_reader->release(); int num_children = children.get_num_children(); - if (!node->has_selective_visibility()) { - for (int i = 0; i < num_children; ++i) { - CullTraverserData next_data(data, children.get_child(i)); - do_traverse(next_data); - } - } else { - int i = node->get_first_visible_child(); - while (i < num_children) { - CullTraverserData next_data(data, children.get_child(i)); - do_traverse(next_data); - i = node->get_next_visible_child(i); - } + for (int i = 0; i < num_children; ++i) { + const PandaNode::DownConnection &child = children.get_child_connection(i); + traverse_down(data, child, data._state); } } @@ -246,13 +271,28 @@ draw_bounding_volume(const BoundingVolume *vol, } /** - * Returns true if the current node is fully or partially within the viewing - * area and should be drawn, or false if it (and all of its children) should - * be pruned. + * Implements fake-view-frustum-cull. */ -bool CullTraverser:: -is_in_view(CullTraverserData &data) { - return data.is_in_view(_camera_mask); +void CullTraverser:: +do_fake_cull(const CullTraverserData &data, PandaNode *child, + const TransformState *net_transform, const RenderState *state) { +#ifndef NDEBUG + // Once someone asks for this pointer, we hold its reference count and never + // free it. + static CPT(RenderState) fake_view_frustum_cull_state; + if (fake_view_frustum_cull_state == nullptr) { + fake_view_frustum_cull_state = RenderState::make + (ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)), + TextureAttrib::make_all_off(), + RenderModeAttrib::make(RenderModeAttrib::M_wireframe), + RenderState::get_max_priority()); + } + + CullTraverserData next_data(data, child, net_transform, + state->compose(fake_view_frustum_cull_state), + nullptr); + do_traverse(next_data); +#endif } /** @@ -503,7 +543,7 @@ compute_point(const BoundingSphere *sphere, * Returns a RenderState for rendering the outside surfaces of the bounding * volume visualizations. */ -CPT(RenderState) CullTraverser:: +const RenderState *CullTraverser:: get_bounds_outer_viz_state() { // Once someone asks for this pointer, we hold its reference count and never // free it. @@ -521,7 +561,7 @@ get_bounds_outer_viz_state() { * Returns a RenderState for rendering the inside surfaces of the bounding * volume visualizations. */ -CPT(RenderState) CullTraverser:: +const RenderState *CullTraverser:: get_bounds_inner_viz_state() { // Once someone asks for this pointer, we hold its reference count and never // free it. @@ -538,7 +578,7 @@ get_bounds_inner_viz_state() { /** * Returns a RenderState for increasing the DepthOffset by one. */ -CPT(RenderState) CullTraverser:: +const RenderState *CullTraverser:: get_depth_offset_state() { // Once someone asks for this pointer, we hold its reference count and never // free it. diff --git a/panda/src/pgraph/cullTraverser.h b/panda/src/pgraph/cullTraverser.h index b8ecbe084b..a243a165cc 100644 --- a/panda/src/pgraph/cullTraverser.h +++ b/panda/src/pgraph/cullTraverser.h @@ -76,22 +76,33 @@ PUBLISHED: INLINE PortalClipper *get_portal_clipper() const; INLINE bool get_effective_incomplete_render() const; - - void traverse(const NodePath &root); - void traverse(CullTraverserData &data); - virtual void traverse_below(CullTraverserData &data); - - virtual void end_traverse(); + INLINE bool get_fake_view_frustum_cull() const; INLINE static void flush_level(); + void traverse(const NodePath &root); + void do_traverse(CullTraverserData &data); + INLINE void traverse_below(CullTraverserData &data); + + virtual void end_traverse(); + void draw_bounding_volume(const BoundingVolume *vol, const TransformState *internal_transform) const; -protected: - INLINE void do_traverse(CullTraverserData &data); +public: + INLINE void traverse_down(const CullTraverserData &data, PandaNode *child); + INLINE void traverse_down(const CullTraverserData &data, PandaNode *child, + const TransformState *net_transform, + const RenderState *state); + INLINE void traverse_down(const CullTraverserData &data, + const PandaNode::DownConnection &child); + INLINE void traverse_down(const CullTraverserData &data, + const PandaNode::DownConnection &child, + const RenderState *state); - virtual bool is_in_view(CullTraverserData &data); + void do_fake_cull(const CullTraverserData &data, PandaNode *child, + const TransformState *net_transform, + const RenderState *state); public: // Statistics @@ -106,14 +117,15 @@ private: PT(Geom) make_tight_bounds_viz(PandaNode *node) const; static LVertex compute_point(const BoundingSphere *sphere, PN_stdfloat latitude, PN_stdfloat longitude); - static CPT(RenderState) get_bounds_outer_viz_state(); - static CPT(RenderState) get_bounds_inner_viz_state(); - static CPT(RenderState) get_depth_offset_state(); + static const RenderState *get_bounds_outer_viz_state(); + static const RenderState *get_bounds_inner_viz_state(); + static const RenderState *get_depth_offset_state(); GraphicsStateGuardianBase *_gsg; Thread *_current_thread; PT(SceneSetup) _scene_setup; DrawMask _camera_mask; + bool _fake_view_frustum_cull; bool _has_tag_state_key; std::string _tag_state_key; CPT(RenderState) _initial_state; diff --git a/panda/src/pgraph/cullTraverserData.I b/panda/src/pgraph/cullTraverserData.I index cc5126dc07..ff456b01cb 100644 --- a/panda/src/pgraph/cullTraverserData.I +++ b/panda/src/pgraph/cullTraverserData.I @@ -17,22 +17,18 @@ INLINE CullTraverserData:: CullTraverserData(const NodePath &start, const TransformState *net_transform, - const RenderState *state, + CPT(RenderState) state, GeometricBoundingVolume *view_frustum, Thread *current_thread) : _next(nullptr), _start(start._head), _node_reader(start.node(), current_thread), _net_transform(net_transform), - _state(state), + _state(std::move(state)), _view_frustum(view_frustum), - _cull_planes(CullPlanes::make_empty()), _draw_mask(DrawMask::all_on()), _portal_depth(0) { - // Only update the bounding volume if we're going to end up needing it. - bool check_bounds = (view_frustum != nullptr); - _node_reader.check_cached(check_bounds); } /** @@ -40,24 +36,46 @@ CullTraverserData(const NodePath &start, * node down in the traversal. */ INLINE CullTraverserData:: -CullTraverserData(const CullTraverserData &parent, PandaNode *child) : +CullTraverserData(const CullTraverserData &parent, PandaNode *child, + const TransformState *net_transform, + CPT(RenderState) state, + GeometricBoundingVolume *view_frustum) : _next(&parent), #ifdef _DEBUG _start(nullptr), #endif _node_reader(child, parent._node_reader.get_current_thread()), - _net_transform(parent._net_transform), - _state(parent._state), - _view_frustum(parent._view_frustum), - _cull_planes(parent._cull_planes), + _net_transform(net_transform), + _state(std::move(state)), + _view_frustum(view_frustum), + _instances(parent._instances), + _draw_mask(parent._draw_mask), + _portal_depth(parent._portal_depth) +{ +} + +/** + * This constructor creates a CullTraverserData object that reflects the next + * node down in the traversal. + */ +INLINE CullTraverserData:: +CullTraverserData(const CullTraverserData &parent, + PandaNodePipelineReader &&node_reader, + const TransformState *net_transform, + CPT(RenderState) state, + GeometricBoundingVolume *view_frustum) : + _next(&parent), +#ifdef _DEBUG + _start(nullptr), +#endif + _node_reader(std::move(node_reader)), + _net_transform(net_transform), + _state(std::move(state)), + _view_frustum(view_frustum), _instances(parent._instances), _draw_mask(parent._draw_mask), _portal_depth(parent._portal_depth) { - // Only update the bounding volume if we're going to end up needing it. - bool check_bounds = !_cull_planes->is_empty() || - (_view_frustum != nullptr); - _node_reader.check_cached(check_bounds); } /** @@ -125,32 +143,82 @@ get_net_transform(const CullTraverser *) const { } /** - * Returns true if the current node is within the view frustum, false - * otherwise. If the node's bounding volume falls completely within the view - * frustum, this will also reset the view frustum pointer, saving some work - * for future nodes. + * Returns intersection flags if the current node may be within the view + * frustum, 0 otherwise. */ -INLINE bool CullTraverserData:: -is_in_view(const DrawMask &camera_mask) { - if (_node_reader.get_transform()->is_invalid()) { - // If the transform is invalid, forget it. - return false; - } +INLINE int CullTraverserData:: +is_in_view(const DrawMask &camera_mask) const { + bool check_bounds = (_view_frustum != nullptr || _cull_planes != nullptr); + _node_reader.check_cached(check_bounds); if (!_node_reader.compare_draw_mask(_draw_mask, camera_mask)) { // If there are no draw bits in common with the camera, the node is out. - return false; + return BoundingVolume::IF_no_intersection; } - if (_view_frustum == nullptr && - _cull_planes->is_empty()) { - // If the transform is valid, but we don't have a frustum or any clip - // planes or occluders, it's always in. - return true; + if (_node_reader.get_transform()->is_invalid()) { + // If the transform is invalid, forget it. + return BoundingVolume::IF_no_intersection; } - // Otherwise, compare the bounding volume to the frustum. - return is_in_view_impl(); + if (_view_frustum != nullptr) { + const GeometricBoundingVolume *node_gbv = _node_reader.get_bounds()->as_geometric_bounding_volume(); + nassertr(node_gbv != nullptr, BoundingVolume::IF_no_intersection); + + return _view_frustum->contains(node_gbv); + } + + return BoundingVolume::IF_possible; +} + +/** + * Returns intersection flags if the given child of the current node may be + * within the view frustum, 0 otherwise. + */ +INLINE int CullTraverserData:: +is_child_in_view(const PandaNodePipelineReader &node_reader, const DrawMask &camera_mask) const { + bool check_bounds = (_view_frustum != nullptr || _cull_planes != nullptr); + node_reader.check_cached(check_bounds); + + if (!node_reader.compare_draw_mask(_draw_mask, camera_mask)) { + // If there are no draw bits in common with the camera, the node is out. + return BoundingVolume::IF_no_intersection; + } + + if (_node_reader.get_transform()->is_invalid()) { + // If the transform is invalid, forget it. + return BoundingVolume::IF_no_intersection; + } + + if (_view_frustum != nullptr) { + const GeometricBoundingVolume *node_gbv = node_reader.get_bounds()->as_geometric_bounding_volume(); + nassertr(node_gbv != nullptr, BoundingVolume::IF_no_intersection); + + return _view_frustum->contains(node_gbv); + } + return BoundingVolume::IF_possible; +} + +/** + * Returns intersection flags if the given child of the current node may be + * within the view frustum, 0 otherwise. + */ +INLINE int CullTraverserData:: +is_child_in_view(const PandaNode::DownConnection &child, const DrawMask &camera_mask) const { + // No need to call check_cached here, since we presumably already called it + // on the parent. + if (!child.compare_draw_mask(_draw_mask, camera_mask)) { + // If there are no draw bits in common with the camera, the node is out. + return BoundingVolume::IF_no_intersection; + } + + if (_view_frustum != nullptr) { + const GeometricBoundingVolume *node_gbv = child.get_bounds(); + nassertr(node_gbv != nullptr, BoundingVolume::IF_no_intersection); + + return _view_frustum->contains(node_gbv); + } + return BoundingVolume::IF_possible; } /** diff --git a/panda/src/pgraph/cullTraverserData.cxx b/panda/src/pgraph/cullTraverserData.cxx index 12f71ef12f..cd1a535899 100644 --- a/panda/src/pgraph/cullTraverserData.cxx +++ b/panda/src/pgraph/cullTraverserData.cxx @@ -15,9 +15,6 @@ #include "cullTraverser.h" #include "config_pgraph.h" #include "pandaNode.h" -#include "colorAttrib.h" -#include "textureAttrib.h" -#include "renderModeAttrib.h" #include "clipPlaneAttrib.h" #include "boundingPlane.h" #include "billboardEffect.h" @@ -64,10 +61,28 @@ apply_transform_and_state(CullTraverser *trav) { } if (clip_plane_cull) { - _cull_planes = _cull_planes->apply_state(trav, this, - (const ClipPlaneAttrib *)node_state->get_attrib(ClipPlaneAttrib::get_class_slot()), - (const ClipPlaneAttrib *)_node_reader.get_off_clip_planes(), - (const OccluderEffect *)node_effects->get_effect(OccluderEffect::get_class_type())); + const ClipPlaneAttrib *cpa = (const ClipPlaneAttrib *) + node_state->get_attrib(ClipPlaneAttrib::get_class_slot()); + const OccluderEffect *occluders = (const OccluderEffect *) + node_effects->get_effect(OccluderEffect::get_class_type()); + + if (cpa != nullptr || occluders != nullptr) { + CullPlanes::apply_state(_cull_planes, trav, this, cpa, + (const ClipPlaneAttrib *)_node_reader.get_off_clip_planes(), + occluders); + } + } + + const FogAttrib *fog_attr; + if (node_state->get_attrib(fog_attr)) { + Fog *fog = fog_attr->get_fog(); + if (fog != nullptr) { + // If we just introduced a FogAttrib here, call adjust_to_camera() + // now. This maybe isn't the perfect time to call it, but it's good + // enough; and at this time we have all the information we need for + // it. + fog->adjust_to_camera(trav->get_camera_transform()); + } } } @@ -88,35 +103,90 @@ apply_transform(const TransformState *node_transform) { _net_transform = _net_transform->compose(node_transform); - if ((_view_frustum != nullptr) || - (!_cull_planes->is_empty())) { + if (_view_frustum != nullptr || _cull_planes != nullptr) { // We need to move the viewing frustums into the node's coordinate space // by applying the node's inverse transform. - if (node_transform->is_singular()) { - // But we can't invert a singular transform! Instead of trying, we'll - // just give up on frustum culling from this point down. - _view_frustum = nullptr; - _cull_planes = CullPlanes::make_empty(); - - } else { - CPT(TransformState) inv_transform = - node_transform->invert_compose(TransformState::make_identity()); - + const LMatrix4 *inverse_mat = node_transform->get_inverse_mat(); + if (inverse_mat != nullptr) { // Copy the bounding volumes for the frustums so we can transform // them. if (_view_frustum != nullptr) { _view_frustum = _view_frustum->make_copy()->as_geometric_bounding_volume(); nassertv(_view_frustum != nullptr); - _view_frustum->xform(inv_transform->get_mat()); + _view_frustum->xform(*inverse_mat); } - _cull_planes = _cull_planes->xform(inv_transform->get_mat()); + if (_cull_planes != nullptr) { + _cull_planes = _cull_planes->xform(*inverse_mat); + } + } + else { + // But we can't invert a singular transform! Instead of trying, we'll + // just give up on frustum culling from this point down. + pgraph_cat.warning() + << "Singular transformation detected on node: " << get_node_path() << "\n"; + _view_frustum = nullptr; + _cull_planes = nullptr; } } } } +/** + * Returns intersection flags if any of the children under the current node are + * in view if first transformed by the given transform, false otherwise. + */ +bool CullTraverserData:: +is_instance_in_view(const TransformState *instance_transform, const DrawMask &camera_mask) const { + PT(GeometricBoundingVolume) view_frustum_p; + const GeometricBoundingVolume *view_frustum = nullptr; + + if (_view_frustum != nullptr) { + if (!instance_transform->is_identity()) { + // We need to move the viewing frustums into the node's coordinate space + // by applying the node's inverse transform. + const LMatrix4 *inverse_mat = instance_transform->get_inverse_mat(); + if (inverse_mat != nullptr) { + // Copy the bounding volumes for the frustums so we can transform them. + view_frustum_p = _view_frustum->make_copy()->as_geometric_bounding_volume(); + nassertr(view_frustum_p != nullptr, false); + + view_frustum_p->xform(*inverse_mat); + view_frustum = view_frustum_p; + } else { + // Don't render instances with a singular transformation. + return false; + } + } else { + view_frustum = _view_frustum; + } + } + + PandaNode::Children children = _node_reader.get_children(); + int num_children = children.get_num_children(); + for (int i = 0; i < num_children; ++i) { + const PandaNode::DownConnection &child = children.get_child_connection(i); + + if (!child.compare_draw_mask(_draw_mask, camera_mask)) { + // If there are no draw bits in common with the camera, the node is out. + continue; + } + + if (view_frustum == nullptr) { + return true; + } + + const GeometricBoundingVolume *node_gbv = child.get_bounds(); + nassertd(node_gbv != nullptr) continue; + + if (view_frustum->contains(node_gbv)) { + return true; + } + } + return false; +} + /** * The private, recursive implementation of get_node_path(), this returns the * NodePathComponent representing the NodePath. @@ -152,127 +222,37 @@ r_get_node_path() const { } /** - * The private implementation of is_in_view(). + * Applies the cull planes. Returns true if the node should still be rendered, + * false if it should be culled. */ bool CullTraverserData:: -is_in_view_impl() { - const GeometricBoundingVolume *node_gbv = nullptr; - - if (_view_frustum != nullptr) { - node_gbv = _node_reader.get_bounds()->as_geometric_bounding_volume(); - nassertr(node_gbv != nullptr, false); - - int result = _view_frustum->contains(node_gbv); - - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << get_node_path() << " cull result = " << std::hex << result << std::dec << "\n"; - } - - if (result == BoundingVolume::IF_no_intersection) { - // No intersection at all. Cull. -#ifdef NDEBUG - return false; -#else - if (!fake_view_frustum_cull) { - return false; - } - - // If we have fake view-frustum culling enabled, instead of actually - // culling an object we simply force it to be drawn in red wireframe. - _view_frustum = nullptr; - _state = _state->compose(get_fake_view_frustum_cull_state()); -#endif - - } else if ((result & BoundingVolume::IF_all) != 0) { - // The node and its descendents are completely enclosed within the - // frustum. No need to cull further. - _view_frustum = nullptr; - - } else { - // The node is partially, but not completely, within the viewing - // frustum. - if (_node_reader.is_final()) { - // Normally we'd keep testing child bounding volumes as we continue - // down. But this node has the "final" flag, so the user is claiming - // that there is some important reason we should consider everything - // visible at this point. So be it. - _view_frustum = nullptr; - } - } - } - - if (!_cull_planes->is_empty()) { - if (node_gbv == nullptr) { - node_gbv = _node_reader.get_bounds()->as_geometric_bounding_volume(); - nassertr(node_gbv != nullptr, false); - } - +apply_cull_planes(const CullPlanes *planes, const GeometricBoundingVolume *node_gbv) { + if (!planes->is_empty()) { // Also cull against the current clip planes. int result; - _cull_planes = _cull_planes->do_cull(result, _state, node_gbv); + CPT(CullPlanes) new_planes = planes->do_cull(result, _state, node_gbv); if (pgraph_cat.is_spam()) { pgraph_cat.spam() << get_node_path() << " cull planes cull result = " << std::hex << result << std::dec << "\n"; - _cull_planes->write(pgraph_cat.spam(false)); - } - - if (_node_reader.is_final()) { - // Even though the node may be partially within the clip planes, do no - // more culling against them below this node. - _cull_planes = CullPlanes::make_empty(); - - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << get_node_path() << " is_final, cull planes disabled, state:\n"; - _state->write(pgraph_cat.spam(false), 2); - } + new_planes->write(pgraph_cat.spam(false)); } if (result == BoundingVolume::IF_no_intersection) { // No intersection at all. Cull. -#ifdef NDEBUG return false; -#else - if (!fake_view_frustum_cull) { - return false; - } - _cull_planes = CullPlanes::make_empty(); - _state = _state->compose(get_fake_view_frustum_cull_state()); -#endif - - } else if ((result & BoundingVolume::IF_all) != 0) { - // The node and its descendents are completely in front of all of the + } + else if ((result & BoundingVolume::IF_all) != 0) { + // The node and its descendants are completely in front of all of the // clip planes and occluders. The do_cull() call should therefore have // removed all of the clip planes and occluders. - nassertr(_cull_planes->is_empty(), true); + nassertr(new_planes->is_empty(), true); + } + else if (!_node_reader.is_final() && !new_planes->is_empty()) { + _cull_planes = std::move(new_planes); } } return true; } - -/** - * Returns a RenderState for rendering stuff in red wireframe, strictly for - * the fake_view_frustum_cull effect. - */ -const RenderState *CullTraverserData:: -get_fake_view_frustum_cull_state() { -#ifdef NDEBUG - return nullptr; -#else - // Once someone asks for this pointer, we hold its reference count and never - // free it. - static CPT(RenderState) state; - if (state == nullptr) { - state = RenderState::make - (ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)), - TextureAttrib::make_all_off(), - RenderModeAttrib::make(RenderModeAttrib::M_wireframe), - RenderState::get_max_priority()); - } - return state; -#endif -} diff --git a/panda/src/pgraph/cullTraverserData.h b/panda/src/pgraph/cullTraverserData.h index cdb8dd2a94..9fe4755e34 100644 --- a/panda/src/pgraph/cullTraverserData.h +++ b/panda/src/pgraph/cullTraverserData.h @@ -42,11 +42,19 @@ class EXPCL_PANDA_PGRAPH CullTraverserData { public: INLINE CullTraverserData(const NodePath &start, const TransformState *net_transform, - const RenderState *state, + CPT(RenderState) state, GeometricBoundingVolume *view_frustum, Thread *current_thread); INLINE CullTraverserData(const CullTraverserData &parent, - PandaNode *child); + PandaNode *child, + const TransformState *net_transform, + CPT(RenderState) state, + GeometricBoundingVolume *view_frustum); + INLINE CullTraverserData(const CullTraverserData &parent, + PandaNodePipelineReader &&node_reader, + const TransformState *net_transform, + CPT(RenderState) state, + GeometricBoundingVolume *view_frustum); PUBLISHED: INLINE PandaNode *node() const; @@ -62,14 +70,21 @@ PUBLISHED: INLINE CPT(TransformState) get_internal_transform(const CullTraverser *trav) const; INLINE const TransformState *get_net_transform(const CullTraverser *trav) const; - INLINE bool is_in_view(const DrawMask &camera_mask); + INLINE int is_in_view(const DrawMask &camera_mask) const; INLINE bool is_this_node_hidden(const DrawMask &camera_mask) const; + bool apply_cull_planes(const CullPlanes *planes, const GeometricBoundingVolume *node_gbv); + void apply_transform_and_state(CullTraverser *trav); void apply_transform(const TransformState *node_transform); MAKE_PROPERTY(node_path, get_node_path); +public: + bool is_instance_in_view(const TransformState *instance_transform, const DrawMask &camera_mask) const; + INLINE int is_child_in_view(const PandaNodePipelineReader &node_reader, const DrawMask &camera_mask) const; + INLINE int is_child_in_view(const PandaNode::DownConnection &child, const DrawMask &camera_mask) const; + private: // We store a chain leading all the way to the root, so that we can compose // a NodePath. We may be able to eliminate this requirement in the future. @@ -88,9 +103,6 @@ public: private: PT(NodePathComponent) r_get_node_path() const; - - bool is_in_view_impl(); - static const RenderState *get_fake_view_frustum_cull_state(); }; /* okcircular */ diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index c9f379addc..b7e9842beb 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -59,6 +59,8 @@ GeomNode(const std::string &name) : // GeomNodes have a certain set of bits on by default. set_into_collide_mask(get_default_collide_mask()); + + set_renderable(); } /** @@ -490,17 +492,6 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, return next_transform; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool GeomNode:: -is_renderable() const { - return true; -} - /** * Adds the node's contents to the CullResult we are building up during the * cull traversal, so that it will be drawn at render time. For most nodes @@ -569,7 +560,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { // Cull this Geom. continue; } - if (!data._cull_planes->is_empty()) { + if (data._cull_planes != nullptr) { // Also cull the Geom against the cull planes. CPT(BoundingVolume) geom_volume = geom->get_bounds(current_thread); const GeometricBoundingVolume *geom_gbv = diff --git a/panda/src/pgraph/geomNode.h b/panda/src/pgraph/geomNode.h index 1402d07b5a..c6e28516cb 100644 --- a/panda/src/pgraph/geomNode.h +++ b/panda/src/pgraph/geomNode.h @@ -50,7 +50,6 @@ public: bool &found_any, const TransformState *transform, Thread *current_thread) const; - virtual bool is_renderable() const; virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); virtual CollideMask get_legal_collide_mask() const; diff --git a/panda/src/pgraph/instancedNode.cxx b/panda/src/pgraph/instancedNode.cxx index 567b31316e..c1a51281ea 100644 --- a/panda/src/pgraph/instancedNode.cxx +++ b/panda/src/pgraph/instancedNode.cxx @@ -218,32 +218,29 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { instances = new_list; } - if (data._view_frustum != nullptr || !data._cull_planes->is_empty()) { - // Culling is on, so we need to figure out which instances are visible. - Children children = data.node_reader()->get_children(); - data.node_reader()->release(); - - // Keep track of which instances should be culled away. + if (data._view_frustum != nullptr || data._cull_planes != nullptr) { + // Culling is on, so we need to figure out which instances should be culled. BitArray culled_instances; culled_instances.set_range(0, instances->size()); for (size_t ii = 0; ii < instances->size(); ++ii) { - CullTraverserData instance_data(data); - instance_data.apply_transform((*instances)[ii].get_transform()); + if (data.is_instance_in_view((*instances)[ii].get_transform(), trav->get_camera_mask())) { + culled_instances.clear_bit(ii); + } + } - for (size_t ci = 0; ci < children.size(); ++ci) { - CullTraverserData child_data(instance_data, children.get_child(ci)); - if (child_data.is_in_view(trav->get_camera_mask())) { - // Yep, the instance is in view. - culled_instances.clear_bit(ii); - break; - } + if (!culled_instances.is_zero() && trav->get_fake_view_frustum_cull()) { + // The culled instances are drawn with the fake-view-frustum-cull effect. + data._instances = instances->without(culled_instances ^ BitArray::range(0, instances->size())); + + Children children = data.node_reader()->get_children(); + int num_children = children.get_num_children(); + for (int i = 0; i < num_children; ++i) { + trav->do_fake_cull(data, children.get_child(i), data._net_transform, data._state); } } instances = instances->without(culled_instances); - } else { - data.node_reader()->release(); } if (instances->empty()) { @@ -259,15 +256,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { data._cull_planes = CullPlanes::make_empty(); return true; - - /* - for (const InstanceList::Instance &instance : *instances) { - CullTraverserData instance_data(data); - instance_data.apply_transform(instance.get_transform()); - trav->traverse_below(instance_data); - } - return false; - */ } /** diff --git a/panda/src/pgraph/occluderNode.cxx b/panda/src/pgraph/occluderNode.cxx index 09f8bbd038..a1db42c3c3 100644 --- a/panda/src/pgraph/occluderNode.cxx +++ b/panda/src/pgraph/occluderNode.cxx @@ -55,6 +55,7 @@ OccluderNode(const std::string &name) : PandaNode(name) { set_cull_callback(); + set_renderable(); // OccluderNodes are hidden by default. set_overall_hidden(true); set_double_sided(false); @@ -156,18 +157,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool OccluderNode:: -is_renderable() const { - return true; -} - - /** * Writes a brief description of the node to the indicated output stream. * This is invoked by the << operator. It may be overridden in derived diff --git a/panda/src/pgraph/occluderNode.h b/panda/src/pgraph/occluderNode.h index ebc5db9bc3..484c38753d 100644 --- a/panda/src/pgraph/occluderNode.h +++ b/panda/src/pgraph/occluderNode.h @@ -42,7 +42,6 @@ public: virtual void xform(const LMatrix4 &mat); virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual void output(std::ostream &out) const; diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index 8cbea5c871..bf961413c5 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -773,6 +773,59 @@ get_sort() const { return _sort; } +/** + * Returns the net collide mask of this child. Requires that update_cached() + * has been called on the parent. + */ +INLINE CollideMask PandaNode::DownConnection:: +get_net_collide_mask() const { + return _net_collide_mask; +} + +/** + * Returns the external bounding volume of this child. Requires that + * update_cached() has been called on the parent. + */ +INLINE const GeometricBoundingVolume *PandaNode::DownConnection:: +get_bounds() const { + return _external_bounds; +} + +/** + * Compares the running draw mask computed during a traversal with this child's + * net draw masks. Returns true if the child should be traversed into, or + * false if there is nothing at this level or below that will be visible to + * the indicated camera_mask. + */ +INLINE bool PandaNode::DownConnection:: +compare_draw_mask(DrawMask running_draw_mask, DrawMask camera_mask) const { + // As a special case, if net_draw_show_mask is all 0, it means either that + // all nodes under this node are hidden to all cameras, or that none of them + // are renderable nodes (or some combination). In either case, we might as + // well short-circuit. + if (_net_draw_show_mask.is_zero()) { + return false; + } + + DrawMask net_draw_control_mask, net_draw_show_mask; + net_draw_control_mask = _net_draw_control_mask; + net_draw_show_mask = _net_draw_show_mask; + + // Now the bits that are not in net_draw_control_mask--that is, those bits + // that are not changed by any of the nodes at this level and below--are + // taken from running_draw_mask, which is inherited from above. On the + // other hand, the bits that *are* in net_draw_control_mask--those bits that + // are changed by any of the nodes at this level and below--are taken from + // net_draw_show_mask, which is propagated upwards from below. + + // This way, we will traverse into this node if it has any children which + // want to be visited by the traversal, but we will avoid traversing into it + // if all of its children are hidden to this camera. + DrawMask compare_mask = (running_draw_mask & ~net_draw_control_mask) | (net_draw_show_mask & net_draw_control_mask); + + return !((compare_mask & PandaNode::_overall_bit).is_zero()) && !((compare_mask & camera_mask).is_zero()); +} + /** * */ @@ -1351,6 +1404,18 @@ get_child(int n) const { return (*down)[n].get_child(); } +/** + * Returns the nth child node of this node. See get_num_children(). Also see + * get_children(), if your intention is to iterate through the complete list + * of children; get_children() is preferable in this case. + */ +INLINE const PandaNode::DownConnection &PandaNodePipelineReader:: +get_child_connection(int n) const { + CPT(PandaNode::Down) down = _cdata->get_down(); + //nassertr(n >= 0 && n < (int)down->size(), nullptr); + return (*down)[n]; +} + /** * Returns the sort index of the nth child node of this node (that is, the * number that was passed to add_child()). See get_num_children(). diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index fabae9a272..9e13b3edfe 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -27,6 +27,8 @@ #include "config_mathutil.h" #include "lightReMutexHolder.h" #include "graphicsStateGuardianBase.h" +#include "decalEffect.h" +#include "showBoundsEffect.h" using std::ostream; using std::ostringstream; @@ -387,46 +389,6 @@ cull_callback(CullTraverser *, CullTraverserData &) { return true; } -/** - * Should be overridden by derived classes to return true if this kind of node - * has some restrictions on the set of children that should be rendered. Node - * with this property include LODNodes, SwitchNodes, and SequenceNodes. - * - * If this function returns true, get_first_visible_child() and - * get_next_visible_child() will be called to walk through the list of - * children during cull, instead of iterating through the entire list. This - * method is called after cull_callback(), so cull_callback() may be - * responsible for the decisions as to which children are visible at the - * moment. - */ -bool PandaNode:: -has_selective_visibility() const { - return false; -} - -/** - * Returns the index number of the first visible child of this node, or a - * number >= get_num_children() if there are no visible children of this node. - * This is called during the cull traversal, but only if - * has_selective_visibility() has already returned true. See - * has_selective_visibility(). - */ -int PandaNode:: -get_first_visible_child() const { - return 0; -} - -/** - * Returns the index number of the next visible child of this node following - * the indicated child, or a number >= get_num_children() if there are no more - * visible children of this node. See has_selective_visibility() and - * get_first_visible_child(). - */ -int PandaNode:: -get_next_visible_child(int n) const { - return n + 1; -} - /** * Should be overridden by derived classes to return true if this kind of node * has the special property that just one of its children is visible at any @@ -460,7 +422,8 @@ get_visible_child() const { */ bool PandaNode:: is_renderable() const { - return false; + CDReader cdata(_cycler); + return (cdata->_fancy_bits & FB_renderable) != 0; } /** @@ -1003,7 +966,15 @@ set_effect(const RenderEffect *effect) { OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); cdata->_effects = cdata->_effects->add_effect(effect); + cdata->set_fancy_bit(FB_effects, true); + if (effect->get_type() == DecalEffect::get_class_type()) { + cdata->set_fancy_bit(FB_decal, true); + } + else if (effect->get_type() == ShowBoundsEffect::get_class_type()) { + cdata->set_fancy_bit(FB_show_bounds, true); + cdata->set_fancy_bit(FB_show_tight_bounds, ((const ShowBoundsEffect *)effect)->get_tight()); + } } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); mark_bam_modified(); @@ -1018,7 +989,14 @@ clear_effect(TypeHandle type) { OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); cdata->_effects = cdata->_effects->remove_effect(type); + cdata->set_fancy_bit(FB_effects, !cdata->_effects->is_empty()); + if (type == DecalEffect::get_class_type()) { + cdata->set_fancy_bit(FB_decal, false); + } + else if (type == ShowBoundsEffect::get_class_type()) { + cdata->set_fancy_bit(FB_show_bounds | FB_show_tight_bounds, false); + } } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); mark_bam_modified(); @@ -1067,6 +1045,9 @@ set_effects(const RenderEffects *effects, Thread *current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); cdata->_effects = effects; cdata->set_fancy_bit(FB_effects, !effects->is_empty()); + cdata->set_fancy_bit(FB_decal, effects->has_decal()); + cdata->set_fancy_bit(FB_show_bounds, effects->has_show_bounds()); + cdata->set_fancy_bit(FB_show_tight_bounds, effects->has_show_tight_bounds()); } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); mark_bam_modified(); @@ -1402,7 +1383,8 @@ copy_all_properties(PandaNode *other) { } static const int change_bits = (FB_transform | FB_state | FB_effects | - FB_tag | FB_draw_mask); + FB_tag | FB_draw_mask | FB_decal | + FB_show_bounds | FB_show_tight_bounds); cdataw->_fancy_bits = (cdataw->_fancy_bits & ~change_bits) | (cdatar->_fancy_bits & change_bits); @@ -2536,6 +2518,24 @@ disable_cull_callback() { mark_bam_modified(); } +/** + * Called by a derived class to indicate that there is some value to visiting + * this particular node during the cull traversal for any camera. This will be + * used to optimize the result of get_net_draw_show_mask(), so that any subtrees + * that contain only nodes for which is_renderable() is false need not be + * visited. It also indicates that add_for_draw() should be called if the + * object is determined to be in view of the camera. + */ +void PandaNode:: +set_renderable() { + Thread *current_thread = Thread::get_current_thread(); + OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { + CDStageWriter cdata(_cycler, pipeline_stage, current_thread); + cdata->set_fancy_bit(FB_renderable, true); + } + CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); +} + /** * The private implementation of remove_child(), for a particular pipeline * stage. @@ -3272,7 +3272,7 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe // Start with a clean slate. CollideMask net_collide_mask = cdata->_into_collide_mask; DrawMask net_draw_control_mask, net_draw_show_mask; - bool renderable = is_renderable(); + bool renderable = (cdata->_fancy_bits & FB_renderable) != 0; if (renderable) { // If this node is itself renderable, it contributes to the net draw @@ -3292,16 +3292,22 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe off_clip_planes = ClipPlaneAttrib::make(); } - // Also get the list of the node's children. - Children children(cdata); - int num_vertices = cdata->_internal_vertices; + // Also get the list of the node's children. When the cdataw destructs, it + // will also release the lock, since we've got all the data we need from the + // node. + PT(Down) down; + { + CDStageWriter cdataw(_cycler, pipeline_stage, cdata); + down = cdataw->modify_down(); + } + // Now that we've got all the data we need from the node, we can release // the lock. - _cycler.release_read_stage(pipeline_stage, cdata.take_pointer()); + //_cycler.release_read_stage(pipeline_stage, cdata.take_pointer()); - int num_children = children.get_num_children(); + int num_children = down->size(); // We need to keep references to the bounding volumes, since in a threaded // environment the pointers might go away while we're working (since we're @@ -3334,7 +3340,8 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe // Now expand those contents to include all of our children. for (int i = 0; i < num_children; ++i) { - PandaNode *child = children.get_child(i); + DownConnection &connection = (*down)[i]; + PandaNode *child = connection.get_child(); const ClipPlaneAttrib *orig_cp = DCAST(ClipPlaneAttrib, off_clip_planes); @@ -3348,7 +3355,9 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe // Child needs update. CDStageWriter child_cdataw = child->update_cached(update_bounds, pipeline_stage, child_cdata); - net_collide_mask |= child_cdataw->_net_collide_mask; + CollideMask child_collide_mask = child_cdataw->_net_collide_mask; + net_collide_mask |= child_collide_mask; + connection._net_collide_mask = child_collide_mask; if (drawmask_cat.is_debug()) { drawmask_cat.debug(false) @@ -3425,11 +3434,18 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe child_volumes[child_volumes_i++] = child_cdataw->_external_bounds; } num_vertices += child_cdataw->_nested_vertices; + + connection._external_bounds = child_cdataw->_external_bounds->as_geometric_bounding_volume(); } + connection._net_draw_control_mask = child_control_mask; + connection._net_draw_show_mask = child_show_mask; + } else { // Child is good. - net_collide_mask |= child_cdata->_net_collide_mask; + CollideMask child_collide_mask = child_cdata->_net_collide_mask; + net_collide_mask |= child_collide_mask; + connection._net_collide_mask = child_collide_mask; // See comments in similar block above. if (drawmask_cat.is_debug()) { @@ -3477,7 +3493,12 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe child_volumes[child_volumes_i++] = child_cdata->_external_bounds; } num_vertices += child_cdata->_nested_vertices; + + connection._external_bounds = child_cdata->_external_bounds->as_geometric_bounding_volume(); } + + connection._net_draw_control_mask = child_control_mask; + connection._net_draw_show_mask = child_show_mask; } } @@ -3865,6 +3886,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { set_fancy_bit(FB_state, !_state->is_empty()); set_fancy_bit(FB_effects, !_effects->is_empty()); set_fancy_bit(FB_tag, !_tag_data.is_empty()); + set_fancy_bit(FB_decal, _effects->has_decal()); + set_fancy_bit(FB_show_bounds, _effects->has_show_bounds()); + set_fancy_bit(FB_show_tight_bounds, _effects->has_show_tight_bounds()); // Mark the bounds stale. ++_next_update; diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index d4254f315c..d078839b7e 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -46,6 +46,7 @@ #include "lightReMutex.h" #include "extension.h" #include "simpleHashMap.h" +#include "geometricBoundingVolume.h" class NodePathComponent; class CullTraverser; @@ -97,12 +98,9 @@ public: Thread *current_thread = Thread::get_current_thread()) const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool has_selective_visibility() const; - virtual int get_first_visible_child() const; - virtual int get_next_visible_child(int n) const; virtual bool has_single_child_visibility() const; virtual int get_visible_child() const; - virtual bool is_renderable() const; + virtual bool is_renderable() const final; //CHANGED: see set_renderable() virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); PUBLISHED: @@ -326,6 +324,10 @@ PUBLISHED: FB_tag = 0x0010, FB_draw_mask = 0x0020, FB_cull_callback = 0x0040, + FB_renderable = 0x0080, + FB_decal = 0x0100, + FB_show_bounds = 0x0200, + FB_show_tight_bounds = 0x0400, }; INLINE int get_fancy_bits(Thread *current_thread = Thread::get_current_thread()) const; @@ -371,6 +373,8 @@ protected: void set_cull_callback(); void disable_cull_callback(); + void set_renderable(); + public: virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, @@ -459,11 +463,26 @@ public: INLINE void set_child(PandaNode *child); INLINE int get_sort() const; + INLINE CollideMask get_net_collide_mask() const; + INLINE const GeometricBoundingVolume *get_bounds() const; + + INLINE bool compare_draw_mask(DrawMask running_draw_mask, + DrawMask camera_mask) const; + private: // Child pointers are reference counted. That way, holding a pointer to // the root of a subgraph keeps the entire subgraph around. PT(PandaNode) _child; int _sort; + + // These values are cached here so that a traverser can efficiently check + // whether a node is in view before recursing. + CollideMask _net_collide_mask; + CPT(GeometricBoundingVolume) _external_bounds; + DrawMask _net_draw_control_mask; + DrawMask _net_draw_show_mask; + + friend class PandaNode; }; private: @@ -718,6 +737,10 @@ PUBLISHED: INLINE PandaNode *get_child(size_t n) const; INLINE int get_child_sort(size_t n) const; + INLINE const DownConnection &get_child_connection(size_t n) const { + return (*_down)[n]; + } + PUBLISHED: INLINE PandaNode *operator [](size_t n) const { return get_child(n); } INLINE size_t size() const { return get_num_children(); } @@ -865,6 +888,7 @@ public: INLINE int get_num_children() const; INLINE PandaNode *get_child(int n) const; + INLINE const PandaNode::DownConnection &get_child_connection(int n) const; INLINE int get_child_sort(int n) const; INLINE int find_child(PandaNode *node) const; diff --git a/panda/src/pgraph/planeNode.cxx b/panda/src/pgraph/planeNode.cxx index a54e385e60..dbae5ff960 100644 --- a/panda/src/pgraph/planeNode.cxx +++ b/panda/src/pgraph/planeNode.cxx @@ -65,6 +65,7 @@ PlaneNode(const std::string &name, const LPlane &plane) : _clip_effect(~0) { set_cull_callback(); + set_renderable(); // PlaneNodes are hidden by default. set_overall_hidden(true); @@ -148,17 +149,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool PlaneNode:: -is_renderable() const { - return true; -} - /** * Returns a newly-allocated BoundingVolume that represents the internal * contents of the node. Should be overridden by PandaNode classes that diff --git a/panda/src/pgraph/planeNode.h b/panda/src/pgraph/planeNode.h index 6debd7a217..2a77d68bc2 100644 --- a/panda/src/pgraph/planeNode.h +++ b/panda/src/pgraph/planeNode.h @@ -46,7 +46,6 @@ public: virtual void xform(const LMatrix4 &mat); virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; PUBLISHED: INLINE void set_plane(const LPlane &plane); diff --git a/panda/src/pgraph/portalNode.cxx b/panda/src/pgraph/portalNode.cxx index c7ad0444fe..09f854b0d9 100644 --- a/panda/src/pgraph/portalNode.cxx +++ b/panda/src/pgraph/portalNode.cxx @@ -48,6 +48,7 @@ PortalNode(const std::string &name) : _flags(0) { set_cull_callback(); + set_renderable(); _visible = false; _open = true; @@ -307,18 +308,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool PortalNode:: -is_renderable() const { - return true; -} - - /** * Writes a brief description of the node to the indicated output stream. * This is invoked by the << operator. It may be overridden in derived diff --git a/panda/src/pgraph/portalNode.h b/panda/src/pgraph/portalNode.h index de78847dab..602dd96139 100644 --- a/panda/src/pgraph/portalNode.h +++ b/panda/src/pgraph/portalNode.h @@ -45,7 +45,6 @@ public: virtual void enable_clipping_planes(); virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual void output(std::ostream &out) const; diff --git a/panda/src/pgraph/showBoundsEffect.h b/panda/src/pgraph/showBoundsEffect.h index e0f8a7950f..58ed65e1bc 100644 --- a/panda/src/pgraph/showBoundsEffect.h +++ b/panda/src/pgraph/showBoundsEffect.h @@ -25,7 +25,7 @@ class FactoryParams; * this node. This is generally used only during development to help identify * bounding volume issues. */ -class EXPCL_PANDA_PGRAPH ShowBoundsEffect : public RenderEffect { +class EXPCL_PANDA_PGRAPH ShowBoundsEffect final : public RenderEffect { private: INLINE ShowBoundsEffect(); diff --git a/panda/src/pgraph/transformState.I b/panda/src/pgraph/transformState.I index 437c625bc4..908575ea73 100644 --- a/panda/src/pgraph/transformState.I +++ b/panda/src/pgraph/transformState.I @@ -472,6 +472,20 @@ get_mat() const { return _mat; } +/** + * Returns the inverse of the matrix, or nullptr if it's singular. + */ +INLINE const LMatrix4 *TransformState:: +get_inverse_mat() const { + if (!is_singular()) { + nassertr(_inv_mat != nullptr, nullptr); + return _inv_mat; + } + else { + return nullptr; + } +} + /** * Returns the pos component of the 2-d transform. It is an error to call * this if has_pos() or is_2d() returned false. diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 3e835fcebf..7c34f3ff6e 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -142,6 +142,7 @@ PUBLISHED: INLINE PN_stdfloat get_uniform_scale() const; INLINE const LVecBase3 &get_shear() const; INLINE const LMatrix4 &get_mat() const; + INLINE const LMatrix4 *get_inverse_mat() const; INLINE LVecBase2 get_pos2d() const; INLINE PN_stdfloat get_rotate2d() const; diff --git a/panda/src/pgraphnodes/callbackNode.cxx b/panda/src/pgraphnodes/callbackNode.cxx index bf0a233f3c..2e555dfd1e 100644 --- a/panda/src/pgraphnodes/callbackNode.cxx +++ b/panda/src/pgraphnodes/callbackNode.cxx @@ -30,6 +30,7 @@ CallbackNode(const std::string &name) : PandaNode(name) { PandaNode::set_cull_callback(); + PandaNode::set_renderable(); // Set up a default, infinite bounding volume, unless the user tells us // otherwise. Not sure if this is a great idea, because it means a naive @@ -102,17 +103,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool CallbackNode:: -is_renderable() const { - return true; -} - /** * Adds the node's contents to the CullResult we are building up during the * cull traversal, so that it will be drawn at render time. For most nodes diff --git a/panda/src/pgraphnodes/callbackNode.h b/panda/src/pgraphnodes/callbackNode.h index 0a4d101903..f658137ddc 100644 --- a/panda/src/pgraphnodes/callbackNode.h +++ b/panda/src/pgraphnodes/callbackNode.h @@ -44,7 +44,6 @@ public: virtual bool safe_to_combine() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); virtual void output(std::ostream &out) const; diff --git a/panda/src/pgraphnodes/computeNode.cxx b/panda/src/pgraphnodes/computeNode.cxx index 560e1a8365..a2f0ca5f15 100644 --- a/panda/src/pgraphnodes/computeNode.cxx +++ b/panda/src/pgraphnodes/computeNode.cxx @@ -32,6 +32,7 @@ ComputeNode(const std::string &name) : _dispatcher(new ComputeNode::Dispatcher) { set_internal_bounds(new OmniBoundingVolume); + set_renderable(); } /** @@ -65,17 +66,6 @@ safe_to_combine() const { return false; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool ComputeNode:: -is_renderable() const { - return true; -} - /** * Adds the node's contents to the CullResult we are building up during the * cull traversal, so that it will be drawn at render time. For most nodes diff --git a/panda/src/pgraphnodes/computeNode.h b/panda/src/pgraphnodes/computeNode.h index 475aca2355..e1d0f6f073 100644 --- a/panda/src/pgraphnodes/computeNode.h +++ b/panda/src/pgraphnodes/computeNode.h @@ -47,7 +47,6 @@ public: virtual PandaNode *make_copy() const; virtual bool safe_to_combine() const; - virtual bool is_renderable() const; virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); virtual void output(std::ostream &out) const; diff --git a/panda/src/pgraphnodes/fadeLodNode.cxx b/panda/src/pgraphnodes/fadeLodNode.cxx index f523d01795..265594279b 100644 --- a/panda/src/pgraphnodes/fadeLodNode.cxx +++ b/panda/src/pgraphnodes/fadeLodNode.cxx @@ -164,54 +164,37 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { nassertr(elapsed >= 0.0f && elapsed <= _fade_time, false); + Children children = get_children(); if (elapsed < half_fade_time) { // FIRST HALF OF FADE Fade the new LOD in with z writing off Keep // drawing the old LOD opaque with z writing on - if (out_child >= 0 && out_child < get_num_children()) { - PandaNode *child = get_child(out_child); - if (child != nullptr) { - CullTraverserData next_data_out(data, child); - next_data_out._state = - next_data_out._state->compose(get_fade_1_old_state()); - trav->traverse(next_data_out); - } + if (out_child >= 0 && out_child < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(out_child); + trav->traverse_down(data, child, + data._state->compose(get_fade_1_old_state())); } - if (in_child >= 0 && in_child < get_num_children()) { - PandaNode *child = get_child(in_child); - if (child != nullptr) { - CullTraverserData next_data_in(data, child); - - PN_stdfloat in_alpha = elapsed / half_fade_time; - next_data_in._state = - next_data_in._state->compose(get_fade_1_new_state(in_alpha)); - trav->traverse(next_data_in); - } + if (in_child >= 0 && in_child < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(in_child); + PN_stdfloat in_alpha = elapsed / half_fade_time; + trav->traverse_down(data, child, + data._state->compose(get_fade_1_new_state(in_alpha))); } } else { // SECOND HALF OF FADE: Fade out the old LOD with z write off and // draw the opaque new LOD with z write on - if (in_child >= 0 && in_child < get_num_children()) { - PandaNode *child = get_child(in_child); - if (child != nullptr) { - CullTraverserData next_data_in(data, child); - next_data_in._state = - next_data_in._state->compose(get_fade_2_new_state()); - trav->traverse(next_data_in); - } + if (in_child >= 0 && in_child < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(in_child); + trav->traverse_down(data, child, + data._state->compose(get_fade_2_new_state())); } - if (out_child >= 0 && out_child < get_num_children()) { - PandaNode *child = get_child(out_child); - if (child != nullptr) { - CullTraverserData next_data_out(data, child); - - PN_stdfloat out_alpha = 1.0f - (elapsed - half_fade_time) / half_fade_time; - next_data_out._state = - next_data_out._state->compose(get_fade_2_old_state(out_alpha)); - trav->traverse(next_data_out); - } + if (out_child >= 0 && out_child < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(out_child); + PN_stdfloat out_alpha = 1.0f - (elapsed - half_fade_time) / half_fade_time; + trav->traverse_down(data, child, + data._state->compose(get_fade_2_old_state(out_alpha))); } } } @@ -222,12 +205,10 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // This is the normal case: we're not in the middle of a transition; we're // just drawing one child of the LOD. int index = ldata->_fade_in; - if (index >= 0 && index < get_num_children()) { - PandaNode *child = get_child(index); - if (child != nullptr) { - CullTraverserData next_data(data, child); - trav->traverse(next_data); - } + Children children = get_children(); + if (index >= 0 && index < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(index); + trav->traverse_down(data, child, data._state); } } diff --git a/panda/src/pgraphnodes/lodNode.cxx b/panda/src/pgraphnodes/lodNode.cxx index 2b66c8fa36..a5574dcaa8 100644 --- a/panda/src/pgraphnodes/lodNode.cxx +++ b/panda/src/pgraphnodes/lodNode.cxx @@ -146,7 +146,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { PN_stdfloat lod_scale = cdata->_lod_scale * trav->get_scene()->get_camera_node()->get_lod_scale(); - int num_children = std::min(get_num_children(), (int)cdata->_switch_vector.size()); + Children children = get_children(); + int num_children = std::min(children.get_num_children(), cdata->_switch_vector.size()); if (data._instances == nullptr || cdata->_got_force_switch) { LPoint3 center = cdata->_center * rel_transform->get_mat(); @@ -163,11 +164,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { if (in_range) { // This switch level is in range. Draw its children. - PandaNode *child = get_child(index); - if (child != nullptr) { - CullTraverserData next_data(data, child); - trav->traverse(next_data); - } + const PandaNode::DownConnection &child = children.get_child_connection(index); + trav->traverse_down(data, child); } } } @@ -193,12 +191,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { CPT(InstanceList) instances = data._instances->without(in_range[index]); if (!instances->empty()) { // At least one instance is visible in this switch level. - PandaNode *child = get_child(index); - if (child != nullptr) { - CullTraverserData next_data(data, child); - next_data._instances = instances; - trav->traverse(next_data); - } + const PandaNode::DownConnection &child = children.get_child_connection(index); + trav->traverse_down(data, child); } } } @@ -435,26 +429,23 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) { if (in_range) { // This switch level is in range. Draw its children in the funny // wireframe mode. - if (index < get_num_children()) { - PandaNode *child = get_child(index); - if (child != nullptr) { - CullTraverserData next_data3(data, child); - next_data3._state = next_data3._state->compose(sw.get_viz_model_state()); - trav->traverse(next_data3); - } + Children children = get_children(); + if (index < children.get_num_children()) { + const PandaNode::DownConnection &child = children.get_child_connection(index); + trav->traverse_down(data, child, data._state->compose(sw.get_viz_model_state())); } // And draw the spindle in this color. - CullTraverserData next_data2(data, sw.get_spindle_viz()); - next_data2.apply_transform(viz_transform); - trav->traverse(next_data2); + //CullTraverserData next_data2(data, sw.get_spindle_viz()); + //next_data2.apply_transform(viz_transform); + //trav->traverse(next_data2); } // Draw the rings for this switch level. We do this after we have drawn // the geometry and the spindle. - CullTraverserData next_data(data, sw.get_ring_viz()); - next_data.apply_transform(viz_transform); - trav->traverse(next_data); + //CullTraverserData next_data(data, sw.get_ring_viz()); + //next_data.apply_transform(viz_transform); + //trav->traverse(next_data); } } diff --git a/panda/src/pgraphnodes/nodeCullCallbackData.cxx b/panda/src/pgraphnodes/nodeCullCallbackData.cxx index c1f0ac7464..5a9dbc9e0a 100644 --- a/panda/src/pgraphnodes/nodeCullCallbackData.cxx +++ b/panda/src/pgraphnodes/nodeCullCallbackData.cxx @@ -56,13 +56,6 @@ upcall() { } } - // Now visit all the node's children. - PandaNodePipelineReader *node_reader = _data.node_reader(); - PandaNode::Children children = node_reader->get_children(); - node_reader->release(); - int num_children = children.get_num_children(); - for (int i = 0; i < num_children; ++i) { - CullTraverserData next_data(_data, children.get_child(i)); - _trav->traverse(next_data); - } + // Now traverse below. + _trav->traverse_below(_data); } diff --git a/panda/src/pgraphnodes/selectiveChildNode.I b/panda/src/pgraphnodes/selectiveChildNode.I index beafc0669d..46b40ba3a7 100644 --- a/panda/src/pgraphnodes/selectiveChildNode.I +++ b/panda/src/pgraphnodes/selectiveChildNode.I @@ -16,8 +16,7 @@ */ INLINE SelectiveChildNode:: SelectiveChildNode(const std::string &name) : - PandaNode(name), - _selected_child(0) + PandaNode(name) { } @@ -26,17 +25,6 @@ SelectiveChildNode(const std::string &name) : */ INLINE SelectiveChildNode:: SelectiveChildNode(const SelectiveChildNode ©) : - PandaNode(copy), - _selected_child(copy._selected_child) + PandaNode(copy) { } - -/** - * Selects the indicated child for rendering. This is normally called during - * the cull_callback() method, but it may be called at any time. - */ -INLINE void SelectiveChildNode:: -select_child(int n) { - nassertv(n >= 0); - _selected_child = n; -} diff --git a/panda/src/pgraphnodes/selectiveChildNode.cxx b/panda/src/pgraphnodes/selectiveChildNode.cxx index 0fb38560aa..cfe5e258b4 100644 --- a/panda/src/pgraphnodes/selectiveChildNode.cxx +++ b/panda/src/pgraphnodes/selectiveChildNode.cxx @@ -14,44 +14,3 @@ #include "selectiveChildNode.h" TypeHandle SelectiveChildNode::_type_handle; - - -/** - * Should be overridden by derived classes to return true if this kind of node - * has some restrictions on the set of children that should be rendered. Node - * with this property include LODNodes, SwitchNodes, and SequenceNodes. - * - * If this function returns true, get_first_visible_child() and - * get_next_visible_child() will be called to walk through the list of - * children during cull, instead of iterating through the entire list. This - * method is called after cull_callback(), so cull_callback() may be - * responsible for the decisions as to which children are visible at the - * moment. - */ -bool SelectiveChildNode:: -has_selective_visibility() const { - return true; -} - -/** - * Returns the index number of the first visible child of this node, or a - * number >= get_num_children() if there are no visible children of this node. - * This is called during the cull traversal, but only if - * has_selective_visibility() has already returned true. See - * has_selective_visibility(). - */ -int SelectiveChildNode:: -get_first_visible_child() const { - return _selected_child; -} - -/** - * Returns the index number of the next visible child of this node following - * the indicated child, or a number >= get_num_children() if there are no more - * visible children of this node. See has_selective_visibility() and - * get_first_visible_child(). - */ -int SelectiveChildNode:: -get_next_visible_child(int n) const { - return get_num_children(); -} diff --git a/panda/src/pgraphnodes/selectiveChildNode.h b/panda/src/pgraphnodes/selectiveChildNode.h index 29b0d4581a..aa23a49f1d 100644 --- a/panda/src/pgraphnodes/selectiveChildNode.h +++ b/panda/src/pgraphnodes/selectiveChildNode.h @@ -21,6 +21,8 @@ /** * A base class for nodes like LODNode and SequenceNode that select only one * visible child at a time. + * + * This class is now vestigial. */ class EXPCL_PANDA_PGRAPHNODES SelectiveChildNode : public PandaNode { PUBLISHED: @@ -29,21 +31,6 @@ PUBLISHED: protected: INLINE SelectiveChildNode(const SelectiveChildNode ©); -public: - virtual bool has_selective_visibility() const; - virtual int get_first_visible_child() const; - virtual int get_next_visible_child(int n) const; - -protected: - INLINE void select_child(int n); - -private: - // Not sure if this should be cycled or not. It's not exactly thread-safe - // not to cycle it, but it doesn't really need the full pipeline control. - // It's probably a problem in the non-thread-safe design; need to rethink - // the design a bit. - int _selected_child; - public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/pgraphnodes/sequenceNode.cxx b/panda/src/pgraphnodes/sequenceNode.cxx index 06f0019e6d..48614edbcd 100644 --- a/panda/src/pgraphnodes/sequenceNode.cxx +++ b/panda/src/pgraphnodes/sequenceNode.cxx @@ -88,21 +88,10 @@ make_copy() const { * should be culled. */ bool SequenceNode:: -cull_callback(CullTraverser *, CullTraverserData &) { - select_child(get_frame()); - return true; -} - -/** - * Returns the index number of the first visible child of this node, or a - * number >= get_num_children() if there are no visible children of this node. - * This is called during the cull traversal, but only if - * has_selective_visibility() has already returned true. See - * has_selective_visibility(). - */ -int SequenceNode:: -get_first_visible_child() const { - return get_frame(); +cull_callback(CullTraverser *trav, CullTraverserData &data) { + const PandaNode::DownConnection &child = data.node_reader()->get_child_connection(get_frame()); + trav->traverse_down(data, child); + return false; } /** diff --git a/panda/src/pgraphnodes/sequenceNode.h b/panda/src/pgraphnodes/sequenceNode.h index 96b7698131..ba67bbf18c 100644 --- a/panda/src/pgraphnodes/sequenceNode.h +++ b/panda/src/pgraphnodes/sequenceNode.h @@ -43,7 +43,6 @@ public: virtual bool safe_to_combine_children() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual int get_first_visible_child() const; virtual bool has_single_child_visibility() const; virtual int get_visible_child() const; diff --git a/panda/src/pgraphnodes/switchNode.cxx b/panda/src/pgraphnodes/switchNode.cxx index d16afc56c4..1e3430cb2e 100644 --- a/panda/src/pgraphnodes/switchNode.cxx +++ b/panda/src/pgraphnodes/switchNode.cxx @@ -103,21 +103,10 @@ make_copy() const { * should be culled. */ bool SwitchNode:: -cull_callback(CullTraverser *, CullTraverserData &) { - select_child(get_visible_child()); - return true; -} - -/** - * Returns the index number of the first visible child of this node, or a - * number >= get_num_children() if there are no visible children of this node. - * This is called during the cull traversal, but only if - * has_selective_visibility() has already returned true. See - * has_selective_visibility(). - */ -int SwitchNode:: -get_first_visible_child() const { - return get_visible_child(); +cull_callback(CullTraverser *trav, CullTraverserData &data) { + const PandaNode::DownConnection &child = data.node_reader()->get_child_connection(get_visible_child()); + trav->traverse_down(data, child); + return false; } /** diff --git a/panda/src/pgraphnodes/switchNode.h b/panda/src/pgraphnodes/switchNode.h index 2df989cbd7..1fe3353a68 100644 --- a/panda/src/pgraphnodes/switchNode.h +++ b/panda/src/pgraphnodes/switchNode.h @@ -34,7 +34,6 @@ public: virtual bool safe_to_combine_children() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual int get_first_visible_child() const; virtual bool has_single_child_visibility() const; PUBLISHED: diff --git a/panda/src/pgui/pgEntry.cxx b/panda/src/pgui/pgEntry.cxx index 2fc40d546a..5922ac7f50 100644 --- a/panda/src/pgui/pgEntry.cxx +++ b/panda/src/pgui/pgEntry.cxx @@ -184,8 +184,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { } // Now render the text. - CullTraverserData next_data(data, _text_render_root.node()); - trav->traverse(next_data); + trav->traverse_down(data, _text_render_root.node()); // Now continue to render everything else below this node. return true; diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx index 09a3fbcaac..8229f434dc 100644 --- a/panda/src/pgui/pgItem.cxx +++ b/panda/src/pgui/pgItem.cxx @@ -67,6 +67,7 @@ PGItem(const string &name) : _flags(0) { set_cull_callback(); + set_renderable(); } /** @@ -278,25 +279,13 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { if (state_def_root != nullptr) { // This item has a current state definition that we should use to render // the item. - CullTraverserData next_data(data, state_def_root); - trav->traverse(next_data); + trav->traverse_down(data, state_def_root); } // Now continue to render everything else below this node. return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool PGItem:: -is_renderable() const { - return true; -} - /** * Called when needed to recompute the node's _internal_bound object. Nodes * that contain anything of substance should redefine this to do the right diff --git a/panda/src/pgui/pgItem.h b/panda/src/pgui/pgItem.h index 5467029d52..a27c08d46c 100644 --- a/panda/src/pgui/pgItem.h +++ b/panda/src/pgui/pgItem.h @@ -65,7 +65,6 @@ protected: virtual void draw_mask_changed(); virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, int &internal_vertices, diff --git a/panda/src/pgui/pgTop.cxx b/panda/src/pgui/pgTop.cxx index 3c0948ebda..f4d9f31fda 100644 --- a/panda/src/pgui/pgTop.cxx +++ b/panda/src/pgui/pgTop.cxx @@ -29,6 +29,10 @@ PGTop(const std::string &name) : { set_cull_callback(); + // We flag the PGTop as renderable, even though it technically doesn't have + // anything to render, but we do need the traverser to visit it every frame. + set_renderable(); + _start_sort = 0; // A PGTop node normally has an infinite bounding volume. Screw culling. @@ -111,19 +115,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return false; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool PGTop:: -is_renderable() const { - // We flag the PGTop as renderable, even though it technically doesn't have - // anything to render, but we do need the traverser to visit it every frame. - return true; -} - /** * Sets the MouseWatcher pointer that the PGTop object registers its PG items * with. This must be set before the PG items are active. diff --git a/panda/src/pgui/pgTop.h b/panda/src/pgui/pgTop.h index 1d5e739c8e..f5e6bac554 100644 --- a/panda/src/pgui/pgTop.h +++ b/panda/src/pgui/pgTop.h @@ -46,7 +46,6 @@ protected: public: virtual PandaNode *make_copy() const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; PUBLISHED: void set_mouse_watcher(MouseWatcher *watcher); diff --git a/panda/src/pipeline/cycleDataLockedStageReader.I b/panda/src/pipeline/cycleDataLockedStageReader.I index a471aac0b7..6cee154bb7 100644 --- a/panda/src/pipeline/cycleDataLockedStageReader.I +++ b/panda/src/pipeline/cycleDataLockedStageReader.I @@ -111,7 +111,9 @@ INLINE CycleDataLockedStageReader:: template INLINE const CycleDataType *CycleDataLockedStageReader:: operator -> () const { +#ifdef _DEBUG nassertr(_pointer != nullptr, _cycler->cheat()); +#endif return _pointer; } @@ -122,7 +124,9 @@ operator -> () const { template INLINE CycleDataLockedStageReader:: operator const CycleDataType * () const { +#ifdef _DEBUG nassertr(_pointer != nullptr, _cycler->cheat()); +#endif return _pointer; } diff --git a/panda/src/text/textNode.cxx b/panda/src/text/textNode.cxx index be904d7972..0a8d666432 100644 --- a/panda/src/text/textNode.cxx +++ b/panda/src/text/textNode.cxx @@ -61,6 +61,7 @@ PStatCollector TextNode::_text_generate_pcollector("*:Generate Text"); TextNode:: TextNode(const string &name) : PandaNode(name) { set_cull_callback(); + set_renderable(); if (text_small_caps) { TextProperties::set_small_caps(true); @@ -487,25 +488,13 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { PT(PandaNode) internal_geom = do_get_internal_geom(); if (internal_geom != nullptr) { // Render the text with this node. - CullTraverserData next_data(data, internal_geom); - trav->traverse(next_data); + trav->traverse_down(data, internal_geom); } // Now continue to render everything else below this node. return true; } -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool TextNode:: -is_renderable() const { - return true; -} - /** * Called when needed to recompute the node's _internal_bound object. Nodes * that contain anything of substance should redefine this to do the right diff --git a/panda/src/text/textNode.h b/panda/src/text/textNode.h index 3ace43895e..e40f1c9030 100644 --- a/panda/src/text/textNode.h +++ b/panda/src/text/textNode.h @@ -286,7 +286,6 @@ public: Thread *current_thread) const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); - virtual bool is_renderable() const; virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, int &internal_vertices,