]> git.tdb.fi Git - libs/gl.git/blob - blender/io_mspgl/mesh.py
Support for exporting armatures and vertex groups
[libs/gl.git] / blender / io_mspgl / mesh.py
1 import math
2 import mathutils
3
4 def make_edge_key(i1, i2):
5         return (min(i1, i2), max(i1, i2))
6
7 class Edge:
8         def __init__(self, me):
9                 if me.__class__==Edge:
10                         self._medge = me._medge
11                         self.vertices = me.vertices[:]
12                         self.smooth = me.smooth
13                 else:
14                         self._medge = me
15                         self.smooth = False
16                 self.faces = []
17
18         def __getattr__(self, attr):
19                 return getattr(self._medge, attr)
20
21         def check_smooth(self, limit):
22                 if len(self.faces)!=2:
23                         return
24
25                 d = self.faces[0].normal.dot(self.faces[1].normal)
26                 self.smooth = ((d>limit and self.faces[0].use_smooth and self.faces[1].use_smooth) or d>0.99995)
27
28         def other_face(self, f):
29                 if f.index==self.faces[0].index:
30                         if len(self.faces)>=2:
31                                 return self.faces[1]
32                         else:
33                                 return None
34                 else:
35                         return self.faces[0]
36
37
38 class Vertex:
39         def __init__(self, mv):
40                 if mv.__class__==Vertex:
41                         self._mvert = mv._mvert
42                         self.normal = mv.normal
43                         self.uvs = mv.uvs[:]
44                         self.tan = mv.tan
45                         self.bino = mv.bino
46                         self.group_weight_scale = mv.group_weight_scale
47                 else:
48                         self._mvert = mv
49                         self.uvs = []
50                         self.tan = None
51                         self.bino = None
52                         self.group_weight_scale = 1
53                 self.flag = False
54                 self.faces = []
55
56         def __getattr__(self, attr):
57                 return getattr(self._mvert, attr)
58
59         def __cmp__(self, other):
60                 if other is None:
61                         return 1
62                 return cmp(self.index, other.index)
63
64
65 class Face:
66         def __init__(self, mf):
67                 self._mface = mf
68                 self.edges = []
69                 self.vertices = mf.vertices[:]
70                 self.uvs = []
71                 self.flag = False
72
73         def __getattr__(self, attr):
74                 return getattr(self._mface, attr)
75
76         def __cmp__(self, other):
77                 if other is None:
78                         return 1
79                 return cmp(self.index, other.index)
80
81         def pivot_vertices(self, *vt):
82                 flags = [(v in vt) for v in self.vertices]
83                 l = len(self.vertices)
84                 for i in range(l):
85                         if flags[i] and not flags[(i+l-1)%l]:
86                                 return self.vertices[i:]+self.vertices[:i]
87
88         def get_edge(self, v1, v2):     
89                 key = make_edge_key(v1.index, v2.index)
90                 for e in self.edges:
91                         if e.key==key:
92                                 return e
93                 raise KeyError("No edge %s"%(key,))
94
95         def get_neighbors(self):
96                 neighbors = [e.other_face(self) for e in self.edges]
97                 return list(filter(bool, neighbors))
98
99
100 class Line:
101         def __init__(self, e):
102                 self.edge = e
103                 self.vertices = e.vertices[:]
104                 self.flag = False
105
106
107 class UvLayer:
108         def __init__(self, l, t):
109                 self._layer = l
110                 self.uvtex = t
111                 self.name = self.uvtex.name
112                 self.unit = None
113                 self.hidden = False
114                 dot = self.name.find('.')
115                 if dot>=0:
116                         ext = self.name[dot:]
117                         if ext.startswith(".unit") and ext[5:].isdigit():
118                                 self.unit = int(ext[5:])
119                         elif ext==".hidden":
120                                 self.hidden = True
121
122         def __getattr__(self, attr):
123                 return getattr(self._layer, attr)
124
125 class FakeUvLayer:
126         def __init__(self, n):
127                 self.uvtex = None
128                 self.name = n
129                 self.unit = None
130                 self.hidden = False
131
132 class Mesh:
133         def __init__(self, m):
134                 self._mesh = m
135
136                 self.vertices = [Vertex(v) for v in self.vertices]
137                 self.faces = [Face(f) for f in self.polygons]
138
139                 self.materials = self.materials[:]
140
141                 self.uv_layers = [UvLayer(self.uv_layers[i], self.uv_textures[i]) for i in range(len(self.uv_layers))]
142                 self.assign_texture_units()
143
144                 for f in self.faces:
145                         f.vertices = [self.vertices[i] for i in f.vertices]
146                         for v in f.vertices:
147                                 v.faces.append(f)
148                         for u in self.uv_layers:
149                                 f.uvs.append([u.data[f.loop_indices[i]].uv for i in range(len(f.vertices))])
150
151                 self.edges = dict([(e.key, Edge(e)) for e in self.edges])
152                 for f in self.faces:
153                         for k in f.edge_keys:
154                                 e = self.edges[k]
155                                 e.faces.append(self.faces[f.index])
156                                 f.edges.append(e)
157
158                 self.lines = [Line(e) for e in self.edges.values() if not e.faces]
159
160                 if self.use_auto_smooth:
161                         smooth_limit = math.cos(self.auto_smooth_angle*math.pi/180)
162                 else:
163                         smooth_limit = -1
164
165                 for e in self.edges.values():
166                         e.vertices = [self.vertices[i] for i in e.vertices]
167                         e.check_smooth(smooth_limit)
168
169         def __getattr__(self, attr):
170                 return getattr(self._mesh, attr)
171
172         def splice(self, other):
173                 material_map = []
174                 for m in other.materials:
175                         if m in self.materials:
176                                 material_map.append(self.materials.index(m))
177                         else:
178                                 material_map.append(len(self.materials))
179                                 self.materials.append(m)
180
181                 offset = len(self.vertices)
182                 for v in other.vertices:
183                         v.index += offset
184                         self.vertices.append(v)
185
186                 offset = len(self.faces)
187                 for f in other.faces:
188                         f.index += offset
189                         if other.materials:
190                                 f.material_index = material_map[f.material_index]
191                         self.faces.append(f)
192
193                 for e in other.edges.values():
194                         e.key = make_edge_key(e.vertices[0].index, e.vertices[1].index)
195                         self.edges[e.key] = e
196
197                 self.lines += other.lines
198
199         def flatten_faces(self):
200                 for f in self.faces:
201                         f.use_smooth = False
202
203                 for e in self.edges.values():
204                         e.check_smooth(1)
205
206         def assign_texture_units(self):
207                 # Assign texture units for any non-hidden UV layers that lack one
208                 units = [u.unit for u in self.uv_layers if u.unit is not None]
209                 if units:
210                         free_unit = max(units)+1
211                 else:
212                         free_unit = 0
213                 for u in self.uv_layers:
214                         if u.unit is None:
215                                 if not u.hidden:
216                                         u.unit = free_unit
217                                         free_unit += 1
218
219         def generate_material_uv(self):
220                 self.uv_layers.append(FakeUvLayer("material_tex"))
221                 self.assign_texture_units()
222                 for f in self.faces:
223                         f.uvs.append([((f.material_index+0.5)/len(self.materials), 0.5)]*len(f.vertices))
224
225         def split_vertices(self, find_group_func, progress, *args):
226                 groups = []
227                 for i in range(len(self.vertices)):
228                         v = self.vertices[i]
229                         for f in v.faces:
230                                 f.flag = False
231
232                         vg = []
233                         for f in v.faces:
234                                 if not f.flag:
235                                         vg.append(find_group_func(v, f, *args))
236
237                         groups.append(vg)
238
239                         if progress:
240                                 progress.set_progress(i*0.5/len(self.vertices))
241
242                 for i in range(len(self.vertices)):
243                         if len(groups[i])==1:
244                                 continue
245
246                         for g in groups[i][1:]:
247                                 v = Vertex(self.vertices[i])
248                                 v.index = len(self.vertices)
249                                 self.vertices.append(v)
250
251                                 for f in g:
252                                         for j in range(len(f.edges)):
253                                                 e = f.edges[j]
254
255                                                 if self.vertices[i] not in e.vertices:
256                                                         continue
257
258                                                 if e.other_face(f) not in g and len(e.faces)>=2:
259                                                         e.faces.remove(f)
260                                                         e = Edge(e)
261                                                         f.edges[j] = e
262                                                         e.faces.append(f)
263                                                 else:
264                                                         del self.edges[e.key]
265
266                                                 e.vertices[e.vertices.index(self.vertices[i])] = v
267
268                                                 e.key = make_edge_key(e.vertices[0].index, e.vertices[1].index)
269                                                 self.edges[e.key] = e
270
271                                         self.vertices[i].faces.remove(f)
272                                         f.vertices[f.vertices.index(self.vertices[i])] = v
273                                         v.faces.append(f)
274
275                         if progress:
276                                 progress.set_progress(0.5+i*0.5/len(self.vertices))
277
278         def split_smooth(self, progress = None):
279                 self.split_vertices(self.find_smooth_group, progress)
280
281         def split_uv(self, index, progress = None):
282                 self.split_vertices(self.find_uv_group, progress, index)
283
284         def find_smooth_group(self, vertex, face):
285                 face.flag = True
286                 queue = [face]
287
288                 for f in queue:
289                         for e in f.edges:
290                                 other = e.other_face(f)
291                                 if other not in vertex.faces:
292                                         continue
293
294                                 if e.smooth:
295                                         if not other.flag:
296                                                 other.flag = True
297                                                 queue.append(other)
298
299                 return queue
300
301         def find_uv_group(self, vertex, face, index):
302                 uv = face.uvs[index][face.vertices.index(vertex)]
303                 face.flag = True
304                 group = [face]
305                 for f in vertex.faces:
306                         if not f.flag and f.uvs[index][f.vertices.index(vertex)]==uv:
307                                 f.flag = True
308                                 group.append(f)
309                 return group
310
311         def compute_normals(self):
312                 for v in self.vertices:
313                         if v.faces:
314                                 v.normal = mathutils.Vector()
315                                 for f in v.faces:
316                                         fv = f.pivot_vertices(v)
317                                         edge1 = fv[1].co-fv[0].co
318                                         edge2 = fv[-1].co-fv[0].co
319                                         weight = 1
320                                         if len(f.get_edge(fv[0], fv[1]).faces)==1:
321                                                 weight += 1
322                                         if len(f.get_edge(fv[0], fv[-1]).faces)==1:
323                                                 weight += 1
324                                         v.normal += f.normal*edge1.angle(edge2)*weight
325                                 v.normal.normalize()
326                         else:
327                                 # XXX Should use edges to compute normal
328                                 v.normal = mathutils.Vector(0, 0, 1)
329
330         def compute_uv(self):
331                 for v in self.vertices:
332                         if v.faces:
333                                 f = v.faces[0]
334                                 i = f.vertices.index(v)
335                                 v.uvs = [u[i] for u in f.uvs]
336
337         def compute_tbn(self, index):
338                 if not self.uv_layers:
339                         return
340
341                 for v in self.vertices:
342                         v.tan = mathutils.Vector()
343                         v.bino = mathutils.Vector()
344                         for f in v.faces:
345                                 fv = f.pivot_vertices(v)
346                                 uv0 = fv[0].uvs[index]
347                                 uv1 = fv[1].uvs[index]
348                                 uv2 = fv[-1].uvs[index]
349                                 du1 = uv1[0]-uv0[0]
350                                 du2 = uv2[0]-uv0[0]
351                                 dv1 = uv1[1]-uv0[1]
352                                 dv2 = uv2[1]-uv0[1]
353                                 edge1 = fv[1].co-fv[0].co
354                                 edge2 = fv[-1].co-fv[0].co
355                                 div = (du1*dv2-du2*dv1)
356                                 if div:
357                                         mul = edge1.angle(edge2)/div
358                                         v.tan += (edge1*dv2-edge2*dv1)*mul
359                                         v.bino += (edge2*du1-edge1*du2)*mul
360
361                         if v.tan.length:
362                                 v.tan.normalize()
363                         if v.bino.length:
364                                 v.bino.normalize()
365
366         def sort_vertex_groups(self, max_groups):
367                 for v in self.vertices:
368                         if v.groups:
369                                 v.groups = sorted(v.groups, key=(lambda g: g.weight), reverse=True)
370                                 v.group_weight_scale = 1.0/sum(g.weight for g in v.groups[:max_groups])
371
372         def create_strip(self, face, max_len):
373                 # Find an edge with another unused face next to it
374                 edge = None
375                 for e in face.edges:
376                         other = e.other_face(face)
377                         if other and not other.flag:
378                                 edge = e
379                                 break
380
381                 if not edge:
382                         return None
383
384                 # Add initial vertices so that we'll complete the edge on the first
385                 # iteration
386                 vertices = face.pivot_vertices(*edge.vertices)
387                 if len(vertices)==3:
388                         result = [vertices[-1], vertices[0]]
389                 else:
390                         result = [vertices[-2], vertices[-1]]
391
392                 while 1:
393                         face.flag = True
394
395                         vertices = face.pivot_vertices(*result[-2:])
396                         k = len(result)%2
397
398                         # Quads need special handling because the winding of every other
399                         # triangle in the strip is reversed
400                         if len(vertices)==4 and not k:
401                                 result.append(vertices[3])
402                         result.append(vertices[2])
403                         if len(vertices)==4 and k:
404                                 result.append(vertices[3])
405
406                         if len(result)>=max_len:
407                                 break
408
409                         # Hop over the last edge
410                         edge = face.get_edge(*result[-2:])
411                         face = edge.other_face(face)
412                         if not face or face.flag:
413                                 break
414
415                 return result