Skip to content
Snippets Groups Projects
Commit c307a89e authored by Bastien Montagne's avatar Bastien Montagne
Browse files

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!
parent de7d0111
Branches
No related tags found
No related merge requests found
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
bl_info = { bl_info = {
"name": "Wavefront OBJ format", "name": "Wavefront OBJ format",
"author": "Campbell Barton, Bastien Montagne", "author": "Campbell Barton, Bastien Montagne",
"version": (2, 0, 1), "version": (2, 1, 0),
"blender": (2, 73, 0), "blender": (2, 73, 0),
"location": "File > Import-Export", "location": "File > Import-Export",
"description": "Import-Export OBJ, Import OBJ mesh, UV's, " "description": "Import-Export OBJ, Import OBJ mesh, UV's, "
...@@ -66,11 +66,6 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, OrientationHelper): ...@@ -66,11 +66,6 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, OrientationHelper):
options={'HIDDEN'}, options={'HIDDEN'},
) )
use_ngons = BoolProperty(
name="NGons",
description="Import faces with more than 4 verts as ngons",
default=True,
)
use_edges = BoolProperty( use_edges = BoolProperty(
name="Lines", name="Lines",
description="Import lines and faces with 2 verts as edge", description="Import lines and faces with 2 verts as edge",
...@@ -152,11 +147,9 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, OrientationHelper): ...@@ -152,11 +147,9 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, OrientationHelper):
layout = self.layout layout = self.layout
row = layout.row(align=True) row = layout.row(align=True)
row.prop(self, "use_ngons") row.prop(self, "use_smooth_groups")
row.prop(self, "use_edges") row.prop(self, "use_edges")
layout.prop(self, "use_smooth_groups")
box = layout.box() box = layout.box()
row = box.row() row = box.row()
row.prop(self, "split_mode", expand=True) row.prop(self, "split_mode", expand=True)
......
...@@ -39,25 +39,6 @@ from bpy_extras.io_utils import unpack_list, unpack_face_list ...@@ -39,25 +39,6 @@ from bpy_extras.io_utils import unpack_list, unpack_face_list
from bpy_extras.image_utils import load_image 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): def line_value(line_split):
""" """
Returns 1 string represneting the value for this line 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): ...@@ -437,8 +418,6 @@ def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP):
def create_mesh(new_objects, def create_mesh(new_objects,
has_ngons,
use_ngons,
use_edges, use_edges,
verts_loc, verts_loc,
verts_tex, verts_tex,
...@@ -453,26 +432,19 @@ def create_mesh(new_objects, ...@@ -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 Takes all the data gathered and generates a mesh, adding the new object to new_objects
deals with ngons, sharp edges and assigning materials 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: if unique_smooth_groups:
sharp_edges = {} sharp_edges = set()
smooth_group_users = {context_smooth_group: {} for context_smooth_group in list(unique_smooth_groups.keys())} smooth_group_users = {context_smooth_group: {} for context_smooth_group in unique_smooth_groups.keys()}
context_smooth_group_old = -1 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 context_object = None
# reverse loop through face indices # reverse loop through face indices
for f_idx in range(len(faces) - 1, -1, -1): for f_idx in range(len(faces) - 1, -1, -1):
(face_vert_loc_indices, (face_vert_loc_indices,
face_vert_tex_indices, face_vert_tex_indices,
context_material, context_material,
...@@ -487,13 +459,12 @@ def create_mesh(new_objects, ...@@ -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 elif not face_vert_tex_indices or len_face_vert_loc_indices == 2: # faces that have no texture coords are lines
if use_edges: 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])
# edges.extend( (face_vert_loc_indices[i], face_vert_loc_indices[i+1]) for i in xrange(len_face_vert_loc_indices-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) faces.pop(f_idx)
else:
else:
tot_loops += len_face_vert_loc_indices
# Smooth Group # Smooth Group
if unique_smooth_groups and context_smooth_group: if unique_smooth_groups and context_smooth_group:
# Is a part of of a smooth group and is a face # Is a part of of a smooth group and is a face
...@@ -504,68 +475,22 @@ def create_mesh(new_objects, ...@@ -504,68 +475,22 @@ def create_mesh(new_objects,
for i in range(len_face_vert_loc_indices): for i in range(len_face_vert_loc_indices):
i1 = face_vert_loc_indices[i] i1 = face_vert_loc_indices[i]
i2 = face_vert_loc_indices[i - 1] i2 = face_vert_loc_indices[i - 1]
if i1 > i2: edge_key = (i1, i2) if i1 < i2 else (i2, i1)
i1, i2 = i2, i1 edge_dict[edge_key] = edge_dict.get(edge_key, 0) + 1
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)
# Build sharp edges # Build sharp edges
if unique_smooth_groups: if unique_smooth_groups:
for edge_dict in list(smooth_group_users.values()): for edge_dict in smooth_group_users.values():
for key, users in list(edge_dict.items()): for key, users in edge_dict.items():
if users == 1: # This edge is on the boundry of a group 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 # map the material names to an index
material_mapping = {name: i for i, name in enumerate(unique_materials)} # enumerate over unique_materials keys() material_mapping = {name: i for i, name in enumerate(unique_materials)} # enumerate over unique_materials keys()
materials = [None] * len(unique_materials) 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] materials[index] = unique_materials[name]
me = bpy.data.meshes.new(dataname.decode('utf-8', "replace")) me = bpy.data.meshes.new(dataname.decode('utf-8', "replace"))
...@@ -575,32 +500,37 @@ def create_mesh(new_objects, ...@@ -575,32 +500,37 @@ def create_mesh(new_objects,
me.materials.append(material) me.materials.append(material)
me.vertices.add(len(verts_loc)) 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 # verts_loc is a list of (x, y, z) tuples
me.vertices.foreach_set("co", unpack_list(verts_loc)) me.vertices.foreach_set("co", unpack_list(verts_loc))
# faces is a list of (vert_indices, texco_indices, ...) tuples loops_vert_idx = []
# XXX faces should contain either 3 or 4 verts faces_loop_start = []
# XXX no check for valid face indices faces_loop_total = []
me.tessfaces.foreach_set("vertices_raw", unpack_face_list([f[0] for f in faces])) lidx = 0
for f in faces:
if verts_tex and me.tessfaces: vidx = f[0]
me.tessface_uv_textures.new() 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 context_material_old = -1 # avoid a dict lookup
mat = 0 # rare case it may be un-initialized. mat = 0 # rare case it may be un-initialized.
me_faces = me.tessfaces
for i, face in enumerate(faces): for i, (face, blen_poly) in enumerate(zip(faces, me.polygons)):
if len(face[0]) < 2: if len(face[0]) < 3:
pass # raise Exception("bad face") raise Exception("bad face") # Shall not happen, we got rid of those earlier!
elif len(face[0]) == 2:
if use_edges:
edges.append(face[0])
else:
blender_face = me.tessfaces[i]
(face_vert_loc_indices, (face_vert_loc_indices,
face_vert_tex_indices, face_vert_tex_indices,
...@@ -610,101 +540,37 @@ def create_mesh(new_objects, ...@@ -610,101 +540,37 @@ def create_mesh(new_objects,
) = face ) = face
if context_smooth_group: if context_smooth_group:
blender_face.use_smooth = True blen_poly.use_smooth = True
if context_material: if context_material:
if context_material_old is not context_material: if context_material_old is not context_material:
mat = material_mapping[context_material] mat = material_mapping[context_material]
context_material_old = context_material context_material_old = context_material
blen_poly.material_index = mat
blender_face.material_index = mat
# blender_face.mat= mat
if verts_tex: if verts_tex:
blender_tface = me.tessface_uv_textures[0].data[i]
if context_material: if context_material:
image = unique_material_images[context_material] image = unique_material_images[context_material]
if image: # Can be none if the material dosnt have an image. if image: # Can be none if the material dosnt have an image.
blender_tface.image = image me.uv_textures[0].data[i].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
# 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): blen_uvs = me.uv_layers[0]
# uv.x, uv.y= verts_tex[face_vert_tex_indices[ii]] for j, lidx in enumerate(blen_poly.loop_indices):
del me_faces blen_uvs.data[lidx].uv = verts_tex[face_vert_tex_indices[j]]
# del ALPHA
if use_edges and not edges:
use_edges = False
use_edges = use_edges and bool(edges)
if use_edges: if use_edges:
me.edges.add(len(edges)) me.edges.add(len(edges))
# edges should be a list of (a, b) tuples # edges should be a list of (a, b) tuples
me.edges.foreach_set("vertices", unpack_list(edges)) 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.validate()
me.update(calc_edges=use_edges) me.update(calc_edges=use_edges)
if unique_smooth_groups and sharp_edges: if unique_smooth_groups and sharp_edges:
import bmesh for e in me.edges:
bm = bmesh.new() if e.key in sharp_edges:
bm.from_mesh(me) e.use_edge_sharp = True
# 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
ob = bpy.data.objects.new(me.name, me) ob = bpy.data.objects.new(me.name, me)
new_objects.append(ob) new_objects.append(ob)
...@@ -823,7 +689,6 @@ def get_float_func(filepath): ...@@ -823,7 +689,6 @@ def get_float_func(filepath):
def load(operator, context, filepath, def load(operator, context, filepath,
global_clamp_size=0.0, global_clamp_size=0.0,
use_ngons=True,
use_smooth_groups=True, use_smooth_groups=True,
use_edges=True, use_edges=True,
use_split_objects=True, use_split_objects=True,
...@@ -1119,8 +984,6 @@ def load(operator, context, filepath, ...@@ -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): 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 meshes from the data, warning 'vertex_groups' wont support splitting
create_mesh(new_objects, create_mesh(new_objects,
has_ngons,
use_ngons,
use_edges, use_edges,
verts_loc_split, verts_loc_split,
verts_tex, verts_tex,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment