]> git.tdb.fi Git - libs/gl.git/blob - blender/io_mspgl/mesh.py
Major rework of mesh handling in Blender exporter
[libs/gl.git] / blender / io_mspgl / mesh.py
1 import bpy
2 import math
3 import mathutils
4
5 def make_edge_key(i1, i2):
6         return (min(i1, i2), max(i1, i2))
7
8 class Edge:
9         def __init__(self, me):
10                 if me.__class__==Edge:
11                         self._medge = me._medge
12                         self.vertices = me.vertices[:]
13                         self.smooth = me.smooth
14                 else:
15                         self._medge = me
16                         self.smooth = False
17                 self.key = me.key
18                 self.faces = []
19
20         def __getattr__(self, attr):
21                 return getattr(self._medge, attr)
22
23         def check_smooth(self, limit):
24                 if len(self.faces)!=2:
25                         return
26
27                 d = self.faces[0].normal.dot(self.faces[1].normal)
28                 self.smooth = ((d>limit and self.faces[0].use_smooth and self.faces[1].use_smooth) or d>0.99995)
29
30         def other_face(self, f):
31                 if f.index==self.faces[0].index:
32                         if len(self.faces)>=2:
33                                 return self.faces[1]
34                         else:
35                                 return None
36                 else:
37                         return self.faces[0]
38
39         def other_vertex(self, v):
40                 if v.index==self.vertices[0].index:
41                         return self.vertices[1]
42                 else:
43                         return self.vertices[0]
44
45
46 class Vertex:
47         def __init__(self, mv):
48                 if mv.__class__==Vertex:
49                         self._mvert = mv._mvert
50                         self.uvs = mv.uvs[:]
51                         self.tan = mv.tan
52                         self.bino = mv.bino
53                 else:
54                         self._mvert = mv
55                         self.uvs = []
56                         self.tan = None
57                         self.bino = None
58                 self.index = mv.index
59                 self.co = mv.co
60                 self.normal = mv.normal
61                 self.flag = False
62                 self.edges = []
63                 self.faces = []
64                 self.groups = mv.groups[:]
65
66         def __getattr__(self, attr):
67                 return getattr(self._mvert, attr)
68
69         def __cmp__(self, other):
70                 if other is None:
71                         return 1
72                 return cmp(self.index, other.index)
73
74
75 class VertexGroup:
76         def __init__(self, base):
77                 self._base = base
78                 self.group = base.group
79                 self.weight = base.weight
80
81         def __getattr__(self, attr):
82                 return getattr(self._mvert, attr)
83
84
85 class Face:
86         def __init__(self, mf):
87                 self._mface = mf
88                 self.index = mf.index
89                 self.edges = []
90                 self.vertices = mf.vertices[:]
91                 self.uvs = []
92                 self.flag = False
93
94         def __getattr__(self, attr):
95                 return getattr(self._mface, attr)
96
97         def __cmp__(self, other):
98                 if other is None:
99                         return 1
100                 return cmp(self.index, other.index)
101
102         def pivot_vertices(self, *vt):
103                 flags = [(v in vt) for v in self.vertices]
104                 l = len(self.vertices)
105                 for i in range(l):
106                         if flags[i] and not flags[(i+l-1)%l]:
107                                 return self.vertices[i:]+self.vertices[:i]
108
109         def get_edge(self, v1, v2):     
110                 key = make_edge_key(v1.index, v2.index)
111                 for e in self.edges:
112                         if e.key==key:
113                                 return e
114                 raise KeyError("No edge %s"%(key,))
115
116         def other_edge(self, e, v):
117                 for d in self.edges:
118                         if d!=e and v in d.vertices:
119                                 return d
120
121         def get_neighbors(self):
122                 neighbors = [e.other_face(self) for e in self.edges]
123                 return list(filter(bool, neighbors))
124
125
126 class Line:
127         def __init__(self, e):
128                 self.edge = e
129                 self.vertices = e.vertices[:]
130                 self.flag = False
131
132
133 class UvLayer:
134         def __init__(self, arg):
135                 if type(arg)==str:
136                         self._layer = None
137                         self.name = arg
138                 else:
139                         self._layer = arg
140                         self.name = arg.name
141                         self.uvs = [d.uv for d in self.data]
142
143                 self.unit = None
144                 self.hidden = False
145
146                 dot = self.name.find('.')
147                 if dot>=0:
148                         ext = self.name[dot:]
149                         if ext.startswith(".unit") and ext[5:].isdigit():
150                                 self.unit = int(ext[5:])
151                         elif ext==".hidden":
152                                 self.hidden = True
153
154         def __getattr__(self, attr):
155                 return getattr(self._layer, attr)
156
157
158 class Mesh:
159         def __init__(self, m):
160                 self._mesh = m
161
162                 self.winding_test = m.winding_test
163                 self.tbn_vecs = m.tbn_vecs
164                 self.vertex_groups = m.vertex_groups
165
166                 self.vertices = [Vertex(v) for v in self.vertices]
167                 self.faces = [Face(f) for f in self.polygons]
168                 self.edges = [Edge(e) for e in self.edges]
169                 self.edge_map = {e.key: e for e in self.edges}
170                 self.loops = self.loops[:]
171
172                 self.materials = self.materials[:]
173                 if self.use_uv=='NONE':
174                         self.uv_layers = []
175                 elif self.uv_layers:
176                         self.uv_layers = [UvLayer(u) for u in self.uv_layers]
177                         self.uv_layers = sorted([u for u in self.uv_layers if not u.hidden], key=(lambda u: (u.unit or 1000, u.name)))
178
179                         if self.use_uv=='UNIT0':
180                                 self.uv_layers = [self.uv_layers[0]]
181
182                         next_unit = max((u.unit+1 for u in self.uv_layers if u.unit is not None), default=0)
183                         for u in self.uv_layers:
184                                 if not u.unit:
185                                         u.unit = next_unit
186                                         next_unit += 1
187
188                 for v in self.vertices:
189                         v.groups = [VertexGroup(g) for g in v.groups]
190
191                 for f in self.faces:
192                         if len(f.vertices)>4:
193                                 raise ValueError("Ngons are not supported")
194
195                         f.vertices = [self.vertices[i] for i in f.vertices]
196                         for v in f.vertices:
197                                 v.faces.append(f)
198
199                         for k in f.edge_keys:
200                                 e = self.edge_map[k]
201                                 e.faces.append(f)
202                                 f.edges.append(e)
203
204                 for e in self.edges:
205                         e.vertices = [self.vertices[i] for i in e.vertices]
206                         for v in e.vertices:
207                                 v.edges.append(e)
208
209                 if self.use_lines:
210                         self.lines = [Line(e) for e in self.edges if not e.faces]
211                 else:
212                         self.lines = []
213
214         def __getattr__(self, attr):
215                 return getattr(self._mesh, attr)
216
217         def transform(self, matrix):
218                 for v in self.vertices:
219                         v.co = matrix*v.co
220
221         def splice(self, other):
222                 if len(self.uv_layers)!=len(other.uv_layers):
223                         raise ValueError("Meshes have incompatible UV layers")
224                 for i, u in enumerate(self.uv_layers):
225                         if u.name!=other.uv_layers[i].name:
226                                 raise ValueError("Meshes have incompatible UV layers")
227
228                 material_map = []
229                 for m in other.materials:
230                         if m in self.materials:
231                                 material_map.append(self.materials.index(m))
232                         else:
233                                 material_map.append(len(self.materials))
234                                 self.materials.append(m)
235
236                 for i, u in enumerate(self.uv_layers):
237                         u.uvs += other.uv_layers[i].uvs
238
239                 offset = len(self.vertices)
240                 self.vertices += other.vertices
241                 for v in self.vertices[offset:]:
242                         v.index += offset
243
244                 loop_offset = len(self.loops)
245                 self.loops += other.loops
246
247                 offset = len(self.faces)
248                 self.faces += other.faces
249                 for f in self.faces[offset:]:
250                         f.index += offset
251                         f.loop_start += loop_offset
252                         f.loop_indices = range(f.loop_start, f.loop_start+f.loop_total)
253                         if other.materials:
254                                 f.material_index = material_map[f.material_index]
255
256                 offset = len(self.edges)
257                 self.edges += other.edges
258                 for e in self.edges[offset:]:
259                         e.index += offset
260                         e.key = make_edge_key(e.vertices[0].index, e.vertices[1].index)
261                         self.edge_map[e.key] = e
262
263                 self.lines += other.lines
264
265         def prepare_smoothing(self, progress=None):
266                 smooth_limit = -1
267                 if self.smoothing=='NONE':
268                         for f in self.faces:
269                                 f.use_smooth = False
270
271                         smooth_limit = 1
272                 elif self.use_auto_smooth:
273                         smooth_limit = math.cos(self.auto_smooth_angle)
274
275                 for e in self.edges:
276                         e.check_smooth(smooth_limit)
277
278                 self.split_vertices(self.find_smooth_group, progress)
279
280                 if self.smoothing!='BLENDER':
281                         self.compute_normals()
282
283         def prepare_vertex_groups(self, obj):
284                 for v in self.vertices:
285                         if v.groups:
286                                 weight_sum = sum(g.weight for g in v.groups)
287                                 v.groups = sorted(v.groups, key=(lambda g: g.weight), reverse=True)[:self.max_groups_per_vertex]
288                                 weight_scale = weight_sum/sum(g.weight for g in v.groups)
289                                 for g in v.groups:
290                                         g.weight *= weight_scale
291
292                 if obj.parent and obj.parent.type=="ARMATURE":
293                         armature = obj.parent.data
294                         bone_indices = {b.name: i for i, b in enumerate(armature.bones)}
295                         group_index_map = {i: i for i in range(len(obj.vertex_groups))}
296                         for g in first_obj.vertex_groups:
297                                 if g.name in bone_indices:
298                                         group_index_map[g.index] = bone_indices[g.name]
299
300                         for v in self.vertices:
301                                 for g in v.groups:
302                                         g.group = group_index_map[g.group]
303
304         def prepare_uv(self, obj, progress=None):
305                 if obj.material_tex and self.use_uv!='NONE':
306                         layer = UvLayer("material_tex")
307
308                         if self.use_uv=='UNIT0':
309                                 self.uv_layers = [layer]
310                                 layer.unit = 0
311                         else:
312                                 self.uv_layers.append(layer)
313                                 layer.unit = max((u.unit+1 for u in self.uv_layers if u.unit is not None), default=0)
314
315                         layer.uvs = [None]*len(self.loops)
316                         for f in self.faces:
317                                 uv = mathutils.Vector(((f.material_index+0.5)/len(self.materials), 0.5))
318                                 for i in f.loop_indices:
319                                         layer.uvs[i] = uv
320
321                 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']
322                 array_uv_layers = [u for u in self.uv_layers if u.name in array_uv_layers]
323
324                 if array_uv_layers:
325                         for f in self.faces:
326                                 layer = 0
327                                 if f.material_index<len(self.materials):
328                                         mat = self.materials[f.material_index]
329                                         if mat and mat.array_atlas:
330                                                 layer = mat.array_layer
331
332                                 for l in array_uv_layers:
333                                         for i in f.loop_indices:
334                                                 l.uvs[i] = mathutils.Vector((*l.uvs[i], layer))
335
336                 for f in self.faces:
337                         for u in self.uv_layers:
338                                 f.uvs.append([u.uvs[i] for i in f.loop_indices])
339
340                 tbn_layer_index = -1
341                 if self.tbn_vecs:
342                         uv_names = [u.name for u in self.uv_layers]
343                         if self.tbn_uvtex in uv_names:
344                                 tbn_layer_index = uv_names.index(self.tbn_uvtex)
345                                 self.compute_tbn(tbn_layer_index)
346                                 self.split_vertices(self.find_uv_group, progress, tbn_layer_index)
347
348                 for i in range(len(self.uv_layers)):
349                         self.split_vertices(self.find_uv_group, progress, i)
350
351                 for v in self.vertices:
352                         if v.faces:
353                                 f = v.faces[0]
354                                 i = f.vertices.index(v)
355                                 v.uvs = [u[i] for u in f.uvs]
356                         else:
357                                 v.uvs = [(0.0, 0.0)]*len(self.uv_layers)
358
359         def split_vertices(self, find_group_func, progress, *args):
360                 groups = []
361                 for i in range(len(self.vertices)):
362                         v = self.vertices[i]
363                         for f in v.faces:
364                                 f.flag = False
365
366                         vg = []
367                         for f in v.faces:
368                                 if not f.flag:
369                                         vg.append(find_group_func(v, f, *args))
370
371                         groups.append(vg)
372
373                         if progress:
374                                 progress.set_progress(i*0.5/len(self.vertices))
375
376                 for i in range(len(self.vertices)):
377                         for g in groups[i][1:]:
378                                 v = Vertex(self.vertices[i])
379                                 v.index = len(self.vertices)
380                                 self.vertices.append(v)
381
382                                 v_edges = []
383                                 for e in self.vertices[i].edges:
384                                         e_faces_in_g = [f for f in e.faces if f in g]
385                                         if e_faces_in_g:
386                                                 boundary = len(e_faces_in_g)<len(e.faces)
387                                                 v_edges.append((e, boundary, e_faces_in_g))
388
389                                 for e, boundary, e_faces_in_g in v_edges:
390                                         if boundary:
391                                                 ne = Edge(e)
392                                                 ne.index = len(self.edges)
393                                                 self.edges.append(ne)
394
395                                                 for f in e_faces_in_g:
396                                                         e.faces.remove(f)
397                                                         f.edges[f.edges.index(e)] = ne
398                                                         ne.faces.append(f)
399                                                 e = ne
400                                         else:
401                                                 del self.edge_map[e.key]
402                                                 self.vertices[i].edges.remove(e)
403                                                 v.edges.append(e)
404
405                                         e.vertices[e.vertices.index(self.vertices[i])] = v
406
407                                         e.key = make_edge_key(e.vertices[0].index, e.vertices[1].index)
408                                         self.edge_map[e.key] = e
409
410                                 for f in g:
411                                         self.vertices[i].faces.remove(f)
412                                         f.vertices[f.vertices.index(self.vertices[i])] = v
413                                         v.faces.append(f)
414
415                         if progress:
416                                 progress.set_progress(0.5+i*0.5/len(self.vertices))
417
418         def find_smooth_group(self, vertex, face):
419                 face.flag = True
420
421                 edges = [e for e in face.edges if vertex in e.vertices]
422
423                 group = [face]
424                 for e in edges:
425                         f = face
426                         while e.smooth:
427                                 f = e.other_face(f)
428                                 if not f or f.flag:
429                                         break
430
431                                 f.flag = True
432                                 group.append(f)
433                                 e = f.other_edge(e, vertex)
434
435                 return group
436
437         def find_uv_group(self, vertex, face, index):
438                 uv = face.uvs[index][face.vertices.index(vertex)]
439                 face.flag = True
440
441                 group = [face]
442                 for f in vertex.faces:
443                         if not f.flag and f.uvs[index][f.vertices.index(vertex)]==uv:
444                                 f.flag = True
445                                 group.append(f)
446
447                 return group
448
449         def compute_normals(self):
450                 for v in self.vertices:
451                         v.normal = mathutils.Vector()
452                         for f in v.faces:
453                                 fv = f.pivot_vertices(v)
454                                 edge1 = fv[1].co-fv[0].co
455                                 edge2 = fv[-1].co-fv[0].co
456                                 if edge1.length and edge2.length:
457                                         v.normal += f.normal*edge1.angle(edge2)
458
459                         if v.normal.length:
460                                 v.normal.normalize()
461                         else:
462                                 v.normal = mathutils.Vector((0, 0, 1))
463
464         def compute_tbn(self, index):
465                 layer_uvs = self.uv_layers[index].uvs
466
467                 for v in self.vertices:
468                         v.tan = mathutils.Vector()
469                         v.bino = mathutils.Vector()
470                         for f in v.faces:
471                                 vi = f.vertices.index(v)
472                                 uv0 = layer_uvs[f.loop_indices[vi]]
473                                 uv1 = layer_uvs[f.loop_indices[vi+1]]
474                                 uv2 = layer_uvs[f.loop_indices[vi-1]]
475                                 du1 = uv1[0]-uv0[0]
476                                 du2 = uv2[0]-uv0[0]
477                                 dv1 = uv1[1]-uv0[1]
478                                 dv2 = uv2[1]-uv0[1]
479                                 edge1 = f.vertices[vi+1].co-f.vertices[vi].co
480                                 edge2 = f.vertices[vi-1].co-f.vertices[vi].co
481                                 div = (du1*dv2-du2*dv1)
482                                 if div:
483                                         mul = edge1.angle(edge2)/div
484                                         v.tan += (edge1*dv2-edge2*dv1)*mul
485                                         v.bino += (edge2*du1-edge1*du2)*mul
486
487                         if v.tan.length:
488                                 v.tan.normalize()
489                         if v.bino.length:
490                                 v.bino.normalize()
491
492         def drop_references(self):
493                 for v in self.vertices:
494                         v._mvert = None
495                         for g in v.groups:
496                                 g._base = None
497                 for e in self.edges:
498                         e._medge = None
499                 for f in self.faces:
500                         f._mface = None
501                 for u in self.uv_layers:
502                         u._layer = None
503                 self._mesh = None
504
505         def create_strip(self, face, max_len):
506                 # Find an edge with another unused face next to it
507                 edge = None
508                 for e in face.edges:
509                         other = e.other_face(face)
510                         if other and not other.flag:
511                                 edge = e
512                                 break
513
514                 if not edge:
515                         return None
516
517                 # Add initial vertices so that we'll complete the edge on the first
518                 # iteration
519                 vertices = face.pivot_vertices(*edge.vertices)
520                 if len(vertices)==3:
521                         result = [vertices[-1], vertices[0]]
522                 else:
523                         result = [vertices[-2], vertices[-1]]
524
525                 while 1:
526                         face.flag = True
527
528                         vertices = face.pivot_vertices(*result[-2:])
529                         k = len(result)%2
530
531                         # Quads need special handling because the winding of every other
532                         # triangle in the strip is reversed
533                         if len(vertices)==4 and not k:
534                                 result.append(vertices[3])
535                         result.append(vertices[2])
536                         if len(vertices)==4 and k:
537                                 result.append(vertices[3])
538
539                         if len(result)>=max_len:
540                                 break
541
542                         # Hop over the last edge
543                         edge = face.get_edge(*result[-2:])
544                         face = edge.other_face(face)
545                         if not face or face.flag:
546                                 break
547
548                 return result
549
550 def create_mesh_from_object(context, obj, progress=None):
551         if obj.type!="MESH":
552                 raise Exception("Object is not a mesh")
553
554         objs = [(obj, mathutils.Matrix())]
555         i = 0
556         while i<len(objs):
557                 o, m = objs[i]
558                 i += 1
559                 for c in o.children:
560                         if c.type=="MESH" and c.compound:
561                                 objs.append((c, m*c.matrix_local))
562
563         mesh = None
564         bmeshes = []
565         for o, m in objs:
566                 bmesh = o.to_mesh(context.scene, True, "PREVIEW")
567                 bmeshes.append(bmesh)
568                 me = Mesh(bmesh)
569                 me.transform(m)
570
571                 if mesh:
572                         mesh.splice(me)
573                 else:
574                         mesh = me
575
576         mesh.prepare_smoothing(progress)
577         mesh.prepare_vertex_groups(obj)
578         mesh.prepare_uv(obj, progress)
579
580         mesh.drop_references()
581         for m in bmeshes:
582                 bpy.data.meshes.remove(m)
583
584         return mesh