[Bf-blender-cvs] [e6a9b223844] master: OBJ: New C++ based wavefront OBJ importer

Ankit Meel noreply at git.blender.org
Mon Apr 4 12:41:24 CEST 2022


Commit: e6a9b223844346a34ce195652449fec3229a2ec1
Author: Ankit Meel
Date:   Mon Apr 4 13:36:10 2022 +0300
Branches: master
https://developer.blender.org/rBe6a9b223844346a34ce195652449fec3229a2ec1

OBJ: New C++ based wavefront OBJ importer

This takes state of soc-2020-io-performance branch as it was at
e9bbfd0c8c7 (2021 Oct 31), merges latest master (2022 Apr 4),
adds a bunch of tests, and fixes a bunch of stuff found by said
tests. The fixes are detailed in the differential.

Timings on my machine (Windows, VS2022 release build, AMD Ryzen
5950X 32 threads):

- Rungholt minecraft level (269MB file, 1 mesh): 54.2s -> 14.2s
  (memory usage: 7.0GB -> 1.9GB).
- Blender 3.0 splash scene: "I waited for 90 minutes and gave up"
  -> 109s. Now, this time is not great, but at least 20% of the
  time is spent assigning unique names for the imported objects
  (the scene has 24 thousand objects). This is not specific to obj
  importer, but rather a general issue across blender overall.

Test suite file updates done in Subversion tests repository.

Reviewed By: @howardt, @sybren
Differential Revision: https://developer.blender.org/D13958

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

M	release/scripts/startup/bl_ui/space_topbar.py
M	source/blender/editors/io/io_obj.c
M	source/blender/editors/io/io_obj.h
M	source/blender/editors/io/io_ops.c
M	source/blender/io/wavefront_obj/CMakeLists.txt
M	source/blender/io/wavefront_obj/IO_wavefront_obj.cc
M	source/blender/io/wavefront_obj/IO_wavefront_obj.h
A	source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc
A	source/blender/io/wavefront_obj/importer/importer_mesh_utils.hh
A	source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
A	source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh
A	source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
A	source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
A	source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
A	source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
A	source/blender/io/wavefront_obj/importer/obj_import_nurbs.cc
A	source/blender/io/wavefront_obj/importer/obj_import_nurbs.hh
A	source/blender/io/wavefront_obj/importer/obj_import_objects.hh
A	source/blender/io/wavefront_obj/importer/obj_importer.cc
A	source/blender/io/wavefront_obj/importer/obj_importer.hh
A	source/blender/io/wavefront_obj/importer/parser_string_utils.cc
A	source/blender/io/wavefront_obj/importer/parser_string_utils.hh
A	source/blender/io/wavefront_obj/tests/obj_importer_tests.cc

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

diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 0a428b4ea3f..55dcb4a20eb 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -456,6 +456,7 @@ class TOPBAR_MT_file_import(Menu):
                 "wm.usd_import", text="Universal Scene Description (.usd, .usdc, .usda)")
 
         self.layout.operator("wm.gpencil_import_svg", text="SVG as Grease Pencil")
+        self.layout.operator("wm.obj_import", text="Wavefront (.obj) (experimental)")
 
 
 class TOPBAR_MT_file_export(Menu):
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index df15191916a..97f1e08fdff 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -359,3 +359,87 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
   RNA_def_boolean(
       ot->srna, "smooth_group_bitflags", false, "Generate Bitflags for Smooth Groups", "");
 }
+
+static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+  WM_event_add_fileselect(C, op);
+  return OPERATOR_RUNNING_MODAL;
+}
+
+static int wm_obj_import_exec(bContext *C, wmOperator *op)
+{
+  if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+    BKE_report(op->reports, RPT_ERROR, "No filename given");
+    return OPERATOR_CANCELLED;
+  }
+
+  struct OBJImportParams import_params;
+  RNA_string_get(op->ptr, "filepath", import_params.filepath);
+  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");
+
+  OBJ_import(C, &import_params);
+
+  return OPERATOR_FINISHED;
+}
+
+static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr)
+{
+  uiLayoutSetPropSep(layout, true);
+  uiLayoutSetPropDecorate(layout, false);
+  uiLayout *box = uiLayoutBox(layout);
+
+  uiItemL(box, IFACE_("Transform"), ICON_OBJECT_DATA);
+  uiLayout *col = uiLayoutColumn(box, false);
+  uiLayout *sub = uiLayoutColumn(col, false);
+  uiItemR(sub, imfptr, "clamp_size", 0, NULL, ICON_NONE);
+  sub = uiLayoutColumn(col, false);
+  uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE);
+  uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE);
+}
+
+static void wm_obj_import_draw(bContext *C, wmOperator *op)
+{
+  PointerRNA ptr;
+  wmWindowManager *wm = CTX_wm_manager(C);
+  RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
+  ui_obj_import_settings(op->layout, &ptr);
+}
+
+void WM_OT_obj_import(struct wmOperatorType *ot)
+{
+  ot->name = "Import Wavefront OBJ";
+  ot->description = "Load a Wavefront OBJ scene";
+  ot->idname = "WM_OT_obj_import";
+
+  ot->invoke = wm_obj_import_invoke;
+  ot->exec = wm_obj_import_exec;
+  ot->poll = WM_operator_winactive;
+  ot->ui = wm_obj_import_draw;
+
+  WM_operator_properties_filesel(ot,
+                                 FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO,
+                                 FILE_BLENDER,
+                                 FILE_OPENFILE,
+                                 WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
+                                 FILE_DEFAULTDISPLAY,
+                                 FILE_SORT_ALPHA);
+  RNA_def_float(
+      ot->srna,
+      "clamp_size",
+      0.0f,
+      0.0f,
+      1000.0f,
+      "Clamp Bounding Box",
+      "Resize the objects to keep bounding box under this value. Value 0 diables clamping",
+      0.0f,
+      1000.0f);
+  RNA_def_enum(ot->srna,
+               "forward_axis",
+               io_obj_transform_axis_forward,
+               OBJ_AXIS_NEGATIVE_Z_FORWARD,
+               "Forward Axis",
+               "");
+  RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
+}
diff --git a/source/blender/editors/io/io_obj.h b/source/blender/editors/io/io_obj.h
index cb9cdc8e74d..30857b33f21 100644
--- a/source/blender/editors/io/io_obj.h
+++ b/source/blender/editors/io/io_obj.h
@@ -9,3 +9,4 @@
 struct wmOperatorType;
 
 void WM_OT_obj_export(struct wmOperatorType *ot);
+void WM_OT_obj_import(struct wmOperatorType *ot);
diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c
index ccd19608ddd..094f89d1540 100644
--- a/source/blender/editors/io/io_ops.c
+++ b/source/blender/editors/io/io_ops.c
@@ -59,4 +59,5 @@ void ED_operatortypes_io(void)
   WM_operatortype_append(CACHEFILE_OT_layer_move);
 
   WM_operatortype_append(WM_OT_obj_export);
+  WM_operatortype_append(WM_OT_obj_import);
 }
diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt
index cc375577b52..9cdd96ee7be 100644
--- a/source/blender/io/wavefront_obj/CMakeLists.txt
+++ b/source/blender/io/wavefront_obj/CMakeLists.txt
@@ -3,6 +3,7 @@
 set(INC
   .
   ./exporter
+  ./importer
   ../../blenkernel
   ../../blenlib
   ../../bmesh
@@ -28,6 +29,13 @@ set(SRC
   exporter/obj_export_mtl.cc
   exporter/obj_export_nurbs.cc
   exporter/obj_exporter.cc
+  importer/importer_mesh_utils.cc
+  importer/obj_import_file_reader.cc
+  importer/obj_import_mesh.cc
+  importer/obj_import_mtl.cc
+  importer/obj_import_nurbs.cc
+  importer/obj_importer.cc
+  importer/parser_string_utils.cc
 
   IO_wavefront_obj.h
   exporter/obj_export_file_writer.hh
@@ -36,6 +44,14 @@ set(SRC
   exporter/obj_export_mtl.hh
   exporter/obj_export_nurbs.hh
   exporter/obj_exporter.hh
+  importer/importer_mesh_utils.hh
+  importer/obj_import_file_reader.hh
+  importer/obj_import_mesh.hh
+  importer/obj_import_mtl.hh
+  importer/obj_import_nurbs.hh
+  importer/obj_import_objects.hh
+  importer/obj_importer.hh
+  importer/parser_string_utils.hh
 )
 
 set(LIB
@@ -53,6 +69,7 @@ blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
 if(WITH_GTESTS)
   set(TEST_SRC
     tests/obj_exporter_tests.cc
+    tests/obj_importer_tests.cc
     tests/obj_exporter_tests.hh
   )
 
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
index cebdf33413e..c80d10d9efd 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
@@ -9,6 +9,7 @@
 #include "IO_wavefront_obj.h"
 
 #include "obj_exporter.hh"
+#include "obj_importer.hh"
 
 /**
  * C-interface for the exporter.
@@ -18,3 +19,12 @@ void OBJ_export(bContext *C, const OBJExportParams *export_params)
   SCOPED_TIMER("OBJ export");
   blender::io::obj::exporter_main(C, *export_params);
 }
+
+/**
+ * Time the full import process.
+ */
+void OBJ_import(bContext *C, const OBJImportParams *import_params)
+{
+  SCOPED_TIMER(__func__);
+  blender::io::obj::importer_main(C, *import_params);
+}
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
index 289e37b3885..b80c1e073b8 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
@@ -77,6 +77,17 @@ struct OBJExportParams {
   bool smooth_groups_bitflags;
 };
 
+struct OBJImportParams {
+  /** Full path to the source OBJ file to import. */
+  char filepath[FILE_MAX];
+  /** Value 0 disables clamping. */
+  float clamp_size;
+  eTransformAxisForward forward_axis;
+  eTransformAxisUp up_axis;
+};
+
+void OBJ_import(bContext *C, const struct OBJImportParams *import_params);
+
 void OBJ_export(bContext *C, const struct OBJExportParams *export_params);
 
 #ifdef __cplusplus
diff --git a/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc
new file mode 100644
index 00000000000..7019e67419e
--- /dev/null
+++ b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup obj
+ */
+
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+
+#include "BLI_delaunay_2d.h"
+#include "BLI_math_vector.h"
+#include "BLI_set.hh"
+
+#include "DNA_object_types.h"
+
+#include "IO_wavefront_obj.h"
+
+#include "importer_mesh_utils.hh"
+
+namespace blender::io::obj {
+
+Vector<Vector<int>> fixup_invalid_polygon(Span<float3> vertex_coords,
+                                          Span<int> face_vertex_indices)
+{
+  using namespace blender::meshintersect;
+  if (face_vertex_indices.size() < 3) {
+    return {};
+  }
+
+  /* Calculate face normal, to project verts to 2D. */
+  float normal[3] = {0, 0, 0};
+  float3 co_prev = vertex_coords[face_vertex_indices.last()];
+  for (int idx : face_vertex_indices) {
+    BLI_assert(idx >= 0 && idx < vertex_coords.size());
+    float3 co_curr = vertex_coords[idx];
+    add_newell_cross_v3_v3v3(normal, co_prev, co_curr);
+    co_prev = co_curr;
+  }
+  if (UNLIKELY(normalize_v3(normal) == 0.0f)) {
+    normal[2] = 1.0f;
+  }
+  float axis_mat[3][3];
+  axis_dominant_v3_to_m3(axis_mat, normal);
+
+  /* Prepare data for CDT. */
+  CDT_input<double> input;
+  input.vert.reinitialize(face_vertex_indices.size());
+  input.face.reinitialize(1);
+  input.face[0].resize(face_vertex_indices.size());
+  for (int64_t i = 0; i < face_vertex_indices.size(); ++i) {
+    input.face[0][i] = i;
+  }
+  input.epsilon = 1.0e-6f;
+  input.need_ids = true;
+  /* Project vertices to 2D. */
+  for (size_t i = 0; i < face_vertex_indices.size(); ++i) {
+    int idx = face_vertex_indices[i];
+    BLI_assert(idx >= 0 && idx < vertex_coords.size());
+    float3 coord = vertex_coords[idx];
+    float2 coord2d;
+    mul_v2_m3v3(coord2d, axis_mat, coord);
+    input.vert[i] = double2(coord2d.x, coord2d.y);
+  }
+
+  CDT_result<double> res = delaunay_2d_calc(input, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES);
+
+  /* Emit new face information from CDT result. */
+  Vector<Vector<int>> faces;
+  faces.reserve(res.face.size());
+  for (const auto &f : res.face) {
+    Vector<int> face_verts;
+    face_verts.reserve(f.size());
+    for (int64_t i = 0; i < f.size(); ++i) {
+      int idx = f[i];
+      BLI_assert(idx >= 0 && idx < res.vert_orig.size());
+      if (res.vert_orig[idx].is_empty()) {
+        /* If we have a whole new

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list