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
Tags
No related merge requests found
......@@ -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)
......
......@@ -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,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment