LODNode should test its bounding volumes
This commit is contained in:
parent
1ea1517e59
commit
2ddcce6ad2
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<CData> _cycler;
|
||||
typedef CycleDataReader<CData> CDReader;
|
||||
typedef CycleDataWriter<CData> CDWriter;
|
||||
typedef CycleDataLockedReader<CData> CDLockedReader;
|
||||
typedef CycleDataStageReader<CData> CDStageReader;
|
||||
typedef CycleDataStageWriter<CData> CDStageWriter;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue