OSDN Git Service

9c0d9b5f87d2d28ee816c22f5b9a877bf571e4f5
[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__= "1.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 """
22
23 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
24 BASE_SHAPE_NAME='Basis'
25
26
27 ###############################################################################
28 # import
29 ###############################################################################
30 import os
31 import sys
32 import re
33 import math
34
35 # C extension
36 from meshio import pmd, englishmap
37
38 def isBlender24():
39     return sys.version_info[0]<3
40
41 if isBlender24():
42     # for 2.4
43     import Blender
44     from Blender import Mathutils
45     import bpy
46
47     # wrapper
48     import bl24 as bl
49 else:
50     # for 2.5
51     import bpy
52     from bpy.props import *
53     import mathutils
54
55     # wrapper
56     import bl25 as bl
57
58     xrange=range
59
60
61 class Node(object):
62     __slots__=['o', 'children']
63     def __init__(self, o):
64         self.o=o
65         self.children=[]
66
67
68 ###############################################################################
69 # Blenderのメッシュをワンスキンメッシュ化する
70 ###############################################################################
71 def near(x, y, EPSILON=1e-5):
72     d=x-y
73     return d>=-EPSILON and d<=EPSILON
74
75
76 class VertexKey(object):
77     """
78     重複頂点の検索キー
79     """
80     __slots__=[
81             'x', 'y', 'z', # 位置
82             'nx', 'ny', 'nz', # 法線
83             'u', 'v', # uv
84             ]
85
86     def __init__(self, x, y, z, nx, ny, nz, u, v):
87         self.x=x
88         self.y=y
89         self.z=z
90         self.nx=nx
91         self.ny=ny
92         self.nz=nz
93         self.u=u
94         self.v=v
95
96     def __str__(self):
97         return "<vkey: %f, %f, %f, %f, %f, %f, %f, %f>" % (
98                 self.x, self.y, self.z, self.nx, self.ny, self.nz, self.u, self.v)
99
100     def __hash__(self):
101         #return int((self.x+self.y+self.z+self.nx+self.ny+self.nz+self.u+self.v)*100)
102         return int((self.x+self.y+self.z)*100)
103
104     def __eq__(self, rhs):
105         #return near(self.x, rhs.x) and near(self.y, rhs.y) and near(self.z, rhs.z) and near(self.nx, rhs.nx) and near(self.ny, rhs.ny) and near(self.nz, rhs.nz) and near(self.u, rhs.u) and near(self.v, rhs.v)
106         #return near(self.x, rhs.x) and near(self.y, rhs.y) and near(self.z, rhs.z)
107         return self.x==rhs.x and self.y==rhs.y and self.z==rhs.z
108
109
110 class VertexArray(object):
111     """
112     頂点配列
113     """
114     def __init__(self):
115         # マテリアル毎に分割したインデックス配列
116         self.indexArrays={}
117         # 頂点属性
118         self.vertices=[]
119         self.normals=[]
120         self.uvs=[]
121         # skinning属性
122         self.b0=[]
123         self.b1=[]
124         self.weight=[]
125
126         self.vertexMap={}
127         self.indexMap={}
128
129     def __str__(self):
130         return "<VertexArray %d vertices, %d indexArrays>" % (
131                 len(self.vertices), len(self.indexArrays))
132
133     def zip(self):
134         return zip(
135                 self.vertices, self.normals, self.uvs,
136                 self.b0, self.b1, self.weight)
137
138     def __getIndex(self, base_index, pos, normal, uv, b0, b1, weight0):
139         """
140         頂点属性からその頂点のインデックスを得る
141         """
142         key=VertexKey(
143                 pos[0], pos[1], pos[2],
144                 normal[0], normal[1], normal[2],
145                 uv[0], uv[1])
146         if key in self.vertexMap:
147             # 同じ頂点を登録済み
148             index=self.vertexMap[key]
149         else:
150             index=len(self.vertices)
151             # 新規頂点
152             self.vertexMap[key]=index
153             # append...
154             self.vertices.append((pos.x, pos.y, pos.z))
155             self.normals.append((normal.x, normal.y, normal.z))
156             self.uvs.append((uv.x, uv.y))
157             self.b0.append(b0)
158             self.b1.append(b1)
159             self.weight.append(weight0)
160             
161         # indexのマッピングを保存する
162         if not base_index in self.indexMap:
163             self.indexMap[base_index]=set()
164         self.indexMap[base_index].add(index)
165
166         assert(index<=65535)
167         return index
168
169     def getMappedIndices(self, base_index):
170         return self.indexMap[base_index]
171
172     def addTriangle(self,
173             material,
174             base_index0, base_index1, base_index2,
175             pos0, pos1, pos2,
176             n0, n1, n2,
177             uv0, uv1, uv2,
178             b0_0, b0_1, b0_2,
179             b1_0, b1_1, b1_2,
180             weight0, weight1, weight2
181             ):
182         if not material in self.indexArrays:
183             self.indexArrays[material]=[]
184
185         index0=self.__getIndex(base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
186         index1=self.__getIndex(base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
187         index2=self.__getIndex(base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
188
189         self.indexArrays[material]+=[index0, index1, index2]
190
191
192 class Morph(object):
193     __slots__=['name', 'type', 'offsets']
194     def __init__(self, name, type):
195         self.name=name
196         self.type=type
197         self.offsets=[]
198
199     def add(self, index, offset):
200         self.offsets.append((index, offset))
201
202     def sort(self):
203         self.offsets.sort(lambda l, r: l[0]-r[0])
204
205     def __str__(self):
206         return "<Morph %s>" % self.name
207
208 class IKSolver(object):
209     __slots__=['target', 'effector', 'length', 'iterations', 'weight']
210     def __init__(self, target, effector, length, iterations, weight):
211         self.target=target
212         self.effector=effector
213         self.length=length
214         self.iterations=iterations
215         self.weight=weight
216
217
218 class OneSkinMesh(object):
219     __slots__=['vertexArray', 'morphList']
220     def __init__(self):
221         self.vertexArray=VertexArray()
222         self.morphList=[]
223
224     def __str__(self):
225         return "<OneSkinMesh %s, morph:%d>" % (
226                 self.vertexArray,
227                 len(self.morphList))
228
229     def addMesh(self, obj):
230         if obj.restrictDisplay:
231             # 非表示
232             return
233
234         print("export", obj.name)
235
236         ############################################################
237         # bone weight
238         ############################################################
239         mesh=obj.getData(mesh=True)
240         weightMap={}
241         secondWeightMap={}
242         for name in mesh.getVertGroupNames():
243             for i, w in mesh.getVertsFromGroup(name, 1):
244                 if w>0:
245                     if i in weightMap:
246                         if i in secondWeightMap:
247                             # 上位2つのweightを採用する
248                             if w<secondWeightMap[i]:
249                                 pass
250                             elif w<weightMap[i]:
251                                 # 2つ目を入れ替え
252                                 secondWeightMap[i]=(name, w)
253                             else:
254                                 # 1つ目を入れ替え
255                                 weightMap[i]=(name, w)
256                         else:
257                             if w>weightMap[i][1]:
258                                 # 多い方をweightMapに
259                                 secondWeightMap[i]=weightMap[i]
260                                 weightMap[i]=(name, w)
261                             else:
262                                 secondWeightMap[i]=(name, w)
263                     else:
264                         weightMap[i]=(name, w)
265
266         # 合計値が1になるようにする
267         for i in xrange(len(mesh.verts)):
268         #for i, name_weight in weightMap.items():
269             if i in secondWeightMap:
270                 secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1])
271             elif i in weightMap:
272                 weightMap[i]=(weightMap[i][0], 1.0)
273                 secondWeightMap[i]=("", 0)
274             else:
275                 print "no weight vertex"
276                 weightMap[i]=("", 0)
277                 secondWeightMap[i]=("", 0)
278                 
279
280         ############################################################
281         # faces
282         # 新たにメッシュを生成する
283         ############################################################
284         mesh=Blender.Mesh.New()
285         # not applied modifiers
286         mesh.getFromObject(obj.name, 1)
287         # apply object transform
288         mesh.transform(obj.getMatrix())
289         if len(mesh.verts)==0:
290             return
291
292         for face in mesh.faces:
293             faceVertexCount=len(face.v)
294             material=mesh.materials[face.mat]
295             if faceVertexCount==3:
296                 v0=face.v[0]
297                 v1=face.v[1]
298                 v2=face.v[2]
299                 # triangle
300                 self.vertexArray.addTriangle(
301                         material.name,
302                         v0.index, v1.index, v2.index,
303                         v0.co, v1.co, v2.co,
304                         # ToDo vertex normal
305                         #v0.no, v1.no, v2.no,
306                         face.no, face.no, face.no,
307                         face.uv[0], face.uv[1], face.uv[2],
308                         weightMap[v0.index][0],
309                         weightMap[v1.index][0],
310                         weightMap[v2.index][0],
311                         secondWeightMap[v0.index][0],
312                         secondWeightMap[v1.index][0],
313                         secondWeightMap[v2.index][0],
314                         weightMap[v0.index][1],
315                         weightMap[v1.index][1],
316                         weightMap[v2.index][1]
317                         )
318             elif faceVertexCount==4:
319                 v0=face.v[0]
320                 v1=face.v[1]
321                 v2=face.v[2]
322                 v3=face.v[3]
323                 # quadrangle
324                 self.vertexArray.addTriangle(
325                         material.name,
326                         v0.index, v1.index, v2.index,
327                         v0.co, v1.co, v2.co,
328                         #v0.no, v1.no, v2.no,
329                         face.no, face.no, face.no,
330                         face.uv[0], face.uv[1], face.uv[2],
331                         weightMap[v0.index][0],
332                         weightMap[v1.index][0],
333                         weightMap[v2.index][0],
334                         secondWeightMap[v0.index][0],
335                         secondWeightMap[v1.index][0],
336                         secondWeightMap[v2.index][0],
337                         weightMap[v0.index][1],
338                         weightMap[v1.index][1],
339                         weightMap[v2.index][1]
340                         )
341                 self.vertexArray.addTriangle(
342                         material.name,
343                         v2.index, v3.index, v0.index,
344                         v2.co, v3.co, v0.co,
345                         #v2.no, v3.no, v0.no,
346                         face.no, face.no, face.no,
347                         face.uv[2], face.uv[3], face.uv[0],
348                         weightMap[v2.index][0],
349                         weightMap[v3.index][0],
350                         weightMap[v0.index][0],
351                         secondWeightMap[v2.index][0],
352                         secondWeightMap[v3.index][0],
353                         secondWeightMap[v0.index][0],
354                         weightMap[v2.index][1],
355                         weightMap[v3.index][1],
356                         weightMap[v0.index][1]
357                         )
358
359         ############################################################
360         # skin
361         ############################################################
362         # base
363         indexRelativeMap={}
364         blenderMesh=obj.getData(mesh=True)
365         baseMorph=None
366         if blenderMesh.key:
367             for b in blenderMesh.key.blocks:
368                 if b.name=='Basis':
369                     print(b.name)
370                     baseMorph=self.__getOrCreateMorph('base', 0)
371                     relativeIndex=0
372                     basis=b
373                     for index in blenderMesh.getVertsFromGroup(
374                             MMD_SHAPE_GROUP_NAME):
375                         v=b.data[index]
376                         pos=[v[0], v[1], v[2]]
377                         indices=self.vertexArray.getMappedIndices(index)
378                         for i in indices:
379                             baseMorph.add(i, pos)
380                             indexRelativeMap[i]=relativeIndex
381                             relativeIndex+=1
382                     break
383             print(len(baseMorph.offsets))
384             baseMorph.sort()
385             assert(basis)
386
387             # shape keys
388             vg=obj.getData(mesh=True).getVertsFromGroup(
389                         MMD_SHAPE_GROUP_NAME)
390             for b in obj.getData(mesh=True).key.blocks:
391                 if b.name=='Basis':
392                     continue
393
394                 print(b.name)
395                 morph=self.__getOrCreateMorph(b.name, 4)
396                 for index, src, dst in zip(
397                         xrange(len(blenderMesh.verts)),
398                         basis.data,
399                         b.data):
400                     offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
401                     if index in vg:
402                         indices=self.vertexArray.getMappedIndices(index)
403                         for i in indices:
404                             morph.add(indexRelativeMap[i], offset)
405                 assert(len(morph.offsets)==len(baseMorph.offsets))
406
407         # sort skinmap
408         original=self.morphList[:]
409         def getIndex(morph):
410             for i, v in enumerate(englishmap.skinMap):
411                 if v[0]==morph.name:
412                     return i
413             print(morph)
414         self.morphList.sort(lambda l, r: getIndex(l)-getIndex(r))
415
416     def __getOrCreateMorph(self, name, type):
417         for m in self.morphList:
418             if m.name==name:
419                 return m
420         m=Morph(name, type)
421         self.morphList.append(m)
422         return m
423
424     def getVertexCount(self):
425         return len(self.vertexArray.vertices)
426
427
428 class Bone(object):
429     __slots__=['index', 'name', 'ik_index',
430             'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
431     def __init__(self, name, pos, tail):
432         self.index=-1
433         self.name=name
434         self.pos=pos
435         self.tail=tail
436         self.parent_index=None
437         self.tail_index=None
438         self.type=0
439         self.isConnect=False
440         self.ik_index=0
441
442     def __eq__(self, rhs):
443         return self.index==rhs.index
444
445     def __str__(self):
446         return "<Bone %s %d>" % (self.name, self.type)
447
448 class BoneBuilder(object):
449     __slots__=['bones', 'boneMap', 'ik_list']
450     def __init__(self):
451         self.bones=[]
452         self.boneMap={}
453         self.ik_list=[]
454
455     def build(self, armatureObj):
456         if not armatureObj:
457             return
458
459         print("gather bones")
460         armature=armatureObj.getData()
461         for b in armature.bones.values():
462             if b.name=='center':
463                 # root bone
464                 bone=Bone(b.name, 
465                         b.head['ARMATURESPACE'][0:3], 
466                         b.tail['ARMATURESPACE'][0:3])
467                 self.__addBone(bone)
468                 self.__getBone(bone, b)
469
470         for b in armature.bones.values():
471             if not b.parent and b.name!='center':
472                 # root bone
473                 bone=Bone(b.name, 
474                         b.head['ARMATURESPACE'][0:3], 
475                         b.tail['ARMATURESPACE'][0:3])
476                 self.__addBone(bone)
477                 self.__getBone(bone, b)
478
479         print("check connection")
480         for b in armature.bones.values():
481             if not b.parent:
482                 self.__checkConnection(b, None)
483
484         print("gather ik")
485         pose = armatureObj.getPose()
486         cSetting=Blender.Constraint.Settings
487         for b in pose.bones.values():
488             for c in b.constraints:
489                 if c.type==Blender.Constraint.Type.IKSOLVER:
490                     ####################
491                     # IK target
492                     ####################
493                     assert(c[cSetting.TARGET]==armatureObj)
494                     target=self.__boneByName(
495                             c[Blender.Constraint.Settings.BONE])
496                     target.type=2
497
498                     ####################
499                     # IK effector
500                     ####################
501                     # IK 接続先
502                     link=self.__boneByName(b.name)
503                     link.type=6
504
505                     # IK chain
506                     e=b.parent
507                     chainLength=c[cSetting.CHAINLEN]
508                     for i in range(chainLength):
509                         # IK影響下
510                         chainBone=self.__boneByName(e.name)
511                         chainBone.type=4
512                         chainBone.ik_index=target.index
513                         e=e.parent
514                     self.ik_list.append(
515                             IKSolver(target, link, chainLength, 
516                                 int(c[cSetting.ITERATIONS] * 0.1), 
517                                 c[cSetting.ROTWEIGHT]
518                                 ))
519
520     def __checkConnection(self, b, p):
521         if Blender.Armature.CONNECTED in b.options:
522             parent=self.__boneByName(p.name)
523             parent.isConnect=True
524
525         for c in b.children:
526             self.__checkConnection(c, b)
527
528     def sortBy(self, boneMap):
529         """
530         boneMap順に並べ替える
531         """
532         original=self.bones[:]
533         def getIndex(bone):
534             for i, k_v in enumerate(boneMap):
535                 if k_v[0]==bone.name:
536                     return i
537             print(bone)
538
539         self.bones.sort(lambda l, r: getIndex(l)-getIndex(r))
540         sortMap={}
541         for i, b in enumerate(self.bones):
542             src=original.index(b)
543             sortMap[src]=i
544         for b in self.bones:
545             b.index=sortMap[b.index]
546             if b.parent_index:
547                 b.parent_index=sortMap[b.parent_index]
548             if b.tail_index:
549                 b.tail_index=sortMap[b.tail_index]
550             if b.ik_index>0:
551                 b.ik_index=sortMap[b.ik_index]
552
553     def getIndex(self, bone):
554         for i, b in enumerate(self.bones):
555             if b==bone:
556                 return i
557         assert(false)
558
559     def indexByName(self, name):
560         return self.getIndex(self.__boneByName(name))
561
562     def __boneByName(self, name):
563         return self.bones[self.boneMap[name]]
564                     
565     def __getBone(self, parent, b):
566         if len(b.children)==0:
567             parent.type=7
568             return
569
570         for i, c in enumerate(b.children):
571             bone=Bone(c.name, 
572                     c.head['ARMATURESPACE'][0:3], 
573                     c.tail['ARMATURESPACE'][0:3])
574             self.__addBone(bone)
575             if parent:
576                 bone.parent_index=parent.index
577                 if i==0:
578                     parent.tail_index=bone.index
579             self.__getBone(bone, c)
580
581     def __addBone(self, bone):
582         bone.index=len(self.bones)
583         self.bones.append(bone)
584         self.boneMap[bone.name]=bone.index
585
586
587 class PmdExporter(object):
588
589     def __init__(self):
590         self.armatureObj=None
591
592     def setup(self, scene):
593         # 木構造を構築する
594         object_node_map={}
595         for o in scene.objects:
596             object_node_map[o]=Node(o)
597         for node in object_node_map.values():
598             if node.o.parent:
599                 object_node_map[node.o.parent].children.append(node)
600         # ルートを得る
601         root=object_node_map[scene.objects.active]
602
603         # ワンスキンメッシュを作る
604         self.oneSkinMesh=OneSkinMesh()
605         self.__createOneSkinMesh(root)
606         print(self.oneSkinMesh)
607         self.name=root.o.name
608
609         # skeleton
610         self.builder=BoneBuilder()
611         self.builder.build(self.armatureObj)
612         self.builder.sortBy(englishmap.boneMap)
613         def getIndex(ik):
614             for i, v in enumerate(englishmap.boneMap):
615                 if v[0]==ik.target.name:
616                     return i
617             return len(englishmap.boneMap)
618         self.builder.ik_list.sort(lambda l, r: getIndex(l)-getIndex(r))
619
620     def __createOneSkinMesh(self, node):
621         ############################################################
622         # search armature modifier
623         ############################################################
624         for m in node.o.modifiers:
625             if m.name=="Armature":
626                 armatureObj=m[Blender.Modifier.Settings.OBJECT]
627                 if not self.armatureObj:
628                     self.armatureObj=armatureObj
629                 elif self.armatureObj!=armatureObj:
630                     print "warning! found multiple armature. ignored.", armatureObj.name
631
632         if node.o.getType()=='Mesh':
633             self.oneSkinMesh.addMesh(node.o)
634
635         for child in node.children:
636             self.__createOneSkinMesh(child)
637
638     def write(self, path):
639         io=pmd.IO()
640         io.name=self.name
641         io.comment="blender export"
642         io.version=1.0
643
644         # 頂点
645         for pos, normal, uv, b0, b1, weight in self.oneSkinMesh.vertexArray.zip():
646             # convert right-handed z-up to left-handed y-up
647             v=io.addVertex()
648             v.pos.x=pos[0]
649             v.pos.y=pos[2]
650             v.pos.z=pos[1]
651             v.normal.x=normal[0]
652             v.normal.y=normal[2]
653             v.normal.z=normal[1]
654             v.uv.x=uv[0]
655             v.uv.y=uv[1]
656             v.bone0=self.builder.boneMap[b0] if b0 in self.builder.boneMap else 0
657             v.bone1=self.builder.boneMap[b1] if b1 in self.builder.boneMap else 0
658             v.weight0=int(100*weight)
659             v.edge_flag=0 # edge flag, 0: enable edge, 1: not edge
660
661         # 面とマテリアル
662         vertexCount=self.oneSkinMesh.getVertexCount()
663         for m, indices in self.oneSkinMesh.vertexArray.indexArrays.items():
664             m=Blender.Material.Get(m)
665             # マテリアル
666             material=io.addMaterial()
667             material.diffuse.r=m.R
668             material.diffuse.g=m.G
669             material.diffuse.b=m.B
670             material.diffuse.a=m.alpha
671             material.sinness=0 if m.spec<1e-5 else m.spec*10
672             material.specular.r=m.specR
673             material.specular.g=m.specG
674             material.specular.b=m.specB
675             material.ambient.r=m.mirR
676             material.ambient.g=m.mirG
677             material.ambient.b=m.mirB
678             material.vertex_count=len(indices)
679             material.toon_index=0
680             material.flag=1 if m.enableSSS else 0
681             # ToDo
682             material.texture=""
683             # 面
684             for i in indices:
685                 assert(i<vertexCount)
686             for i in xrange(0, len(indices), 3):
687                 # reverse triangle
688                 io.indices.append(indices[i+2])
689                 io.indices.append(indices[i+1])
690                 io.indices.append(indices[i])
691
692                 #io.indices.append(indices[i])
693                 #io.indices.append(indices[i+1])
694                 #io.indices.append(indices[i+2])
695
696         # bones
697         for b in self.builder.bones:
698             if b.name.endswith("_t"):
699                 if b.name.startswith("arm twist1_") or b.name.startswith("arm twist2_"):
700                     # skip
701                     print "skip %s" % b.name
702                     continue
703
704             bone=io.addBone()
705
706             v=englishmap.getUnicodeBoneName(b.name)
707             assert(v)
708             cp932=v[1].encode('cp932')
709             bone_name="%s" % cp932
710             assert(len(bone_name)<20)
711             bone.name=bone_name
712
713             bone_english_name="%s" % b.name
714             assert(len(bone_english_name)<20)
715             bone.english_name=bone_english_name
716
717             if len(v)>=3:
718                 # has type
719                 if v[2]==5:
720                     b.ik_index=self.builder.indexByName('eyes')
721                 bone.type=v[2]
722             else:
723                 bone.type=b.type
724
725             # parent index
726             bone.parent_index=b.parent_index if b.parent_index!=None else 0xFFFF
727
728             # tail index
729             if b.tail_index!=None:
730                 if bone.type==9:
731                     bone.tail_index=0
732                 else:
733                     bone.tail_index=b.tail_index
734             else:
735                 bone.tail_index=0
736
737             bone.ik_index=b.ik_index
738
739             # convert right-handed z-up to left-handed y-up
740             bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
741             bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
742             bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
743
744         # IK
745         for ik in self.builder.ik_list:
746             solver=io.addIK()
747             solver.index=self.builder.getIndex(ik.target)
748             solver.target=self.builder.getIndex(ik.effector)
749             solver.length=ik.length
750             b=self.builder.bones[ik.effector.parent_index]
751             for i in xrange(solver.length):
752                 solver.children.append(self.builder.getIndex(b))
753                 b=self.builder.bones[b.parent_index]
754             solver.iterations=ik.iterations
755             solver.weight=ik.weight
756
757         # 表情
758         for i, m in enumerate(self.oneSkinMesh.morphList):
759             # morph
760             morph=io.addMorph()
761
762             v=englishmap.getUnicodeSkinName(m.name)
763             assert(v)
764             cp932=v[1].encode('cp932')
765             morph.name="%s\n" % cp932
766
767             morph.english_name="%s\n" % m.name
768             m.type=v[2]
769             morph.type=v[2]
770             for index, offset in m.offsets:
771                 # convert right-handed z-up to left-handed y-up
772                 morph.append(index, offset[0], offset[2], offset[1])
773             morph.vertex_count=len(m.offsets)
774
775         # 表情枠
776         # type==0はbase
777         for i, m in enumerate(self.oneSkinMesh.morphList):
778             if m.type==3:
779                 io.face_list.append(i)
780         for i, m in enumerate(self.oneSkinMesh.morphList):
781             if m.type==2:
782                 io.face_list.append(i)
783         for i, m in enumerate(self.oneSkinMesh.morphList):
784             if m.type==1:
785                 io.face_list.append(i)
786         for i, m in enumerate(self.oneSkinMesh.morphList):
787             if m.type==4:
788                 io.face_list.append(i)
789
790         # ボーン表示枠
791         def createBoneDisplayName(name, english):
792             boneDisplayName=io.addBoneDisplayName()
793             boneDisplayName.name=name.decode('utf-8').encode('cp932')
794             boneDisplayName.english_name=english
795         boneDisplayName=createBoneDisplayName("IK\n", "IK\n")
796         boneDisplayName=createBoneDisplayName("体(上)\n", "Body[u]\n")
797         boneDisplayName=createBoneDisplayName("髪\n", "Hair\n")
798         boneDisplayName=createBoneDisplayName("腕\n", "Arms\n")
799         boneDisplayName=createBoneDisplayName("指\n", "Fingers\n")
800         boneDisplayName=createBoneDisplayName("体(下)\n", "Body[l]\n")
801         boneDisplayName=createBoneDisplayName("足\n", "Legs\n")
802         for i, b in enumerate(self.builder.bones):
803             if i==0:
804                 continue
805             if b.type in [6, 7]:
806                 continue
807             io.addBoneDisplay(i, getBoneDisplayGroup(b))
808
809         # English
810         io.english_name="blender export"
811         io.english_coment="blender export"
812
813         for i in range(10):
814             io.getToonTexture(i).name="toon%02d.bmp\n" % i
815
816         # 書き込み
817         return io.write(path.encode(bl.FS_ENCODING))
818
819
820 def getBoneDisplayGroup(bone):
821     boneGroups=[
822             [ # IK
823                 "necktie IK", "hair IK_L", "hair IK_R", "leg IK_L", "leg IK_R",
824                 "toe IK_L", "toe IK_R", 
825                 ],
826             [ # 体(上)
827                 "upper body", "neck", "head", "eye_L", "eye_R",
828                 "necktie1", "necktie2", "necktie3", "eyes", 
829                 "eyelight_L", "eyelight_R",
830                 ],
831             [ # 髪
832                 "front hair1", "front hair2", "front hair3",
833                 "hair1_L", "hair2_L", "hair3_L", 
834                 "hair4_L", "hair5_L", "hair6_L",
835                 "hair1_R", "hair2_R", "hair3_R", 
836                 "hair4_R", "hair5_R", "hair6_R",
837                 ],
838             [ # 腕
839                 "shoulder_L", "arm_L", "arm twist_L", "elbow_L", 
840                 "wrist twist_L", "wrist_L", "sleeve_L", 
841                 "shoulder_R", "arm_R", "arm twist_R", "elbow_R", 
842                 "wrist twist_R", "wrist_R", "sleeve_R", 
843                 ],
844             [ # 指
845                 "thumb1_L", "thumb2_L", "fore1_L", "fore2_L", "fore3_L",
846                 "middle1_L", "middle2_L", "middle3_L",
847                 "third1_L", "third2_L", "third3_L",
848                 "little1_L", "little2_L", "little3_L",
849                 "thumb1_R", "thumb2_R", "fore1_R", "fore2_R", "fore3_R",
850                 "middle1_R", "middle2_R", "middle3_R",
851                 "third1_R", "third2_R", "third3_R",
852                 "little1_R", "little2_R", "little3_R",
853                 ],
854             [ # 体(下)
855                 "lower body",  "waist accessory", 
856                 "front skirt_L", "back skirt_L",
857                 "front skirt_R", "back skirt_R",
858                 ],
859             [ # 足
860                 "leg_L", "knee_L", "ankle_L",
861                 "leg_R", "knee_R", "ankle_R",
862                 ],
863             ]
864     index=1
865     for g in boneGroups:
866         if bone.name in g:
867             return index
868         index+=1
869     print(bone)
870     return -1
871
872
873 def __execute(filename, scene):
874     exporter=PmdExporter()
875     exporter.setup(scene)
876     exporter.write(filename)
877
878
879 if isBlender24():
880     # 24
881     def execute_24(filename):
882         filename=filename.decode(bl.INTERNAL_ENCODING)
883         print("pmd exporter: %s" % filename)
884
885         Blender.Window.WaitCursor(1) 
886         t = Blender.sys.time() 
887
888         scene = bpy.data.scenes.active
889         __execute(filename, scene)
890
891         print 'finished in %.2f seconds' % (Blender.sys.time()-t) 
892         Blender.Redraw()
893         Blender.Window.WaitCursor(0) 
894
895     Blender.Window.FileSelector(
896              execute_24,
897              'Export Metasequoia PMD',
898              Blender.sys.makename(ext='.pmd'))
899
900 else:
901     # 25
902     pass
903