Browse Source

gobj: better handle NaNs in vertex data when calculating bounds

rdb 7 years ago
parent
commit
73452957ee
3 changed files with 94 additions and 36 deletions
  1. 7 5
      panda/src/gobj/geom.cxx
  2. 45 31
      panda/src/gobj/geomPrimitive.cxx
  3. 42 0
      tests/gobj/test_geom.py

+ 7 - 5
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;
         }
       }

+ 45 - 31
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()) {

+ 42 - 0
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)