]> git.tdb.fi Git - libs/gl.git/commitdiff
Major rework of mesh handling in Blender exporter
authorMikko Rasa <tdb@tdb.fi>
Fri, 3 May 2019 17:13:45 +0000 (20:13 +0300)
committerMikko Rasa <tdb@tdb.fi>
Fri, 3 May 2019 17:18:24 +0000 (20:18 +0300)
All of the preprocessing has been moved out of the MeshExporter class and
into a utility function.  This should make it easier for other exporter
classes to use meshes.

blender/io_mspgl/export_mesh.py
blender/io_mspgl/mesh.py

index b695b40e1e09af236ee8d1d0cde3c0703a56f4a9..6d90c8faf6d5f435ac4daec803e8a02cb0200b86 100644 (file)
@@ -192,24 +192,7 @@ class MeshExporter:
                if obj is None:
                        obj = context.active_object
 
-               objs = [(obj, mathutils.Matrix())]
-               check = objs
-               while check:
-                       children = []
-                       for o, m in check:
-                               for c in o.children:
-                                       if c.compound:
-                                               children.append((c, m*c.matrix_local))
-                       objs += children
-                       check = children
-
-               if not objs:
-                       raise Exception("Nothing to export")
-               for o, m in objs:
-                       if o.type!="MESH":
-                               raise Exception("Can only export Mesh data")
-
-               from .mesh import Mesh
+               from .mesh import create_mesh_from_object
                from .util import Progress
 
                if self.show_progress:
@@ -219,84 +202,7 @@ class MeshExporter:
                else:
                        progress = None
 
-               mesh = None
-               bmeshes = []
-               winding_test = False
-               for o, m in objs:
-                       if o.data.winding_test:
-                               winding_test = True
-                       if o.material_tex:
-                               self.material_tex = True
-                       bmesh = o.to_mesh(context.scene, True, "PREVIEW")
-                       bmeshes.append(bmesh)
-                       me = Mesh(bmesh)
-                       me.transform(m)
-                       if not mesh:
-                               mesh = me
-                       else:
-                               mesh.splice(me)
-
-               if progress:
-                       progress.set_task("Smoothing", 0.05, 0.35)
-               if mesh.smoothing=="NONE":
-                       mesh.flatten_faces()
-               mesh.split_smooth(progress)
-
-               if mesh.smoothing!="BLENDER":
-                       mesh.compute_normals()
-
-               if mesh.vertex_groups:
-                       mesh.sort_vertex_groups(mesh.max_groups_per_vertex)
-
-                       # Create a mapping from vertex group indices to bone indices
-                       first_obj = objs[0][0]
-                       group_index_map = dict((i, i) for i in range(len(first_obj.vertex_groups)))
-                       if first_obj.parent and first_obj.parent.type=="ARMATURE":
-                               armature = first_obj.parent.data
-                               bone_indices = dict((armature.bones[i].name, i) for i in range(len(armature.bones)))
-                               for g in first_obj.vertex_groups:
-                                       if g.name in bone_indices:
-                                               group_index_map[g.index] = bone_indices[g.name]
-
-               if self.material_tex and mesh.materials:
-                       mesh.generate_material_uv()
-
-               texunits = []
-               force_unit0 = False
-               if mesh.uv_layers and (mesh.use_uv!="NONE" or self.material_tex):
-                       # Figure out which UV layers to export
-                       if mesh.use_uv=="ALL":
-                               texunits = range(len(mesh.uv_layers))
-                       elif self.material_tex:
-                               # The material UV layer is always the last one
-                               texunits = [len(mesh.uv_layers)-1]
-                               force_unit0 = True
-                       else:
-                               for i, u in enumerate(mesh.uv_layers):
-                                       if u.unit==0:
-                                               texunits = [i]
-                                               break
-                       texunits = [(i, mesh.uv_layers[i]) for i in texunits]
-                       texunits = [u for u in texunits if not u[1].hidden]
-
-                       if mesh.tbn_vecs:
-                               # TBN coordinates must be generated before vertices are split by any other layer
-                               uv_names = [u.name for i, u in texunits]
-                               if mesh.tbn_uvtex in uv_names:
-                                       tbn_index = uv_names.index(mesh.tbn_uvtex)
-                                       unit = texunits[tbn_index]
-                                       del texunits[tbn_index]
-                                       texunits.insert(0, unit)
-
-                       for i, u in texunits:
-                               if progress:
-                                       progress.set_task("Splitting UVs", 0.35+0.3*i/len(texunits), 0.35+0.3*(i+1)/len(texunits))
-                               mesh.split_uv(i, progress)
-                               if mesh.tbn_vecs and u.name==mesh.tbn_uvtex:
-                                       mesh.compute_uv()
-                                       mesh.compute_tbn(i)
-
-                       mesh.compute_uv()
+               mesh = create_mesh_from_object(context, obj, progress)
 
                strips = []
                loose = mesh.faces
@@ -312,10 +218,10 @@ class MeshExporter:
                out_file = open_output(out_file)
 
                fmt = ["NORMAL3"]
-               if texunits:
-                       for i, u in texunits:
-                               size = str(len(mesh.vertices[0].uvs[i]))
-                               if u.unit==0 or force_unit0:
+               if mesh.uv_layers:
+                       for u in mesh.uv_layers:
+                               size = str(len(u.uvs[0]))
+                               if u.unit==0:
                                        fmt.append("TEXCOORD"+size)
                                else:
                                        fmt.append("TEXCOORD%s_%d"%(size, u.unit))
@@ -334,10 +240,10 @@ class MeshExporter:
                        if v.normal!=normal:
                                out_file.write("normal3", *v.normal)
                                normal = v.normal
-                       for i, u in texunits:
+                       for i, u in enumerate(mesh.uv_layers):
                                if v.uvs[i]!=uvs.get(i):
                                        size = str(len(v.uvs[i]))
-                                       if u.unit==0 or force_unit0:
+                                       if u.unit==0:
                                                out_file.write("texcoord"+size, *v.uvs[i])
                                        else:
                                                out_file.write("multitexcoord"+size, u.unit, *v.uvs[i])
@@ -379,19 +285,16 @@ class MeshExporter:
                                        out_file.write("indices", f.vertices[0].index, f.vertices[i-1].index, f.vertices[i].index)
                        out_file.end()
 
-               if mesh.use_lines and mesh.lines:
+               if mesh.lines:
                        out_file.begin("batch", "LINES")
                        for l in mesh.lines:
                                out_file.write("indices", l.vertices[0].index, l.vertices[1].index)
                        out_file.end()
 
-               if winding_test:
+               if mesh.winding_test:
                        out_file.write("winding", "COUNTERCLOCKWISE")
 
                if progress:
                        progress.set_task("Done", 1.0, 1.0)
 
-               for m in bmeshes:
-                       bpy.data.meshes.remove(m)
-
                return mesh
index d05b9de844d91eeccf113fe9e961386041fb3baf..62d7506303b349d9e3af9f914f5b4e39d0e6319f 100644 (file)
@@ -1,3 +1,4 @@
+import bpy
 import math
 import mathutils
 
@@ -13,6 +14,7 @@ class Edge:
                else:
                        self._medge = me
                        self.smooth = False
+               self.key = me.key
                self.faces = []
 
        def __getattr__(self, attr):
@@ -45,22 +47,21 @@ class Vertex:
        def __init__(self, mv):
                if mv.__class__==Vertex:
                        self._mvert = mv._mvert
-                       self.co = mv.co
-                       self.normal = mv.normal
                        self.uvs = mv.uvs[:]
                        self.tan = mv.tan
                        self.bino = mv.bino
-                       self.group_weight_scale = mv.group_weight_scale
                else:
                        self._mvert = mv
-                       self.co = mv.co
-                       self.normal = mv.normal
                        self.uvs = []
                        self.tan = None
                        self.bino = None
-                       self.group_weight_scale = 1
+               self.index = mv.index
+               self.co = mv.co
+               self.normal = mv.normal
                self.flag = False
+               self.edges = []
                self.faces = []
+               self.groups = mv.groups[:]
 
        def __getattr__(self, attr):
                return getattr(self._mvert, attr)
@@ -71,9 +72,20 @@ class Vertex:
                return cmp(self.index, other.index)
 
 
+class VertexGroup:
+       def __init__(self, base):
+               self._base = base
+               self.group = base.group
+               self.weight = base.weight
+
+       def __getattr__(self, attr):
+               return getattr(self._mvert, attr)
+
+
 class Face:
        def __init__(self, mf):
                self._mface = mf
+               self.index = mf.index
                self.edges = []
                self.vertices = mf.vertices[:]
                self.uvs = []
@@ -101,6 +113,11 @@ class Face:
                                return e
                raise KeyError("No edge %s"%(key,))
 
+       def other_edge(self, e, v):
+               for d in self.edges:
+                       if d!=e and v in d.vertices:
+                               return d
+
        def get_neighbors(self):
                neighbors = [e.other_face(self) for e in self.edges]
                return list(filter(bool, neighbors))
@@ -114,12 +131,18 @@ class Line:
 
 
 class UvLayer:
-       def __init__(self, l, t):
-               self._layer = l
-               self.uvtex = t
-               self.name = self.uvtex.name
+       def __init__(self, arg):
+               if type(arg)==str:
+                       self._layer = None
+                       self.name = arg
+               else:
+                       self._layer = arg
+                       self.name = arg.name
+                       self.uvs = [d.uv for d in self.data]
+
                self.unit = None
                self.hidden = False
+
                dot = self.name.find('.')
                if dot>=0:
                        ext = self.name[dot:]
@@ -131,60 +154,62 @@ class UvLayer:
        def __getattr__(self, attr):
                return getattr(self._layer, attr)
 
-class FakeUvLayer:
-       def __init__(self, n):
-               self.uvtex = None
-               self.name = n
-               self.unit = None
-               self.hidden = False
 
 class Mesh:
        def __init__(self, m):
                self._mesh = m
 
+               self.winding_test = m.winding_test
+               self.tbn_vecs = m.tbn_vecs
+               self.vertex_groups = m.vertex_groups
+
                self.vertices = [Vertex(v) for v in self.vertices]
                self.faces = [Face(f) for f in self.polygons]
+               self.edges = [Edge(e) for e in self.edges]
+               self.edge_map = {e.key: e for e in self.edges}
+               self.loops = self.loops[:]
 
                self.materials = self.materials[:]
+               if self.use_uv=='NONE':
+                       self.uv_layers = []
+               elif self.uv_layers:
+                       self.uv_layers = [UvLayer(u) for u in self.uv_layers]
+                       self.uv_layers = sorted([u for u in self.uv_layers if not u.hidden], key=(lambda u: (u.unit or 1000, u.name)))
+
+                       if self.use_uv=='UNIT0':
+                               self.uv_layers = [self.uv_layers[0]]
+
+                       next_unit = max((u.unit+1 for u in self.uv_layers if u.unit is not None), default=0)
+                       for u in self.uv_layers:
+                               if not u.unit:
+                                       u.unit = next_unit
+                                       next_unit += 1
 
-               self.uv_layers = [UvLayer(self.uv_layers[i], self.uv_textures[i]) for i in range(len(self.uv_layers))]
-               self.assign_texture_units()
+               for v in self.vertices:
+                       v.groups = [VertexGroup(g) for g in v.groups]
 
                for f in self.faces:
                        if len(f.vertices)>4:
                                raise ValueError("Ngons are not supported")
+
                        f.vertices = [self.vertices[i] for i in f.vertices]
                        for v in f.vertices:
                                v.faces.append(f)
-                       for u in self.uv_layers:
-                               f.uvs.append([u.data[f.loop_indices[i]].uv for i in range(len(f.vertices))])
-                       if f.material_index<len(self.materials):
-                               mat = self.materials[f.material_index]
-                               if mat and mat.array_atlas:
-                                       layer = (mat.array_layer,)
-                                       print(f.uvs, layer)
-                                       for i in range(len(f.uvs)):
-                                               f.uvs[i] = [mathutils.Vector(tuple(u)+layer) for u in f.uvs[i]]
-
-               self.edges = dict([(e.key, Edge(e)) for e in self.edges])
-               for f in self.faces:
+
                        for k in f.edge_keys:
-                               e = self.edges[k]
-                               e.faces.append(self.faces[f.index])
+                               e = self.edge_map[k]
+                               e.faces.append(f)
                                f.edges.append(e)
 
-               self.lines = [Line(e) for e in self.edges.values() if not e.faces]
-               for l in self.lines:
-                       l.vertices = [self.vertices[i] for i in l.vertices]
+               for e in self.edges:
+                       e.vertices = [self.vertices[i] for i in e.vertices]
+                       for v in e.vertices:
+                               v.edges.append(e)
 
-               if self.use_auto_smooth:
-                       smooth_limit = math.cos(self.auto_smooth_angle)
+               if self.use_lines:
+                       self.lines = [Line(e) for e in self.edges if not e.faces]
                else:
-                       smooth_limit = -1
-
-               for e in self.edges.values():
-                       e.vertices = [self.vertices[i] for i in e.vertices]
-                       e.check_smooth(smooth_limit)
+                       self.lines = []
 
        def __getattr__(self, attr):
                return getattr(self._mesh, attr)
@@ -194,6 +219,12 @@ class Mesh:
                        v.co = matrix*v.co
 
        def splice(self, other):
+               if len(self.uv_layers)!=len(other.uv_layers):
+                       raise ValueError("Meshes have incompatible UV layers")
+               for i, u in enumerate(self.uv_layers):
+                       if u.name!=other.uv_layers[i].name:
+                               raise ValueError("Meshes have incompatible UV layers")
+
                material_map = []
                for m in other.materials:
                        if m in self.materials:
@@ -202,49 +233,128 @@ class Mesh:
                                material_map.append(len(self.materials))
                                self.materials.append(m)
 
+               for i, u in enumerate(self.uv_layers):
+                       u.uvs += other.uv_layers[i].uvs
+
                offset = len(self.vertices)
-               for v in other.vertices:
+               self.vertices += other.vertices
+               for v in self.vertices[offset:]:
                        v.index += offset
-                       self.vertices.append(v)
+
+               loop_offset = len(self.loops)
+               self.loops += other.loops
 
                offset = len(self.faces)
-               for f in other.faces:
+               self.faces += other.faces
+               for f in self.faces[offset:]:
                        f.index += offset
+                       f.loop_start += loop_offset
+                       f.loop_indices = range(f.loop_start, f.loop_start+f.loop_total)
                        if other.materials:
                                f.material_index = material_map[f.material_index]
-                       self.faces.append(f)
 
-               for e in other.edges.values():
+               offset = len(self.edges)
+               self.edges += other.edges
+               for e in self.edges[offset:]:
+                       e.index += offset
                        e.key = make_edge_key(e.vertices[0].index, e.vertices[1].index)
-                       self.edges[e.key] = e
+                       self.edge_map[e.key] = e
 
                self.lines += other.lines
 
-       def flatten_faces(self):
-               for f in self.faces:
-                       f.use_smooth = False
+       def prepare_smoothing(self, progress=None):
+               smooth_limit = -1
+               if self.smoothing=='NONE':
+                       for f in self.faces:
+                               f.use_smooth = False
 
-               for e in self.edges.values():
-                       e.check_smooth(1)
+                       smooth_limit = 1
+               elif self.use_auto_smooth:
+                       smooth_limit = math.cos(self.auto_smooth_angle)
+
+               for e in self.edges:
+                       e.check_smooth(smooth_limit)
+
+               self.split_vertices(self.find_smooth_group, progress)
+
+               if self.smoothing!='BLENDER':
+                       self.compute_normals()
+
+       def prepare_vertex_groups(self, obj):
+               for v in self.vertices:
+                       if v.groups:
+                               weight_sum = sum(g.weight for g in v.groups)
+                               v.groups = sorted(v.groups, key=(lambda g: g.weight), reverse=True)[:self.max_groups_per_vertex]
+                               weight_scale = weight_sum/sum(g.weight for g in v.groups)
+                               for g in v.groups:
+                                       g.weight *= weight_scale
+
+               if obj.parent and obj.parent.type=="ARMATURE":
+                       armature = obj.parent.data
+                       bone_indices = {b.name: i for i, b in enumerate(armature.bones)}
+                       group_index_map = {i: i for i in range(len(obj.vertex_groups))}
+                       for g in first_obj.vertex_groups:
+                               if g.name in bone_indices:
+                                       group_index_map[g.index] = bone_indices[g.name]
+
+                       for v in self.vertices:
+                               for g in v.groups:
+                                       g.group = group_index_map[g.group]
+
+       def prepare_uv(self, obj, progress=None):
+               if obj.material_tex and self.use_uv!='NONE':
+                       layer = UvLayer("material_tex")
+
+                       if self.use_uv=='UNIT0':
+                               self.uv_layers = [layer]
+                               layer.unit = 0
+                       else:
+                               self.uv_layers.append(layer)
+                               layer.unit = max((u.unit+1 for u in self.uv_layers if u.unit is not None), default=0)
+
+                       layer.uvs = [None]*len(self.loops)
+                       for f in self.faces:
+                               uv = mathutils.Vector(((f.material_index+0.5)/len(self.materials), 0.5))
+                               for i in f.loop_indices:
+                                       layer.uvs[i] = uv
+
+               array_uv_layers = [t.uv_layer for m in self.materials if m.array_atlas for t in m.texture_slots if t and t.texture_coords=='UV']
+               array_uv_layers = [u for u in self.uv_layers if u.name in array_uv_layers]
+
+               if array_uv_layers:
+                       for f in self.faces:
+                               layer = 0
+                               if f.material_index<len(self.materials):
+                                       mat = self.materials[f.material_index]
+                                       if mat and mat.array_atlas:
+                                               layer = mat.array_layer
+
+                               for l in array_uv_layers:
+                                       for i in f.loop_indices:
+                                               l.uvs[i] = mathutils.Vector((*l.uvs[i], layer))
 
-       def assign_texture_units(self):
-               # Assign texture units for any non-hidden UV layers that lack one
-               units = [u.unit for u in self.uv_layers if u.unit is not None]
-               if units:
-                       free_unit = max(units)+1
-               else:
-                       free_unit = 0
-               for u in self.uv_layers:
-                       if u.unit is None:
-                               if not u.hidden:
-                                       u.unit = free_unit
-                                       free_unit += 1
-
-       def generate_material_uv(self):
-               self.uv_layers.append(FakeUvLayer("material_tex"))
-               self.assign_texture_units()
                for f in self.faces:
-                       f.uvs.append([((f.material_index+0.5)/len(self.materials), 0.5)]*len(f.vertices))
+                       for u in self.uv_layers:
+                               f.uvs.append([u.uvs[i] for i in f.loop_indices])
+
+               tbn_layer_index = -1
+               if self.tbn_vecs:
+                       uv_names = [u.name for u in self.uv_layers]
+                       if self.tbn_uvtex in uv_names:
+                               tbn_layer_index = uv_names.index(self.tbn_uvtex)
+                               self.compute_tbn(tbn_layer_index)
+                               self.split_vertices(self.find_uv_group, progress, tbn_layer_index)
+
+               for i in range(len(self.uv_layers)):
+                       self.split_vertices(self.find_uv_group, progress, i)
+
+               for v in self.vertices:
+                       if v.faces:
+                               f = v.faces[0]
+                               i = f.vertices.index(v)
+                               v.uvs = [u[i] for u in f.uvs]
+                       else:
+                               v.uvs = [(0.0, 0.0)]*len(self.uv_layers)
 
        def split_vertices(self, find_group_func, progress, *args):
                groups = []
@@ -264,41 +374,38 @@ class Mesh:
                                progress.set_progress(i*0.5/len(self.vertices))
 
                for i in range(len(self.vertices)):
-                       if len(groups[i])==1:
-                               continue
-
                        for g in groups[i][1:]:
                                v = Vertex(self.vertices[i])
                                v.index = len(self.vertices)
                                self.vertices.append(v)
 
                                v_edges = []
-                               v_edge_keys = set()
-                               for f in g:
-                                       for e in f.edges:
-                                               if e.key in v_edge_keys or self.vertices[i] not in e.vertices:
-                                                       continue
-
-                                               e_faces_in_g = [c for c in e.faces if c in g]
+                               for e in self.vertices[i].edges:
+                                       e_faces_in_g = [f for f in e.faces if f in g]
+                                       if e_faces_in_g:
                                                boundary = len(e_faces_in_g)<len(e.faces)
                                                v_edges.append((e, boundary, e_faces_in_g))
-                                               v_edge_keys.add(e.key)
 
                                for e, boundary, e_faces_in_g in v_edges:
                                        if boundary:
                                                ne = Edge(e)
-                                               for c in e_faces_in_g:
-                                                       e.faces.remove(c)
-                                                       c.edges[c.edges.index(e)] = ne
-                                                       ne.faces.append(c)
+                                               ne.index = len(self.edges)
+                                               self.edges.append(ne)
+
+                                               for f in e_faces_in_g:
+                                                       e.faces.remove(f)
+                                                       f.edges[f.edges.index(e)] = ne
+                                                       ne.faces.append(f)
                                                e = ne
                                        else:
-                                               del self.edges[e.key]
+                                               del self.edge_map[e.key]
+                                               self.vertices[i].edges.remove(e)
+                                               v.edges.append(e)
 
                                        e.vertices[e.vertices.index(self.vertices[i])] = v
 
                                        e.key = make_edge_key(e.vertices[0].index, e.vertices[1].index)
-                                       self.edges[e.key] = e
+                                       self.edge_map[e.key] = e
 
                                for f in g:
                                        self.vertices[i].faces.remove(f)
@@ -308,89 +415,69 @@ class Mesh:
                        if progress:
                                progress.set_progress(0.5+i*0.5/len(self.vertices))
 
-       def split_smooth(self, progress=None):
-               self.split_vertices(self.find_smooth_group, progress)
-
-       def split_uv(self, index, progress=None):
-               self.split_vertices(self.find_uv_group, progress, index)
-
        def find_smooth_group(self, vertex, face):
                face.flag = True
-               queue = [face]
 
-               for f in queue:
-                       for e in f.edges:
-                               other = e.other_face(f)
-                               if other not in vertex.faces:
-                                       continue
+               edges = [e for e in face.edges if vertex in e.vertices]
+
+               group = [face]
+               for e in edges:
+                       f = face
+                       while e.smooth:
+                               f = e.other_face(f)
+                               if not f or f.flag:
+                                       break
 
-                               if e.smooth:
-                                       if not other.flag:
-                                               other.flag = True
-                                               queue.append(other)
+                               f.flag = True
+                               group.append(f)
+                               e = f.other_edge(e, vertex)
 
-               return queue
+               return group
 
        def find_uv_group(self, vertex, face, index):
                uv = face.uvs[index][face.vertices.index(vertex)]
                face.flag = True
+
                group = [face]
                for f in vertex.faces:
                        if not f.flag and f.uvs[index][f.vertices.index(vertex)]==uv:
                                f.flag = True
                                group.append(f)
+
                return group
 
        def compute_normals(self):
                for v in self.vertices:
-                       if v.faces:
-                               v.normal = mathutils.Vector()
-                               for f in v.faces:
-                                       fv = f.pivot_vertices(v)
-                                       edge1 = fv[1].co-fv[0].co
-                                       edge2 = fv[-1].co-fv[0].co
-                                       if edge1.length and edge2.length:
-                                               weight = 1
-                                               if len(f.get_edge(fv[0], fv[1]).faces)==1:
-                                                       weight += 1
-                                               if len(f.get_edge(fv[0], fv[-1]).faces)==1:
-                                                       weight += 1
-                                               v.normal += f.normal*edge1.angle(edge2)*weight
-                               if v.normal.length:
-                                       v.normal.normalize()
-                               else:
-                                       v.normal = mathutils.Vector((0, 0, 1))
-                       else:
-                               # XXX Should use edges to compute normal
-                               v.normal = mathutils.Vector((0, 0, 1))
+                       v.normal = mathutils.Vector()
+                       for f in v.faces:
+                               fv = f.pivot_vertices(v)
+                               edge1 = fv[1].co-fv[0].co
+                               edge2 = fv[-1].co-fv[0].co
+                               if edge1.length and edge2.length:
+                                       v.normal += f.normal*edge1.angle(edge2)
 
-       def compute_uv(self):
-               for v in self.vertices:
-                       if v.faces:
-                               f = v.faces[0]
-                               i = f.vertices.index(v)
-                               v.uvs = [u[i] for u in f.uvs]
+                       if v.normal.length:
+                               v.normal.normalize()
                        else:
-                               v.uvs = [(0.0, 0.0)]*len(self.uv_layers)
+                               v.normal = mathutils.Vector((0, 0, 1))
 
        def compute_tbn(self, index):
-               if not self.uv_layers:
-                       return
+               layer_uvs = self.uv_layers[index].uvs
 
                for v in self.vertices:
                        v.tan = mathutils.Vector()
                        v.bino = mathutils.Vector()
                        for f in v.faces:
-                               fv = f.pivot_vertices(v)
-                               uv0 = fv[0].uvs[index]
-                               uv1 = fv[1].uvs[index]
-                               uv2 = fv[-1].uvs[index]
+                               vi = f.vertices.index(v)
+                               uv0 = layer_uvs[f.loop_indices[vi]]
+                               uv1 = layer_uvs[f.loop_indices[vi+1]]
+                               uv2 = layer_uvs[f.loop_indices[vi-1]]
                                du1 = uv1[0]-uv0[0]
                                du2 = uv2[0]-uv0[0]
                                dv1 = uv1[1]-uv0[1]
                                dv2 = uv2[1]-uv0[1]
-                               edge1 = fv[1].co-fv[0].co
-                               edge2 = fv[-1].co-fv[0].co
+                               edge1 = f.vertices[vi+1].co-f.vertices[vi].co
+                               edge2 = f.vertices[vi-1].co-f.vertices[vi].co
                                div = (du1*dv2-du2*dv1)
                                if div:
                                        mul = edge1.angle(edge2)/div
@@ -402,11 +489,18 @@ class Mesh:
                        if v.bino.length:
                                v.bino.normalize()
 
-       def sort_vertex_groups(self, max_groups):
+       def drop_references(self):
                for v in self.vertices:
-                       if v.groups:
-                               v.groups = sorted(v.groups, key=(lambda g: g.weight), reverse=True)
-                               v.group_weight_scale = 1.0/sum(g.weight for g in v.groups[:max_groups])
+                       v._mvert = None
+                       for g in v.groups:
+                               g._base = None
+               for e in self.edges:
+                       e._medge = None
+               for f in self.faces:
+                       f._mface = None
+               for u in self.uv_layers:
+                       u._layer = None
+               self._mesh = None
 
        def create_strip(self, face, max_len):
                # Find an edge with another unused face next to it
@@ -452,3 +546,39 @@ class Mesh:
                                break
 
                return result
+
+def create_mesh_from_object(context, obj, progress=None):
+       if obj.type!="MESH":
+               raise Exception("Object is not a mesh")
+
+       objs = [(obj, mathutils.Matrix())]
+       i = 0
+       while i<len(objs):
+               o, m = objs[i]
+               i += 1
+               for c in o.children:
+                       if c.type=="MESH" and c.compound:
+                               objs.append((c, m*c.matrix_local))
+
+       mesh = None
+       bmeshes = []
+       for o, m in objs:
+               bmesh = o.to_mesh(context.scene, True, "PREVIEW")
+               bmeshes.append(bmesh)
+               me = Mesh(bmesh)
+               me.transform(m)
+
+               if mesh:
+                       mesh.splice(me)
+               else:
+                       mesh = me
+
+       mesh.prepare_smoothing(progress)
+       mesh.prepare_vertex_groups(obj)
+       mesh.prepare_uv(obj, progress)
+
+       mesh.drop_references()
+       for m in bmeshes:
+               bpy.data.meshes.remove(m)
+
+       return mesh