diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 8bede7327b..3b6dee026a 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -1338,7 +1338,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { // Now actually compute the bounding volume. We do this by using // calc_tight_bounds to determine our box first. LPoint3 pmin, pmax; - PN_stdfloat sq_center_dist; + PN_stdfloat sq_center_dist = 0.0f; bool found_any = false; do_calc_tight_bounds(pmin, pmax, sq_center_dist, found_any, vertex_data, false, LMatrix4::ident_mat(), @@ -1379,7 +1379,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { LPoint3 aabb_center = (pmin + pmax) * 0.5f; PN_stdfloat best_sq_radius = (pmax - aabb_center).length_squared(); - if (btype != BoundingVolume::BT_fastest && + if (btype != BoundingVolume::BT_fastest && best_sq_radius > 0.0f && aabb_center.length_squared() / best_sq_radius >= (0.2f * 0.2f)) { // Hmm, this is an off-center model. Maybe we can do a better job // by calculating the bounding sphere from the AABB center. @@ -1389,7 +1389,8 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { 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) { + if (found_any && better_sq_radius > 0.0f && + 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) { @@ -1409,7 +1410,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { cdata->_internal_bounds = new BoundingBox(pmin, pmax); break; - } else if (sq_center_dist <= best_sq_radius) { + } else if (sq_center_dist >= 0.0f && 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 = @@ -1420,7 +1421,8 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { // 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)); + new BoundingSphere(aabb_center, + (best_sq_radius > 0.0f) ? csqrt(best_sq_radius) : 0.0f); break; } } diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 2cd5e2739a..6fa66329ba 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -1607,13 +1607,16 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, } if (got_mat) { - if (!found_any) { - reader.set_row_unsafe(cdata->_first_vertex); + // Find the first non-NaN vertex. + while (!found_any && i < cdata->_num_vertices) { + reader.set_row(cdata->_first_vertex + i); 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; + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } ++i; } @@ -1630,13 +1633,16 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, sq_center_dist = max(sq_center_dist, vertex.length_squared()); } } else { - 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; + // Find the first non-NaN vertex. + while (!found_any && i < cdata->_num_vertices) { + reader.set_row(cdata->_first_vertex + i); + LPoint3 first_vertex = reader.get_data3(); + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } ++i; } @@ -1664,15 +1670,19 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, int strip_cut_index = get_strip_cut_index(cdata->_index_type); 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; + // Find the first non-NaN vertex. + while (!found_any && !index.is_at_end()) { + int ii = index.get_data1i(); + if (ii != strip_cut_index) { + reader.set_row(ii); + LPoint3 first_vertex = mat.xform_point(reader.get_data3()); + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } + } } while (!index.is_at_end()) { @@ -1692,15 +1702,19 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, sq_center_dist = max(sq_center_dist, vertex.length_squared()); } } 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; + // Find the first non-NaN vertex. + while (!found_any && !index.is_at_end()) { + int ii = index.get_data1i(); + if (ii != strip_cut_index) { + reader.set_row(ii); + LVecBase3 first_vertex = reader.get_data3(); + if (!first_vertex.is_nan()) { + min_point = first_vertex; + max_point = first_vertex; + sq_center_dist = first_vertex.length_squared(); + found_any = true; + } + } } while (!index.is_at_end()) { diff --git a/tests/gobj/test_geom.py b/tests/gobj/test_geom.py index 2a5a462659..24eeab852f 100644 --- a/tests/gobj/test_geom.py +++ b/tests/gobj/test_geom.py @@ -40,3 +40,45 @@ def test_geom_decompose(): # Old primitive should still be unchanged assert prim == geom.get_primitive(0) + + +def test_geom_calc_sphere_bounds(): + # Ensure that it ignores NaN + data = core.GeomVertexData("", core.GeomVertexFormat.get_v3(), core.Geom.UH_static) + vertex = core.GeomVertexWriter(data, "vertex") + vertex.add_data3((float("NaN"), 0, 0)) + vertex.add_data3((1, 1, 1)) + vertex.add_data3((1, 1, 2)) + + prim = core.GeomPoints(core.Geom.UH_static) + prim.add_next_vertices(3) + + geom = core.Geom(data) + geom.add_primitive(prim) + geom.set_bounds_type(core.BoundingVolume.BT_sphere) + + bounds = geom.get_bounds() + assert isinstance(bounds, core.BoundingSphere) + assert bounds.get_center() == (1, 1, 1.5) + assert bounds.get_radius() == 0.5 + + +def test_geom_calc_box_bounds(): + # Ensure that it ignores NaN + data = core.GeomVertexData("", core.GeomVertexFormat.get_v3(), core.Geom.UH_static) + vertex = core.GeomVertexWriter(data, "vertex") + vertex.add_data3((float("NaN"), 0, 0)) + vertex.add_data3((1, 1, 1)) + vertex.add_data3((1, 1, 2)) + + prim = core.GeomPoints(core.Geom.UH_static) + prim.add_next_vertices(3) + + geom = core.Geom(data) + geom.add_primitive(prim) + geom.set_bounds_type(core.BoundingVolume.BT_box) + + bounds = geom.get_bounds() + assert isinstance(bounds, core.BoundingBox) + assert bounds.get_min() == (1, 1, 1) + assert bounds.get_max() == (1, 1, 2)