OSDN Git Service

update for blender2.53.
[meshio/meshio.git] / swig / blender / pmd_export.py
1 #!BPY
2 # coding: utf-8
3 """
4  Name: 'MikuMikuDance model (.pmd)...'
5  Blender: 248
6  Group: 'Export'
7  Tooltip: 'Export PMD file for MikuMikuDance.'
8 """
9 __author__= ["ousttrue"]
10 __version__= "2.0"
11 __url__=()
12 __bpydoc__="""
13 pmd Importer
14
15 This script exports a pmd model.
16
17 0.1 20100318: first implementation.
18 0.2 20100519: refactoring. use C extension.
19 1.0 20100530: implement basic features.
20 1.1 20100612: integrate 2.4 and 2.5.
21 1.2 20100616: implement rigid body.
22 1.3 20100619: fix rigid body, bone weight.
23 1.4 20100626: refactoring.
24 1.5 20100629: sphere map.
25 1.6 20100710: toon texture & bone group.
26 1.7 20100711: separate vertex with normal or uv.
27 2.0 20100724: update for Blender2.53.
28 """
29
30 bl_addon_info = {
31         'category': 'Import/Export',
32         'name': 'Export: MikuMikuDance Model Format (.pmd)',
33         'author': 'ousttrue',
34         'version': '2.0',
35         'blender': (2, 5, 3),
36         'location': 'File > Export',
37         'description': 'Export to the MikuMikuDance Model Format (.pmd)',
38         'warning': '', # used for warning icon and text in addons panel
39         'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
40         }
41
42 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
43 MMD_MB_NAME='mb_name'
44 MMD_MB_COMMENT='mb_comment'
45 MMD_COMMENT='comment'
46 BASE_SHAPE_NAME='Basis'
47 RIGID_NAME='rigid_name'
48 RIGID_SHAPE_TYPE='rigid_shape_type'
49 RIGID_PROCESS_TYPE='rigid_process_type'
50 RIGID_BONE_NAME='rigid_bone_name'
51 #RIGID_LOCATION='rigid_loation'
52 RIGID_GROUP='ribid_group'
53 RIGID_INTERSECTION_GROUP='rigid_intersection_group'
54 RIGID_WEIGHT='rigid_weight'
55 RIGID_LINEAR_DAMPING='rigid_linear_damping'
56 RIGID_ANGULAR_DAMPING='rigid_angular_damping'
57 RIGID_RESTITUTION='rigid_restitution'
58 RIGID_FRICTION='rigid_friction'
59 CONSTRAINT_NAME='constraint_name'
60 CONSTRAINT_A='const_a'
61 CONSTRAINT_B='const_b'
62 CONSTRAINT_POS_MIN='const_pos_min'
63 CONSTRAINT_POS_MAX='const_pos_max'
64 CONSTRAINT_ROT_MIN='const_rot_min'
65 CONSTRAINT_ROT_MAX='const_rot_max'
66 CONSTRAINT_SPRING_POS='const_spring_pos'
67 CONSTRAINT_SPRING_ROT='const_spring_rot'
68 TOON_TEXTURE_OBJECT='ToonTextures'
69
70
71 ###############################################################################
72 # import
73 ###############################################################################
74 import os
75 import sys
76
77 # C extension
78 from meshio import pmd, englishmap
79
80 def isBlender24():
81     return sys.version_info[0]<3
82
83 if isBlender24():
84     # for 2.4
85     import Blender
86     from Blender import Mathutils
87     import bpy
88
89     # wrapper
90     import bl24 as bl
91
92     def setMaterialParams(material, m):
93         # diffuse
94         material.diffuse.r=m.R
95         material.diffuse.g=m.G
96         material.diffuse.b=m.B
97         material.diffuse.a=m.alpha
98         # specular
99         material.sinness=0 if m.spec<1e-5 else m.spec*10
100         material.specular.r=m.specR
101         material.specular.g=m.specG
102         material.specular.b=m.specB
103         # ambient
104         material.ambient.r=m.mirR
105         material.ambient.g=m.mirG
106         material.ambient.b=m.mirB
107         # flag
108         material.flag=1 if m.enableSSS else 0
109
110     def toCP932(s):
111         return s
112
113
114 else:
115     # for 2.5
116     import bpy
117     import mathutils
118
119     # wrapper
120     import bl25 as bl
121
122     xrange=range
123
124     def setMaterialParams(material, m):
125         # diffuse
126         material.diffuse.r=m.diffuse_color[0]
127         material.diffuse.g=m.diffuse_color[1]
128         material.diffuse.b=m.diffuse_color[2]
129         material.diffuse.a=m.alpha
130         # specular
131         material.sinness=0 if m.specular_toon_size<1e-5 else m.specular_hardness*10
132         material.specular.r=m.specular_color[0]
133         material.specular.g=m.specular_color[1]
134         material.specular.b=m.specular_color[2]
135         # ambient
136         material.ambient.r=m.mirror_color[0]
137         material.ambient.g=m.mirror_color[1]
138         material.ambient.b=m.mirror_color[2]
139         # flag
140         material.flag=1 if m.subsurface_scattering.enabled else 0
141         # toon
142         material.toon_index=7
143
144     def toCP932(s):
145         return s.encode('cp932')
146
147
148 class Node(object):
149     __slots__=['o', 'children']
150     def __init__(self, o):
151         self.o=o
152         self.children=[]
153
154
155 ###############################################################################
156 # Blenderのメッシュをワンスキンメッシュ化する
157 ###############################################################################
158 def near(x, y, EPSILON=1e-5):
159     d=x-y
160     return d>=-EPSILON and d<=EPSILON
161
162
163 class VertexAttribute(object):
164     __slots__=[
165             'nx', 'ny', 'nz', # normal
166             'u', 'v', # uv
167             ]
168     def __init__(self, nx, ny, nz, u, v):
169         self.nx=nx
170         self.ny=ny
171         self.nz=nz
172         self.u=u
173         self.v=v
174
175     def __str__(self):
176         return "<vkey: %f, %f, %f, %f, %f>" % (
177                 self.nx, self.ny, self.nz, self.u, self.v)
178
179     def __hash__(self):
180         return self.nx + self.ny + self.nz + self.u + self.v
181
182     def __eq__(self, rhs):
183         return self.nx==rhs.nx and self.ny==rhs.ny and self.nz==rhs.nz and self.u==rhs.u and self.v==rhs.v
184
185
186 class VertexKey(object):
187     __slots__=[
188             'obj_index', 'index',
189             ]
190
191     def __init__(self, obj_index, index):
192         self.obj_index=obj_index
193         self.index=index
194
195     def __str__(self):
196         return "<vkey: %d, %d>" % (self.obj_index, self.index)
197
198     def __hash__(self):
199         return self.index*100+self.obj_index
200
201     def __eq__(self, rhs):
202         return self.obj_index==rhs.obj_index and self.index==rhs.index
203
204
205 class VertexArray(object):
206     """
207     頂点配列
208     """
209     __slots__=[
210             'indexArrays',
211             'positions',
212             'attributes', # normal and uv
213             'b0', 'b1', 'weight',
214             'vertexMap',
215             'objectMap',
216             ]
217     def __init__(self):
218         # indexArrays split with each material
219         self.indexArrays={}
220
221         self.positions=[]
222         self.attributes=[]
223         self.b0=[]
224         self.b1=[]
225         self.weight=[]
226
227         self.vertexMap={}
228         self.objectMap={}
229
230     def __str__(self):
231         return "<VertexArray %d positions, %d indexArrays>" % (
232                 len(self.positions), len(self.indexArrays))
233
234     def zip(self):
235         return zip(
236                 self.positions, self.attributes,
237                 self.b0, self.b1, self.weight)
238
239     def each(self):
240         keys=[key for key in self.indexArrays.keys()]
241         keys.sort()
242         for key in keys:
243             yield(key, self.indexArrays[key])
244
245     def __addOrGetIndex(self, obj_index, base_index, pos, normal, uv, b0, b1, weight0):
246         key=VertexKey(obj_index, base_index)
247         attribute=VertexAttribute( 
248                 normal[0], normal[1], normal[2],
249                 uv[0], uv[1])
250         if key in self.vertexMap:
251             if attribute in self.vertexMap[key]:
252                 return self.vertexMap[key][attribute]
253             else:
254                 return self.__addVertex(self.vertexMap[key],
255                         pos, attribute, b0, b1, weight0)
256         else:
257             vertexMapKey={}
258             self.vertexMap[key]=vertexMapKey
259             return self.__addVertex(vertexMapKey,
260                     pos, attribute, b0, b1, weight0)
261
262     def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0):
263         index=len(self.positions)
264         vertexMapKey[attribute]=index
265         # position
266         self.positions.append((pos.x, pos.y, pos.z))
267         # unique attribute
268         self.attributes.append(attribute)
269         # shared attribute
270         self.b0.append(b0)
271         self.b1.append(b1)
272         self.weight.append(weight0)
273         assert(index<=65535)
274         return index
275             
276     def getMappedIndex(self, obj_name, base_index):
277         return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)]
278
279     def addTriangle(self,
280             object_name, material,
281             base_index0, base_index1, base_index2,
282             pos0, pos1, pos2,
283             n0, n1, n2,
284             uv0, uv1, uv2,
285             b0_0, b0_1, b0_2,
286             b1_0, b1_1, b1_2,
287             weight0, weight1, weight2
288             ):
289         if object_name in self.objectMap:
290             obj_index=self.objectMap[object_name]
291         else:
292             obj_index=len(self.objectMap)
293             self.objectMap[object_name]=obj_index
294         index0=self.__addOrGetIndex(obj_index, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
295         index1=self.__addOrGetIndex(obj_index, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
296         index2=self.__addOrGetIndex(obj_index, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
297
298         if not material in self.indexArrays:
299             self.indexArrays[material]=[]
300         self.indexArrays[material]+=[index0, index1, index2]
301
302
303 class Morph(object):
304     __slots__=['name', 'type', 'offsets']
305     def __init__(self, name, type):
306         self.name=name
307         self.type=type
308         self.offsets=[]
309
310     def add(self, index, offset):
311         self.offsets.append((index, offset))
312
313     def sort(self):
314         if isBlender24():
315             self.offsets.sort(lambda l, r: l[0]-r[0])
316         else:
317             self.offsets.sort(key=lambda e: e[0])
318
319     def __str__(self):
320         return "<Morph %s>" % self.name
321
322 class IKSolver(object):
323     __slots__=['target', 'effector', 'length', 'iterations', 'weight']
324     def __init__(self, target, effector, length, iterations, weight):
325         self.target=target
326         self.effector=effector
327         self.length=length
328         self.iterations=iterations
329         self.weight=weight
330
331
332 class OneSkinMesh(object):
333     __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', ]
334     def __init__(self):
335         self.vertexArray=VertexArray()
336         self.morphList=[]
337         self.rigidbodies=[]
338         self.constraints=[]
339
340     def __str__(self):
341         return "<OneSkinMesh %s, morph:%d>" % (
342                 self.vertexArray,
343                 len(self.morphList))
344
345     def addMesh(self, obj):
346         if not bl.object.isVisible(obj):
347             return
348         self.__mesh(obj)
349         self.__skin(obj)
350         self.__rigidbody(obj)
351         self.__constraint(obj)
352
353     def __getWeightMap(self, obj, mesh):
354         # bone weight
355         weightMap={}
356         secondWeightMap={}
357         def setWeight(i, name, w):
358             if w>0:
359                 if i in weightMap:
360                     if i in secondWeightMap:
361                         # 上位2つのweightを採用する
362                         if w<secondWeightMap[i][1]:
363                             pass
364                         elif w<weightMap[i][1]:
365                             # 2つ目を入れ替え
366                             secondWeightMap[i]=(name, w)
367                         else:
368                             # 1つ目を入れ替え
369                             weightMap[i]=(name, w)
370                     else:
371                         if w>weightMap[i][1]:
372                             # 多い方をweightMapに
373                             secondWeightMap[i]=weightMap[i]
374                             weightMap[i]=(name, w)
375                         else:
376                             secondWeightMap[i]=(name, w)
377                 else:
378                     weightMap[i]=(name, w)
379
380         # ToDo bone weightと関係ないvertex groupを除外する
381         if isBlender24():
382             for name in bl.object.getVertexGroupNames(obj):
383                 for i, w in mesh.getVertsFromGroup(name, 1):
384                     setWeight(i, name, w)
385         else:
386             for i, v in enumerate(mesh.verts):
387                 if len(v.groups)>0:
388                     for g in v.groups:
389                         setWeight(i, obj.vertex_groups[g.group].name, g.weight)
390                 else:
391                     setWeight(i, obj.vertex_groups[0].name, 1)
392
393         # 合計値が1になるようにする
394         for i in xrange(len(mesh.verts)):
395             if i in secondWeightMap:
396                 secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1])
397             elif i in weightMap:
398                 weightMap[i]=(weightMap[i][0], 1.0)
399                 secondWeightMap[i]=("", 0)
400             else:
401                 print("no weight vertex")
402                 weightMap[i]=("", 0)
403                 secondWeightMap[i]=("", 0)
404
405         return weightMap, secondWeightMap
406
407     def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap):
408         # 各面の処理
409         for i, face in enumerate(mesh.faces):
410             faceVertexCount=bl.face.getVertexCount(face)
411             material=mesh.materials[bl.face.getMaterialIndex(face)]
412             v=[mesh.verts[index] for index in bl.face.getVertices(face)]
413             uv=bl.mesh.getFaceUV(
414                     mesh, i, face, bl.face.getVertexCount(face))
415             # flip triangle
416             if faceVertexCount==3:
417                 # triangle
418                 self.vertexArray.addTriangle(
419                         obj_name, material.name,
420                         v[2].index, 
421                         v[1].index, 
422                         v[0].index,
423                         v[2].co, 
424                         v[1].co, 
425                         v[0].co,
426                         bl.vertex.getNormal(v[2]), 
427                         bl.vertex.getNormal(v[1]), 
428                         bl.vertex.getNormal(v[0]),
429                         uv[2], 
430                         uv[1], 
431                         uv[0],
432                         weightMap[v[2].index][0],
433                         weightMap[v[1].index][0],
434                         weightMap[v[0].index][0],
435                         secondWeightMap[v[2].index][0],
436                         secondWeightMap[v[1].index][0],
437                         secondWeightMap[v[0].index][0],
438                         weightMap[v[2].index][1],
439                         weightMap[v[1].index][1],
440                         weightMap[v[0].index][1]
441                         )
442             elif faceVertexCount==4:
443                 # quadrangle
444                 self.vertexArray.addTriangle(
445                         obj_name, material.name,
446                         v[2].index, 
447                         v[1].index, 
448                         v[0].index,
449                         v[2].co, 
450                         v[1].co, 
451                         v[0].co,
452                         bl.vertex.getNormal(v[2]), 
453                         bl.vertex.getNormal(v[1]), 
454                         bl.vertex.getNormal(v[0]), 
455                         uv[2], 
456                         uv[1], 
457                         uv[0],
458                         weightMap[v[2].index][0],
459                         weightMap[v[1].index][0],
460                         weightMap[v[0].index][0],
461                         secondWeightMap[v[2].index][0],
462                         secondWeightMap[v[1].index][0],
463                         secondWeightMap[v[0].index][0],
464                         weightMap[v[2].index][1],
465                         weightMap[v[1].index][1],
466                         weightMap[v[0].index][1]
467                         )
468                 self.vertexArray.addTriangle(
469                         obj_name, material.name,
470                         v[0].index, 
471                         v[3].index, 
472                         v[2].index,
473                         v[0].co, 
474                         v[3].co, 
475                         v[2].co,
476                         bl.vertex.getNormal(v[0]), 
477                         bl.vertex.getNormal(v[3]), 
478                         bl.vertex.getNormal(v[2]), 
479                         uv[0], 
480                         uv[3], 
481                         uv[2],
482                         weightMap[v[0].index][0],
483                         weightMap[v[3].index][0],
484                         weightMap[v[2].index][0],
485                         secondWeightMap[v[0].index][0],
486                         secondWeightMap[v[3].index][0],
487                         secondWeightMap[v[2].index][0],
488                         weightMap[v[0].index][1],
489                         weightMap[v[3].index][1],
490                         weightMap[v[2].index][1]
491                         )
492
493     def __mesh(self, obj):
494         if isBlender24():
495             pass
496         else:
497             if RIGID_SHAPE_TYPE in obj:
498                 return
499             if CONSTRAINT_A in obj:
500                 return
501
502         #if not bl.modifier.hasType(obj, 'ARMATURE'):
503         #    return
504
505         bl.message("export: %s" % obj.name)
506
507         # メッシュのコピーを生成してオブジェクトの行列を適用する
508         copyMesh, copyObj=bl.object.duplicate(obj)
509         if len(copyMesh.verts)>0:
510             # apply transform
511             copyObj.scale=obj.scale
512             bpy.ops.object.scale_apply()
513             copyObj.rotation_euler=obj.rotation_euler
514             bpy.ops.object.rotation_apply()
515             copyObj.location=obj.location
516             bpy.ops.object.location_apply()
517             # apply modifier
518             for m in [m for m in copyObj.modifiers]:
519                 if m.type=='SOLIDFY':
520                     continue
521                 elif m.type=='ARMATURE':
522                     continue
523                 elif m.type=='MIRROR':
524                     bpy.ops.object.modifier_apply(modifier=m.name)
525                 else:
526                     print(m.type)
527
528             weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh)
529             self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap)
530         bl.object.delete(copyObj)
531
532     def createEmptyBasicSkin(self):
533         self.__getOrCreateMorph('base', 0)
534
535     def __skin(self, obj):
536         if not bl.object.hasShapeKey(obj):
537             return
538
539         indexRelativeMap={}
540         blenderMesh=bl.object.getData(obj)
541         baseMorph=None
542
543         # shape keys
544         vg=bl.object.getVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
545
546         # base
547         used=set()
548         for b in bl.object.getShapeKeys(obj):
549             if b.name==BASE_SHAPE_NAME:
550                 baseMorph=self.__getOrCreateMorph('base', 0)
551                 basis=b
552
553                 relativeIndex=0
554                 for index in vg:
555                     v=bl.shapekey.getByIndex(b, index)
556                     pos=[v[0], v[1], v[2]]
557
558                     indices=self.vertexArray.getMappedIndex(obj.name, index)
559                     for attribute, i in indices.items():
560                         if i in used:
561                             continue
562                         used.add(i)
563
564                         baseMorph.add(i, pos)
565                         indexRelativeMap[i]=relativeIndex
566                         relativeIndex+=1
567
568                 break
569         assert(basis)
570         print(basis.name, len(baseMorph.offsets))
571
572         if len(baseMorph.offsets)==0:
573             return
574
575         # shape keys
576         for b in bl.object.getShapeKeys(obj):
577             if b.name==BASE_SHAPE_NAME:
578                 continue
579
580             print(b.name)
581             morph=self.__getOrCreateMorph(b.name, 4)
582             used=set()
583             for index, src, dst in zip(
584                     xrange(len(blenderMesh.verts)),
585                     bl.shapekey.get(basis),
586                     bl.shapekey.get(b)):
587                 offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
588                 if offset[0]==0 and offset[1]==0 and offset[2]==0:
589                     continue
590                 if index in vg:
591                     indices=self.vertexArray.getMappedIndex(obj.name, index)
592                     for attribute, i in indices.items():
593                         if i in used:
594                             continue
595                         used.add(i) 
596                         morph.add(indexRelativeMap[i], offset)
597             assert(len(morph.offsets)<len(baseMorph.offsets))
598
599         # sort skinmap
600         original=self.morphList[:]
601         def getIndex(morph):
602             for i, v in enumerate(englishmap.skinMap):
603                 if v[0]==morph.name:
604                     return i
605             print(morph)
606             return len(englishmap.skinMap)
607         if isBlender24():
608             self.morphList.sort(lambda l, r: getIndex(l)-getIndex(r))
609         else:
610             self.morphList.sort(key=getIndex)
611
612     def __rigidbody(self, obj):
613         if isBlender24():
614             return
615         if not RIGID_SHAPE_TYPE in obj:
616             return
617         self.rigidbodies.append(obj)
618
619     def __constraint(self, obj):
620         if isBlender24():
621             return
622         if not CONSTRAINT_A in obj:
623             return
624         self.constraints.append(obj)
625
626     def __getOrCreateMorph(self, name, type):
627         for m in self.morphList:
628             if m.name==name:
629                 return m
630         m=Morph(name, type)
631         self.morphList.append(m)
632         return m
633
634     def getVertexCount(self):
635         return len(self.vertexArray.positions)
636
637
638 class Bone(object):
639     __slots__=['index', 'name', 'ik_index',
640             'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
641     def __init__(self, name, pos, tail, isConnect):
642         self.index=-1
643         self.name=name
644         self.pos=pos
645         self.tail=tail
646         self.parent_index=None
647         self.tail_index=None
648         self.type=0
649         self.isConnect=isConnect
650         self.ik_index=0
651
652     def __eq__(self, rhs):
653         return self.index==rhs.index
654
655     def __str__(self):
656         return "<Bone %s %d>" % (self.name, self.type)
657
658 class BoneBuilder(object):
659     __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',]
660     def __init__(self):
661         self.bones=[]
662         self.boneMap={}
663         self.ik_list=[]
664         self.bone_groups=[]
665
666     def getBoneGroup(self, bone):
667         for i, g in enumerate(self.bone_groups):
668             for b in g[1]:
669                 if b==bone.name:
670                     return i+1
671         print('no gorup', bone)
672         return 0
673
674     def build(self, armatureObj):
675         if not armatureObj:
676             return
677
678         bl.message("build skeleton")
679         armature=bl.object.getData(armatureObj)
680
681         ####################
682         # bone group
683         ####################
684         for g in bl.object.boneGroups(armatureObj):
685             self.bone_groups.append((g.name, []))
686
687         ####################
688         # get bones
689         ####################
690         for b in armature.bones.values():
691             if not b.parent:
692                 # root bone
693                 bone=Bone(b.name, 
694                         bl.bone.getHeadLocal(b),
695                         bl.bone.getTailLocal(b),
696                         False)
697                 self.__addBone(bone)
698                 self.__getBone(bone, b)
699
700         for b in armature.bones.values():
701             if not b.parent:
702                 self.__checkConnection(b, None)
703
704         ####################
705         # get IK
706         ####################
707         pose = bl.object.getPose(armatureObj)
708         for b in pose.bones.values():
709             ####################
710             # assing bone group
711             ####################
712             self.__assignBoneGroup(b, b.bone_group)
713             for c in b.constraints:
714                 if bl.constraint.isIKSolver(c):
715                     ####################
716                     # IK target
717                     ####################
718                     target=self.__boneByName(bl.constraint.ikTarget(c))
719                     target.type=2
720
721                     ####################
722                     # IK effector
723                     ####################
724                     # IK 接続先
725                     link=self.__boneByName(b.name)
726                     link.type=6
727
728                     # IK chain
729                     e=b.parent
730                     chainLength=bl.constraint.ikChainLen(c)
731                     for i in range(chainLength):
732                         # IK影響下
733                         chainBone=self.__boneByName(e.name)
734                         chainBone.type=4
735                         chainBone.ik_index=target.index
736                         e=e.parent
737                     self.ik_list.append(
738                             IKSolver(target, link, chainLength, 
739                                 int(bl.constraint.ikItration(c) * 0.1), 
740                                 bl.constraint.ikRotationWeight(c)
741                                 ))
742
743         ####################
744
745         # boneのsort
746         self._sortBy()
747         self._fix()
748         # IKのsort
749         def getIndex(ik):
750             for i, v in enumerate(englishmap.boneMap):
751                 if v[0]==ik.target.name:
752                     return i
753             return len(englishmap.boneMap)
754         if isBlender24():
755             self.ik_list.sort(lambda l, r: getIndex(l)-getIndex(r))
756         else:
757             self.ik_list.sort(key=getIndex)
758
759     def __assignBoneGroup(self, poseBone, boneGroup):
760         if boneGroup:
761             for g in self.bone_groups:
762                 if g[0]==boneGroup.name:
763                     g[1].append(poseBone.name)
764
765     def __checkConnection(self, b, p):
766         if bl.bone.isConnected(b):
767             parent=self.__boneByName(p.name)
768             parent.isConnect=True
769
770         for c in b.children:
771             self.__checkConnection(c, b)
772
773     def _sortBy(self):
774         """
775         boneMap順に並べ替える
776         """
777         boneMap=englishmap.boneMap
778         original=self.bones[:]
779         def getIndex(bone):
780             for i, k_v in enumerate(boneMap):
781                 if k_v[0]==bone.name:
782                     return i
783             print(bone)
784             return len(boneMap)
785
786         if isBlender24():
787             self.bones.sort(lambda l, r: getIndex(l)-getIndex(r))
788         else:
789             self.bones.sort(key=getIndex)
790
791         sortMap={}
792         for i, b in enumerate(self.bones):
793             src=original.index(b)
794             sortMap[src]=i
795         for b in self.bones:
796             b.index=sortMap[b.index]
797             if b.parent_index:
798                 b.parent_index=sortMap[b.parent_index]
799             if b.tail_index:
800                 b.tail_index=sortMap[b.tail_index]
801             if b.ik_index>0:
802                 b.ik_index=sortMap[b.ik_index]
803
804     def _fix(self):
805         """
806         調整
807         """
808         for b in self.bones:
809             # parent index
810             if b.parent_index==None:
811                 b.parent_index=0xFFFF
812             else:
813                 if b.type==6 or b.type==7:
814                     # fix tail bone
815                     parent=self.bones[b.parent_index]
816                     #print('parnet', parent.name)
817                     parent.tail_index=b.index
818
819         for b in self.bones:
820             if b.tail_index==None:
821                 b.tail_index=0
822             elif b.type==9:
823                 b.tail_index==0
824
825     def getIndex(self, bone):
826         for i, b in enumerate(self.bones):
827             if b==bone:
828                 return i
829         assert(false)
830
831     def indexByName(self, name):
832         if name=='':
833             return 0
834         else:
835             return self.getIndex(self.__boneByName(name))
836
837     def __boneByName(self, name):
838         return self.boneMap[name]
839                     
840     def __getBone(self, parent, b):
841         if len(b.children)==0:
842             parent.type=7
843             return
844
845         for i, c in enumerate(b.children):
846             bone=Bone(c.name, 
847                     bl.bone.getHeadLocal(c),
848                     bl.bone.getTailLocal(c),
849                     bl.bone.isConnected(c))
850             self.__addBone(bone)
851             if parent:
852                 bone.parent_index=parent.index
853                 #if i==0:
854                 if bone.isConnect or (not parent.tail_index and parent.tail==bone.pos):
855                     parent.tail_index=bone.index
856             self.__getBone(bone, c)
857
858     def __addBone(self, bone):
859         bone.index=len(self.bones)
860         self.bones.append(bone)
861         self.boneMap[bone.name]=bone
862
863
864 class PmdExporter(object):
865
866     __slots__=[
867             'armatureObj',
868             'oneSkinMesh',
869             'englishName',
870             'englishComment',
871             'name',
872             'comment',
873             'skeleton',
874             ]
875     def setup(self):
876         self.armatureObj=None
877
878         # 木構造を構築する
879         object_node_map={}
880         for o in bl.object.each():
881             object_node_map[o]=Node(o)
882         for o in bl.object.each():
883             node=object_node_map[o]
884             if node.o.parent:
885                 object_node_map[node.o.parent].children.append(node)
886
887         # ルートを得る
888         root=object_node_map[bl.object.getActive()]
889         o=root.o
890         self.englishName=o.name
891         self.englishComment=o[MMD_COMMENT] if MMD_COMMENT in o else 'blender export\n'
892         self.name=o[MMD_MB_NAME] if MMD_MB_NAME in o else 'Blenderエクスポート'
893         self.comment=o[MMD_MB_COMMENT] if MMD_MB_COMMENT in o else 'Blnderエクスポート\n'
894
895         # ワンスキンメッシュを作る
896         self.oneSkinMesh=OneSkinMesh()
897         self.__createOneSkinMesh(root)
898         bl.message(self.oneSkinMesh)
899         if len(self.oneSkinMesh.morphList)==0:
900             # create emtpy skin
901             self.oneSkinMesh.createEmptyBasicSkin()
902
903         # skeleton
904         self.skeleton=BoneBuilder()
905         self.skeleton.build(self.armatureObj)
906
907     def __createOneSkinMesh(self, node):
908         ############################################################
909         # search armature modifier
910         ############################################################
911         for m in node.o.modifiers:
912             if bl.modifier.isType(m, 'ARMATURE'):
913                 armatureObj=bl.modifier.getArmatureObject(m)
914                 if not self.armatureObj:
915                     self.armatureObj=armatureObj
916                 elif self.armatureObj!=armatureObj:
917                     print("warning! found multiple armature. ignored.", 
918                             armatureObj.name)
919
920         if node.o.type.upper()=='MESH':
921             self.oneSkinMesh.addMesh(node.o)
922
923         for child in node.children:
924             self.__createOneSkinMesh(child)
925
926     def write(self, path):
927         io=pmd.IO()
928         io.setName(toCP932(self.name))
929         io.setComment(toCP932(self.comment))
930         io.version=1.0
931
932         # 頂点
933         for pos, attribute, b0, b1, weight in self.oneSkinMesh.vertexArray.zip():
934             # convert right-handed z-up to left-handed y-up
935             v=io.addVertex()
936             v.pos.x=pos[0]
937             v.pos.y=pos[2]
938             v.pos.z=pos[1]
939             v.normal.x=attribute.nx
940             v.normal.y=attribute.ny
941             v.normal.z=attribute.nz
942             v.uv.x=attribute.u
943             v.uv.y=1.0-attribute.v # reverse vertical
944             v.bone0=self.skeleton.indexByName(b0)
945             v.bone1=self.skeleton.indexByName(b1)
946             v.weight0=int(100*weight)
947             v.edge_flag=0 # edge flag, 0: enable edge, 1: not edge
948
949         # 面とマテリアル
950         vertexCount=self.oneSkinMesh.getVertexCount()
951         for material_name, indices in self.oneSkinMesh.vertexArray.each():
952             #print('material:', material_name)
953             m=bl.material.get(material_name)
954             # マテリアル
955             material=io.addMaterial()
956             setMaterialParams(material, m)
957
958             material.vertex_count=len(indices)
959             material.toon_index=0
960             textures=[os.path.basename(path) 
961                 for path in bl.material.eachEnalbeTexturePath(m)]
962             if len(textures)>0:
963                 material.setTexture(toCP932('*'.join(textures)))
964             else:
965                 material.setTexture(toCP932(""))
966             # 面
967             for i in indices:
968                 assert(i<vertexCount)
969             for i in xrange(0, len(indices), 3):
970                 # reverse triangle
971                 io.indices.append(indices[i])
972                 io.indices.append(indices[i+1])
973                 io.indices.append(indices[i+2])
974
975         # bones
976         boneNameMap={}
977         for i, b in enumerate(self.skeleton.bones):
978             bone=io.addBone()
979
980             # name
981             boneNameMap[b.name]=i
982             v=englishmap.getUnicodeBoneName(b.name)
983             if not v:
984                 v=[b.name, b.name]
985             assert(v)
986             cp932=v[1].encode('cp932')
987             assert(len(cp932)<20)
988             bone.setName(cp932)
989
990             # english name
991             bone_english_name=toCP932(b.name)
992             assert(len(bone_english_name)<20)
993             bone.setEnglishName(bone_english_name)
994
995             if len(v)>=3:
996                 # has type
997                 if v[2]==5:
998                     b.ik_index=self.skeleton.indexByName('eyes')
999                 bone.type=v[2]
1000             else:
1001                 bone.type=b.type
1002
1003             bone.parent_index=b.parent_index
1004             bone.tail_index=b.tail_index
1005             bone.ik_index=b.ik_index
1006
1007             # convert right-handed z-up to left-handed y-up
1008             bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
1009             bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
1010             bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
1011
1012         # IK
1013         for ik in self.skeleton.ik_list:
1014             solver=io.addIK()
1015             solver.index=self.skeleton.getIndex(ik.target)
1016             solver.target=self.skeleton.getIndex(ik.effector)
1017             solver.length=ik.length
1018             b=self.skeleton.bones[ik.effector.parent_index]
1019             for i in xrange(solver.length):
1020                 solver.children.append(self.skeleton.getIndex(b))
1021                 b=self.skeleton.bones[b.parent_index]
1022             solver.iterations=ik.iterations
1023             solver.weight=ik.weight
1024
1025         # 表情
1026         for i, m in enumerate(self.oneSkinMesh.morphList):
1027             # morph
1028             morph=io.addMorph()
1029
1030             v=englishmap.getUnicodeSkinName(m.name)
1031             if not v:
1032                 v=[m.name, m.name, 0]
1033             assert(v)
1034             cp932=v[1].encode('cp932')
1035             morph.setName(cp932)
1036             morph.setEnglishName(m.name.encode('cp932'))
1037             m.type=v[2]
1038             morph.type=v[2]
1039             for index, offset in m.offsets:
1040                 # convert right-handed z-up to left-handed y-up
1041                 morph.append(index, offset[0], offset[2], offset[1])
1042             morph.vertex_count=len(m.offsets)
1043
1044         # 表情枠
1045         # type==0はbase
1046         for i, m in enumerate(self.oneSkinMesh.morphList):
1047             if m.type==3:
1048                 io.face_list.append(i)
1049         for i, m in enumerate(self.oneSkinMesh.morphList):
1050             if m.type==2:
1051                 io.face_list.append(i)
1052         for i, m in enumerate(self.oneSkinMesh.morphList):
1053             if m.type==1:
1054                 io.face_list.append(i)
1055         for i, m in enumerate(self.oneSkinMesh.morphList):
1056             if m.type==4:
1057                 io.face_list.append(i)
1058
1059         # ボーングループ
1060         for g in self.skeleton.bone_groups:
1061             boneDisplayName=io.addBoneGroup()
1062             # name
1063             name=englishmap.getUnicodeBoneGroupName(g[0])
1064             if not name:
1065                 name=g[0]
1066             boneDisplayName.setName(toCP932(name+'\n'))
1067             # english
1068             englishName=g[0]
1069             boneDisplayName.setEnglishName(toCP932(englishName+'\n'))
1070
1071         # ボーングループメンバー
1072         for i, b in enumerate(self.skeleton.bones):
1073             if i==0:
1074                continue
1075             if b.type in [6, 7]:
1076                continue
1077             io.addBoneDisplay(i, self.skeleton.getBoneGroup(b))
1078
1079         #assert(len(io.bones)==len(io.bone_display_list)+1)
1080
1081         # English
1082         io.setEnglishName(toCP932(self.englishName))
1083         io.setEnglishComment(toCP932(self.englishComment))
1084
1085         # toon
1086         toonMeshObject=None
1087         for o in bl.object.each():
1088             try:
1089                 if o.name.startswith(TOON_TEXTURE_OBJECT):
1090                     toonMeshObject=o
1091             except:
1092                 p(o.name)
1093             break
1094         if toonMeshObject:
1095             toonMesh=bl.object.getData(toonMeshObject)
1096             toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
1097             for i in range(10):
1098                 t=bl.material.getTexture(toonMaterial, i)
1099                 if t:
1100                     io.getToonTexture(i).setName(toCP932(t.name))
1101                 else:
1102                     io.getToonTexture(i).setName(toCP932("toon%02d.bmp\n" % i))
1103         else:
1104             for i in range(10):
1105                 io.getToonTexture(i).setName(toCP932("toon%02d.bmp\n" % i))
1106
1107         # rigid body
1108         rigidNameMap={}
1109         for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
1110             name=obj[RIGID_NAME] if RIGID_NAME in obj else obj.name
1111             print(name)
1112             rigidBody=io.addRigidBody()
1113             rigidBody.setName(name.encode('cp932'))
1114             rigidNameMap[name]=i
1115             boneIndex=boneNameMap[obj[RIGID_BONE_NAME]]
1116             if boneIndex==0:
1117                 boneIndex=0xFFFF
1118                 bone=self.skeleton.bones[0]
1119             else:
1120                 bone=self.skeleton.bones[boneIndex]
1121             rigidBody.boneIndex=boneIndex
1122             #rigidBody.position.x=obj[RIGID_LOCATION][0]
1123             #rigidBody.position.y=obj[RIGID_LOCATION][1]
1124             #rigidBody.position.z=obj[RIGID_LOCATION][2]
1125             rigidBody.position.x=obj.location.x-bone.pos[0]
1126             rigidBody.position.y=obj.location.z-bone.pos[2]
1127             rigidBody.position.z=obj.location.y-bone.pos[1]
1128             rigidBody.rotation.x=-obj.rotation_euler[0]
1129             rigidBody.rotation.y=-obj.rotation_euler[2]
1130             rigidBody.rotation.z=-obj.rotation_euler[1]
1131             rigidBody.processType=obj[RIGID_PROCESS_TYPE]
1132             rigidBody.group=obj[RIGID_GROUP]
1133             rigidBody.target=obj[RIGID_INTERSECTION_GROUP]
1134             rigidBody.weight=obj[RIGID_WEIGHT]
1135             rigidBody.linearDamping=obj[RIGID_LINEAR_DAMPING]
1136             rigidBody.angularDamping=obj[RIGID_ANGULAR_DAMPING]
1137             rigidBody.restitution=obj[RIGID_RESTITUTION]
1138             rigidBody.friction=obj[RIGID_FRICTION]
1139             if obj[RIGID_SHAPE_TYPE]==0:
1140                 rigidBody.shapeType=pmd.SHAPE_SPHERE
1141                 rigidBody.w=obj.scale[0]
1142             elif obj[RIGID_SHAPE_TYPE]==1:
1143                 rigidBody.shapeType=pmd.SHAPE_BOX
1144                 rigidBody.w=obj.scale[0]
1145                 rigidBody.d=obj.scale[1]
1146                 rigidBody.h=obj.scale[2]
1147             elif obj[RIGID_SHAPE_TYPE]==2:
1148                 rigidBody.shapeType=pmd.SHAPE_CAPSULE
1149                 rigidBody.w=obj.scale[0]
1150                 rigidBody.h=obj.scale[2]
1151
1152         # constraint
1153         for obj in self.oneSkinMesh.constraints:
1154             constraint=io.addConstraint()
1155             constraint.setName(obj[CONSTRAINT_NAME].encode('cp932'))
1156             constraint.rigidA=rigidNameMap[obj[CONSTRAINT_A]]
1157             constraint.rigidB=rigidNameMap[obj[CONSTRAINT_B]]
1158             constraint.pos.x=obj.location[0]
1159             constraint.pos.y=obj.location[2]
1160             constraint.pos.z=obj.location[1]
1161             constraint.rot.x=-obj.rotation_euler[0]
1162             constraint.rot.y=-obj.rotation_euler[2]
1163             constraint.rot.z=-obj.rotation_euler[1]
1164             constraint.constraintPosMin.x=obj[CONSTRAINT_POS_MIN][0]
1165             constraint.constraintPosMin.y=obj[CONSTRAINT_POS_MIN][1]
1166             constraint.constraintPosMin.z=obj[CONSTRAINT_POS_MIN][2]
1167             constraint.constraintPosMax.x=obj[CONSTRAINT_POS_MAX][0]
1168             constraint.constraintPosMax.y=obj[CONSTRAINT_POS_MAX][1]
1169             constraint.constraintPosMax.z=obj[CONSTRAINT_POS_MAX][2]
1170             constraint.constraintRotMin.x=obj[CONSTRAINT_ROT_MIN][0]
1171             constraint.constraintRotMin.y=obj[CONSTRAINT_ROT_MIN][1]
1172             constraint.constraintRotMin.z=obj[CONSTRAINT_ROT_MIN][2]
1173             constraint.constraintRotMax.x=obj[CONSTRAINT_ROT_MAX][0]
1174             constraint.constraintRotMax.y=obj[CONSTRAINT_ROT_MAX][1]
1175             constraint.constraintRotMax.z=obj[CONSTRAINT_ROT_MAX][2]
1176             constraint.springPos.x=obj[CONSTRAINT_SPRING_POS][0]
1177             constraint.springPos.y=obj[CONSTRAINT_SPRING_POS][1]
1178             constraint.springPos.z=obj[CONSTRAINT_SPRING_POS][2]
1179             constraint.springRot.x=obj[CONSTRAINT_SPRING_ROT][0]
1180             constraint.springRot.y=obj[CONSTRAINT_SPRING_ROT][1]
1181             constraint.springRot.z=obj[CONSTRAINT_SPRING_ROT][2]
1182
1183         # 書き込み
1184         bl.message('write %s' % path)
1185         return io.write(path)
1186
1187
1188 def __execute(filename):
1189     active=bl.object.getActive()
1190     if not active:
1191         print("abort. no active object.")
1192         return
1193     exporter=PmdExporter()
1194     exporter.setup()
1195     print(exporter)
1196     exporter.write(filename)
1197     bl.object.activate(active)
1198
1199
1200 if isBlender24():
1201     # for 2.4
1202     def execute_24(filename):
1203         bl.initialize('pmd_export', bpy.data.scenes.active)
1204         __execute(filename.decode(bl.INTERNAL_ENCODING))
1205         bl.finalize()
1206
1207     Blender.Window.FileSelector(
1208              execute_24,
1209              'Export Metasequoia PMD',
1210              Blender.sys.makename(ext='.pmd'))
1211
1212 else:
1213     # for 2.5
1214     def execute_25(filename, scene):
1215         bl.initialize('pmd_export', scene)
1216         __execute(filename)
1217         bl.finalize()
1218
1219     # operator
1220     class EXPORT_OT_pmd(bpy.types.Operator):
1221         '''Save a Metasequoia PMD file.'''
1222         bl_idname = "export_scene.pmd"
1223         bl_label = 'Export PMD'
1224
1225         # List of operator properties, the attributes will be assigned
1226         # to the class instance from the operator settings before calling.
1227         filepath = bpy.props.StringProperty()
1228         filename = bpy.props.StringProperty()
1229         directory = bpy.props.StringProperty()
1230
1231         def execute(self, context):
1232             execute_25(
1233                     self.properties.filepath, 
1234                     context.scene
1235                     )
1236             return 'FINISHED'
1237
1238         def invoke(self, context, event):
1239             wm=context.manager
1240             wm.add_fileselect(self)
1241             return 'RUNNING_MODAL'
1242
1243     # register menu
1244     def menu_func(self, context): 
1245         default_path=bpy.data.filepath.replace(".blend", ".pmd")
1246         self.layout.operator(
1247                 EXPORT_OT_pmd.bl_idname, 
1248                 text="Miku Miku Dance Model(.pmd)",
1249                 icon='PLUGIN'
1250                 ).filepath=default_path
1251
1252     def register():
1253         bpy.types.register(EXPORT_OT_pmd)
1254         bpy.types.INFO_MT_file_export.append(menu_func)
1255
1256     def unregister():
1257         bpy.types.unregister(EXPORT_OT_pmd)
1258         bpy.types.INFO_MT_file_export.remove(menu_func)
1259
1260     if __name__ == "__main__":
1261         register()
1262