OSDN Git Service

d85d2b3af32990f528bcfcb9b32e8ed23e765cf3
[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 import bpy
7
8
9 # ファイルシステムの文字コード
10 # 改造版との共用のため
11 FS_ENCODING=sys.getfilesystemencoding()
12 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
13     INTERNAL_ENCODING='utf-8'
14 else:
15     INTERNAL_ENCODING=FS_ENCODING
16
17
18 ###############################################################################
19 # Writer
20 ###############################################################################
21 class Writer(object):
22     def __init__(self, path, encoding):
23         self.io=open(path, "wb")
24         self.encoding=encoding
25
26     def write(self, s):
27         self.io.write(s)
28
29     def flush(self):
30         self.io.flush()
31
32     def close(self):
33         self.io.close()
34
35
36 ###############################################################################
37 # ProgressBar
38 ###############################################################################
39 class ProgressBar(object):
40     def __init__(self, base):
41         print "#### %s ####" % base
42         self.base=base
43         self.start=Blender.sys.time() 
44         self.set('<start>', 0)
45
46     def advance(self, message, progress):
47         self.progress+=float(progress)
48         self._print(message)
49
50     def set(self, message, progress):
51         self.progress=float(progress)
52         self._print(message)
53
54     def _print(self, message):
55         print message
56         message="%s: %s" % (self.base, message)
57         if message.__class__ is unicode:
58             message=message.encode(FS_ENCODING)
59         Blender.Window.DrawProgressBar(self.progress, message)
60
61     def finish(self):
62         self.progress=1.0
63         message='finished in %.2f sec' % (Blender.sys.time()-self.start)
64         self.set(message, 1.0)
65
66
67 ###############################################################################
68 # for mqo mikoto bone.
69 ###############################################################################
70 class MikotoBone(object):
71     __slots__=[
72             'name',
73             'iHead', 'iTail', 'iUp',
74             'vHead', 'vTail', 'vUp',
75             'parent', 'isFloating',
76             'children',
77             ]
78     def __init__(self, face=None, vertices=None, materials=None):
79         self.parent=None
80         self.isFloating=False
81         self.children=[]
82         if not face:
83             self.name='root'
84             return
85
86         self.name=materials[face.material_index].name.encode('utf-8')
87
88         i0=face.indices[0]
89         i1=face.indices[1]
90         i2=face.indices[2]
91         v0=vertices[i0]
92         v1=vertices[i1]
93         v2=vertices[i2]
94         e01=v1-v0
95         e12=v2-v1
96         e20=v0-v2
97         sqNorm0=e01.getSqNorm()
98         sqNorm1=e12.getSqNorm()
99         sqNorm2=e20.getSqNorm()
100         if sqNorm0>sqNorm1:
101             if sqNorm1>sqNorm2:
102                 # e01 > e12 > e20
103                 self.iHead=i2
104                 self.iTail=i1
105                 self.iUp=i0
106             else:
107                 if sqNorm0>sqNorm2:
108                     # e01 > e20 > e12
109                     self.iHead=i2
110                     self.iTail=i0
111                     self.iUp=i1
112                 else:
113                     # e20 > e01 > e12
114                     self.iHead=i1
115                     self.iTail=i0
116                     self.iUp=i2
117         else:
118             # 0 < 1
119             if sqNorm1<sqNorm2:
120                 # e20 > e12 > e01
121                 self.iHead=i1
122                 self.iTail=i2
123                 self.iUp=i0
124             else:
125                 if sqNorm0<sqNorm2:
126                     # e12 > e20 > e01
127                     self.iHead=i0
128                     self.iTail=i2
129                     self.iUp=i1
130                 else:
131                     # e12 > e01 > e20
132                     self.iHead=i0
133                     self.iTail=i1
134                     self.iUp=i2
135         self.vHead=vertices[self.iHead]
136         self.vTail=vertices[self.iTail]
137         self.vUp=vertices[self.iUp]
138
139         if self.name.endswith('[]'):
140             basename=self.name[0:-2]
141             # expand LR name
142             if self.vTail.x>0:
143                 self.name="%s_L" % basename
144             else:
145                 self.name="%s_R" % basename
146
147
148     def setParent(self, parent, floating=False):
149         if floating:
150             self.isFloating=True
151         self.parent=parent
152         parent.children.append(self)
153
154     def printTree(self, indent=''):
155         print("%s%s" % (indent, self.name))
156         for child in self.children:
157             child.printTree(indent+'  ')
158
159
160 def build_armature(armature, mikotoBone, parent=None):
161     """
162     create a armature bone.
163     """
164     bone = Armature.Editbone()
165     bone.name = mikotoBone.name.encode('utf-8')
166     armature.bones[bone.name] = bone
167
168     bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())
169     bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())
170     if parent:
171         bone.parent=parent
172         if mikotoBone.isFloating:
173             pass
174         else:
175             bone.options=[Armature.CONNECTED]
176
177     for child in mikotoBone.children:
178         build_armature(armature, child, bone)
179
180
181 def create_armature(scene, mqo):
182     """
183     create armature
184     """
185     boneObject=None
186     for o in mqo.objects:
187         if o.name.startswith('bone'):
188             boneObject=o
189             break
190     if not boneObject:
191         return
192
193     tailMap={}
194     for f in boneObject.faces:
195         if f.index_count!=3:
196             print("invalid index_count: %d" % f.index_count)
197             continue
198         b=MikotoBone(f, boneObject.vertices, mqo.materials)
199         tailMap[b.iTail]=b
200
201     #################### 
202     # build mikoto bone tree
203     #################### 
204     mikotoRoot=MikotoBone()
205
206     for b in tailMap.values():
207         # each bone has unique parent or is root bone.
208         if b.iHead in tailMap:
209             b.setParent(tailMap[b.iHead])
210         else: 
211             isFloating=False
212             for e in boneObject.edges:
213                 if  b.iHead==e.indices[0]:
214                     # floating bone
215                     if e.indices[1] in tailMap:
216                         b.setParent(tailMap[e.indices[1]], True)
217                         isFloating=True
218                         break
219                 elif b.iHead==e.indices[1]:
220                     # floating bone
221                     if e.indices[0] in tailMap:
222                         b.setParent(tailMap[e.indices[0]], True)
223                         isFloating=True
224                         break
225             if isFloating:
226                 continue
227
228             # no parent bone
229             b.setParent(mikotoRoot, True)
230
231     if len(mikotoRoot.children)==0:
232         print("no root bone")
233         return
234
235     if len(mikotoRoot.children)==1:
236         # single root
237         mikotoRoot=mikotoRoot.children[0]
238         mikotoRoot.parent=None
239     else:
240         mikotoRoot.vHead=Vector3(0, 10, 0)
241         mikotoRoot.vTail=Vector3(0, 0, 0)
242
243     #################### 
244     # create armature
245     #################### 
246     armature = Armature.New()
247     # link to object
248     armature_object = scene.objects.new(armature)
249     # create action
250     act = Armature.NLA.NewAction()
251     act.setActive(armature_object)
252     # set XRAY
253     armature_object.drawMode |= Object.DrawModes.XRAY
254     # armature settings
255     armature.drawType = Armature.OCTAHEDRON
256     armature.envelopes = False
257     armature.vertexGroups = True
258     armature.mirrorEdit = True
259     armature.drawNames=True
260
261     # edit bones
262     armature.makeEditable()
263     build_armature(armature, mikotoRoot)
264     armature.update()
265
266     return armature_object
267         
268
269 class TrianglePlane(object):
270     """
271     mikoto方式ボーンのアンカーウェイト計算用。
272     (不完全)
273     """
274     __slots__=['normal', 
275             'v0', 'v1', 'v2',
276             ]
277     def __init__(self, v0, v1, v2):
278         self.v0=v0
279         self.v1=v1
280         self.v2=v2
281
282     def isInsideXY(self, p):
283         v0=Vector2(self.v0.x, self.v0.y)
284         v1=Vector2(self.v1.x, self.v1.y)
285         v2=Vector2(self.v2.x, self.v2.y)
286         e01=v1-v0
287         e12=v2-v1
288         e20=v0-v2
289         c0=Vector2.cross(e01, p-v0)
290         c1=Vector2.cross(e12, p-v1)
291         c2=Vector2.cross(e20, p-v2)
292         if c0>=0 and c1>=0 and c2>=0:
293             return True
294         if c0<=0 and c1<=0 and c2<=0:
295             return True
296
297     def isInsideYZ(self, p):
298         v0=Vector2(self.v0.y, self.v0.z)
299         v1=Vector2(self.v1.y, self.v1.z)
300         v2=Vector2(self.v2.y, self.v2.z)
301         e01=v1-v0
302         e12=v2-v1
303         e20=v0-v2
304         c0=Vector2.cross(e01, p-v0)
305         c1=Vector2.cross(e12, p-v1)
306         c2=Vector2.cross(e20, p-v2)
307         if c0>=0 and c1>=0 and c2>=0:
308             return True
309         if c0<=0 and c1<=0 and c2<=0:
310             return True
311
312     def isInsideZX(self, p):
313         v0=Vector2(self.v0.z, self.v0.x)
314         v1=Vector2(self.v1.z, self.v1.x)
315         v2=Vector2(self.v2.z, self.v2.x)
316         e01=v1-v0
317         e12=v2-v1
318         e20=v0-v2
319         c0=Vector2.cross(e01, p-v0)
320         c1=Vector2.cross(e12, p-v1)
321         c2=Vector2.cross(e20, p-v2)
322         if c0>=0 and c1>=0 and c2>=0:
323             return True
324         if c0<=0 and c1<=0 and c2<=0:
325             return True
326
327
328 class MikotoAnchor(object):
329     """
330     mikoto方式スケルトンのアンカー。
331     """
332     __slots__=[
333             "triangles", "bbox",
334             ]
335     def __init__(self):
336         self.triangles=[]
337         self.bbox=None
338
339     def push(self, face, vertices):
340         if face.index_count==3:
341             self.triangles.append(TrianglePlane(
342                 vertices[face.indices[0]],
343                 vertices[face.indices[1]],
344                 vertices[face.indices[2]]
345                 ))
346         elif face.index_count==4:
347             self.triangles.append(TrianglePlane(
348                 vertices[face.indices[0]],
349                 vertices[face.indices[1]],
350                 vertices[face.indices[2]]
351                 ))
352             self.triangles.append(TrianglePlane(
353                 vertices[face.indices[2]],
354                 vertices[face.indices[3]],
355                 vertices[face.indices[0]]
356                 ))
357         # bounding box
358         if not self.bbox:
359             self.bbox=BoundingBox(vertices[face.indices[0]])
360         for i in face.indices:
361             self.bbox.expand(vertices[i])
362
363
364     def calcWeight(self, v):
365         if not self.bbox.isInside(v):
366             return 0
367
368         if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):
369             return 1.0
370         else:
371             return 0
372         
373     def anyXY(self, x, y):
374         for t in self.triangles:
375             if t.isInsideXY(Vector2(x, y)):
376                 return True
377         return False
378
379     def anyYZ(self, y, z):
380         for t in self.triangles:
381             if t.isInsideYZ(Vector2(y, z)):
382                 return True
383         return False
384
385     def anyZX(self, z, x):
386         for t in self.triangles:
387             if t.isInsideZX(Vector2(z, x)):
388                 return True
389         return False
390
391
392 def create_bone_weight(scene, mqo, armature_object, objects):
393     """
394     create mikoto bone weight.
395     """
396     anchorMap={}
397     # setup mikoto anchors
398     for o in mqo.objects:
399         if o.name.startswith("anchor"):
400             for f in o.faces:
401                 name=mqo.materials[f.material_index].name
402                 if name.endswith('[]'):
403                     basename=name[0:-2]
404                     v=o.vertices[f.indices[0]]
405                     if(v.x>0):
406                         # L
407                         name_L=basename+'_L'
408                         if not name_L in anchorMap:
409                             anchorMap[name_L]=MikotoAnchor()
410                         anchorMap[name_L].push(f, o.vertices)
411                     elif(v.x<0):
412                         # R
413                         name_R=basename+'_R'
414                         if not name_R in anchorMap:
415                             anchorMap[name_R]=MikotoAnchor()
416                         anchorMap[name_R].push(f, o.vertices)
417                     else:
418                         print("no side", v)
419                 else:
420                     if not name in anchorMap:
421                         anchorMap[name]=MikotoAnchor()
422                     anchorMap[name].push(f, o.vertices)
423
424     for o in objects:
425         # add armature modifier
426         mod=o.modifiers.append(Modifier.Types.ARMATURE)
427         mod[Modifier.Settings.OBJECT] = armature_object
428         mod[Modifier.Settings.ENVELOPES] = False
429         o.makeDisplayList()
430         # create vertex group
431         mesh=o.getData(mesh=True)
432         for name in anchorMap.keys():
433             mesh.addVertGroup(name)
434         mesh.update()
435                  
436     # assing vertices to vertex group
437     for o in objects:
438         mesh=o.getData(mesh=True)
439         for i, mvert in enumerate(mesh.verts):
440             hasWeight=False
441             for name, anchor in anchorMap.items():
442                 weight=anchor.calcWeight(mvert.co)
443                 if weight>0:
444                     mesh.assignVertsToGroup(
445                             name, [i], weight, Mesh.AssignModes.ADD)
446                     hasWeight=True
447             if not hasWeight:
448                 # debug orphan vertex
449                 print('orphan', mvert)
450         mesh.update()
451
452 ###############################################################################
453 def createEmptyObject(scene, name):
454     empty=scene.objects.new("Empty")
455     empty.setName(name)
456     return empty
457
458
459 def createMqoMaterial(m):
460     material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))
461     material.mode |= Blender.Material.Modes.SHADELESS
462     material.rgbCol = [m.color.r, m.color.g, m.color.b]
463     material.alpha = m.color.a
464     material.amb = m.ambient
465     material.spec = m.specular
466     material.hard = int(255 * m.power)
467     return material
468
469 def createPmdMaterial(m):
470     material=Blender.Material.New()
471     material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
472     material.setRef(1)
473     material.diffuseSize = 3.14/2
474     material.setDiffuseSmooth(0)
475     material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
476     material.setSpecSize(0)
477     material.setSpec(0)
478     material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
479     material.setAlpha(m.diffuse.a)
480     material.setSpec(m.shinness*0.1)
481     material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
482     material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
483     material.enableSSS=True if m.flag==1 else False
484     return material
485
486 def createTexture(path):
487     image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))
488     texture = Blender.Texture.New(path.encode(INTERNAL_ENCODING))
489     texture.type = Blender.Texture.Types.IMAGE
490     texture.image = image
491     return texture, image
492
493
494 def materialAddTexture(material, texture):
495     material.mode = material.mode | Blender.Material.Modes.TEXFACE
496     material.setTexture(0, texture, Blender.Texture.TexCo.UV)
497
498
499 def createMesh(scene, name):
500     mesh = Blender.Mesh.New()
501     mesh_object=scene.objects.new(mesh, name.encode(INTERNAL_ENCODING))
502     return mesh, mesh_object
503
504
505 def objectMakeParent(parent, child):
506     parent.makeParent([child])
507
508
509 def meshAddMaterial(mesh, material):
510     mesh.materials+=[material]
511
512
513 def meshAddMqoGeometry(mesh, o, materials, imageMap, scale):
514     # add vertices
515     mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy
516     mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])
517     # add faces
518     mesh_faces=[]
519     for face in o.faces:
520         face_indices=[]
521         for i in xrange(face.index_count):
522             face_indices.append(face.getIndex(i)+1)
523         mesh_faces.append(face_indices)
524     #new_faces=mesh.faces.extend([face.indices for face in o.faces], 
525     new_faces=mesh.faces.extend(mesh_faces,
526             #ignoreDups=True, 
527             indexList=True)
528     mesh.update()
529     
530     # gather used materials
531     materialMap = {}
532     if new_faces:
533         for i in new_faces:
534             if type(i) is int:
535                 materialMap[o.faces[i].material_index]=True
536
537     # blender limits 16 materials per mesh
538     # separate mesh ?
539     for i, material_index in enumerate(materialMap.keys()):
540         if i>=16:
541             print("over 16 materials!")
542             break
543         mesh.materials+=[materials[material_index]]
544         materialMap[material_index]=i
545     
546     # set face params
547     for i, f in enumerate(o.faces):       
548         if not type(new_faces[i]) is int:
549             continue
550
551         face=mesh.faces[new_faces[i]]
552
553         uv_array=[]
554         for i in xrange(f.index_count):
555             uv_array.append(Blender.Mathutils.Vector(
556                 f.getUV(i).x, 
557                 1.0-f.getUV(i).y)
558                 )
559         try:
560             face.uv=uv_array
561         except Exception as msg:
562             #print msg
563             #print face.index, uv_array
564             pass
565     
566         if f.material_index in materialMap:
567             face.mat = materialMap[f.material_index]
568
569         face.smooth = 1
570
571     # rmeove dummy 0 vertex
572     mesh.verts.delete(0)
573         
574     mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH
575     mesh.maxSmoothAngle = int(o.smoothing)
576     mesh.smooth()
577     mesh.calcNormals()
578     mesh.flipNormals()
579     mesh.update()
580
581     # mirror modifier
582     if o.mirror:
583         mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)
584
585 def getTexture(m, dirname):
586     tex=""
587     aplane=""
588     # texture
589     for texture in m.getTextures():
590         if texture and texture.tex and texture.tex.getImage():
591             image=texture.tex.getImage()
592             if not image:
593                 continue
594             imagePath=Blender.sys.expandpath(image.getFilename())
595             if len(dirname)>0 and imagePath.startswith(dirname):
596                 # 相対パスに変換する
597                 imagePath=imagePath[len(dirname)+1:len(imagePath)]
598             if texture.mtCol>0:
599                 tex=" tex(\"%s\")" % imagePath
600             elif texture.mtAlpha>0:
601                 aplane=" aplane(\"%s\")" % imagePath
602     return tex, aplane
603
604 def objectDuplicate(scene, obj):
605     mesh, dumy=createMesh(scene, obj.name.decode(INTERNAL_ENCODING))
606     # not apply modifiers
607     mesh.getFromObject(obj.name, 1)
608     # apply matrix
609     dumy.setMatrix(obj.matrixWorld)
610     return mesh, dumy
611
612 def faceVertexCount(face):
613     return len(face.v)
614
615 def faceVertices(face):
616     # flip
617     return [v.index for v in reversed(face.v)]
618
619 def meshHasUV(mesh):
620     return mesh.faceUV
621
622 def faceHasUV(mesh, i, face):
623     return len(face.uv)>0
624
625 def faceGetUV(mesh, i, face, count):
626     # flip
627     return reversed(face.uv)
628
629 def materialToMqo(m):
630     return "\"%s\" shader(3) col(%f %f %f %f)" % (
631             m.name, m.rgbCol[0], m.rgbCol[1], m.rgbCol[2], m.alpha)
632
633 def faceMaterialIndex(face):
634     return face.mat
635
636 def objectGetData(o):
637     return o.getData(mesh=True)
638
639 def objectAddArmatureModifier(o, armature_object):
640     mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
641     mod[Blender.Modifier.Settings.OBJECT] = armature_object
642     mod[Blender.Modifier.Settings.ENVELOPES] = False
643
644 def objectSelect(o):
645     o.select(True)
646
647 def objectGetPose(o):
648     return o.getPose()
649
650 def poseBoneLimit(n, b):
651     if n.endswith("_t"):
652         return
653     if n.startswith("knee_"):
654         b.lockYRot=True
655         b.lockZRot=True
656         b.limitX=True
657         b.limitMin=[0, 0, 0]
658         b.limitMax=[180, 0, 0]
659     elif n.startswith("ankle_"):
660         b.lockYRot=True
661
662 def enterEditMode():
663     Blender.Window.EditMode(1)
664
665 def exitEditMode():
666     Blender.Window.EditMode(0)
667
668 def objectDeselectAll():
669     for o in bpy.data.scenes.active.objects:
670         o.select(False)
671
672 def objectActivate(scene, o):
673     o.select(True )
674     scene.objects.active=o
675
676 def meshAddVertexGroup(meshObject, name):
677     meshObject.getData(mesh=True).addVertGroup(name)
678
679 def meshUseVertexUv(mesh):
680     mesh.vertexUV = 1
681
682 def vertexSetNormal(mvert, normal):
683     mvert.no=Mathutils.Vector(*normal)
684
685 def vertexSetUv(mvert, uv):
686     mvert.uvco=uv
687
688 def meshAssignVertexGroup(meshObject, name, index, weight):
689     mesh.getData(mesh=True).assignVertsToGroup(name, 
690             [index], weight, Blender.Mesh.AssignModes.ADD)
691
692 def meshCreateVerteicesAndFaces(mesh, vertices, faces):
693     mesh.verts.extend(vertices)
694     mesh.faces.extend(mesh_face_indices, ignoreDups=True)
695
696 def meshAddUV(mesh):
697     mesh.addUVLayer('NewUV')
698
699 def meshVertsDelete(mesh, remove_vertices):
700     mesh.verts.delete(remove_vertices)
701