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
Cherry-picked from: eb1755be35 + fab14f7854 + 8c072cdc93
This commit is contained in:
Aras Pranckevicius 2022-03-21 20:15:36 +02:00
parent 1781c6002a
commit b444ed2b38
Notes: blender-bot 2023-06-12 00:52:52 +02:00
Referenced by issue #96511, New OBJ exporter no longer groups faces by material, causing issues with some other apps (e.g. Procreate)
Referenced by issue #96241, 3.1: Potential candidates for corrective releases
4 changed files with 48 additions and 1 deletions

View File

@ -325,7 +325,12 @@ void OBJWriter::write_poly_elements(const OBJMesh &obj_mesh_data,
obj_mesh_data.tot_uv_vertices());
const int tot_polygons = obj_mesh_data.tot_polygons();
for (int i = 0; i < tot_polygons; i++) {
for (int idx = 0; idx < tot_polygons; 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
* index here according to the order. */
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);

View File

@ -195,6 +195,25 @@ 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. */
std::sort(poly_order_.begin(), poly_order_.end(), [&](int a, int b) {
int mat_a = mpolys[a].mat_nr;
int mat_b = mpolys[b].mat_nr;
if (mat_a != mat_b) {
return mat_a < mat_b;
}
return a < b;
});
}
const Material *OBJMesh::get_object_material(const int16_t mat_nr) const
{
/**

View File

@ -101,6 +101,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:
/**
@ -212,6 +216,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.

View File

@ -171,6 +171,9 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
if (export_params.export_smooth_groups) {
obj_mesh->calc_smooth_groups(export_params.smooth_groups_bitflags);
}
if (export_params.export_materials) {
obj_mesh->calc_poly_order();
}
if (export_params.export_normals) {
obj_writer.write_poly_normals(*obj_mesh);
}