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:
-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