[Bf-blender-cvs] [6dd89afa966] master: Fix obj exporter tests by deduping normals and printing with less precision.

Howard Trickey noreply at git.blender.org
Tue Jan 18 05:27:27 CET 2022


Commit: 6dd89afa966042f8ae402c848655ac0dc0d795fe
Author: Howard Trickey
Date:   Mon Jan 17 23:22:40 2022 -0500
Branches: master
https://developer.blender.org/rB6dd89afa966042f8ae402c848655ac0dc0d795fe

Fix obj exporter tests by deduping normals and printing with less precision.

Some new obj exporter tests were disabled because the normals were different
in the last decimal place on different platforms.
The old python exporter deduped normals with their coordinates rounded to
four decimal places. This change does the same in the new exporter.
On one test, this produced a file 25% smaller and even ran 10% faster.

===================================================================

M	source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
M	source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
M	source/blender/io/wavefront_obj/exporter/obj_export_io.hh
M	source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
M	source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
M	source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc

===================================================================

diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
index 45fa75c65b3..8c845c34db2 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
@@ -175,23 +175,13 @@ void OBJWriter::write_uv_coords(OBJMesh &r_obj_mesh_data) const
   }
 }
 
-void OBJWriter::write_poly_normals(const OBJMesh &obj_mesh_data) const
+void OBJWriter::write_poly_normals(OBJMesh &obj_mesh_data)
 {
   obj_mesh_data.ensure_mesh_normals();
-  Vector<float3> lnormals;
-  const int tot_polygons = obj_mesh_data.tot_polygons();
-  for (int i = 0; i < tot_polygons; i++) {
-    if (obj_mesh_data.is_ith_poly_smooth(i)) {
-      obj_mesh_data.calc_loop_normals(i, lnormals);
-      for (const float3 &lnormal : lnormals) {
-        file_handler_->write<eOBJSyntaxElement::normal>(lnormal[0], lnormal[1], lnormal[2]);
-      }
-    }
-    else {
-      float3 poly_normal = obj_mesh_data.calc_poly_normal(i);
-      file_handler_->write<eOBJSyntaxElement::normal>(
-          poly_normal[0], poly_normal[1], poly_normal[2]);
-    }
+  Vector<float3> normals;
+  obj_mesh_data.store_normal_coords_and_indices(normals);
+  for (const float3 &normal : normals) {
+    file_handler_->write<eOBJSyntaxElement::normal>(normal[0], normal[1], normal[2]);
   }
 }
 
@@ -298,28 +288,17 @@ void OBJWriter::write_poly_elements(const OBJMesh &obj_mesh_data,
   const func_vert_uv_normal_indices poly_element_writer = get_poly_element_writer(
       obj_mesh_data.tot_uv_vertices());
 
-  /* Number of normals may not be equal to number of polygons due to smooth shading. */
-  int per_object_tot_normals = 0;
   const int tot_polygons = obj_mesh_data.tot_polygons();
   for (int i = 0; i < tot_polygons; i++) {
     Vector<int> poly_vertex_indices = obj_mesh_data.calc_poly_vertex_indices(i);
     Span<int> poly_uv_indices = obj_mesh_data.calc_poly_uv_indices(i);
-    /* For an Object, a normal index depends on how many of its normals have been written before
-     * it. This is unknown because of smooth shading. So pass "per object total normals"
-     * and update it after each call. */
-    int new_normals = 0;
-    Vector<int> poly_normal_indices;
-    std::tie(new_normals, poly_normal_indices) = obj_mesh_data.calc_poly_normal_indices(
-        i, per_object_tot_normals);
-    per_object_tot_normals += new_normals;
+    Vector<int> poly_normal_indices = obj_mesh_data.calc_poly_normal_indices(i);
 
     last_poly_smooth_group = write_smooth_group(obj_mesh_data, i, last_poly_smooth_group);
     last_poly_vertex_group = write_vertex_group(obj_mesh_data, i, last_poly_vertex_group);
     last_poly_mat_nr = write_poly_material(obj_mesh_data, i, last_poly_mat_nr, matname_fn);
     (this->*poly_element_writer)(poly_vertex_indices, poly_uv_indices, poly_normal_indices);
   }
-  /* Unusual: Other indices are updated in #OBJWriter::update_index_offsets. */
-  index_offsets_.normal_offset += per_object_tot_normals;
 }
 
 void OBJWriter::write_edges_indices(const OBJMesh &obj_mesh_data) const
@@ -390,7 +369,7 @@ void OBJWriter::update_index_offsets(const OBJMesh &obj_mesh_data)
 {
   index_offsets_.vertex_offset += obj_mesh_data.tot_vertices();
   index_offsets_.uv_vertex_offset += obj_mesh_data.tot_uv_vertices();
-  /* Normal index is updated right after writing the normals. */
+  index_offsets_.normal_offset += obj_mesh_data.tot_normal_indices();
 }
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
index 3403d059068..1cad179a70c 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
@@ -79,13 +79,14 @@ class OBJWriter : NonMovable, NonCopyable {
   void write_vertex_coords(const OBJMesh &obj_mesh_data) const;
   /**
    * Write UV vertex coordinates for all vertices as `vt u v`.
-   * \note UV indices are stored here, but written later.
+   * \note UV indices are stored here, but written with polygons later.
    */
   void write_uv_coords(OBJMesh &obj_mesh_data) const;
   /**
    * Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z".
+   * \note Normal indices ares stored here, but written with polygons later.
    */
-  void write_poly_normals(const OBJMesh &obj_mesh_data) const;
+  void write_poly_normals(OBJMesh &obj_mesh_data);
   /**
    * Write smooth group if polygon at the given index is shaded smooth else "s 0"
    */
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
index 6d0ff1aa6a5..a6f0174d68b 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
@@ -130,7 +130,7 @@ syntax_elem_to_formatting(const eOBJSyntaxElement key)
       return {"vt %f %f\n", 2, is_type_float<T...>};
     }
     case eOBJSyntaxElement::normal: {
-      return {"vn %f %f %f\n", 3, is_type_float<T...>};
+      return {"vn %.4f %.4f %.4f\n", 3, is_type_float<T...>};
     }
     case eOBJSyntaxElement::poly_element_begin: {
       return {"f", 0, is_type_string_related<T...>};
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
index ab1448aa10c..ea39235aa1d 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
@@ -27,6 +27,7 @@
 #include "BKE_object.h"
 
 #include "BLI_listbase.h"
+#include "BLI_map.hh"
 #include "BLI_math.h"
 
 #include "DEG_depsgraph_query.h"
@@ -146,6 +147,11 @@ int16_t OBJMesh::tot_materials() const
   return export_mesh_eval_->totcol;
 }
 
+int OBJMesh::tot_normal_indices() const
+{
+  return tot_normal_indices_;
+}
+
 int OBJMesh::ith_smooth_group(const int poly_index) const
 {
   /* Calculate smooth groups first: #OBJMesh::calc_smooth_groups. */
@@ -297,6 +303,7 @@ Span<int> OBJMesh::calc_poly_uv_indices(const int poly_index) const
   BLI_assert(poly_index < uv_indices_.size());
   return uv_indices_[poly_index];
 }
+
 float3 OBJMesh::calc_poly_normal(const int poly_index) const
 {
   float3 r_poly_normal;
@@ -308,41 +315,87 @@ float3 OBJMesh::calc_poly_normal(const int poly_index) const
   return r_poly_normal;
 }
 
-void OBJMesh::calc_loop_normals(const int poly_index, Vector<float3> &r_loop_normals) const
+/** Round \a f to \a round_digits decimal digits. */
+static float round_float_to_n_digits(const float f, int round_digits)
 {
-  r_loop_normals.clear();
-  const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
-  const float(
-      *lnors)[3] = (const float(*)[3])(CustomData_get_layer(&export_mesh_eval_->ldata, CD_NORMAL));
-  for (int loop_of_poly = 0; loop_of_poly < mpoly.totloop; loop_of_poly++) {
-    float3 loop_normal;
-    copy_v3_v3(loop_normal, lnors[mpoly.loopstart + loop_of_poly]);
-    mul_mat3_m4_v3(world_and_axes_transform_, loop_normal);
-    r_loop_normals.append(loop_normal);
+  float scale = powf(10.0, round_digits);
+  return ceilf((scale * f - 0.49999999f)) / scale;
+}
+
+static float3 round_float3_to_n_digits(const float3 &v, int round_digits)
+{
+  float3 ans;
+  ans.x = round_float_to_n_digits(v.x, round_digits);
+  ans.y = round_float_to_n_digits(v.y, round_digits);
+  ans.z = round_float_to_n_digits(v.z, round_digits);
+  return ans;
+}
+
+void OBJMesh::store_normal_coords_and_indices(Vector<float3> &r_normal_coords)
+{
+  /* We'll round normal components to 4 digits.
+   * This will cover up some minor differences
+   * between floating point calculations on different platforms.
+   * Since normals are normalized, there will be no perceptible loss
+   * of precision when rounding to 4 digits. */
+  constexpr int round_digits = 4;
+  int cur_normal_index = 0;
+  Map<float3, int> normal_to_index;
+  /* We don't know how many unique normals there will be, but this is a guess.*/
+  normal_to_index.reserve(export_mesh_eval_->totpoly);
+  loop_to_normal_index_.resize(export_mesh_eval_->totloop);
+  loop_to_normal_index_.fill(-1);
+  const float(*lnors)[3] = (const float(*)[3])(
+      CustomData_get_layer(&export_mesh_eval_->ldata, CD_NORMAL));
+  for (int poly_index = 0; poly_index < export_mesh_eval_->totpoly; ++poly_index) {
+    const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
+    bool need_per_loop_normals = is_ith_poly_smooth(poly_index);
+    if (need_per_loop_normals) {
+      for (int loop_of_poly = 0; loop_of_poly < mpoly.totloop; ++loop_of_poly) {
+        float3 loop_normal;
+        int loop_index = mpoly.loopstart + loop_of_poly;
+        BLI_assert(loop_index < export_mesh_eval_->totloop);
+        copy_v3_v3(loop_normal, lnors[loop_index]);
+        mul_mat3_m4_v3(world_and_axes_transform_, loop_normal);
+        float3 rounded_loop_normal = round_float3_to_n_digits(loop_normal, round_digits);
+        int loop_norm_index = normal_to_index.lookup_default(rounded_loop_normal, -1);
+        if (loop_norm_index == -1) {
+          loop_norm_index = cur_normal_index++;
+          normal_to_index.add(rounded_loop_normal, loop_norm_index);
+          r_normal_coords.append(rounded_loop_normal);
+        }
+        loop_to_normal_index_[loop_index] = loop_norm_index;
+      }
+    }
+    else {
+      float3 poly_normal = calc_poly_normal(poly_index);
+      float3 rounded_poly_normal = round_float3_to_n_digits(poly_normal, round_digits);
+      int poly_norm_index = normal_to_index.lookup_default(rounded_poly_normal, -1);
+      if (poly_norm_index == -1) {
+        poly_norm_index = cur_normal_index++;
+        normal_to_index.add(rounded_poly_normal, poly_norm_index);
+        r_normal_coords.append(rounded_poly_normal);
+      }
+      for (int i = 0; i < mpoly.totloop; ++i) {
+        int loop_index = mpoly.loopstart + i;
+        BLI_assert(loop_index < export_mesh_eval_->totloop);
+        loop_to_normal_index_[loop_index] = poly_norm_index;
+

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list