From c307a89e281a2e8830ba99e4bb1a46fcf4ee77cd Mon Sep 17 00:00:00 2001
From: Bastien Montagne <montagne29@wanadoo.fr>
Date: Wed, 18 Feb 2015 16:07:35 +0100
Subject: [PATCH] OBJ Importer: Rework/cleanup of mesh generation code.

That code was doing some rather crazy things, like:
* tessellating ngons, and un-tesselate them later;
* use up to two bmesh transformations for each imported mesh;
* ...

Now, removed the 'use_ngons' option and all the tesselated mess,
we always only work in polygon context, user can triangulate later if really needed.

Got rid of all bmesh stuff.

Also, some cleanup of 'old' fashioned code, like e.g. converting dict views to list
before iterating on those...

With pure-tessellated .obj, we gain nearly nothing (about 5% quicker - and much nicer code).

With .obj containing a fair amount of ngons, we devide import time by six!
E.g. with a test mesh of 475000 polygons (1100000 triangles), we go from 60 seconds (old code)
to less than 10 seconds with new code!
---
 io_scene_obj/__init__.py   |  11 +-
 io_scene_obj/import_obj.py | 259 +++++++++----------------------------
 2 files changed, 63 insertions(+), 207 deletions(-)

diff --git a/io_scene_obj/__init__.py b/io_scene_obj/__init__.py
index c1eb79506..bf7749b83 100644
--- a/io_scene_obj/__init__.py
+++ b/io_scene_obj/__init__.py
@@ -21,7 +21,7 @@
 bl_info = {
     "name": "Wavefront OBJ format",
     "author": "Campbell Barton, Bastien Montagne",
-    "version": (2, 0, 1),
+    "version": (2, 1, 0),
     "blender": (2, 73, 0),
     "location": "File > Import-Export",
     "description": "Import-Export OBJ, Import OBJ mesh, UV's, "
@@ -66,11 +66,6 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, OrientationHelper):
             options={'HIDDEN'},
             )
 
-    use_ngons = BoolProperty(
-            name="NGons",
-            description="Import faces with more than 4 verts as ngons",
-            default=True,
-            )
     use_edges = BoolProperty(
             name="Lines",
             description="Import lines and faces with 2 verts as edge",
@@ -152,11 +147,9 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, OrientationHelper):
         layout = self.layout
 
         row = layout.row(align=True)
-        row.prop(self, "use_ngons")
+        row.prop(self, "use_smooth_groups")
         row.prop(self, "use_edges")
 
-        layout.prop(self, "use_smooth_groups")
-
         box = layout.box()
         row = box.row()
         row.prop(self, "split_mode", expand=True)
diff --git a/io_scene_obj/import_obj.py b/io_scene_obj/import_obj.py
index 84a1fb01d..cba060c89 100644
--- a/io_scene_obj/import_obj.py
+++ b/io_scene_obj/import_obj.py
@@ -39,25 +39,6 @@ from bpy_extras.io_utils import unpack_list, unpack_face_list
 from bpy_extras.image_utils import load_image
 
 
-def mesh_untessellate(me, fgon_edges):
-    import bmesh
-    bm = bmesh.new()
-    bm.from_mesh(me)
-    verts = bm.verts[:]
-    get = bm.edges.get
-    edges = [get((verts[key[0]], verts[key[1]])) for key in fgon_edges]
-    try:
-        bmesh.ops.dissolve_edges(bm, edges=edges, use_verts=False)
-    except:
-        # Possible dissolve fails for some edges
-        # but dont fail silently unless this is a real bug.
-        import traceback
-        traceback.print_exc()
-
-    bm.to_mesh(me)
-    bm.free()
-
-
 def line_value(line_split):
     """
     Returns 1 string represneting the value for this line
@@ -437,8 +418,6 @@ def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP):
 
 
 def create_mesh(new_objects,
-                has_ngons,
-                use_ngons,
                 use_edges,
                 verts_loc,
                 verts_tex,
@@ -453,26 +432,19 @@ def create_mesh(new_objects,
     Takes all the data gathered and generates a mesh, adding the new object to new_objects
     deals with ngons, sharp edges and assigning materials
     """
-    from bpy_extras.mesh_utils import ngon_tessellate
-
-    if not has_ngons:
-        use_ngons = False
 
     if unique_smooth_groups:
-        sharp_edges = {}
-        smooth_group_users = {context_smooth_group: {} for context_smooth_group in list(unique_smooth_groups.keys())}
+        sharp_edges = set()
+        smooth_group_users = {context_smooth_group: {} for context_smooth_group in unique_smooth_groups.keys()}
         context_smooth_group_old = -1
 
-    # Split ngons into tri's
-    fgon_edges = set()  # Used for storing fgon keys
-    if use_edges:
-        edges = []
+    edges = []
+    tot_loops = 0
 
     context_object = None
 
     # reverse loop through face indices
     for f_idx in range(len(faces) - 1, -1, -1):
-
         (face_vert_loc_indices,
          face_vert_tex_indices,
          context_material,
@@ -487,13 +459,12 @@ def create_mesh(new_objects,
 
         elif not face_vert_tex_indices or len_face_vert_loc_indices == 2:  # faces that have no texture coords are lines
             if use_edges:
-                # generators are better in python 2.4+ but can't be used in 2.3
-                # edges.extend( (face_vert_loc_indices[i], face_vert_loc_indices[i+1]) for i in xrange(len_face_vert_loc_indices-1) )
-                edges.extend([(face_vert_loc_indices[i], face_vert_loc_indices[i + 1]) for i in range(len_face_vert_loc_indices - 1)])
-
+                edges.extend((face_vert_loc_indices[i], face_vert_loc_indices[i + 1])
+                             for i in range(len_face_vert_loc_indices - 1))
             faces.pop(f_idx)
-        else:
 
+        else:
+            tot_loops += len_face_vert_loc_indices
             # Smooth Group
             if unique_smooth_groups and context_smooth_group:
                 # Is a part of of a smooth group and is a face
@@ -504,68 +475,22 @@ def create_mesh(new_objects,
                 for i in range(len_face_vert_loc_indices):
                     i1 = face_vert_loc_indices[i]
                     i2 = face_vert_loc_indices[i - 1]
-                    if i1 > i2:
-                        i1, i2 = i2, i1
-
-                    try:
-                        edge_dict[i1, i2] += 1
-                    except KeyError:
-                        edge_dict[i1, i2] = 1
-
-            # NGons into triangles
-            if has_ngons and len_face_vert_loc_indices > 4:
-
-                ngon_face_indices = ngon_tessellate(verts_loc, face_vert_loc_indices)
-                faces.extend([([face_vert_loc_indices[ngon[0]],
-                                face_vert_loc_indices[ngon[1]],
-                                face_vert_loc_indices[ngon[2]],
-                                ],
-                               [face_vert_tex_indices[ngon[0]],
-                                face_vert_tex_indices[ngon[1]],
-                                face_vert_tex_indices[ngon[2]],
-                                ],
-                               context_material,
-                               context_smooth_group,
-                               context_object,
-                              )
-                             for ngon in ngon_face_indices]
-                            )
-
-                # edges to make ngons
-                if use_ngons:
-                    edge_users = {}
-                    for ngon in ngon_face_indices:
-                        for i in (0, 1, 2):
-                            i1 = face_vert_loc_indices[ngon[i]]
-                            i2 = face_vert_loc_indices[ngon[i - 1]]
-                            if i1 > i2:
-                                i1, i2 = i2, i1
-
-                            try:
-                                edge_users[i1, i2] += 1
-                            except KeyError:
-                                edge_users[i1, i2] = 1
-
-                    for key, users in edge_users.items():
-                        if users > 1:
-                            fgon_edges.add(key)
-
-                # remove all after 3, means we dont have to pop this one.
-                faces.pop(f_idx)
+                    edge_key = (i1, i2) if i1 < i2 else (i2, i1)
+                    edge_dict[edge_key] = edge_dict.get(edge_key, 0) + 1
 
     # Build sharp edges
     if unique_smooth_groups:
-        for edge_dict in list(smooth_group_users.values()):
-            for key, users in list(edge_dict.items()):
+        for edge_dict in smooth_group_users.values():
+            for key, users in edge_dict.items():
                 if users == 1:  # This edge is on the boundry of a group
-                    sharp_edges[key] = None
+                    sharp_edges.add(key)
 
     # map the material names to an index
     material_mapping = {name: i for i, name in enumerate(unique_materials)}  # enumerate over unique_materials keys()
 
     materials = [None] * len(unique_materials)
 
-    for name, index in list(material_mapping.items()):
+    for name, index in material_mapping.items():
         materials[index] = unique_materials[name]
 
     me = bpy.data.meshes.new(dataname.decode('utf-8', "replace"))
@@ -575,136 +500,77 @@ def create_mesh(new_objects,
         me.materials.append(material)
 
     me.vertices.add(len(verts_loc))
-    me.tessfaces.add(len(faces))
+    me.loops.add(tot_loops)
+    me.polygons.add(len(faces))
 
     # verts_loc is a list of (x, y, z) tuples
     me.vertices.foreach_set("co", unpack_list(verts_loc))
 
-    # faces is a list of (vert_indices, texco_indices, ...) tuples
-    # XXX faces should contain either 3 or 4 verts
-    # XXX no check for valid face indices
-    me.tessfaces.foreach_set("vertices_raw", unpack_face_list([f[0] for f in faces]))
-
-    if verts_tex and me.tessfaces:
-        me.tessface_uv_textures.new()
+    loops_vert_idx = []
+    faces_loop_start = []
+    faces_loop_total = []
+    lidx = 0
+    for f in faces:
+        vidx = f[0]
+        nbr_vidx = len(vidx)
+        loops_vert_idx.extend(vidx)
+        faces_loop_start.append(lidx)
+        faces_loop_total.append(nbr_vidx)
+        lidx += nbr_vidx
+
+    me.loops.foreach_set("vertex_index", loops_vert_idx)
+    me.polygons.foreach_set("loop_start", faces_loop_start)
+    me.polygons.foreach_set("loop_total", faces_loop_total)
+
+    if verts_tex and me.polygons:
+        me.uv_textures.new()
 
     context_material_old = -1  # avoid a dict lookup
     mat = 0  # rare case it may be un-initialized.
-    me_faces = me.tessfaces
 
-    for i, face in enumerate(faces):
-        if len(face[0]) < 2:
-            pass  # raise Exception("bad face")
-        elif len(face[0]) == 2:
-            if use_edges:
-                edges.append(face[0])
-        else:
+    for i, (face, blen_poly) in enumerate(zip(faces, me.polygons)):
+        if len(face[0]) < 3:
+            raise Exception("bad face")  # Shall not happen, we got rid of those earlier!
 
-            blender_face = me.tessfaces[i]
+        (face_vert_loc_indices,
+         face_vert_tex_indices,
+         context_material,
+         context_smooth_group,
+         context_object,
+         ) = face
 
-            (face_vert_loc_indices,
-             face_vert_tex_indices,
-             context_material,
-             context_smooth_group,
-             context_object,
-             ) = face
+        if context_smooth_group:
+            blen_poly.use_smooth = True
 
-            if context_smooth_group:
-                blender_face.use_smooth = True
+        if context_material:
+            if context_material_old is not context_material:
+                mat = material_mapping[context_material]
+                context_material_old = context_material
+            blen_poly.material_index = mat
 
+        if verts_tex:
             if context_material:
-                if context_material_old is not context_material:
-                    mat = material_mapping[context_material]
-                    context_material_old = context_material
-
-                blender_face.material_index = mat
-#                blender_face.mat= mat
-
-            if verts_tex:
-
-                blender_tface = me.tessface_uv_textures[0].data[i]
-
-                if context_material:
-                    image = unique_material_images[context_material]
-                    if image:  # Can be none if the material dosnt have an image.
-                        blender_tface.image = image
-
-                # BUG - Evil eekadoodle problem where faces that have vert index 0 location at 3 or 4 are shuffled.
-                if len(face_vert_loc_indices) == 4:
-                    if face_vert_loc_indices[2] == 0 or face_vert_loc_indices[3] == 0:
-                        face_vert_tex_indices = face_vert_tex_indices[2], face_vert_tex_indices[3], face_vert_tex_indices[0], face_vert_tex_indices[1]
-                else:  # length of 3
-                    if face_vert_loc_indices[2] == 0:
-                        face_vert_tex_indices = face_vert_tex_indices[1], face_vert_tex_indices[2], face_vert_tex_indices[0]
-                # END EEEKADOODLE FIX
+                image = unique_material_images[context_material]
+                if image:  # Can be none if the material dosnt have an image.
+                    me.uv_textures[0].data[i].image = image
 
-                # assign material, uv's and image
-                blender_tface.uv1 = verts_tex[face_vert_tex_indices[0]]
-                blender_tface.uv2 = verts_tex[face_vert_tex_indices[1]]
-                blender_tface.uv3 = verts_tex[face_vert_tex_indices[2]]
-
-                if len(face_vert_loc_indices) == 4:
-                    blender_tface.uv4 = verts_tex[face_vert_tex_indices[3]]
-
-#                for ii, uv in enumerate(blender_face.uv):
-#                    uv.x, uv.y=  verts_tex[face_vert_tex_indices[ii]]
-    del me_faces
-#     del ALPHA
-
-    if use_edges and not edges:
-        use_edges = False
+            blen_uvs = me.uv_layers[0]
+            for j, lidx in enumerate(blen_poly.loop_indices):
+                blen_uvs.data[lidx].uv = verts_tex[face_vert_tex_indices[j]]
 
+    use_edges = use_edges and bool(edges)
     if use_edges:
         me.edges.add(len(edges))
-
         # edges should be a list of (a, b) tuples
         me.edges.foreach_set("vertices", unpack_list(edges))
-#         me_edges.extend( edges )
-
-#     del me_edges
-
-    # Add edge faces.
-#     me_edges= me.edges
-
-    def edges_match(e1, e2):
-        return (e1[0] == e2[0] and e1[1] == e2[1]) or (e1[0] == e2[1] and e1[1] == e2[0])
 
     me.validate()
     me.update(calc_edges=use_edges)
 
     if unique_smooth_groups and sharp_edges:
-        import bmesh
-        bm = bmesh.new()
-        bm.from_mesh(me)
-        # to avoid slow iterator lookups later / indexing verts is slow in bmesh
-        bm_verts = bm.verts[:]
-
-        for sharp_edge in sharp_edges.keys():
-            vert1 = bm_verts[sharp_edge[0]]
-            vert2 = bm_verts[sharp_edge[1]]
-            if vert1 != vert2:
-                edge = bm.edges.get((vert1, vert2))
-                if edge is not None:
-                    me.edges[edge.index].use_edge_sharp = True
-
-        bm.free()
-        del bm
-
-    mesh_untessellate(me, fgon_edges)
-
-    # XXX slow
-#     if unique_smooth_groups and sharp_edges:
-#         for sharp_edge in sharp_edges.keys():
-#             for ed in me.edges:
-#                 if edges_match(sharp_edge, ed.vertices):
-#                     ed.use_edge_sharp = True
-
-#     if unique_smooth_groups and sharp_edges:
-#         SHARP= Mesh.EdgeFlags.SHARP
-#         for ed in me.findEdges( sharp_edges.keys() ):
-#             if ed is not None:
-#                 me_edges[ed].flag |= SHARP
-#         del SHARP
+        for e in me.edges:
+            if e.key in sharp_edges:
+                e.use_edge_sharp = True
 
     ob = bpy.data.objects.new(me.name, me)
     new_objects.append(ob)
@@ -823,7 +689,6 @@ def get_float_func(filepath):
 
 def load(operator, context, filepath,
          global_clamp_size=0.0,
-         use_ngons=True,
          use_smooth_groups=True,
          use_edges=True,
          use_split_objects=True,
@@ -1119,8 +984,6 @@ def load(operator, context, filepath,
     for verts_loc_split, faces_split, unique_materials_split, dataname in split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP):
         # Create meshes from the data, warning 'vertex_groups' wont support splitting
         create_mesh(new_objects,
-                    has_ngons,
-                    use_ngons,
                     use_edges,
                     verts_loc_split,
                     verts_tex,
-- 
GitLab