OSDN Git Service

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