OSDN Git Service

fb180c174781989ce46eed2e7db9fd48cbd59e5f
[meshio/meshio.git] / swig / blender / bl24.py
1 # coding: utf-8
2 import sys
3 import os
4 import Blender
5 from Blender import Mathutils
6
7
8\e$B%U%!%$%k%7%9%F%`$NJ8;z%3!<%I\e(B
9\e$B2~B$HG$H$N6&MQ$N$?$a\e(B
10 FS_ENCODING=sys.getfilesystemencoding()
11 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
12     INTERNAL_ENCODING='utf-8'
13 else:
14     INTERNAL_ENCODING=FS_ENCODING
15
16
17 def createEmptyObject(scene, name):
18     empty=scene.objects.new("Empty")
19     empty.setName(name)
20     return empty
21
22
23 def createMqoMaterial(m):
24     material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))
25     material.mode |= Blender.Material.Modes.SHADELESS
26     material.rgbCol = [m.color.r, m.color.g, m.color.b]
27     material.alpha = m.color.a
28     material.amb = m.ambient
29     material.spec = m.specular
30     material.hard = int(255 * m.power)
31     return material
32
33
34 def createTexture(path):
35     image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))
36     texture = Blender.Texture.New(path.encode(INTERNAL_ENCODING))
37     texture.type = Blender.Texture.Types.IMAGE
38     texture.image = image
39     return texture, image
40
41
42 def materialAddTexture(material, texture):
43     material.mode = material.mode | Blender.Material.Modes.TEXFACE
44     material.setTexture(0, texture, Blender.Texture.TexCo.UV)
45
46
47 def createMesh(scene, name):
48     mesh = Blender.Mesh.New()
49     mesh_object=scene.objects.new(mesh, name.encode(INTERNAL_ENCODING))
50     return mesh, mesh_object
51
52
53 def objectMakeParent(parent, child):
54     parent.makeParent([child])
55
56
57 def meshAddMqoGeometry(mesh, o, materials, imageMap, scale):
58     # add vertices
59     mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy
60     mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])
61     # add faces
62     mesh_faces=[]
63     for face in o.faces:
64         face_indices=[]
65         for i in xrange(face.index_count):
66             face_indices.append(face.getIndex(i)+1)
67         mesh_faces.append(face_indices)
68     #new_faces=mesh.faces.extend([face.indices for face in o.faces], 
69     new_faces=mesh.faces.extend(mesh_faces,
70             #ignoreDups=True, 
71             indexList=True)
72     mesh.update()
73     
74     # gather used materials
75     materialMap = {}
76     if new_faces:
77         for i in new_faces:
78             if type(i) is int:
79                 materialMap[o.faces[i].material_index]=True
80
81     # blender limits 16 materials per mesh
82     # separate mesh ?
83     for i, material_index in enumerate(materialMap.keys()):
84         if i>=16:
85             print("over 16 materials!")
86             break
87         mesh.materials+=[materials[material_index]]
88         materialMap[material_index]=i
89     
90     # set face params
91     for i, f in enumerate(o.faces):       
92         if not type(new_faces[i]) is int:
93             continue
94
95         face=mesh.faces[new_faces[i]]
96
97         uv_array=[]
98         for i in xrange(f.index_count):
99             uv_array.append(Blender.Mathutils.Vector(
100                 f.getUV(i).x, 
101                 1.0-f.getUV(i).y)
102                 )
103         try:
104             face.uv=uv_array
105         except Exception as msg:
106             #print msg
107             #print face.index, uv_array
108             pass
109     
110         if f.material_index in materialMap:
111             face.mat = materialMap[f.material_index]
112
113         face.smooth = 1
114
115     # rmeove dummy 0 vertex
116     mesh.verts.delete(0)
117         
118     mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH
119     mesh.maxSmoothAngle = int(o.smoothing)
120     mesh.smooth()
121     mesh.calcNormals()
122     mesh.flipNormals()
123     mesh.update()
124
125     # mirror modifier
126     if o.mirror:
127         mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)
128
129 ###############################################################################
130 # for mqo mikoto bone.
131 ###############################################################################
132 class MikotoBone(object):
133     __slots__=[
134             'name',
135             'iHead', 'iTail', 'iUp',
136             'vHead', 'vTail', 'vUp',
137             'parent', 'isFloating',
138             'children',
139             ]
140     def __init__(self, face=None, vertices=None, materials=None):
141         self.parent=None
142         self.isFloating=False
143         self.children=[]
144         if not face:
145             self.name='root'
146             return
147
148         self.name=materials[face.material_index].name.encode('utf-8')
149
150         i0=face.indices[0]
151         i1=face.indices[1]
152         i2=face.indices[2]
153         v0=vertices[i0]
154         v1=vertices[i1]
155         v2=vertices[i2]
156         e01=v1-v0
157         e12=v2-v1
158         e20=v0-v2
159         sqNorm0=e01.getSqNorm()
160         sqNorm1=e12.getSqNorm()
161         sqNorm2=e20.getSqNorm()
162         if sqNorm0>sqNorm1:
163             if sqNorm1>sqNorm2:
164                 # e01 > e12 > e20
165                 self.iHead=i2
166                 self.iTail=i1
167                 self.iUp=i0
168             else:
169                 if sqNorm0>sqNorm2:
170                     # e01 > e20 > e12
171                     self.iHead=i2
172                     self.iTail=i0
173                     self.iUp=i1
174                 else:
175                     # e20 > e01 > e12
176                     self.iHead=i1
177                     self.iTail=i0
178                     self.iUp=i2
179         else:
180             # 0 < 1
181             if sqNorm1<sqNorm2:
182                 # e20 > e12 > e01
183                 self.iHead=i1
184                 self.iTail=i2
185                 self.iUp=i0
186             else:
187                 if sqNorm0<sqNorm2:
188                     # e12 > e20 > e01
189                     self.iHead=i0
190                     self.iTail=i2
191                     self.iUp=i1
192                 else:
193                     # e12 > e01 > e20
194                     self.iHead=i0
195                     self.iTail=i1
196                     self.iUp=i2
197         self.vHead=vertices[self.iHead]
198         self.vTail=vertices[self.iTail]
199         self.vUp=vertices[self.iUp]
200
201         if self.name.endswith('[]'):
202             basename=self.name[0:-2]
203             # expand LR name
204             if self.vTail.x>0:
205                 self.name="%s_L" % basename
206             else:
207                 self.name="%s_R" % basename
208
209
210     def setParent(self, parent, floating=False):
211         if floating:
212             self.isFloating=True
213         self.parent=parent
214         parent.children.append(self)
215
216     def printTree(self, indent=''):
217         print("%s%s" % (indent, self.name))
218         for child in self.children:
219             child.printTree(indent+'  ')
220
221
222 def build_armature(armature, mikotoBone, parent=None):
223     """
224     create a armature bone.
225     """
226     bone = Armature.Editbone()
227     bone.name = mikotoBone.name.encode('utf-8')
228     armature.bones[bone.name] = bone
229
230     bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())
231     bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())
232     if parent:
233         bone.parent=parent
234         if mikotoBone.isFloating:
235             pass
236         else:
237             bone.options=[Armature.CONNECTED]
238
239     for child in mikotoBone.children:
240         build_armature(armature, child, bone)
241
242
243 def create_armature(scene, mqo):
244     """
245     create armature
246     """
247     boneObject=None
248     for o in mqo.objects:
249         if o.name.startswith('bone'):
250             boneObject=o
251             break
252     if not boneObject:
253         return
254
255     tailMap={}
256     for f in boneObject.faces:
257         if f.index_count!=3:
258             print("invalid index_count: %d" % f.index_count)
259             continue
260         b=MikotoBone(f, boneObject.vertices, mqo.materials)
261         tailMap[b.iTail]=b
262
263     #################### 
264     # build mikoto bone tree
265     #################### 
266     mikotoRoot=MikotoBone()
267
268     for b in tailMap.values():
269         # each bone has unique parent or is root bone.
270         if b.iHead in tailMap:
271             b.setParent(tailMap[b.iHead])
272         else: 
273             isFloating=False
274             for e in boneObject.edges:
275                 if  b.iHead==e.indices[0]:
276                     # floating bone
277                     if e.indices[1] in tailMap:
278                         b.setParent(tailMap[e.indices[1]], True)
279                         isFloating=True
280                         break
281                 elif b.iHead==e.indices[1]:
282                     # floating bone
283                     if e.indices[0] in tailMap:
284                         b.setParent(tailMap[e.indices[0]], True)
285                         isFloating=True
286                         break
287             if isFloating:
288                 continue
289
290             # no parent bone
291             b.setParent(mikotoRoot, True)
292
293     if len(mikotoRoot.children)==0:
294         print("no root bone")
295         return
296
297     if len(mikotoRoot.children)==1:
298         # single root
299         mikotoRoot=mikotoRoot.children[0]
300         mikotoRoot.parent=None
301     else:
302         mikotoRoot.vHead=Vector3(0, 10, 0)
303         mikotoRoot.vTail=Vector3(0, 0, 0)
304
305     #################### 
306     # create armature
307     #################### 
308     armature = Armature.New()
309     # link to object
310     armature_object = scene.objects.new(armature)
311     # create action
312     act = Armature.NLA.NewAction()
313     act.setActive(armature_object)
314     # set XRAY
315     armature_object.drawMode |= Object.DrawModes.XRAY
316     # armature settings
317     armature.drawType = Armature.OCTAHEDRON
318     armature.envelopes = False
319     armature.vertexGroups = True
320     armature.mirrorEdit = True
321     armature.drawNames=True
322
323     # edit bones
324     armature.makeEditable()
325     build_armature(armature, mikotoRoot)
326     armature.update()
327
328     return armature_object
329         
330
331 class TrianglePlane(object):
332     """
333     mikoto\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#\e(B
334     (\e$BIT40A4\e(B)
335     """
336     __slots__=['normal', 
337             'v0', 'v1', 'v2',
338             ]
339     def __init__(self, v0, v1, v2):
340         self.v0=v0
341         self.v1=v1
342         self.v2=v2
343
344     def isInsideXY(self, p):
345         v0=Vector2(self.v0.x, self.v0.y)
346         v1=Vector2(self.v1.x, self.v1.y)
347         v2=Vector2(self.v2.x, self.v2.y)
348         e01=v1-v0
349         e12=v2-v1
350         e20=v0-v2
351         c0=Vector2.cross(e01, p-v0)
352         c1=Vector2.cross(e12, p-v1)
353         c2=Vector2.cross(e20, p-v2)
354         if c0>=0 and c1>=0 and c2>=0:
355             return True
356         if c0<=0 and c1<=0 and c2<=0:
357             return True
358
359     def isInsideYZ(self, p):
360         v0=Vector2(self.v0.y, self.v0.z)
361         v1=Vector2(self.v1.y, self.v1.z)
362         v2=Vector2(self.v2.y, self.v2.z)
363         e01=v1-v0
364         e12=v2-v1
365         e20=v0-v2
366         c0=Vector2.cross(e01, p-v0)
367         c1=Vector2.cross(e12, p-v1)
368         c2=Vector2.cross(e20, p-v2)
369         if c0>=0 and c1>=0 and c2>=0:
370             return True
371         if c0<=0 and c1<=0 and c2<=0:
372             return True
373
374     def isInsideZX(self, p):
375         v0=Vector2(self.v0.z, self.v0.x)
376         v1=Vector2(self.v1.z, self.v1.x)
377         v2=Vector2(self.v2.z, self.v2.x)
378         e01=v1-v0
379         e12=v2-v1
380         e20=v0-v2
381         c0=Vector2.cross(e01, p-v0)
382         c1=Vector2.cross(e12, p-v1)
383         c2=Vector2.cross(e20, p-v2)
384         if c0>=0 and c1>=0 and c2>=0:
385             return True
386         if c0<=0 and c1<=0 and c2<=0:
387             return True
388
389
390 class MikotoAnchor(object):
391     """
392     mikoto\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#\e(B
393     """
394     __slots__=[
395             "triangles", "bbox",
396             ]
397     def __init__(self):
398         self.triangles=[]
399         self.bbox=None
400
401     def push(self, face, vertices):
402         if face.index_count==3:
403             self.triangles.append(TrianglePlane(
404                 vertices[face.indices[0]],
405                 vertices[face.indices[1]],
406                 vertices[face.indices[2]]
407                 ))
408         elif face.index_count==4:
409             self.triangles.append(TrianglePlane(
410                 vertices[face.indices[0]],
411                 vertices[face.indices[1]],
412                 vertices[face.indices[2]]
413                 ))
414             self.triangles.append(TrianglePlane(
415                 vertices[face.indices[2]],
416                 vertices[face.indices[3]],
417                 vertices[face.indices[0]]
418                 ))
419         # bounding box
420         if not self.bbox:
421             self.bbox=BoundingBox(vertices[face.indices[0]])
422         for i in face.indices:
423             self.bbox.expand(vertices[i])
424
425
426     def calcWeight(self, v):
427         if not self.bbox.isInside(v):
428             return 0
429
430         if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):
431             return 1.0
432         else:
433             return 0
434         
435     def anyXY(self, x, y):
436         for t in self.triangles:
437             if t.isInsideXY(Vector2(x, y)):
438                 return True
439         return False
440
441     def anyYZ(self, y, z):
442         for t in self.triangles:
443             if t.isInsideYZ(Vector2(y, z)):
444                 return True
445         return False
446
447     def anyZX(self, z, x):
448         for t in self.triangles:
449             if t.isInsideZX(Vector2(z, x)):
450                 return True
451         return False
452
453
454 def create_bone_weight(scene, mqo, armature_object, objects):
455     """
456     create mikoto bone weight.
457     """
458     anchorMap={}
459     # setup mikoto anchors
460     for o in mqo.objects:
461         if o.name.startswith("anchor"):
462             for f in o.faces:
463                 name=mqo.materials[f.material_index].name
464                 if name.endswith('[]'):
465                     basename=name[0:-2]
466                     v=o.vertices[f.indices[0]]
467                     if(v.x>0):
468                         # L
469                         name_L=basename+'_L'
470                         if not name_L in anchorMap:
471                             anchorMap[name_L]=MikotoAnchor()
472                         anchorMap[name_L].push(f, o.vertices)
473                     elif(v.x<0):
474                         # R
475                         name_R=basename+'_R'
476                         if not name_R in anchorMap:
477                             anchorMap[name_R]=MikotoAnchor()
478                         anchorMap[name_R].push(f, o.vertices)
479                     else:
480                         print("no side", v)
481                 else:
482                     if not name in anchorMap:
483                         anchorMap[name]=MikotoAnchor()
484                     anchorMap[name].push(f, o.vertices)
485
486     for o in objects:
487         # add armature modifier
488         mod=o.modifiers.append(Modifier.Types.ARMATURE)
489         mod[Modifier.Settings.OBJECT] = armature_object
490         mod[Modifier.Settings.ENVELOPES] = False
491         o.makeDisplayList()
492         # create vertex group
493         mesh=o.getData(mesh=True)
494         for name in anchorMap.keys():
495             mesh.addVertGroup(name)
496         mesh.update()
497                  
498     # assing vertices to vertex group
499     for o in objects:
500         mesh=o.getData(mesh=True)
501         for i, mvert in enumerate(mesh.verts):
502             hasWeight=False
503             for name, anchor in anchorMap.items():
504                 weight=anchor.calcWeight(mvert.co)
505                 if weight>0:
506                     mesh.assignVertsToGroup(
507                             name, [i], weight, Mesh.AssignModes.ADD)
508                     hasWeight=True
509             if not hasWeight:
510                 # debug orphan vertex
511                 print('orphan', mvert)
512         mesh.update()
513