Cull improvements: tighter sphere generation

This commit is contained in:
rdb 2015-01-08 23:17:06 +01:00
parent c04cd29246
commit b9899ba747
8 changed files with 310 additions and 108 deletions

View File

@ -386,7 +386,8 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
Thread *current_thread) const { Thread *current_thread) const {
CDReader cdata(_cycler, current_thread); CDReader cdata(_cycler, current_thread);
do_calc_tight_bounds(min_point, max_point, found_any, PN_stdfloat sq_radius;
do_calc_tight_bounds(min_point, max_point, sq_radius, found_any,
vertex_data, got_mat, mat, vertex_data, got_mat, mat,
InternalName::get_vertex(), InternalName::get_vertex(),
cdata, current_thread); cdata, current_thread);
@ -408,6 +409,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
INLINE void Geom:: INLINE void Geom::
calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, Thread *current_thread) const { bool &found_any, Thread *current_thread) const {
calc_tight_bounds(min_point, max_point, found_any, calc_tight_bounds(min_point, max_point, found_any,
get_vertex_data(current_thread), false, get_vertex_data(current_thread), false,
LMatrix4::ident_mat(), LMatrix4::ident_mat(),
@ -429,7 +431,8 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
Thread *current_thread) const { Thread *current_thread) const {
CDReader cdata(_cycler, current_thread); CDReader cdata(_cycler, current_thread);
do_calc_tight_bounds(min_point, max_point, found_any, PN_stdfloat sq_radius;
do_calc_tight_bounds(min_point, max_point, sq_radius, found_any,
vertex_data, got_mat, mat, vertex_data, got_mat, mat,
column_name, cdata, current_thread); column_name, cdata, current_thread);
} }

View File

@ -48,7 +48,7 @@ make_cow_copy() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: Geom::Constructor // Function: Geom::Constructor
// Access: Published // Access: Published
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
Geom:: Geom::
Geom(const GeomVertexData *data) { Geom(const GeomVertexData *data) {
@ -68,7 +68,7 @@ Geom(const GeomVertexData *data) {
Geom:: Geom::
Geom(const Geom &copy) : Geom(const Geom &copy) :
CopyOnWriteObject(copy), CopyOnWriteObject(copy),
_cycler(copy._cycler) _cycler(copy._cycler)
{ {
} }
@ -98,7 +98,7 @@ operator = (const Geom &copy) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: Geom::Destructor // Function: Geom::Destructor
// Access: Published, Virtual // Access: Published, Virtual
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
Geom:: Geom::
~Geom() { ~Geom() {
@ -224,7 +224,7 @@ offset_vertices(const GeomVertexData *data, int offset) {
gobj_cat.warning() gobj_cat.warning()
<< *prim << " is invalid for " << *data << ":\n"; << *prim << " is invalid for " << *data << ":\n";
prim->write(gobj_cat.warning(false), 4); prim->write(gobj_cat.warning(false), 4);
all_is_valid = false; all_is_valid = false;
} }
#endif #endif
@ -272,9 +272,9 @@ make_nonindexed(bool composite_only) {
// making nonindexed, since there's no particular advantage to // making nonindexed, since there's no particular advantage to
// having indexed points (as opposed to, say, indexed triangles or // having indexed points (as opposed to, say, indexed triangles or
// indexed lines). // indexed lines).
if (primitive->is_indexed() && if (primitive->is_indexed() &&
(primitive->is_composite() || (primitive->is_composite() ||
primitive->is_exact_type(GeomPoints::get_class_type()) || primitive->is_exact_type(GeomPoints::get_class_type()) ||
!composite_only)) { !composite_only)) {
primitive->make_nonindexed(new_data, orig_data); primitive->make_nonindexed(new_data, orig_data);
++num_changed; ++num_changed;
@ -720,11 +720,11 @@ unify_in_place(int max_indices, bool preserve_order) {
// Copy prim into smaller prims, no one of which has more than // Copy prim into smaller prims, no one of which has more than
// max_indices vertices. // max_indices vertices.
int i = 0; int i = 0;
while (i < prim->get_num_primitives()) { while (i < prim->get_num_primitives()) {
PT(GeomPrimitive) smaller = prim->make_copy(); PT(GeomPrimitive) smaller = prim->make_copy();
smaller->clear_vertices(); smaller->clear_vertices();
while (i < prim->get_num_primitives() && while (i < prim->get_num_primitives() &&
smaller->get_num_vertices() + prim->get_primitive_num_vertices(i) < max_indices) { smaller->get_num_vertices() + prim->get_primitive_num_vertices(i) < max_indices) {
int start = prim->get_primitive_start(i); int start = prim->get_primitive_start(i);
int end = prim->get_primitive_end(i); int end = prim->get_primitive_end(i);
@ -732,13 +732,13 @@ unify_in_place(int max_indices, bool preserve_order) {
smaller->add_vertex(prim->get_vertex(n)); smaller->add_vertex(prim->get_vertex(n));
} }
smaller->close_primitive(); smaller->close_primitive();
++i; ++i;
} }
cdata->_primitives.push_back(smaller.p()); cdata->_primitives.push_back(smaller.p());
} }
} else { } else {
// The prim has few enough vertices; keep it. // The prim has few enough vertices; keep it.
cdata->_primitives.push_back(prim); cdata->_primitives.push_back(prim);
@ -887,7 +887,7 @@ get_num_bytes() const {
int num_bytes = sizeof(Geom); int num_bytes = sizeof(Geom);
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
num_bytes += (*pi).get_read_pointer()->get_num_bytes(); num_bytes += (*pi).get_read_pointer()->get_num_bytes();
@ -914,7 +914,7 @@ request_resident() const {
bool resident = true; bool resident = true;
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
if (!(*pi).get_read_pointer()->request_resident()) { if (!(*pi).get_read_pointer()->request_resident()) {
@ -942,11 +942,11 @@ void Geom::
transform_vertices(const LMatrix4 &mat) { transform_vertices(const LMatrix4 &mat) {
PT(GeomVertexData) new_data = modify_vertex_data(); PT(GeomVertexData) new_data = modify_vertex_data();
CPT(GeomVertexFormat) format = new_data->get_format(); CPT(GeomVertexFormat) format = new_data->get_format();
int ci; int ci;
for (ci = 0; ci < format->get_num_points(); ci++) { for (ci = 0; ci < format->get_num_points(); ci++) {
GeomVertexRewriter data(new_data, format->get_point(ci)); GeomVertexRewriter data(new_data, format->get_point(ci));
while (!data.is_at_end()) { while (!data.is_at_end()) {
const LPoint3 &point = data.get_data3(); const LPoint3 &point = data.get_data3();
data.set_data3(point * mat); data.set_data3(point * mat);
@ -954,7 +954,7 @@ transform_vertices(const LMatrix4 &mat) {
} }
for (ci = 0; ci < format->get_num_vectors(); ci++) { for (ci = 0; ci < format->get_num_vectors(); ci++) {
GeomVertexRewriter data(new_data, format->get_vector(ci)); GeomVertexRewriter data(new_data, format->get_vector(ci));
while (!data.is_at_end()) { while (!data.is_at_end()) {
const LVector3 &vector = data.get_data3(); const LVector3 &vector = data.get_data3();
data.set_data3(normalize(vector * mat)); data.set_data3(normalize(vector * mat));
@ -1036,7 +1036,7 @@ get_nested_vertices(Thread *current_thread) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: Geom::output // Function: Geom::output
// Access: Published, Virtual // Access: Published, Virtual
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void Geom:: void Geom::
output(ostream &out) const { output(ostream &out) const {
@ -1046,7 +1046,7 @@ output(ostream &out) const {
int num_faces = 0; int num_faces = 0;
pset<TypeHandle> types; pset<TypeHandle> types;
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
CPT(GeomPrimitive) prim = (*pi).get_read_pointer(); CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
@ -1065,7 +1065,7 @@ output(ostream &out) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: Geom::write // Function: Geom::write
// Access: Published, Virtual // Access: Published, Virtual
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void Geom:: void Geom::
write(ostream &out, int indent_level) const { write(ostream &out, int indent_level) const {
@ -1073,7 +1073,7 @@ write(ostream &out, int indent_level) const {
// Get a list of the primitive types contained within this object. // Get a list of the primitive types contained within this object.
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
(*pi).get_read_pointer()->write(out, indent_level); (*pi).get_read_pointer()->write(out, indent_level);
@ -1228,7 +1228,7 @@ release_all() {
// rendered. // rendered.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
GeomContext *Geom:: GeomContext *Geom::
prepare_now(PreparedGraphicsObjects *prepared_objects, prepare_now(PreparedGraphicsObjects *prepared_objects,
GraphicsStateGuardianBase *gsg) { GraphicsStateGuardianBase *gsg) {
Contexts::const_iterator ci; Contexts::const_iterator ci;
ci = _contexts.find(prepared_objects); ci = _contexts.find(prepared_objects);
@ -1304,10 +1304,11 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
// Now actually compute the bounding volume. We do this by using // Now actually compute the bounding volume. We do this by using
// calc_tight_bounds to determine our box first. // calc_tight_bounds to determine our box first.
LPoint3 min, max; LPoint3 pmin, pmax;
PN_stdfloat sq_center_dist;
bool found_any = false; bool found_any = false;
do_calc_tight_bounds(min, max, found_any, vertex_data, do_calc_tight_bounds(pmin, pmax, sq_center_dist, found_any,
false, LMatrix4::ident_mat(), vertex_data, false, LMatrix4::ident_mat(),
InternalName::get_vertex(), InternalName::get_vertex(),
cdata, current_thread); cdata, current_thread);
@ -1318,22 +1319,83 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
if (found_any) { if (found_any) {
// Then we put the bounding volume around both of those points. // Then we put the bounding volume around both of those points.
if (btype == BoundingVolume::BT_sphere) { PN_stdfloat avg_box_area;
// The user specifically requested a BoundingSphere, so oblige. switch (btype) {
BoundingBox box(min, max); case BoundingVolume::BT_best:
box.local_object(); case BoundingVolume::BT_fastest:
case BoundingVolume::BT_default:
{
// When considering a box, calculate (roughly) the average area
// of the sides. We will use this to determine whether a sphere
// or box is a better fit.
PN_stdfloat min_extent = min(pmax[0] - pmin[0],
min(pmax[1] - pmin[1],
pmax[2] - pmin[2]));
PN_stdfloat max_extent = max(pmax[0] - pmin[0],
max(pmax[1] - pmin[1],
pmax[2] - pmin[2]));
avg_box_area = ((min_extent * min_extent) + (max_extent * max_extent)) / 2;
}
// Fall through
case BoundingVolume::BT_sphere:
{
// Determine the best radius for a bounding sphere.
LPoint3 aabb_center = (pmin + pmax) * 0.5f;
PN_stdfloat best_sq_radius = (pmax - aabb_center).length_squared();
PT(BoundingSphere) sphere = new BoundingSphere; if (btype != BoundingVolume::BT_fastest &&
sphere->extend_by(&box); aabb_center.length_squared() / best_sq_radius >= (0.2f * 0.2f)) {
cdata->_internal_bounds = sphere; // Hmm, this is an off-center model. Maybe we can do a better
// job by calculating the bounding sphere from the AABB center.
} else { PN_stdfloat better_sq_radius;
// The user requested a BoundingBox, or did not specify. bool found_any = false;
cdata->_internal_bounds = new BoundingBox(min, max); do_calc_sphere_radius(aabb_center, better_sq_radius, found_any,
vertex_data, cdata, current_thread);
if (found_any && better_sq_radius <= best_sq_radius) {
// Great. This is as good a sphere as we're going to get.
if (btype == BoundingVolume::BT_best &&
avg_box_area < better_sq_radius * MathNumbers::pi) {
// But the box is better, anyway. Use that instead.
cdata->_internal_bounds = new BoundingBox(pmin, pmax);
break;
}
cdata->_internal_bounds =
new BoundingSphere(aabb_center, csqrt(better_sq_radius));
break;
}
}
if (btype != BoundingVolume::BT_sphere &&
avg_box_area < sq_center_dist * MathNumbers::pi) {
// A box is probably a tighter fit.
cdata->_internal_bounds = new BoundingBox(pmin, pmax);
break;
} else if (sq_center_dist <= best_sq_radius) {
// No, but a sphere centered on the origin is apparently
// still better than a sphere around the bounding box.
cdata->_internal_bounds =
new BoundingSphere(LPoint3::origin(), csqrt(sq_center_dist));
break;
} else if (btype == BoundingVolume::BT_sphere) {
// This is the worst sphere we can make, which is why we will only
// do it when the user specifically requests a sphere.
cdata->_internal_bounds =
new BoundingSphere(aabb_center, csqrt(best_sq_radius));
break;
}
}
// Fall through.
case BoundingVolume::BT_box:
cdata->_internal_bounds = new BoundingBox(pmin, pmax);
} }
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
CPT(GeomPrimitive) prim = (*pi).get_read_pointer(); CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
@ -1360,18 +1422,38 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void Geom:: void Geom::
do_calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, do_calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, PN_stdfloat &sq_center_dist, bool &found_any,
const GeomVertexData *vertex_data, const GeomVertexData *vertex_data,
bool got_mat, const LMatrix4 &mat, bool got_mat, const LMatrix4 &mat,
const InternalName *column_name, const InternalName *column_name,
const CData *cdata, Thread *current_thread) const { const CData *cdata, Thread *current_thread) const {
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
CPT(GeomPrimitive) prim = (*pi).get_read_pointer(); CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
prim->calc_tight_bounds(min_point, max_point, found_any, vertex_data, prim->calc_tight_bounds(min_point, max_point, sq_center_dist,
got_mat, mat, column_name, current_thread); found_any, vertex_data, got_mat, mat,
column_name, current_thread);
}
}
////////////////////////////////////////////////////////////////////
// Function: Geom::do_calc_sphere_radius
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
void Geom::
do_calc_sphere_radius(const LPoint3 &center, PN_stdfloat &sq_radius,
bool &found_any, const GeomVertexData *vertex_data,
const CData *cdata, Thread *current_thread) const {
Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end();
++pi) {
CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
prim->calc_sphere_radius(center, sq_radius, found_any,
vertex_data, current_thread);
} }
} }
@ -1412,7 +1494,7 @@ check_will_be_valid(const GeomVertexData *vertex_data) const {
CDReader cdata(_cycler); CDReader cdata(_cycler);
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
if (!(*pi).get_read_pointer()->check_valid(vertex_data)) { if (!(*pi).get_read_pointer()->check_valid(vertex_data)) {
@ -1432,10 +1514,10 @@ void Geom::
reset_usage_hint(Geom::CData *cdata) { reset_usage_hint(Geom::CData *cdata) {
cdata->_usage_hint = UH_unspecified; cdata->_usage_hint = UH_unspecified;
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
cdata->_usage_hint = min(cdata->_usage_hint, cdata->_usage_hint = min(cdata->_usage_hint,
(*pi).get_read_pointer()->get_usage_hint()); (*pi).get_read_pointer()->get_usage_hint());
} }
cdata->_got_usage_hint = true; cdata->_got_usage_hint = true;
@ -1450,7 +1532,7 @@ void Geom::
reset_geom_rendering(Geom::CData *cdata) { reset_geom_rendering(Geom::CData *cdata) {
cdata->_geom_rendering = 0; cdata->_geom_rendering = 0;
Primitives::const_iterator pi; Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin(); for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end(); pi != cdata->_primitives.end();
++pi) { ++pi) {
cdata->_geom_rendering |= (*pi).get_read_pointer()->get_geom_rendering(); cdata->_geom_rendering |= (*pi).get_read_pointer()->get_geom_rendering();
@ -1626,7 +1708,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: Geom::CDataCache::Destructor // Function: Geom::CDataCache::Destructor
// Access: Public, Virtual // Access: Public, Virtual
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
Geom::CDataCache:: Geom::CDataCache::
~CDataCache() { ~CDataCache() {
@ -1661,11 +1743,11 @@ evict_callback() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: Geom::CacheEntry::output // Function: Geom::CacheEntry::output
// Access: Public, Virtual // Access: Public, Virtual
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void Geom::CacheEntry:: void Geom::CacheEntry::
output(ostream &out) const { output(ostream &out) const {
out << "geom " << (void *)_source << ", " out << "geom " << (void *)_source << ", "
<< (const void *)_key._modifier; << (const void *)_key._modifier;
} }
@ -1777,7 +1859,7 @@ check_usage_hint() const {
#ifdef DO_PIPELINING #ifdef DO_PIPELINING
unref_delete((CycleData *)_cdata); unref_delete((CycleData *)_cdata);
#endif #endif
Geom::CDWriter fresh_cdata(((Geom *)_object.p())->_cycler, Geom::CDWriter fresh_cdata(((Geom *)_object.p())->_cycler,
false, _current_thread); false, _current_thread);
((GeomPipelineReader *)this)->_cdata = fresh_cdata; ((GeomPipelineReader *)this)->_cdata = fresh_cdata;
#ifdef DO_PIPELINING #ifdef DO_PIPELINING
@ -1802,12 +1884,12 @@ check_usage_hint() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: GeomPipelineReader::check_valid // Function: GeomPipelineReader::check_valid
// Access: Public // Access: Public
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool GeomPipelineReader:: bool GeomPipelineReader::
check_valid(const GeomVertexDataPipelineReader *data_reader) const { check_valid(const GeomVertexDataPipelineReader *data_reader) const {
Geom::Primitives::const_iterator pi; Geom::Primitives::const_iterator pi;
for (pi = _cdata->_primitives.begin(); for (pi = _cdata->_primitives.begin();
pi != _cdata->_primitives.end(); pi != _cdata->_primitives.end();
++pi) { ++pi) {
CPT(GeomPrimitive) primitive = (*pi).get_read_pointer(); CPT(GeomPrimitive) primitive = (*pi).get_read_pointer();
@ -1820,7 +1902,7 @@ check_valid(const GeomVertexDataPipelineReader *data_reader) const {
return true; return true;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: GeomPipelineReader::draw // Function: GeomPipelineReader::draw
// Access: Public // Access: Public
@ -1833,7 +1915,7 @@ draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
bool all_ok = gsg->begin_draw_primitives(this, munger, data_reader, force); bool all_ok = gsg->begin_draw_primitives(this, munger, data_reader, force);
if (all_ok) { if (all_ok) {
Geom::Primitives::const_iterator pi; Geom::Primitives::const_iterator pi;
for (pi = _cdata->_primitives.begin(); for (pi = _cdata->_primitives.begin();
pi != _cdata->_primitives.end(); pi != _cdata->_primitives.end();
++pi) { ++pi) {
CPT(GeomPrimitive) primitive = (*pi).get_read_pointer(); CPT(GeomPrimitive) primitive = (*pi).get_read_pointer();

View File

@ -174,12 +174,17 @@ private:
void compute_internal_bounds(CData *cdata, Thread *current_thread) const; void compute_internal_bounds(CData *cdata, Thread *current_thread) const;
void do_calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, void do_calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, PN_stdfloat &sq_center_dist, bool &found_any,
const GeomVertexData *vertex_data, const GeomVertexData *vertex_data,
bool got_mat, const LMatrix4 &mat, bool got_mat, const LMatrix4 &mat,
const InternalName *column_name, const InternalName *column_name,
const CData *cdata, Thread *current_thread) const; const CData *cdata, Thread *current_thread) const;
void do_calc_sphere_radius(const LPoint3 &center,
PN_stdfloat &sq_radius, bool &found_any,
const GeomVertexData *vertex_data,
const CData *cdata, Thread *current_thread) const;
void clear_prepared(PreparedGraphicsObjects *prepared_objects); void clear_prepared(PreparedGraphicsObjects *prepared_objects);
bool check_will_be_valid(const GeomVertexData *vertex_data) const; bool check_will_be_valid(const GeomVertexData *vertex_data) const;

View File

@ -1699,10 +1699,14 @@ get_strip_cut_index(NumericType index_type) {
// points are found. It is the caller's responsibility // points are found. It is the caller's responsibility
// to initialize min_point, max_point, and found_any // to initialize min_point, max_point, and found_any
// before calling this function. // before calling this function.
// It also sets sq_center_dist, which is the square of
// the maximum distance of the points to the center.
// This can be useful when deciding whether a sphere
// volume might be more appropriate.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void GeomPrimitive:: void GeomPrimitive::
calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, PN_stdfloat &sq_center_dist, bool &found_any,
const GeomVertexData *vertex_data, const GeomVertexData *vertex_data,
bool got_mat, const LMatrix4 &mat, bool got_mat, const LMatrix4 &mat,
const InternalName *column_name, const InternalName *column_name,
@ -1714,54 +1718,84 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
} }
CDReader cdata(_cycler, current_thread); CDReader cdata(_cycler, current_thread);
int i = 0;
if (cdata->_vertices.is_null()) { if (cdata->_vertices.is_null()) {
// Nonindexed case. // Nonindexed case.
nassertv(cdata->_num_vertices != -1); nassertv(cdata->_num_vertices != -1);
if (cdata->_num_vertices == 0) {
return;
}
if (got_mat) { if (got_mat) {
for (int i = 0; i < cdata->_num_vertices; i++) { if (!found_any) {
reader.set_row_unsafe(cdata->_first_vertex);
LPoint3 first_vertex = mat.xform_point(reader.get_data3());
min_point = first_vertex;
max_point = first_vertex;
sq_center_dist = first_vertex.length_squared();
found_any = true;
++i;
}
for (; i < cdata->_num_vertices; ++i) {
reader.set_row_unsafe(cdata->_first_vertex + i); reader.set_row_unsafe(cdata->_first_vertex + i);
LPoint3 vertex = mat.xform_point(reader.get_data3()); LPoint3 vertex = mat.xform_point(reader.get_data3());
if (found_any) { min_point.set(min(min_point[0], vertex[0]),
min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]),
min(min_point[1], vertex[1]), min(min_point[2], vertex[2]));
min(min_point[2], vertex[2])); max_point.set(max(max_point[0], vertex[0]),
max_point.set(max(max_point[0], vertex[0]), max(max_point[1], vertex[1]),
max(max_point[1], vertex[1]), max(max_point[2], vertex[2]));
max(max_point[2], vertex[2])); sq_center_dist = max(sq_center_dist, vertex.length_squared());
} else {
min_point = vertex;
max_point = vertex;
found_any = true;
}
} }
} else { } else {
for (int i = 0; i < cdata->_num_vertices; i++) { if (!found_any) {
reader.set_row_unsafe(cdata->_first_vertex);
const LVecBase3 &first_vertex = reader.get_data3();
min_point = first_vertex;
max_point = first_vertex;
sq_center_dist = first_vertex.length_squared();
found_any = true;
++i;
}
for (; i < cdata->_num_vertices; ++i) {
reader.set_row_unsafe(cdata->_first_vertex + i); reader.set_row_unsafe(cdata->_first_vertex + i);
const LVecBase3 &vertex = reader.get_data3(); const LVecBase3 &vertex = reader.get_data3();
if (found_any) { min_point.set(min(min_point[0], vertex[0]),
min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]),
min(min_point[1], vertex[1]), min(min_point[2], vertex[2]));
min(min_point[2], vertex[2])); max_point.set(max(max_point[0], vertex[0]),
max_point.set(max(max_point[0], vertex[0]), max(max_point[1], vertex[1]),
max(max_point[1], vertex[1]), max(max_point[2], vertex[2]));
max(max_point[2], vertex[2])); sq_center_dist = max(sq_center_dist, vertex.length_squared());
} else {
min_point = vertex;
max_point = vertex;
found_any = true;
}
} }
} }
} else { } else {
// Indexed case. // Indexed case.
GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread); GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
if (index.is_at_end()) {
return;
}
int strip_cut_index = get_strip_cut_index(cdata->_index_type); int strip_cut_index = get_strip_cut_index(cdata->_index_type);
if (got_mat) { if (got_mat) {
if (!found_any) {
int first_index = index.get_data1i();
nassertv(first_index != strip_cut_index);
reader.set_row_unsafe(first_index);
LPoint3 first_vertex = mat.xform_point(reader.get_data3());
min_point = first_vertex;
max_point = first_vertex;
sq_center_dist = first_vertex.length_squared();
found_any = true;
}
while (!index.is_at_end()) { while (!index.is_at_end()) {
int ii = index.get_data1i(); int ii = index.get_data1i();
if (ii == strip_cut_index) { if (ii == strip_cut_index) {
@ -1770,20 +1804,26 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
reader.set_row_unsafe(ii); reader.set_row_unsafe(ii);
LPoint3 vertex = mat.xform_point(reader.get_data3()); LPoint3 vertex = mat.xform_point(reader.get_data3());
if (found_any) { min_point.set(min(min_point[0], vertex[0]),
min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]),
min(min_point[1], vertex[1]), min(min_point[2], vertex[2]));
min(min_point[2], vertex[2])); max_point.set(max(max_point[0], vertex[0]),
max_point.set(max(max_point[0], vertex[0]), max(max_point[1], vertex[1]),
max(max_point[1], vertex[1]), max(max_point[2], vertex[2]));
max(max_point[2], vertex[2])); sq_center_dist = max(sq_center_dist, vertex.length_squared());
} else {
min_point = vertex;
max_point = vertex;
found_any = true;
}
} }
} else { } else {
if (!found_any) {
int first_index = index.get_data1i();
nassertv(first_index != strip_cut_index);
reader.set_row_unsafe(first_index);
const LVecBase3 &first_vertex = reader.get_data3();
min_point = first_vertex;
max_point = first_vertex;
sq_center_dist = first_vertex.length_squared();
found_any = true;
}
while (!index.is_at_end()) { while (!index.is_at_end()) {
int ii = index.get_data1i(); int ii = index.get_data1i();
if (ii == strip_cut_index) { if (ii == strip_cut_index) {
@ -1792,23 +1832,81 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
reader.set_row_unsafe(ii); reader.set_row_unsafe(ii);
const LVecBase3 &vertex = reader.get_data3(); const LVecBase3 &vertex = reader.get_data3();
if (found_any) { min_point.set(min(min_point[0], vertex[0]),
min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]),
min(min_point[1], vertex[1]), min(min_point[2], vertex[2]));
min(min_point[2], vertex[2])); max_point.set(max(max_point[0], vertex[0]),
max_point.set(max(max_point[0], vertex[0]), max(max_point[1], vertex[1]),
max(max_point[1], vertex[1]), max(max_point[2], vertex[2]));
max(max_point[2], vertex[2])); sq_center_dist = max(sq_center_dist, vertex.length_squared());
} else {
min_point = vertex;
max_point = vertex;
found_any = true;
}
} }
} }
} }
} }
////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::calc_sphere_radius
// Access: Public, Virtual
// Description: Expands radius so that a sphere with the given
// center point fits all of the vertices.
//
// The center point is assumed to already have been
// transformed by the matrix, if one is given.
////////////////////////////////////////////////////////////////////
void GeomPrimitive::
calc_sphere_radius(const LPoint3 &center, PN_stdfloat &sq_radius,
bool &found_any, const GeomVertexData *vertex_data,
Thread *current_thread) const {
GeomVertexReader reader(vertex_data, InternalName::get_vertex(), current_thread);
if (!reader.has_column()) {
// No vertex data.
return;
}
if (!found_any) {
sq_radius = 0.0;
}
CDReader cdata(_cycler, current_thread);
if (cdata->_vertices.is_null()) {
// Nonindexed case.
nassertv(cdata->_num_vertices != -1);
if (cdata->_num_vertices == 0) {
return;
}
found_any = true;
for (int i = 0; i < cdata->_num_vertices; ++i) {
reader.set_row_unsafe(cdata->_first_vertex + i);
const LVecBase3 &vertex = reader.get_data3();
sq_radius = max(sq_radius, (vertex - center).length_squared());
}
} else {
// Indexed case.
GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
if (index.is_at_end()) {
return;
}
found_any = true;
int strip_cut_index = get_strip_cut_index(cdata->_index_type);
while (!index.is_at_end()) {
int ii = index.get_data1i();
if (ii == strip_cut_index) {
continue;
}
reader.set_row_unsafe(ii);
const LVecBase3 &vertex = reader.get_data3();
sq_radius = max(sq_radius, (vertex - center).length_squared());
}
}
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: GeomPrimitive::decompose_impl // Function: GeomPrimitive::decompose_impl
// Access: Protected, Virtual // Access: Protected, Virtual

View File

@ -207,12 +207,17 @@ public:
bool force) const=0; bool force) const=0;
void calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, void calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, PN_stdfloat &sq_center_dist, bool &found_any,
const GeomVertexData *vertex_data, const GeomVertexData *vertex_data,
bool got_mat, const LMatrix4 &mat, bool got_mat, const LMatrix4 &mat,
const InternalName *column_name, const InternalName *column_name,
Thread *current_thread) const; Thread *current_thread) const;
void calc_sphere_radius(const LPoint3 &center,
PN_stdfloat &sq_radius, bool &found_any,
const GeomVertexData *vertex_data,
Thread *current_thread) const;
protected: protected:
virtual CPT(GeomPrimitive) decompose_impl() const; virtual CPT(GeomPrimitive) decompose_impl() const;
virtual CPT(GeomVertexArrayData) rotate_impl() const; virtual CPT(GeomVertexArrayData) rotate_impl() const;

View File

@ -182,6 +182,9 @@ string_bounds_type(const string &str) {
} else if (strcmp(str.c_str(), "best") == 0) { } else if (strcmp(str.c_str(), "best") == 0) {
return BT_best; return BT_best;
} else if (strcmp(str.c_str(), "fastest") == 0) {
return BT_fastest;
} else if (strcmp(str.c_str(), "sphere") == 0) { } else if (strcmp(str.c_str(), "sphere") == 0) {
return BT_sphere; return BT_sphere;
@ -521,6 +524,9 @@ operator << (ostream &out, BoundingVolume::BoundsType type) {
case BoundingVolume::BT_best: case BoundingVolume::BT_best:
return out << "best"; return out << "best";
case BoundingVolume::BT_fastest:
return out << "fastest";
case BoundingVolume::BT_sphere: case BoundingVolume::BT_sphere:
return out << "sphere"; return out << "sphere";

View File

@ -106,6 +106,7 @@ PUBLISHED:
BT_best, BT_best,
BT_sphere, BT_sphere,
BT_box, BT_box,
BT_fastest,
}; };
public: public:

View File

@ -50,7 +50,9 @@ ConfigVariableEnum<BoundingVolume::BoundsType> bounds_type
("bounds-type", BoundingVolume::BT_sphere, ("bounds-type", BoundingVolume::BT_sphere,
PRC_DESC("Specify the type of bounding volume that is created automatically " PRC_DESC("Specify the type of bounding volume that is created automatically "
"by Panda to enclose geometry. Use 'sphere' or 'box', or use " "by Panda to enclose geometry. Use 'sphere' or 'box', or use "
"'best' to let Panda decide which is most appropriate.")); "'best' to let Panda decide which is most appropriate. You can "
"also use 'fastest' if you don't want Panda to waste much time "
"computing the most optimal bounding volume."));
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: init_libmathutil // Function: init_libmathutil