From 2ddcce6ad2df24142aa8e0b6772337d146547e23 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 12 Sep 2006 00:15:59 +0000 Subject: [PATCH] LODNode should test its bounding volumes --- panda/src/pgraph/config_pgraph.cxx | 8 + panda/src/pgraph/config_pgraph.h | 1 + panda/src/pgraph/fadeLodNode.cxx | 67 +++--- panda/src/pgraph/lodNode.I | 21 ++ panda/src/pgraph/lodNode.cxx | 314 ++++++++++++++++++++++------- panda/src/pgraph/lodNode.h | 11 + panda/src/pgraph/pandaNode.cxx | 36 ++++ panda/src/pgraph/pandaNode.h | 1 + 8 files changed, 358 insertions(+), 101 deletions(-) diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index c0b1b0e2ee..6906d6cdbf 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -219,6 +219,14 @@ ConfigVariableDouble lod_fade_time PRC_DESC("The default amount of time (in seconds) over which a FadeLODNode " "transitions between its different levels.")); +ConfigVariableBool verify_lods +("verify-lods", true, + PRC_DESC("When this is true, LODNodes will test when they are rendered to " + "ensure that each child's geometry fits entirely within the radius " + "defined by its switch-out distance. When it is false, LODNodes " + "may have any switch in and out distances, regardless of the " + "actual size of their geometry. This test is only made in NDEBUG " + "mode (the variable is ignored in a production build).")); ConfigVariableBool show_vertex_animation ("show-vertex-animation", false, diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index d3979d9b73..659daa2872 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -51,6 +51,7 @@ extern ConfigVariableInt max_collect_indices; extern ConfigVariableBool polylight_info; extern ConfigVariableDouble lod_fade_time; +extern ConfigVariableBool verify_lods; extern ConfigVariableBool show_vertex_animation; extern ConfigVariableBool show_transparency; diff --git a/panda/src/pgraph/fadeLodNode.cxx b/panda/src/pgraph/fadeLodNode.cxx index 4c4e4f2a6c..3565cf6edc 100755 --- a/panda/src/pgraph/fadeLodNode.cxx +++ b/panda/src/pgraph/fadeLodNode.cxx @@ -77,6 +77,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return show_switches_cull_callback(trav, data); } + consider_verify_lods(trav, data); + Camera *camera = trav->get_scene()->get_camera_node(); NodePath this_np = data._node_path.get_node_path(); FadeLODNodeData *ldata = @@ -126,21 +128,27 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // Fade the new LOD in with z writing off // Keep drawing the old LOD opaque with z writing on if (ldata->_fade_out >= 0 && ldata->_fade_out < get_num_children()) { - CullTraverserData next_data_out(data, get_child(ldata->_fade_out)); - trav->traverse(next_data_out); + PandaNode *child = get_child(ldata->_fade_out); + if (child != (PandaNode *)NULL) { + CullTraverserData next_data_out(data, child); + trav->traverse(next_data_out); + } } if (ldata->_fade_in >= 0 && ldata->_fade_in < get_num_children()) { - CullTraverserData next_data_in(data, get_child(ldata->_fade_in)); - - float in_alpha = elapsed / half_fade_time; - LVecBase4f alpha_scale(1.0f, 1.0f, 1.0f, in_alpha); - - next_data_in._state = - next_data_in._state->compose(get_fade_out_state())->compose - (RenderState::make(ColorScaleAttrib::make(alpha_scale))); - - trav->traverse(next_data_in); + PandaNode *child = get_child(ldata->_fade_in); + if (child != (PandaNode *)NULL) { + CullTraverserData next_data_in(data, child); + + float in_alpha = elapsed / half_fade_time; + LVecBase4f alpha_scale(1.0f, 1.0f, 1.0f, in_alpha); + + next_data_in._state = + next_data_in._state->compose(get_fade_out_state())->compose + (RenderState::make(ColorScaleAttrib::make(alpha_scale))); + + trav->traverse(next_data_in); + } } } else if (elapsed < _fade_time) { @@ -148,21 +156,27 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { //Fade out the old LOD with z write off and //draw the opaque new LOD with z write on if (ldata->_fade_in >= 0 && ldata->_fade_in < get_num_children()) { - CullTraverserData next_data_in(data, get_child(ldata->_fade_in)); - trav->traverse(next_data_in); + PandaNode *child = get_child(ldata->_fade_in); + if (child != (PandaNode *)NULL) { + CullTraverserData next_data_in(data, child); + trav->traverse(next_data_in); + } } if (ldata->_fade_out >= 0 && ldata->_fade_out < get_num_children()) { - CullTraverserData next_data_out(data, get_child(ldata->_fade_out)); + PandaNode *child = get_child(ldata->_fade_out); + if (child != (PandaNode *)NULL) { + CullTraverserData next_data_out(data, child); - float out_alpha = 1.0f - (elapsed - half_fade_time) / half_fade_time; - LVecBase4f alpha_scale(1.0f, 1.0f, 1.0f, out_alpha); - - next_data_out._state = - next_data_out._state->compose(get_fade_out_state())->compose - (RenderState::make(ColorScaleAttrib::make(alpha_scale))); - - trav->traverse(next_data_out); + float out_alpha = 1.0f - (elapsed - half_fade_time) / half_fade_time; + LVecBase4f alpha_scale(1.0f, 1.0f, 1.0f, out_alpha); + + next_data_out._state = + next_data_out._state->compose(get_fade_out_state())->compose + (RenderState::make(ColorScaleAttrib::make(alpha_scale))); + + trav->traverse(next_data_out); + } } } else { @@ -177,8 +191,11 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // transition; we're just drawing one child of the LOD. int index = ldata->_fade_in; if (index >= 0 && index < get_num_children()) { - CullTraverserData next_data(data, get_child(index)); - trav->traverse(next_data); + PandaNode *child = get_child(index); + if (child != (PandaNode *)NULL) { + CullTraverserData next_data(data, child); + trav->traverse(next_data); + } } } diff --git a/panda/src/pgraph/lodNode.I b/panda/src/pgraph/lodNode.I index 7fd9991ef1..24efe46453 100644 --- a/panda/src/pgraph/lodNode.I +++ b/panda/src/pgraph/lodNode.I @@ -56,6 +56,8 @@ LODNode(const LODNode ©) : //////////////////////////////////////////////////////////////////// INLINE void LODNode:: add_switch(float in, float out) { + nassertv(in >= out); + CDWriter cdata(_cycler); cdata->_switch_vector.push_back(Switch(in, out)); cdata->check_limits(); @@ -73,6 +75,8 @@ add_switch(float in, float out) { //////////////////////////////////////////////////////////////////// INLINE bool LODNode:: set_switch(int index, float in, float out) { + nassertr(in >= out, false); + CDWriter cdata(_cycler); nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), false); cdata->_switch_vector[index].set_range(in, out); @@ -244,6 +248,23 @@ is_any_shown() const { return (cdata->_num_shown != 0); } +//////////////////////////////////////////////////////////////////// +// Function: LODNode::consider_verify_lods +// Access: Protected +// Description: To be called internally when the node is rendered, +// this will raise an assertion if verify-lods is +// configured true, and verify_child_bounds() returns +// false. +//////////////////////////////////////////////////////////////////// +INLINE void LODNode:: +consider_verify_lods(CullTraverser *trav, CullTraverserData &data) { +#ifndef NDEBUG + if (verify_lods) { + do_auto_verify_lods(trav, data); + } +#endif // NDEBUG +} + //////////////////////////////////////////////////////////////////// // Function: LODNode::CData::Constructor // Access: Public diff --git a/panda/src/pgraph/lodNode.cxx b/panda/src/pgraph/lodNode.cxx index 2dfa76d2fd..a6c8f3b7fc 100644 --- a/panda/src/pgraph/lodNode.cxx +++ b/panda/src/pgraph/lodNode.cxx @@ -35,7 +35,9 @@ #include "cullFaceAttrib.h" #include "textureAttrib.h" #include "boundingSphere.h" +#include "geometricBoundingVolume.h" #include "look_at.h" +#include "nodePath.h" TypeHandle LODNode::_type_handle; @@ -132,10 +134,32 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { return show_switches_cull_callback(trav, data); } - int index = compute_child(trav, data); - if (index >= 0 && index < get_num_children()) { - CullTraverserData next_data(data, get_child(index)); - trav->traverse(next_data); + consider_verify_lods(trav, data); + + CDReader cdata(_cycler); + + CPT(TransformState) rel_transform = get_rel_transform(trav, data); + LPoint3f center = cdata->_center * rel_transform->get_mat(); + float dist2 = center.dot(center); + + int num_children = min(get_num_children(), (int)cdata->_switch_vector.size()); + for (int index = 0; index < num_children; ++index) { + const Switch &sw = cdata->_switch_vector[index]; + bool in_range; + if (cdata->_got_force_switch) { + in_range = (cdata->_force_switch == index); + } else { + in_range = sw.in_range_2(dist2); + } + + if (in_range) { + // This switch level is in range. Draw its children. + PandaNode *child = get_child(index); + if (child != (PandaNode *)NULL) { + CullTraverserData next_data(data, child); + trav->traverse(next_data); + } + } } // Now return false indicating that we have already taken care of @@ -269,6 +293,35 @@ hide_all_switches() { mark_internal_bounds_stale(); } +//////////////////////////////////////////////////////////////////// +// Function: LODNode::verify_child_bounds +// Access: Published +// Description: Returns true if the bounding volumes for the geometry +// of each fhild node entirely fits within the +// switch_in radius for that child, or false otherwise. +// It is almost always a mistake for the geometry of an +// LOD level to be larger than its switch_in radius. +//////////////////////////////////////////////////////////////////// +bool LODNode:: +verify_child_bounds() const { + bool okflag = true; + CDReader cdata(_cycler); + + for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) { + float suggested_radius; + if (!do_verify_child_bounds(cdata, index, suggested_radius)) { + const Switch &sw = cdata->_switch_vector[index]; + pgraph_cat.warning() + << "Level " << index << " geometry of " << *this + << " is larger than its switch radius; suggest radius of " + << suggested_radius << " instead of " << sw.get_in() << "\n"; + okflag = false; + } + } + + return okflag; +} + //////////////////////////////////////////////////////////////////// // Function: LODNode::compute_child // Access: Protected @@ -290,36 +343,11 @@ compute_child(CullTraverser *trav, CullTraverserData &data) { return cdata->_force_switch; } - // Get a pointer to the camera node. - Camera *camera = trav->get_scene()->get_camera_node(); - - // Get the LOD center in camera space. If the camera has a special - // LOD center defined, use that; otherwise, if it has a cull center, - // use that; otherwise, use the modelview transform (which is camera - // space). - CPT(TransformState) rel_transform; - - NodePath lod_center = camera->get_lod_center(); - if (!lod_center.is_empty()) { - rel_transform = - lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav)); - } else { - NodePath cull_center = camera->get_cull_center(); - if (!cull_center.is_empty()) { - rel_transform = - cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav)); - } else { - rel_transform = data.get_modelview_transform(trav); - } - } - + CPT(TransformState) rel_transform = get_rel_transform(trav, data); LPoint3f center = cdata->_center * rel_transform->get_mat(); - - // Now measure the distance to the LOD center, and use that to - // determine which child to display. float dist2 = center.dot(center); - for (int index = 0; index < (int)cdata->_switch_vector.size(); index++) { + for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) { if (cdata->_switch_vector[index].in_range_2(dist2)) { if (pgraph_cat.is_debug()) { pgraph_cat.debug() @@ -353,27 +381,7 @@ bool LODNode:: show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) { CDReader cdata(_cycler); - // Get a pointer to the camera node. - Camera *camera = trav->get_scene()->get_camera_node(); - - // Get the camera space transform. This bit is the same as the code - // in compute_child(), above. - CPT(TransformState) rel_transform; - - NodePath lod_center = camera->get_lod_center(); - if (!lod_center.is_empty()) { - rel_transform = - lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav)); - } else { - NodePath cull_center = camera->get_cull_center(); - if (!cull_center.is_empty()) { - rel_transform = - cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav)); - } else { - rel_transform = data.get_modelview_transform(trav); - } - } - + CPT(TransformState) rel_transform = get_rel_transform(trav, data); LPoint3f center = cdata->_center * rel_transform->get_mat(); float dist2 = center.dot(center); @@ -382,22 +390,12 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) { LMatrix4f mat; look_at(mat, -center, LVector3f(0.0f, 0.0f, 1.0f)); mat.set_row(3, center); - CPT(TransformState) viz_transform = TransformState::make_mat(mat); + CPT(TransformState) viz_transform = + rel_transform->invert_compose(TransformState::make_mat(mat)); - viz_transform = rel_transform->invert_compose(viz_transform); - viz_transform = data.get_net_transform(trav)->compose(viz_transform); - - SwitchVector::const_iterator si; - for (si = cdata->_switch_vector.begin(); - si != cdata->_switch_vector.end(); - ++si) { - const Switch &sw = (*si); + for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) { + const Switch &sw = cdata->_switch_vector[index]; if (sw.is_shown()) { - CullTraverserData next_data(data, sw.get_ring_viz()); - next_data._net_transform = viz_transform; - trav->traverse(next_data); - - int index = (si - cdata->_switch_vector.begin()); bool in_range; if (cdata->_got_force_switch) { in_range = (cdata->_force_switch == index); @@ -406,19 +404,34 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) { } if (in_range) { - // This switch level is in range. Draw the spindle in this - // color. - CullTraverserData next_data2(data, sw.get_spindle_viz()); - next_data2._net_transform = viz_transform; - trav->traverse(next_data2); - - // And draw its children in the funny wireframe mode. + // This switch level is in range. Draw its children in the + // funny wireframe mode. if (index < get_num_children()) { - CullTraverserData next_data3(data, get_child(index)); - next_data3._state = next_data3._state->compose(sw.get_viz_model_state()); - trav->traverse(next_data3); + PandaNode *child = get_child(index); + if (child != (PandaNode *)NULL) { + CullTraverserData next_data3(data, child); + next_data3._state = next_data3._state->compose(sw.get_viz_model_state()); + trav->traverse(next_data3); + } } + + // And draw the spindle in this color. + CullTraverserData next_data2(data, sw.get_spindle_viz()); + next_data2.apply_transform_and_state(trav, viz_transform, + RenderState::make_empty(), + RenderEffects::make_empty(), + ClipPlaneAttrib::make()); + 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_and_state(trav, viz_transform, + RenderState::make_empty(), + RenderEffects::make_empty(), + ClipPlaneAttrib::make()); + trav->traverse(next_data); } } @@ -471,6 +484,37 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const { return bound; } +//////////////////////////////////////////////////////////////////// +// Function: LODNode::get_rel_transform +// Access: Protected +// Description: Returns the relative transform to convert from the +// LODNode space to the camera space. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) LODNode:: +get_rel_transform(CullTraverser *trav, CullTraverserData &data) { + // Get a pointer to the camera node. + Camera *camera = trav->get_scene()->get_camera_node(); + + // Get the camera space transform. + CPT(TransformState) rel_transform; + + NodePath lod_center = camera->get_lod_center(); + if (!lod_center.is_empty()) { + rel_transform = + lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav)); + } else { + NodePath cull_center = camera->get_cull_center(); + if (!cull_center.is_empty()) { + rel_transform = + cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav)); + } else { + rel_transform = data.get_modelview_transform(trav); + } + } + + return rel_transform; +} + //////////////////////////////////////////////////////////////////// // Function: LODNode::do_show_switch // Access: Private @@ -501,6 +545,124 @@ do_hide_switch(LODNode::CData *cdata, int index) { cdata->_switch_vector[index].hide(); } +//////////////////////////////////////////////////////////////////// +// Function: LODNode::do_verify_child_bounds +// Access: Private +// Description: The private implementation of verify_child_bounds(), +// this checks the bounding volume of just one child. +// +// If the return value is false, suggested_radius is +// filled with a radius that ought to be large enough to +// include the child. +//////////////////////////////////////////////////////////////////// +bool LODNode:: +do_verify_child_bounds(const LODNode::CData *cdata, int index, + float &suggested_radius) const { + suggested_radius = 0.0f; + + if (index < get_num_children()) { + PandaNode *child = get_child(index); + if (child != (PandaNode *)NULL) { + CPT(BoundingVolume) bv = child->get_bounds(); + if (bv->is_empty()) { + // This child has no geometry, so no one cares anyway. + return true; + } + + const Switch &sw = cdata->_switch_vector[index]; + + const GeometricBoundingVolume *gbv; + DCAST_INTO_R(gbv, bv, false); + BoundingSphere sphere(cdata->_center, sw.get_in()); + sphere.local_object(); + + int flags = sphere.contains(gbv); + if ((flags & BoundingVolume::IF_all) != 0) { + // This child's radius completely encloses its bounding volume. + // Perfect. (And this is the most common case.) + return true; + } + + if (flags == 0) { + // This child's radius doesn't even come close to containing + // its volume. + sphere.extend_by(gbv); + suggested_radius = sphere.get_radius(); + return false; + } + + // This child's radius partially encloses its (loose) bounding + // volume. We have to look closer to determine whether it, in + // fact, fully encloses its geometry. + LPoint3f min_point(0.0f, 0.0f, 0.0f); + LPoint3f max_point(0.0f, 0.0f, 0.0f); + + bool found_any = false; + child->calc_tight_bounds(min_point, max_point, found_any, + TransformState::make_identity(), + Thread::get_current_thread()); + if (!found_any) { + // Hmm, the child has no geometry after all. + return true; + } + + // Now we have a bounding box. Define the largest sphere we can + // that fits within this box. All we can say about this sphere + // is that it should definitely fit entirely within a bounding + // sphere that contains all the points of the child. + LPoint3f box_center = (min_point + max_point) / 2.0f; + float box_radius = min(min(max_point[0] - box_center[0], + max_point[1] - box_center[1]), + max_point[2] - box_center[2]); + + BoundingSphere box_sphere(box_center, box_radius); + box_sphere.local_object(); + + // So if any part of this inscribed sphere is outside of the + // radius, then the radius is bad. + flags = sphere.contains(&box_sphere); + if (flags != BoundingVolume::IF_all) { + // No good. + sphere.extend_by(gbv); + suggested_radius = sphere.get_radius(); + return false; + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LODNode::do_auto_verify_lods +// Access: Private +// Description: Called internally by consider_verify_lods(). +//////////////////////////////////////////////////////////////////// +void LODNode:: +do_auto_verify_lods(CullTraverser *trav, CullTraverserData &data) { + UpdateSeq seq; + get_bounds(seq); + CDLockedReader cdata(_cycler); + if (seq != cdata->_bounds_seq) { + // Time to validate the children again. + for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) { + float suggested_radius; + if (!do_verify_child_bounds(cdata, index, suggested_radius)) { + const Switch &sw = cdata->_switch_vector[index]; + ostringstream strm; + strm + << "Level " << index << " geometry of " << data._node_path + << " is larger than its switch radius; suggest radius of " + << suggested_radius << " instead of " << sw.get_in() + << " (configure verify-lods 0 to ignore this error)"; + nassert_raise(strm.str()); + } + } + CDWriter cdataw(_cycler, cdata); + cdataw->_bounds_seq = seq; + } +} + //////////////////////////////////////////////////////////////////// // Function: LODNode::get_default_show_color // Access: Private, Static diff --git a/panda/src/pgraph/lodNode.h b/panda/src/pgraph/lodNode.h index bd81a31aec..2c6418c0f5 100644 --- a/panda/src/pgraph/lodNode.h +++ b/panda/src/pgraph/lodNode.h @@ -79,16 +79,25 @@ PUBLISHED: void hide_all_switches(); INLINE bool is_any_shown() const; + bool verify_child_bounds() const; + protected: int compute_child(CullTraverser *trav, CullTraverserData &data); bool show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data); virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const; + INLINE void consider_verify_lods(CullTraverser *trav, CullTraverserData &data); + + CPT(TransformState) get_rel_transform(CullTraverser *trav, CullTraverserData &data); + private: class CData; void do_show_switch(CData *cdata, int index, const Colorf &color); void do_hide_switch(CData *cdata, int index); + bool do_verify_child_bounds(const CData *cdata, int index, + float &suggested_radius) const; + void do_auto_verify_lods(CullTraverser *trav, CullTraverserData &data); static const Colorf &get_default_show_color(int index); @@ -152,6 +161,7 @@ private: LPoint3f _center; SwitchVector _switch_vector; size_t _lowest, _highest; + UpdateSeq _bounds_seq; bool _got_force_switch; int _force_switch; @@ -161,6 +171,7 @@ private: PipelineCycler _cycler; typedef CycleDataReader CDReader; typedef CycleDataWriter CDWriter; + typedef CycleDataLockedReader CDLockedReader; typedef CycleDataStageReader CDStageReader; typedef CycleDataStageWriter CDStageWriter; diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index d84b7485e7..6a241e6497 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -1821,6 +1821,41 @@ get_bounds(Thread *current_thread) const { return cdata->_external_bounds; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_bounds +// Access: Published +// Description: This flavor of get_bounds() return the external +// bounding volume, and also fills in seq with the +// bounding volume's current sequence number. When this +// sequence number changes, it indicates that the +// bounding volume might have changed, e.g. because some +// nested child's bounding volume has changed. +// +// Although this might occasionally increment without +// changing the bounding volume, the bounding volume +// will never change without incrementing this counter, +// so as long as this counter remains unchanged you can +// be confident the bounding volume is also unchanged. +//////////////////////////////////////////////////////////////////// +CPT(BoundingVolume) PandaNode:: +get_bounds(UpdateSeq &seq, Thread *current_thread) const { + int pipeline_stage = current_thread->get_pipeline_stage(); + CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread); + if (cdata->_last_update != cdata->_next_update) { + // The cache is stale; it needs to be rebuilt. + CPT(BoundingVolume) result; + { + CDStageWriter cdataw = + ((PandaNode *)this)->update_bounds(pipeline_stage, cdata); + result = cdataw->_external_bounds; + seq = cdataw->_last_update; + } + return result; + } + seq = cdata->_last_update; + return cdata->_external_bounds; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::mark_bounds_stale // Access: Published @@ -3079,6 +3114,7 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) { drawmask_cat.debug(false) << "} " << *this << "::update_bounds();\n"; } + return cdataw; } diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 8969cd0668..f9e4fc6494 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -240,6 +240,7 @@ PUBLISHED: void set_bound(const BoundingVolume *volume); INLINE void clear_bounds(); CPT(BoundingVolume) get_bounds(Thread *current_thread = Thread::get_current_thread()) const; + CPT(BoundingVolume) get_bounds(UpdateSeq &seq, Thread *current_thread = Thread::get_current_thread()) const; INLINE CPT(BoundingVolume) get_internal_bounds() const; void mark_bounds_stale(Thread *current_thread = Thread::get_current_thread()) const;