Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHoward Trickey <howard.trickey@gmail.com>2022-01-18 07:22:40 +0300
committerHoward Trickey <howard.trickey@gmail.com>2022-01-18 07:22:40 +0300
commit6dd89afa966042f8ae402c848655ac0dc0d795fe (patch)
tree9c46d63d75f17bfa40a1622d6be2480a413eea22 /source/blender/io
parentdb496a0b7dcf1ab024aece9858257c40529138e4 (diff)
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.
Diffstat (limited to 'source/blender/io')
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc35
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh5
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_io.hh2
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc103
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh26
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc14
6 files changed, 116 insertions, 69 deletions
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;
+ }
+ }
}
+ tot_normal_indices_ = cur_normal_index;
}
-std::pair<int, Vector<int>> OBJMesh::calc_poly_normal_indices(
- const int poly_index, const int object_tot_prev_normals) const
+Vector<int> OBJMesh::calc_poly_normal_indices(const int poly_index) const
{
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
const int totloop = mpoly.totloop;
Vector<int> r_poly_normal_indices(totloop);
-
- if (is_ith_poly_smooth(poly_index)) {
- for (int poly_loop_index = 0; poly_loop_index < totloop; poly_loop_index++) {
- /* Using polygon loop index is fine because polygon/loop normals and their normal indices are
- * written by looping over #Mesh.mpoly /#Mesh.mloop in the same order. */
- r_poly_normal_indices[poly_loop_index] = object_tot_prev_normals + poly_loop_index;
- }
- /* For a smooth-shaded polygon, #Mesh.totloop -many loop normals are written. */
- return {totloop, r_poly_normal_indices};
- }
for (int poly_loop_index = 0; poly_loop_index < totloop; poly_loop_index++) {
- r_poly_normal_indices[poly_loop_index] = object_tot_prev_normals;
+ int loop_index = mpoly.loopstart + poly_loop_index;
+ r_poly_normal_indices[poly_loop_index] = loop_to_normal_index_[loop_index];
}
- /* For a flat-shaded polygon, one polygon normal is written. */
- return {1, r_poly_normal_indices};
+ return r_poly_normal_indices;
}
int16_t OBJMesh::get_poly_deform_group_index(const int poly_index) const
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
index e6d2853d040..3113a82b4d1 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
@@ -78,6 +78,15 @@ class OBJMesh : NonCopyable {
*/
Vector<Vector<int>> uv_indices_;
/**
+ * Per-loop normal index.
+ */
+ Vector<int> loop_to_normal_index_;
+ /*
+ * Total number of normal indices (maximum entry, plus 1, in
+ * the loop_to_norm_index_ vector).
+ */
+ int tot_normal_indices_ = NEGATIVE_INIT;
+ /**
* Total smooth groups in an object.
*/
int tot_smooth_groups_ = NEGATIVE_INIT;
@@ -97,6 +106,7 @@ class OBJMesh : NonCopyable {
int tot_vertices() const;
int tot_polygons() const;
int tot_uv_vertices() const;
+ int tot_normal_indices() const;
int tot_edges() const;
/**
@@ -162,18 +172,16 @@ class OBJMesh : NonCopyable {
*/
float3 calc_poly_normal(int poly_index) const;
/**
- * Calculate a polygon's polygon/loop normal indices.
- * \param object_tot_prev_normals Number of normals of this Object written so far.
- * \return Number of distinct normal indices.
+ * Find the unqique normals of the mesh and return them in \a r_normal_coords.
+ * Store the indices into that vector with for each loop in this OBJMesh.
*/
- std::pair<int, Vector<int>> calc_poly_normal_indices(int poly_index,
- int object_tot_prev_normals) const;
+ void store_normal_coords_and_indices(Vector<float3> &r_normal_coords);
/**
- * Calculate loop normals of a polygon at the given index.
- *
- * Should be used for smooth-shaded polygons.
+ * Calculate a polygon's polygon/loop normal indices.
+ * \param poly_index Index of the polygon to calculate indices for.
+ * \return Vector of normal indices, aligned with vertices of polygon.
*/
- void calc_loop_normals(int poly_index, Vector<float3> &r_loop_normals) const;
+ Vector<int> calc_poly_normal_indices(int poly_index) const;
/**
* Find the index of the vertex group with the maximum number of vertices in a polygon.
* The index indices into the #Object.defbase.
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
index 3b44a72ca0c..1890b349fd1 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
@@ -29,6 +29,8 @@
#include "obj_exporter_tests.hh"
namespace blender::io::obj {
+/* Set this true to keep comparison-failing test ouput in temp file directory. */
+constexpr bool save_failing_test_output = false;
/* This is also the test name. */
class obj_exporter_test : public BlendfileLoadingBaseTest {
@@ -294,8 +296,14 @@ class obj_exporter_regression_test : public obj_exporter_test {
std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj;
std::string golden_str = read_temp_file_in_string(golden_file_path);
- ASSERT_TRUE(strings_equal_after_first_lines(output_str, golden_str));
- BLI_delete(out_file_path.c_str(), false, false);
+ bool are_equal = strings_equal_after_first_lines(output_str, golden_str);
+ if (save_failing_test_output && !are_equal) {
+ printf("failing test output in %s\n", out_file_path.c_str());
+ }
+ ASSERT_TRUE(are_equal);
+ if (!save_failing_test_output || are_equal) {
+ BLI_delete(out_file_path.c_str(), false, false);
+ }
if (!golden_mtl.empty()) {
std::string out_mtl_file_path = tempdir + BLI_path_basename(golden_mtl.c_str());
std::string output_mtl_str = read_temp_file_in_string(out_mtl_file_path);
@@ -390,7 +398,6 @@ TEST_F(obj_exporter_regression_test, cube_all_data_triangulated)
_export.params);
}
-#if 0
TEST_F(obj_exporter_regression_test, suzanne_all_data)
{
OBJExportParamsDefault _export;
@@ -415,6 +422,5 @@ TEST_F(obj_exporter_regression_test, all_objects)
"io_tests/obj/all_objects.mtl",
_export.params);
}
-#endif
} // namespace blender::io::obj