OBJ: add split by objects/groups import options (T103839)
The new C++ OBJ importer was missing "split by objects" / "split by groups" import settings of the older Python importer. Implements T103839. Added test coverage for all 4 possible combinations of these two options.
This commit is contained in:
parent
b2746876f2
commit
b599820418
|
@ -410,6 +410,8 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op)
|
|||
import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size");
|
||||
import_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis");
|
||||
import_params.up_axis = RNA_enum_get(op->ptr, "up_axis");
|
||||
import_params.use_split_objects = RNA_boolean_get(op->ptr, "use_split_objects");
|
||||
import_params.use_split_groups = RNA_boolean_get(op->ptr, "use_split_groups");
|
||||
import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups");
|
||||
import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
|
||||
import_params.relative_paths = ((U.flag & USER_RELPATHS) != 0);
|
||||
|
@ -472,6 +474,8 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr)
|
|||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Options"), ICON_EXPORT);
|
||||
col = uiLayoutColumn(box, false);
|
||||
uiItemR(col, imfptr, "use_split_objects", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, imfptr, "use_split_groups", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, imfptr, "import_vertex_groups", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
|
||||
}
|
||||
|
@ -531,6 +535,16 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
|
|||
RNA_def_property_update_runtime(prop, (void *)forward_axis_update);
|
||||
prop = RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
|
||||
RNA_def_property_update_runtime(prop, (void *)up_axis_update);
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_split_objects",
|
||||
true,
|
||||
"Split By Object",
|
||||
"Import each OBJ 'o' as a separate object");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_split_groups",
|
||||
false,
|
||||
"Split By Group",
|
||||
"Import each OBJ 'g' as a separate object");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"import_vertex_groups",
|
||||
false,
|
||||
|
|
|
@ -68,6 +68,8 @@ struct OBJImportParams {
|
|||
float global_scale;
|
||||
eIOAxis forward_axis;
|
||||
eIOAxis up_axis;
|
||||
bool use_split_objects;
|
||||
bool use_split_groups;
|
||||
bool import_vertex_groups;
|
||||
bool validate_meshes;
|
||||
bool relative_paths;
|
||||
|
|
|
@ -363,6 +363,24 @@ static void geom_update_smooth_group(const char *p, const char *end, bool &r_sta
|
|||
r_state_shaded_smooth = smooth != 0;
|
||||
}
|
||||
|
||||
static void geom_new_object(const char *p,
|
||||
const char *end,
|
||||
bool &r_state_shaded_smooth,
|
||||
std::string &r_state_group_name,
|
||||
int &r_state_material_index,
|
||||
Geometry *&r_curr_geom,
|
||||
Vector<std::unique_ptr<Geometry>> &r_all_geometries)
|
||||
{
|
||||
r_state_shaded_smooth = false;
|
||||
r_state_group_name = "";
|
||||
/* Reset object-local material index that's used in face infos.
|
||||
* NOTE: do not reset the material name; that has to carry over
|
||||
* into the next object if needed. */
|
||||
r_state_material_index = -1;
|
||||
r_curr_geom = create_geometry(
|
||||
r_curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries);
|
||||
}
|
||||
|
||||
OBJParser::OBJParser(const OBJImportParams &import_params, size_t read_buffer_size = 64 * 1024)
|
||||
: import_params_(import_params), read_buffer_size_(read_buffer_size)
|
||||
{
|
||||
|
@ -534,22 +552,34 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
|
|||
}
|
||||
/* Objects. */
|
||||
else if (parse_keyword(p, end, "o")) {
|
||||
state_shaded_smooth = false;
|
||||
state_group_name = "";
|
||||
/* Reset object-local material index that's used in face infos.
|
||||
* NOTE: do not reset the material name; that has to carry over
|
||||
* into the next object if needed. */
|
||||
state_material_index = -1;
|
||||
curr_geom = create_geometry(
|
||||
curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries);
|
||||
if (import_params_.use_split_objects) {
|
||||
geom_new_object(p,
|
||||
end,
|
||||
state_shaded_smooth,
|
||||
state_group_name,
|
||||
state_material_index,
|
||||
curr_geom,
|
||||
r_all_geometries);
|
||||
}
|
||||
}
|
||||
/* Groups. */
|
||||
else if (parse_keyword(p, end, "g")) {
|
||||
geom_update_group(StringRef(p, end).trim(), state_group_name);
|
||||
int new_index = curr_geom->group_indices_.size();
|
||||
state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index);
|
||||
if (new_index == state_group_index) {
|
||||
curr_geom->group_order_.append(state_group_name);
|
||||
if (import_params_.use_split_groups) {
|
||||
geom_new_object(p,
|
||||
end,
|
||||
state_shaded_smooth,
|
||||
state_group_name,
|
||||
state_material_index,
|
||||
curr_geom,
|
||||
r_all_geometries);
|
||||
}
|
||||
else {
|
||||
geom_update_group(StringRef(p, end).trim(), state_group_name);
|
||||
int new_index = curr_geom->group_indices_.size();
|
||||
state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index);
|
||||
if (new_index == state_group_index) {
|
||||
curr_geom->group_order_.append(state_group_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Smoothing groups. */
|
||||
|
|
|
@ -53,7 +53,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
|
|||
obj->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, ob_name.c_str());
|
||||
|
||||
create_vertices(mesh);
|
||||
create_polys_loops(mesh, import_params.import_vertex_groups);
|
||||
create_polys_loops(mesh, import_params.import_vertex_groups && !import_params.use_split_groups);
|
||||
create_edges(mesh);
|
||||
create_uv_verts(mesh);
|
||||
create_normals(mesh);
|
||||
|
@ -222,8 +222,11 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups)
|
|||
continue;
|
||||
}
|
||||
const int group_index = curr_face.vertex_group_index;
|
||||
MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[mloop.v], group_index);
|
||||
dw->weight = 1.0f;
|
||||
/* Note: face might not belong to any group */
|
||||
if (group_index >= 0 || 1) {
|
||||
MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[mloop.v], group_index);
|
||||
dw->weight = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,19 @@ struct Expectation {
|
|||
|
||||
class obj_importer_test : public BlendfileLoadingBaseTest {
|
||||
public:
|
||||
obj_importer_test()
|
||||
{
|
||||
params.global_scale = 1.0f;
|
||||
params.clamp_size = 0;
|
||||
params.forward_axis = IO_AXIS_NEGATIVE_Z;
|
||||
params.up_axis = IO_AXIS_Y;
|
||||
params.validate_meshes = true;
|
||||
params.use_split_objects = true;
|
||||
params.use_split_groups = false;
|
||||
params.import_vertex_groups = false;
|
||||
params.relative_paths = true;
|
||||
params.clear_selection = true;
|
||||
}
|
||||
void import_and_check(const char *path,
|
||||
const Expectation *expect,
|
||||
size_t expect_count,
|
||||
|
@ -59,16 +72,6 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
|
|||
return;
|
||||
}
|
||||
|
||||
OBJImportParams params;
|
||||
params.global_scale = 1.0f;
|
||||
params.clamp_size = 0;
|
||||
params.forward_axis = IO_AXIS_NEGATIVE_Z;
|
||||
params.up_axis = IO_AXIS_Y;
|
||||
params.validate_meshes = true;
|
||||
params.import_vertex_groups = false;
|
||||
params.relative_paths = true;
|
||||
params.clear_selection = true;
|
||||
|
||||
std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path;
|
||||
strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1);
|
||||
const size_t read_buffer_size = 650;
|
||||
|
@ -81,6 +84,32 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
|
|||
deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
|
||||
DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | DEG_ITER_OBJECT_FLAG_VISIBLE |
|
||||
DEG_ITER_OBJECT_FLAG_DUPLI;
|
||||
|
||||
constexpr bool print_result_scene = false;
|
||||
if (print_result_scene) {
|
||||
printf("Result was:\n");
|
||||
DEG_OBJECT_ITER_BEGIN (°_iter_settings, object) {
|
||||
printf(" {\"%s\", ", object->id.name);
|
||||
if (object->type == OB_MESH) {
|
||||
Mesh *mesh = BKE_object_get_evaluated_mesh(object);
|
||||
const Span<float3> positions = mesh->vert_positions();
|
||||
printf("OB_MESH, %i, %i, %i, %i, float3(%g, %g, %g), float3(%g, %g, %g)",
|
||||
mesh->totvert,
|
||||
mesh->totedge,
|
||||
mesh->totpoly,
|
||||
mesh->totloop,
|
||||
positions.first().x,
|
||||
positions.first().y,
|
||||
positions.first().z,
|
||||
positions.last().x,
|
||||
positions.last().y,
|
||||
positions.last().z);
|
||||
}
|
||||
printf("},\n");
|
||||
}
|
||||
DEG_OBJECT_ITER_END;
|
||||
}
|
||||
|
||||
size_t object_index = 0;
|
||||
DEG_OBJECT_ITER_BEGIN (°_iter_settings, object) {
|
||||
if (object_index >= expect_count) {
|
||||
|
@ -152,6 +181,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
|
|||
const int ima_count = BLI_listbase_count(&bfile->main->images);
|
||||
EXPECT_EQ(ima_count, expect_image_count);
|
||||
}
|
||||
|
||||
OBJImportParams params;
|
||||
};
|
||||
|
||||
TEST_F(obj_importer_test, import_cube)
|
||||
|
@ -784,4 +815,58 @@ TEST_F(obj_importer_test, import_vertices)
|
|||
import_and_check("vertices.obj", expect, std::size(expect), 0);
|
||||
}
|
||||
|
||||
TEST_F(obj_importer_test, import_split_options_by_object)
|
||||
{
|
||||
/* Default is to split by object */
|
||||
Expectation expect[] = {
|
||||
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
|
||||
{"OBBox", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, -1, 1)},
|
||||
{"OBPyramid", OB_MESH, 5, 8, 5, 16, float3(3, 1, -1), float3(4, 0, 2)},
|
||||
};
|
||||
import_and_check("split_options.obj", expect, std::size(expect), 0);
|
||||
}
|
||||
|
||||
TEST_F(obj_importer_test, import_split_options_by_group)
|
||||
{
|
||||
params.use_split_objects = false;
|
||||
params.use_split_groups = true;
|
||||
Expectation expect[] = {
|
||||
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
|
||||
{"OBBoxOne", OB_MESH, 4, 4, 1, 4, float3(1, -1, -1), float3(-1, -1, 1)},
|
||||
{"OBBoxTwo", OB_MESH, 6, 7, 2, 8, float3(1, 1, 1), float3(-1, -1, 1)},
|
||||
{"OBBoxTwo.001", OB_MESH, 6, 7, 2, 8, float3(1, 1, -1), float3(-1, -1, -1)},
|
||||
{"OBPyrBottom", OB_MESH, 4, 4, 1, 4, float3(3, 1, -1), float3(3, -1, -1)},
|
||||
{"OBPyrSides", OB_MESH, 5, 8, 4, 12, float3(3, 1, -1), float3(4, 0, 2)},
|
||||
{"OBsplit_options", OB_MESH, 4, 4, 1, 4, float3(1, 1, -1), float3(-1, 1, 1)},
|
||||
};
|
||||
import_and_check("split_options.obj", expect, std::size(expect), 0);
|
||||
}
|
||||
|
||||
TEST_F(obj_importer_test, import_split_options_by_object_and_group)
|
||||
{
|
||||
params.use_split_objects = true;
|
||||
params.use_split_groups = true;
|
||||
Expectation expect[] = {
|
||||
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
|
||||
{"OBBox", OB_MESH, 4, 4, 1, 4, float3(1, 1, -1), float3(-1, 1, 1)},
|
||||
{"OBBoxOne", OB_MESH, 4, 4, 1, 4, float3(1, -1, -1), float3(-1, -1, 1)},
|
||||
{"OBBoxTwo", OB_MESH, 6, 7, 2, 8, float3(1, 1, 1), float3(-1, -1, 1)},
|
||||
{"OBBoxTwo.001", OB_MESH, 6, 7, 2, 8, float3(1, 1, -1), float3(-1, -1, -1)},
|
||||
{"OBPyrBottom", OB_MESH, 4, 4, 1, 4, float3(3, 1, -1), float3(3, -1, -1)},
|
||||
{"OBPyrSides", OB_MESH, 5, 8, 4, 12, float3(3, 1, -1), float3(4, 0, 2)},
|
||||
};
|
||||
import_and_check("split_options.obj", expect, std::size(expect), 0);
|
||||
}
|
||||
|
||||
TEST_F(obj_importer_test, import_split_options_none)
|
||||
{
|
||||
params.use_split_objects = false;
|
||||
params.use_split_groups = false;
|
||||
Expectation expect[] = {
|
||||
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
|
||||
{"OBsplit_options", OB_MESH, 13, 20, 11, 40, float3(1, 1, -1), float3(4, 0, 2)},
|
||||
};
|
||||
import_and_check("split_options.obj", expect, std::size(expect), 0);
|
||||
}
|
||||
|
||||
} // namespace blender::io::obj
|
||||
|
|
Loading…
Reference in New Issue