From: Mikko Rasa Date: Thu, 3 Feb 2011 21:23:12 +0000 (+0000) Subject: Improve smoothing in blender exporter X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=203ef7534bcbb98b2cddfe9a52128134e46d4133 Improve smoothing in blender exporter Support exporting multiple sets of texture coordinates --- diff --git a/blender/io_mesh_mspgl/__init__.py b/blender/io_mesh_mspgl/__init__.py index 1445ff40..5ff3b4bc 100644 --- a/blender/io_mesh_mspgl/__init__.py +++ b/blender/io_mesh_mspgl/__init__.py @@ -13,10 +13,19 @@ class ExportMspGL(bpy.types.Operator, ExportHelper): optimize_cache = bpy.props.BoolProperty(name="Optimize cache", description="Optimize element order for vertex cache", default=True) cache_size = bpy.props.IntProperty(name="Cache size", description="Simulated vertex cache size used in optimization", default=64, min=8, max=1024) export_lines = bpy.props.BoolProperty(name="Export lines", description="Export edges without faces as lines", default=False) + export_uv = bpy.props.EnumProperty(name="Export UV", description="Export UV coordinates", default="UNIT0", + items=(("NONE", "None", "No UV coordinates are exported"), + ("UNIT0", "Unit 0", "UV coordinates for unit 0 are exported"), + ("ALL", "All", "All UV coordinates are exported"))) tbn_vecs = bpy.props.BoolProperty(name="TBN vectors", description="Compute tangent and binormal vectors for vertices", default=False) + tbn_uvtex = bpy.props.StringProperty(name="TBN UV layer", description="UV layer to use as basis for TBN vectors", default="") compound = bpy.props.BoolProperty(name="Compound", description="Combine all selected objects into one for exporting", default=False) object = bpy.props.BoolProperty(name="Object", description="Export an object instead of a mesh", default=False) material_tex = bpy.props.BoolProperty(name="Material texture", description="Generate a texture based on material colors", default=False) + smoothing = bpy.props.EnumProperty(name="Smoothing", description="Smoothing method to use", default="MSPGL", + items=(("NONE", "None", "No smoothing"), + ("BLENDER", "Blender", "Use Blender's vertex normals"), + ("MSPGL", "MspGL", "Compute vertex normals internally"))) def execute(self, context): from . import export_mspgl diff --git a/blender/io_mesh_mspgl/export_mspgl.py b/blender/io_mesh_mspgl/export_mspgl.py index 6de38f87..e2082cb8 100644 --- a/blender/io_mesh_mspgl/export_mspgl.py +++ b/blender/io_mesh_mspgl/export_mspgl.py @@ -71,10 +71,13 @@ class Exporter: self.optimize_cache = False self.cache_size = 64 self.export_lines = True + self.export_uv = "UNIT0" self.tbn_vecs = False + self.tbn_uvtex = "" self.compound = False self.object = False self.material_tex = False + self.smoothing = "MSPGL" def stripify(self, mesh, progress = None): for f in mesh.faces: @@ -236,20 +239,30 @@ class Exporter: mesh.splice(Mesh(bmesh)) progress.set_task("Smoothing", 0.05, 0.35) - mesh.split_smooth() + if self.smoothing=="NONE": + mesh.flatten_faces() + mesh.split_smooth(progress) - mesh.compute_normals() + if self.smoothing!="BLENDER": + mesh.compute_normals() if self.material_tex: mesh.generate_material_uv() - if mesh.has_uv: - progress.set_task("Splitting UVs", 0.35, 0.65) - mesh.split_uv() + texunits = [] + if mesh.uv_textures and self.export_uv!="NONE": + if self.export_uv=="UNIT0": + texunits = [0] + else: + texunits = list(range(len(mesh.uv_textures))) + + for i in texunits: + 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) mesh.compute_uv() if self.tbn_vecs: - mesh.compute_tbn() + mesh.compute_tbn(self.tbn_uvtex) strips = [] loose = mesh.faces @@ -264,23 +277,29 @@ class Exporter: out_file.begin("mesh") fmt = "NORMAL3" - if mesh.has_uv: + if texunits: fmt += "_TEXCOORD2" + for i in texunits[1:]: + fmt += "_TEXCOORD2%d"%i if self.tbn_vecs: fmt += "_ATTRIB33_ATTRIB34" fmt += "_VERTEX3" out_file.begin("vertices", fmt) normal = None - uv = None + uvs = [None]*len(mesh.uv_textures) tan = None bino = None for v in mesh.vertices: if v.normal!=normal: out_file.write("normal3", *v.normal) normal = v.normal - if v.uv!=uv: - out_file.write("texcoord2", *v.uv) - uv = v.uv + for i in texunits: + if v.uvs[i]!=uvs[i]: + if i==0: + out_file.write("texcoord2", *v.uvs[i]) + else: + out_file.write("multitexcoord2", i, *v.uvs[i]) + uvs[i] = v.uvs[i] if v.tan!=tan: out_file.write("attrib3", 3, *v.tan) tan = v.tan diff --git a/blender/io_mesh_mspgl/mesh.py b/blender/io_mesh_mspgl/mesh.py index 5af85787..e3405f9f 100644 --- a/blender/io_mesh_mspgl/mesh.py +++ b/blender/io_mesh_mspgl/mesh.py @@ -23,8 +23,7 @@ class Edge: return d = self.faces[0].normal.dot(self.faces[1].normal) - if (d>limit and self.faces[0].use_smooth and self.faces[1].use_smooth) or d>0.999: - self.smooth = True + self.smooth = ((d>limit and self.faces[0].use_smooth and self.faces[1].use_smooth) or d>0.99995) def other_face(self, f): if f.index==self.faces[0].index: @@ -41,10 +40,10 @@ class Vertex: if mv.__class__==Vertex: self._mvert = mv._mvert self.normal = mv.normal - self.uv = mv.uv + self.uvs = mv.uvs[:] else: self._mvert = mv - self.uv = None + self.uvs = [] self.flag = False self.faces = [] self.tan = None @@ -64,7 +63,7 @@ class Face: self._mface = mf self.edges = [] self.vertices = mf.vertices[:] - self.uv = None + self.uvs = [] self.flag = False self.material = None @@ -98,27 +97,34 @@ class Line: self.flag = False +def uvtex_unit_number(uvtex): + dot = uvtex.name.find('.') + if dot!=-1 and uvtex.name[dot+1:dot+5]=="unit" and uvtex.name[dot+5:].isdigit(): + return int(uvtex.name[dot+5]) + else: + return 1000 + class Mesh: def __init__(self, m): self._mesh = m - self.vertices = [Vertex(v) for v in m.vertices] - self.faces = [Face(f) for f in m.faces] - self.materials = m.materials[:] - self.has_uv = False - uvtex = None - if m.uv_textures: - uvtex = self.uv_textures[0] - self.has_uv = True + self.vertices = [Vertex(v) for v in self.vertices] + self.faces = [Face(f) for f in self.faces] + + self.materials = self.materials[:] + + self.uv_textures = [u for u in self.uv_textures if not u.name.endswith(".hidden")] + self.uv_textures.sort(key=uvtex_unit_number) for f in self.faces: f.vertices = [self.vertices[i] for i in f.vertices] - if uvtex: - f.uv = uvtex.data[f.index].uv for v in f.vertices: v.faces.append(f) + for u in self.uv_textures: + r = u.data[f.index].uv_raw; + f.uvs.append([(r[i], r[i+1]) for i in range(0, 8, 2)]) - self.edges = dict([(e.key, Edge(e)) for e in m.edges]) + 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] @@ -127,8 +133,8 @@ class Mesh: self.lines = [Line(e) for e in self.edges.values() if not e.faces] - if m.use_auto_smooth: - smooth_limit = math.cos(m.auto_smooth_angle*math.pi/180) + if self.use_auto_smooth: + smooth_limit = math.cos(self.auto_smooth_angle*math.pi/180) else: smooth_limit = -1 @@ -164,13 +170,20 @@ class Mesh: self.edges[e.key] = e self.lines += other.lines - + + def flatten_faces(self): + for f in self.faces: + f.use_smooth = False + + for e in self.edges.values(): + e.check_smooth(1) + def generate_material_uv(self): for f in self.faces: f.uv = ([(f.material_index+0.5)/len(self.materials), 0.5],)*len(f.vertices) self.has_uv = True - def split_vertices(self, find_group_func, progress = None): + def split_vertices(self, find_group_func, progress, *args): groups = [] for i in range(len(self.vertices)): v = self.vertices[i] @@ -180,7 +193,7 @@ class Mesh: vg = [] for f in v.faces: if not f.flag: - vg.append(find_group_func(v, f)) + vg.append(find_group_func(v, f, *args)) groups.append(vg) @@ -204,7 +217,6 @@ class Mesh: continue if e.other_face(f) not in g and len(e.faces)>=2: - k = e.faces.index(f) e.faces.remove(f) e = Edge(e) f.edges[j] = e @@ -227,8 +239,8 @@ class Mesh: def split_smooth(self, progress = None): self.split_vertices(self.find_smooth_group, progress) - def split_uv(self, progress = None): - self.split_vertices(self.find_uv_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 @@ -237,7 +249,6 @@ class Mesh: for f in queue: for e in f.edges: other = e.other_face(f) - #if not other or other.index not in face_indices: if other not in vertex.faces: continue @@ -248,12 +259,12 @@ class Mesh: return queue - def find_uv_group(self, vertex, face): - uv = face.uv[face.vertices.index(vertex)] + 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.uv[f.vertices.index(vertex)]==uv: + if not f.flag and f.uvs[index][f.vertices.index(vertex)]==uv: f.flag = True group.append(f) return group @@ -263,7 +274,15 @@ class Mesh: if v.faces: v.normal = mathutils.Vector() for f in v.faces: - v.normal += f.normal + fv = f.pivot_vertices(v) + edge1 = fv[1].co-fv[0].co + edge2 = fv[-1].co-fv[0].co + 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 v.normal.normalize() else: # XXX Should use edges to compute normal @@ -272,26 +291,40 @@ class Mesh: def compute_uv(self): for v in self.vertices: if v.faces: - v.uv = v.faces[0].uv[v.faces[0].vertices.index(v)] + f = v.faces[0] + i = f.vertices.index(v) + v.uvs = [u[i] for u in f.uvs] + + def compute_tbn(self, uvtex): + if not self.uv_textures: + return + + uvtex_names = [u.name for u in self.uv_textures] + if uvtex in uvtex_names: + uvtex_index = uvtex_names.index(uvtex) + else: + uvtex_index = 0 - def compute_tbn(self): for v in self.vertices: v.tan = mathutils.Vector() v.bino = mathutils.Vector() for f in v.faces: - fv = f.pivot_vertices(False, v) - v1 = fv[1] - v2 = fv[-1] - du1 = v1.uv[0]-v.uv[0] - du2 = v2.uv[0]-v.uv[0] - dv1 = v1.uv[1]-v.uv[1] - dv2 = v2.uv[1]-v.uv[1] - div = du1*dv2-du2*dv1 + fv = f.pivot_vertices(v) + uv0 = fv[0].uvs[uvtex_index] + uv1 = fv[1].uvs[uvtex_index] + uv2 = fv[-1].uvs[uvtex_index] + 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 + div = (du1*dv2-du2*dv1) if div: - v.tan += (edge1*dv2-edge2*dv1)/div - v.bino += (edge2*du1-edge1*du2)/div + mul = edge1.angle(edge2)/div + v.tan += (edge1*dv2-edge2*dv1)*mul + v.bino += (edge2*du1-edge1*du2)*mul + if v.tan.length: v.tan.normalize() if v.bino.length: