OSDN Git Service

fix.
[meshio/meshio.git] / swig / blender24 / pmd_export.py
index 5cbf3b7..c407b4e 100644 (file)
@@ -7,11 +7,12 @@
  Tooltip: 'Export PMD file for MikuMikuDance.'
 """
 __author__= ["ousttrue"]
-__version__= "0.2"
+__version__= "1.0"
 __url__=()
 __bpydoc__="""
 0.1 20100318 first implementation.
 0.2 20100519 refactoring. use C extension.
+1.0 20100530 implement, basic features.
 """
 import Blender
 import os
@@ -29,6 +30,7 @@ else:
     INTERNAL_ENCODING=FS_ENCODING
 
 
+MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
 EXTENSION = u'.pmd'
 
 
@@ -67,11 +69,18 @@ class VertexKey(object):
         self.u=u
         self.v=v
 
+    def __str__(self):
+        return "<vkey: %f, %f, %f, %f, %f, %f, %f, %f>" % (
+                self.x, self.y, self.z, self.nx, self.ny, self.nz, self.u, self.v)
+
     def __hash__(self):
-        return int((self.x+self.y+self.z+self.nx+self.ny+self.nz+self.u+self.v)*100)
+        #return int((self.x+self.y+self.z+self.nx+self.ny+self.nz+self.u+self.v)*100)
+        return int((self.x+self.y+self.z)*100)
 
     def __eq__(self, rhs):
-        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)
+        #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)
+        #return near(self.x, rhs.x) and near(self.y, rhs.y) and near(self.z, rhs.z)
+        return self.x==rhs.x and self.y==rhs.y and self.z==rhs.z
 
 
 class VertexArray(object):
@@ -91,6 +100,7 @@ class VertexArray(object):
         self.weight=[]
 
         self.vertexMap={}
+        self.indexMap={}
 
     def __str__(self):
         return "<VertexArray %d vertices, %d indexArrays>" % (
@@ -101,7 +111,7 @@ class VertexArray(object):
                 self.vertices, self.normals, self.uvs,
                 self.b0, self.b1, self.weight)
 
-    def getIndex(self, pos, normal, uv, b0, b1, weight0):
+    def __getIndex(self, base_index, pos, normal, uv, b0, b1, weight0):
         """
         頂点属性からその頂点のインデックスを得る
         """
@@ -109,19 +119,35 @@ class VertexArray(object):
                 pos[0], pos[1], pos[2],
                 normal[0], normal[1], normal[2],
                 uv[0], uv[1])
-        if not key in self.vertexMap:
-            self.vertexMap[key]=len(self.vertices)
+        if key in self.vertexMap:
+            # 同じ頂点を登録済み
+            index=self.vertexMap[key]
+        else:
+            index=len(self.vertices)
+            # 新規頂点
+            self.vertexMap[key]=index
+            # append...
             self.vertices.append((pos.x, pos.y, pos.z))
             self.normals.append((normal.x, normal.y, normal.z))
             self.uvs.append((uv.x, uv.y))
-            # ToDo
             self.b0.append(b0)
             self.b1.append(b1)
             self.weight.append(weight0)
-        return self.vertexMap[key]
+            
+        # indexのマッピングを保存する
+        if not base_index in self.indexMap:
+            self.indexMap[base_index]=set()
+        self.indexMap[base_index].add(index)
+
+        assert(index<=65535)
+        return index
+
+    def getMappedIndices(self, base_index):
+        return self.indexMap[base_index]
 
     def addTriangle(self,
             material,
+            base_index0, base_index1, base_index2,
             pos0, pos1, pos2,
             n0, n1, n2,
             uv0, uv1, uv2,
@@ -132,33 +158,51 @@ class VertexArray(object):
         if not material in self.indexArrays:
             self.indexArrays[material]=[]
 
-        index0=self.getIndex(pos0, n0, uv0, b0_0, b1_0, weight0)
-        index1=self.getIndex(pos1, n1, uv1, b0_1, b1_1, weight1)
-        index2=self.getIndex(pos2, n2, uv2, b0_2, b1_2, weight2)
+        index0=self.__getIndex(base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
+        index1=self.__getIndex(base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
+        index2=self.__getIndex(base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
 
         self.indexArrays[material]+=[index0, index1, index2]
 
 
-class OneSkinMesh(object):
-    __slots__=['armatureObj', 'vertexArray']
-    def __init__(self, root):
-        self.armatureObj=None
-        self.vertexArray=VertexArray()
-        self.__create(root)
+class Morph(object):
+    __slots__=['name', 'type', 'offsets']
+    def __init__(self, name, type):
+        self.name=name
+        self.type=type
+        self.offsets=[]
+
+    def add(self, index, offset):
+        self.offsets.append((index, offset))
+
+    def sort(self):
+        self.offsets.sort(lambda l, r: l[0]-r[0])
 
     def __str__(self):
-        return "<OneSkinMesh armature:%s, %s>" % (
-                self.armatureObj.name if self.armatureObj else None, 
-                self.vertexArray)
+        return "<Morph %s>" % self.name
 
-    def __create(self, node):
-        if node.o.getType()=='Mesh':
-            self.__addMesh(node.o)
+class IKSolver(object):
+    __slots__=['target', 'effector', 'length', 'iterations', 'weight']
+    def __init__(self, target, effector, length, iterations, weight):
+        self.target=target
+        self.effector=effector
+        self.length=length
+        self.iterations=iterations
+        self.weight=weight
 
-        for child in node.children:
-            self.__create(child)
 
-    def __addMesh(self, obj):
+class OneSkinMesh(object):
+    __slots__=['vertexArray', 'morphList']
+    def __init__(self):
+        self.vertexArray=VertexArray()
+        self.morphList=[]
+
+    def __str__(self):
+        return "<OneSkinMesh %s, morph:%d>" % (
+                self.vertexArray,
+                len(self.morphList))
+
+    def addMesh(self, obj):
         if obj.restrictDisplay:
             # 非表示
             return
@@ -166,17 +210,6 @@ class OneSkinMesh(object):
         print("export", obj.name)
 
         ############################################################
-        # search armature modifier
-        ############################################################
-        for m in obj.modifiers:
-            if m.name=="Armature":
-                armatureObj=m[Blender.Modifier.Settings.OBJECT]
-                if not self.armatureObj:
-                    self.armatureObj=armatureObj
-                elif self.armatureObj!=armatureObj:
-                    print "warning! found multiple armature. ignored.", armatureObj.name
-
-        ############################################################
         # bone weight
         ############################################################
         mesh=obj.getData(mesh=True)
@@ -242,8 +275,11 @@ class OneSkinMesh(object):
                 # triangle
                 self.vertexArray.addTriangle(
                         material.name,
+                        v0.index, v1.index, v2.index,
                         v0.co, v1.co, v2.co,
-                        v0.no, v1.no, v2.no,
+                        # ToDo vertex normal
+                        #v0.no, v1.no, v2.no,
+                        face.no, face.no, face.no,
                         face.uv[0], face.uv[1], face.uv[2],
                         weightMap[v0.index][0],
                         weightMap[v1.index][0],
@@ -263,8 +299,10 @@ class OneSkinMesh(object):
                 # quadrangle
                 self.vertexArray.addTriangle(
                         material.name,
+                        v0.index, v1.index, v2.index,
                         v0.co, v1.co, v2.co,
-                        v0.no, v1.no, v2.no,
+                        #v0.no, v1.no, v2.no,
+                        face.no, face.no, face.no,
                         face.uv[0], face.uv[1], face.uv[2],
                         weightMap[v0.index][0],
                         weightMap[v1.index][0],
@@ -278,8 +316,10 @@ class OneSkinMesh(object):
                         )
                 self.vertexArray.addTriangle(
                         material.name,
+                        v2.index, v3.index, v0.index,
                         v2.co, v3.co, v0.co,
-                        v2.no, v3.no, v0.no,
+                        #v2.no, v3.no, v0.no,
+                        face.no, face.no, face.no,
                         face.uv[2], face.uv[3], face.uv[0],
                         weightMap[v2.index][0],
                         weightMap[v3.index][0],
@@ -292,51 +332,231 @@ class OneSkinMesh(object):
                         weightMap[v0.index][1]
                         )
 
+        ############################################################
+        # skin
+        ############################################################
+        # base
+        indexRelativeMap={}
+        blenderMesh=obj.getData(mesh=True)
+        baseMorph=None
+        if blenderMesh.key:
+            for b in blenderMesh.key.blocks:
+                if b.name=='Basis':
+                    print(b.name)
+                    baseMorph=self.__getOrCreateMorph('base', 0)
+                    relativeIndex=0
+                    basis=b
+                    for index in blenderMesh.getVertsFromGroup(
+                            MMD_SHAPE_GROUP_NAME):
+                        v=b.data[index]
+                        pos=[v[0], v[1], v[2]]
+                        indices=self.vertexArray.getMappedIndices(index)
+                        for i in indices:
+                            baseMorph.add(i, pos)
+                            indexRelativeMap[relativeIndex]=i
+                            relativeIndex+=1
+                    break
+            print(len(baseMorph.offsets))
+            baseMorph.sort()
+            assert(basis)
+
+            # shape keys
+            vg=obj.getData(mesh=True).getVertsFromGroup(
+                        MMD_SHAPE_GROUP_NAME)
+            for b in obj.getData(mesh=True).key.blocks:
+                if b.name=='Basis':
+                    continue
+
+                print(b.name)
+                morph=self.__getOrCreateMorph(b.name, 4)
+                for index, src, dst in zip(
+                        xrange(len(blenderMesh.verts)),
+                        basis.data,
+                        b.data):
+                    offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
+                    if index in vg:
+                        indices=self.vertexArray.getMappedIndices(index)
+                        for i in indices:
+                            morph.add(indexRelativeMap[i], offset)
+                assert(len(morph.offsets)==len(baseMorph.offsets))
+
+        # sort skinmap
+        if len(self.morphList)>1:
+            original=self.morphList[:]
+            def getIndex(morph):
+                for i, v in enumerate(englishmap.skinMap):
+                    if v[0]==morph.name:
+                        return i
+                #print(morph)
+                return 0
+            self.morphList.sort(lambda l, r: getIndex(l)-getIndex(r))
+
+    def __getOrCreateMorph(self, name, type):
+        for m in self.morphList:
+            if m.name==name:
+                return m
+        m=Morph(name, type)
+        self.morphList.append(m)
+        return m
+
     def getVertexCount(self):
         return len(self.vertexArray.vertices)
 
 
 class Bone(object):
-    def __init__(self, bone):
-        self.name=bone.name
-        pos=bone.head['ARMATURESPACE']
-        self.pos=[pos.x, pos.y, pos.z]
+    __slots__=['index', 'name', 'ik_index',
+            'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
+    def __init__(self, name, pos, tail):
+        self.index=-1
+        self.name=name
+        self.pos=pos
+        self.tail=tail
         self.parent_index=None
         self.tail_index=None
+        self.type=0
+        self.isConnect=False
+        self.ik_index=0
+
+    def __eq__(self, rhs):
+        return self.index==rhs.index
 
     def __str__(self):
-        return "<Bone %s>" % self.name
+        return "<Bone %s %d>" % (self.name, self.type)
 
 class BoneBuilder(object):
-    __slots__=['bones', 'boneMap']
+    __slots__=['bones', 'boneMap', 'ik_list']
     def __init__(self):
         self.bones=[]
         self.boneMap={}
+        self.ik_list=[]
 
     def build(self, armatureObj):
-        if armatureObj:
-            armature=armatureObj.getData()
-            for b in armature.bones.values():
-                if not b.parent:
-                    # root bone
-                    bone=Bone(b)
-                    self.addBone(bone)
-                    self.getBone(bone, b)
-
-    def getBone(self, parent, b):
+        if not armatureObj:
+            return
+
+        print("gather bones")
+        armature=armatureObj.getData()
+        for b in armature.bones.values():
+            if b.name=='center':
+                # root bone
+                bone=Bone(b.name, 
+                        b.head['ARMATURESPACE'][0:3], 
+                        b.tail['ARMATURESPACE'][0:3])
+                self.__addBone(bone)
+                self.__getBone(bone, b)
+
+        for b in armature.bones.values():
+            if not b.parent and b.name!='center':
+                # root bone
+                bone=Bone(b.name, 
+                        b.head['ARMATURESPACE'][0:3], 
+                        b.tail['ARMATURESPACE'][0:3])
+                self.__addBone(bone)
+                self.__getBone(bone, b)
+
+        print("check connection")
+        for b in armature.bones.values():
+            if not b.parent:
+                self.__checkConnection(b, None)
+
+        print("gather ik")
+        pose = armatureObj.getPose()
+        cSetting=Blender.Constraint.Settings
+        for b in pose.bones.values():
+            for c in b.constraints:
+                if c.type==Blender.Constraint.Type.IKSOLVER:
+                    ####################
+                    # IK target
+                    ####################
+                    assert(c[cSetting.TARGET]==armatureObj)
+                    target=self.__boneByName(
+                            c[Blender.Constraint.Settings.BONE])
+                    target.type=2
+
+                    ####################
+                    # IK effector
+                    ####################
+                    # IK 接続先
+                    link=self.__boneByName(b.name)
+                    link.type=6
+
+                    # IK chain
+                    e=b.parent
+                    chainLength=c[cSetting.CHAINLEN]
+                    for i in range(chainLength):
+                        # IK影響下
+                        chainBone=self.__boneByName(e.name)
+                        chainBone.type=4
+                        chainBone.ik_index=target.index
+                        e=e.parent
+                    self.ik_list.append(
+                            IKSolver(target, link, chainLength, 
+                                int(c[cSetting.ITERATIONS] * 0.1), 
+                                c[cSetting.ROTWEIGHT]
+                                ))
+
+    def __checkConnection(self, b, p):
+        if Blender.Armature.CONNECTED in b.options:
+            parent=self.__boneByName(p.name)
+            parent.isConnect=True
+
+        for c in b.children:
+            self.__checkConnection(c, b)
+
+    def sortBy(self, boneMap):
+        """
+        boneMap順に並べ替える
+        """
+        original=self.bones[:]
+        def getIndex(bone):
+            for i, k_v in enumerate(boneMap):
+                if k_v[0]==bone.name:
+                    return i
+            print(bone)
+
+        self.bones.sort(lambda l, r: getIndex(l)-getIndex(r))
+        sortMap={}
+        for i, b in enumerate(self.bones):
+            src=original.index(b)
+            sortMap[src]=i
+        for b in self.bones:
+            b.index=sortMap[b.index]
+            if b.parent_index:
+                b.parent_index=sortMap[b.parent_index]
+            if b.tail_index:
+                b.tail_index=sortMap[b.tail_index]
+            if b.ik_index>0:
+                b.ik_index=sortMap[b.ik_index]
+
+    def getIndex(self, bone):
+        for i, b in enumerate(self.bones):
+            if b==bone:
+                return i
+        assert(false)
+
+    def indexByName(self, name):
+        return self.getIndex(self.__boneByName(name))
+
+    def __boneByName(self, name):
+        return self.bones[self.boneMap[name]]
+                    
+    def __getBone(self, parent, b):
         if len(b.children)==0:
+            parent.type=7
             return
 
         for i, c in enumerate(b.children):
-            bone=Bone(c)
-            self.addBone(bone)
+            bone=Bone(c.name, 
+                    c.head['ARMATURESPACE'][0:3], 
+                    c.tail['ARMATURESPACE'][0:3])
+            self.__addBone(bone)
             if parent:
                 bone.parent_index=parent.index
                 if i==0:
                     parent.tail_index=bone.index
-            self.getBone(bone, c)
+            self.__getBone(bone, c)
 
-    def addBone(self, bone):
+    def __addBone(self, bone):
         bone.index=len(self.bones)
         self.bones.append(bone)
         self.boneMap[bone.name]=bone.index
@@ -344,6 +564,9 @@ class BoneBuilder(object):
 
 class PmdExporter(object):
 
+    def __init__(self):
+        self.armatureObj=None
+
     def setup(self, scene):
         # 木構造を構築する
         object_node_map={}
@@ -356,33 +579,49 @@ class PmdExporter(object):
         root=object_node_map[scene.objects.active]
 
         # ワンスキンメッシュを作る
-        self.oneSkinMesh=OneSkinMesh(root)
+        self.oneSkinMesh=OneSkinMesh()
+        self.__createOneSkinMesh(root)
         print(self.oneSkinMesh)
         self.name=root.o.name
 
+        # skeleton
+        self.builder=BoneBuilder()
+        self.builder.build(self.armatureObj)
+        self.builder.sortBy(englishmap.boneMap)
+        def getIndex(ik):
+            for i, v in enumerate(englishmap.boneMap):
+                if v[0]==ik.target.name:
+                    return i
+            return len(englishmap.boneMap)
+        self.builder.ik_list.sort(lambda l, r: getIndex(l)-getIndex(r))
+
+    def __createOneSkinMesh(self, node):
+        ############################################################
+        # search armature modifier
+        ############################################################
+        for m in node.o.modifiers:
+            if m.name=="Armature":
+                armatureObj=m[Blender.Modifier.Settings.OBJECT]
+                if not self.armatureObj:
+                    self.armatureObj=armatureObj
+                elif self.armatureObj!=armatureObj:
+                    print "warning! found multiple armature. ignored.", armatureObj.name
+
+        if node.o.getType()=='Mesh':
+            self.oneSkinMesh.addMesh(node.o)
+
+        for child in node.children:
+            self.__createOneSkinMesh(child)
+
     def write(self, path):
+        print('write')
         io=pmd.IO()
         io.name=self.name
         io.comment="blender export"
         io.version=1.0
 
-        # bones
-        builder=BoneBuilder()
-        builder.build(self.oneSkinMesh.armatureObj)
-        for b in builder.bones:
-            bone=io.addBone()
-            bone.name=b.name
-            bone.type=0
-            bone.parent_index=b.parent_index if b.parent_index!=None else 0xFFFF
-            bone.tail_index=b.tail_index if b.tail_index!=None else 0xFFFF
-            # ToDo
-            bone.ik_index=0xFFFF
-            # convert right-handed z-up to left-handed y-up
-            bone.pos.x=b.pos[0]
-            bone.pos.y=b.pos[2]
-            bone.pos.z=b.pos[1]
-
         # 頂点
+        print('vertices')
         for pos, normal, uv, b0, b1, weight in self.oneSkinMesh.vertexArray.zip():
             # convert right-handed z-up to left-handed y-up
             v=io.addVertex()
@@ -394,15 +633,15 @@ class PmdExporter(object):
             v.normal.z=normal[1]
             v.uv.x=uv[0]
             v.uv.y=uv[1]
-            v.bone0=builder.boneMap[b0] if b0 in builder.boneMap else 0
-            v.bone1=builder.boneMap[b1] if b1 in builder.boneMap else 0
+            v.bone0=self.builder.boneMap[b0] if b0 in self.builder.boneMap else 0
+            v.bone1=self.builder.boneMap[b1] if b1 in self.builder.boneMap else 0
             v.weight0=int(100*weight)
             v.edge_flag=0 # edge flag, 0: enable edge, 1: not edge
 
         # 面とマテリアル
+        print('faces and materials')
         vertexCount=self.oneSkinMesh.getVertexCount()
         for m, indices in self.oneSkinMesh.vertexArray.indexArrays.items():
-            print(m, len(indices))
             m=Blender.Material.Get(m)
             # マテリアル
             material=io.addMaterial()
@@ -410,15 +649,16 @@ class PmdExporter(object):
             material.diffuse.g=m.G
             material.diffuse.b=m.B
             material.diffuse.a=m.alpha
-            material.sinness=m.spec
+            material.sinness=0 if m.spec<1e-5 else m.spec*10
             material.specular.r=m.specR
             material.specular.g=m.specG
             material.specular.b=m.specB
-            material.ambient.r=m.amb
-            material.ambient.g=m.amb
-            material.ambient.b=m.amb
+            material.ambient.r=m.mirR
+            material.ambient.g=m.mirG
+            material.ambient.b=m.mirB
             material.vertex_count=len(indices)
             material.toon_index=0
+            material.flag=1 if m.enableSSS else 0
             # ToDo
             material.texture=""
             # 面
@@ -434,9 +674,187 @@ class PmdExporter(object):
                 #io.indices.append(indices[i+1])
                 #io.indices.append(indices[i+2])
 
-        # 書き込み
-        return io.write(path.encode(FS_ENCODING))
+        # bones
+        print('bones')
+        for b in self.builder.bones:
+            if b.name.endswith("_t"):
+                if b.name.startswith("arm twist1_") or b.name.startswith("arm twist2_"):
+                    # skip
+                    print "skip %s" % b.name
+                    continue
+
+            bone=io.addBone()
+
+            v=englishmap.getUnicodeBoneName(b.name)
+            assert(v)
+            cp932=v[1].encode('cp932')
+            bone_name="%s" % cp932
+            assert(len(bone_name)<20)
+            bone.name=bone_name
+
+            bone_english_name="%s" % b.name
+            assert(len(bone_english_name)<20)
+            bone.english_name=bone_english_name
+
+            if len(v)>=3:
+                # has type
+                if v[2]==5:
+                    b.ik_index=self.builder.indexByName('eyes')
+                bone.type=v[2]
+            else:
+                bone.type=b.type
 
+            # parent index
+            bone.parent_index=b.parent_index if b.parent_index!=None else 0xFFFF
+
+            # tail index
+            if b.tail_index!=None:
+                if bone.type==9:
+                    bone.tail_index=0
+                else:
+                    bone.tail_index=b.tail_index
+            else:
+                bone.tail_index=0
+
+            bone.ik_index=b.ik_index
+
+            # convert right-handed z-up to left-handed y-up
+            bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
+            bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
+            bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
+
+        # IK
+        print('ik')
+        for ik in self.builder.ik_list:
+            solver=io.addIK()
+            solver.index=self.builder.getIndex(ik.target)
+            solver.target=self.builder.getIndex(ik.effector)
+            solver.length=ik.length
+            b=self.builder.bones[ik.effector.parent_index]
+            for i in xrange(solver.length):
+                solver.children.append(self.builder.getIndex(b))
+                b=self.builder.bones[b.parent_index]
+            solver.iterations=ik.iterations
+            solver.weight=ik.weight
+
+        # 表情
+        print('shape keys')
+        for i, m in enumerate(self.oneSkinMesh.morphList):
+            # morph
+            morph=io.addMorph()
+
+            v=englishmap.getUnicodeSkinName(m.name)
+            assert(v)
+            cp932=v[1].encode('cp932')
+            morph.name="%s\n" % cp932
+
+            morph.english_name="%s\n" % m.name
+            m.type=v[2]
+            morph.type=v[2]
+            for index, offset in m.offsets:
+                # convert right-handed z-up to left-handed y-up
+                morph.append(index, offset[0], offset[2], offset[1])
+            morph.vertex_count=len(m.offsets)
+
+        # 表情枠
+        print('display list')
+        # type==0はbase
+        for i, m in enumerate(self.oneSkinMesh.morphList):
+            if m.type==3:
+                io.face_list.append(i)
+        for i, m in enumerate(self.oneSkinMesh.morphList):
+            if m.type==2:
+                io.face_list.append(i)
+        for i, m in enumerate(self.oneSkinMesh.morphList):
+            if m.type==1:
+                io.face_list.append(i)
+        for i, m in enumerate(self.oneSkinMesh.morphList):
+            if m.type==4:
+                io.face_list.append(i)
+
+        # ボーン表示枠
+        print('bone display list')
+        def createBoneDisplayName(name, english):
+            boneDisplayName=io.addBoneDisplayName()
+            boneDisplayName.name=name.decode('utf-8').encode('cp932')
+            boneDisplayName.english_name=english
+        boneDisplayName=createBoneDisplayName("IK\n", "IK\n")
+        boneDisplayName=createBoneDisplayName("体(上)\n", "Body[u]\n")
+        boneDisplayName=createBoneDisplayName("髪\n", "Hair\n")
+        boneDisplayName=createBoneDisplayName("腕\n", "Arms\n")
+        boneDisplayName=createBoneDisplayName("指\n", "Fingers\n")
+        boneDisplayName=createBoneDisplayName("体(下)\n", "Body[l]\n")
+        boneDisplayName=createBoneDisplayName("足\n", "Legs\n")
+        for i, b in enumerate(self.builder.bones):
+            if i==0:
+                continue
+            if b.type in [6, 7]:
+                continue
+            io.addBoneDisplay(i, getBoneDisplayGroup(b))
+
+        # English
+        print('english')
+        io.english_name="blender export"
+        io.english_coment="blender export"
+
+        for i in range(10):
+            io.getToonTexture(i).name="toon%02d.bmp\n" % i
+
+        # 書き込み
+        return io.write(path)
+
+
+def getBoneDisplayGroup(bone):
+    boneGroups=[
+            [ # IK
+                "necktie IK", "hair IK_L", "hair IK_R", "leg IK_L", "leg IK_R",
+                "toe IK_L", "toe IK_R", 
+                ],
+            [ # 体(上)
+                "upper body", "neck", "head", "eye_L", "eye_R",
+                "necktie1", "necktie2", "necktie3", "eyes", 
+                "eyelight_L", "eyelight_R",
+                ],
+            [ # 髪
+                "front hair1", "front hair2", "front hair3",
+                "hair1_L", "hair2_L", "hair3_L", 
+                "hair4_L", "hair5_L", "hair6_L",
+                "hair1_R", "hair2_R", "hair3_R", 
+                "hair4_R", "hair5_R", "hair6_R",
+                ],
+            [ # 腕
+                "shoulder_L", "arm_L", "arm twist_L", "elbow_L", 
+                "wrist twist_L", "wrist_L", "sleeve_L", 
+                "shoulder_R", "arm_R", "arm twist_R", "elbow_R", 
+                "wrist twist_R", "wrist_R", "sleeve_R", 
+                ],
+            [ # 指
+                "thumb1_L", "thumb2_L", "fore1_L", "fore2_L", "fore3_L",
+                "middle1_L", "middle2_L", "middle3_L",
+                "third1_L", "third2_L", "third3_L",
+                "little1_L", "little2_L", "little3_L",
+                "thumb1_R", "thumb2_R", "fore1_R", "fore2_R", "fore3_R",
+                "middle1_R", "middle2_R", "middle3_R",
+                "third1_R", "third2_R", "third3_R",
+                "little1_R", "little2_R", "little3_R",
+                ],
+            [ # 体(下)
+                "lower body",  "waist accessory", 
+                "front skirt_L", "back skirt_L",
+                "front skirt_R", "back skirt_R",
+                ],
+            [ # 足
+                "leg_L", "knee_L", "ankle_L",
+                "leg_R", "knee_R", "ankle_R",
+                ],
+            ]
+    index=1
+    for g in boneGroups:
+        if bone.name in g:
+            return index
+        index+=1
+    print(bone)
+    return -1
 
 def export_pmd(filename):
     filename=filename.decode(INTERNAL_ENCODING)