[Bf-blender-cvs] [eb1755be355] master: Fix T96511: New OBJ exporter no longer groups faces by material

Aras Pranckevicius noreply at git.blender.org
Mon Mar 21 19:20:18 CET 2022


Commit: eb1755be355a7dc71e1274da53e2ba1405c08a9b
Author: Aras Pranckevicius
Date:   Mon Mar 21 20:15:36 2022 +0200
Branches: master
https://developer.blender.org/rBeb1755be355a7dc71e1274da53e2ba1405c08a9b

Fix T96511: New OBJ exporter no longer groups faces by material

Old python exporter in 3.0 and earlier ordered faces by material,
but the new C++ exporter in 3.1+ did not, and was just writing them
in whatever is the order of the mesh data structure.

This mostly does not cause problems, except in some apps e.g.
Procreate -- for large enough meshes, this lack of
"order by material" (which ends up having more usemtl lines)
ends up creating more mesh subsets than necessary inside Procreate.

The change is not computationally heavy, e.g. exporting 6-level
subdivided Monkey mesh goes 1085ms -> 1105ms on my machine.

Reviewed By: @howardt
Differential Revision: https://developer.blender.org/D14368

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

M	source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
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/exporter/obj_exporter.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 3f9fc3b1dbb..acfdaa29b52 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
@@ -308,14 +308,20 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
       obj_mesh_data.tot_uv_vertices());
 
   const int tot_polygons = obj_mesh_data.tot_polygons();
-  obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+  obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int idx) {
+    /* Polygon order for writing into the file is not necessarily the same
+     * as order in the mesh; it will be sorted by material indices. Remap current
+     * and previous indices here according to the order. */
+    int prev_i = obj_mesh_data.remap_poly_index(idx - 1);
+    int i = obj_mesh_data.remap_poly_index(idx);
+
     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);
     Vector<int> poly_normal_indices = obj_mesh_data.calc_poly_normal_indices(i);
 
     /* Write smoothing group if different from previous. */
     {
-      const int prev_group = get_smooth_group(obj_mesh_data, export_params_, i - 1);
+      const int prev_group = get_smooth_group(obj_mesh_data, export_params_, prev_i);
       const int group = get_smooth_group(obj_mesh_data, export_params_, i);
       if (group != prev_group) {
         buf.write<eOBJSyntaxElement::smooth_group>(group);
@@ -324,8 +330,8 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
 
     /* Write vertex group if different from previous. */
     if (export_params_.export_vertex_groups) {
-      const int16_t prev_group = i == 0 ? NEGATIVE_INIT :
-                                          obj_mesh_data.get_poly_deform_group_index(i - 1);
+      const int16_t prev_group = idx == 0 ? NEGATIVE_INIT :
+                                            obj_mesh_data.get_poly_deform_group_index(prev_i);
       const int16_t group = obj_mesh_data.get_poly_deform_group_index(i);
       if (group != prev_group) {
         buf.write<eOBJSyntaxElement::object_group>(
@@ -336,7 +342,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
 
     /* Write material name and material group if different from previous. */
     if (export_params_.export_materials && obj_mesh_data.tot_materials() > 0) {
-      const int16_t prev_mat = i == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(i - 1);
+      const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(prev_i);
       const int16_t mat = obj_mesh_data.ith_poly_matnr(i);
       if (mat != prev_mat) {
         if (mat == NOT_FOUND) {
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 7d3e3cd84fd..9975f300150 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,10 @@
 
 #include "obj_export_mesh.hh"
 
+#ifdef WITH_TBB
+#  include <tbb/parallel_sort.h>
+#endif
+
 namespace blender::io::obj {
 OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Object *mesh_object)
 {
@@ -76,6 +80,7 @@ void OBJMesh::clear()
   uv_coords_.clear_and_make_inline();
   loop_to_normal_index_.clear_and_make_inline();
   normal_coords_.clear_and_make_inline();
+  poly_order_.clear_and_make_inline();
   if (poly_smooth_groups_) {
     MEM_freeN(poly_smooth_groups_);
     poly_smooth_groups_ = nullptr;
@@ -193,6 +198,26 @@ void OBJMesh::calc_smooth_groups(const bool use_bitflags)
                                                    use_bitflags);
 }
 
+void OBJMesh::calc_poly_order()
+{
+  const int tot_polys = tot_polygons();
+  poly_order_.resize(tot_polys);
+  for (int i = 0; i < tot_polys; ++i) {
+    poly_order_[i] = i;
+  }
+  const MPoly *mpolys = export_mesh_eval_->mpoly;
+  /* Sort polygons by their material index. */
+#ifdef WITH_TBB
+  tbb::parallel_sort(poly_order_.begin(), poly_order_.end(), [&](int a, int b) {
+#else
+  std::sort(poly_order_.begin(), poly_order_.end(), [&](const Vert *a, const Vert *b) {
+#endif
+    int mat_a = mpolys[a].mat_nr;
+    int mat_b = mpolys[b].mat_nr;
+    return mat_a < mat_b;
+  });
+}
+
 const Material *OBJMesh::get_object_material(const int16_t mat_nr) 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 03dbd1b799f..7650a220810 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
@@ -95,6 +95,10 @@ class OBJMesh : NonCopyable {
    * Polygon aligned array of their smooth groups.
    */
   int *poly_smooth_groups_ = nullptr;
+  /**
+   * Order in which the polygons should be written into the file (sorted by material index).
+   */
+  Vector<int> poly_order_;
 
  public:
   /**
@@ -218,6 +222,22 @@ class OBJMesh : NonCopyable {
    */
   std::optional<std::array<int, 2>> calc_loose_edge_vert_indices(int edge_index) const;
 
+  /**
+   * Calculate the order in which the polygons should be written into the file (sorted by material
+   * index).
+   */
+  void calc_poly_order();
+
+  /**
+   * Remap polygon index according to polygon writing order.
+   * When materials are not being written, the polygon order array
+   * might be empty, in which case remap is a no-op.
+   */
+  int remap_poly_index(int i) const
+  {
+    return i < 0 || i >= poly_order_.size() ? i : poly_order_[i];
+  }
+
  private:
   /**
    * Free the mesh if _the exporter_ created it.
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
index 2cef5192337..30670d45ef7 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
@@ -204,6 +204,9 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
         if (export_params.export_smooth_groups) {
           obj.calc_smooth_groups(export_params.smooth_groups_bitflags);
         }
+        if (export_params.export_materials) {
+          obj.calc_poly_order();
+        }
         if (export_params.export_normals) {
           obj_writer.write_poly_normals(fh, obj);
         }



More information about the Bf-blender-cvs mailing list