OSDN Git Service

fix.
[meshio/meshio.git] / swig / blender24 / pmd_export.py
index 60ec6e0..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
@@ -177,6 +178,8 @@ class Morph(object):
     def sort(self):
         self.offsets.sort(lambda l, r: l[0]-r[0])
 
+    def __str__(self):
+        return "<Morph %s>" % self.name
 
 class IKSolver(object):
     __slots__=['target', 'effector', 'length', 'iterations', 'weight']
@@ -350,7 +353,7 @@ class OneSkinMesh(object):
                         indices=self.vertexArray.getMappedIndices(index)
                         for i in indices:
                             baseMorph.add(i, pos)
-                            indexRelativeMap[i]=relativeIndex
+                            indexRelativeMap[relativeIndex]=i
                             relativeIndex+=1
                     break
             print(len(baseMorph.offsets))
@@ -377,6 +380,17 @@ class OneSkinMesh(object):
                             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:
@@ -390,7 +404,7 @@ class OneSkinMesh(object):
 
 
 class Bone(object):
-    __slots__=['index', 'name', 
+    __slots__=['index', 'name', 'ik_index',
             'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
     def __init__(self, name, pos, tail):
         self.index=-1
@@ -401,6 +415,7 @@ class Bone(object):
         self.tail_index=None
         self.type=0
         self.isConnect=False
+        self.ik_index=0
 
     def __eq__(self, rhs):
         return self.index==rhs.index
@@ -444,20 +459,6 @@ class BoneBuilder(object):
             if not b.parent:
                 self.__checkConnection(b, None)
 
-        print("create tail")
-        before=self.bones[:]
-        for b in before:
-            if not b.isConnect:
-                # 末端の非表示ボーン
-                if not b.tail:
-                    print(b.name)
-                    assert(False)
-                bone=Bone(b.name+'_t', b.tail, None)
-                bone.type=7
-                self.__addBone(bone)
-                bone.parent_index=b.index
-                b.tail_index=bone.index
-
         print("gather ik")
         pose = armatureObj.getPose()
         cSetting=Blender.Constraint.Settings
@@ -465,45 +466,34 @@ class BoneBuilder(object):
             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_tail=self.bones[link.tail_index]
-                    if link_tail.type==7:
-                        # replace ...先 to IK接続先
-                        link_tail.type=6
+                    link.type=6
 
                     # IK chain
-                    e=b
+                    e=b.parent
                     chainLength=c[cSetting.CHAINLEN]
                     for i in range(chainLength):
                         # IK影響下
-                        self.__boneByName(e.name).type=4
+                        chainBone=self.__boneByName(e.name)
+                        chainBone.type=4
+                        chainBone.ik_index=target.index
                         e=e.parent
-
-                    ####################
-                    # IK target
-                    ####################
-                    assert(c[cSetting.TARGET]==armatureObj)
-                    target=self.__boneByName(
-                            c[Blender.Constraint.Settings.BONE])
-                    target.type=2
-
                     self.ik_list.append(
-                            IKSolver(target, link_tail, chainLength, 
-                                c[cSetting.ITERATIONS], c.influence))
-                    # 非末端IK
-                    target_tail=self.bones[target.tail_index]
-                    if target_tail.type!=7:
-                        target_bone=armature.bones[target.name]
-                        bone=Bone(target.name+'_t', 
-                                target_bone.tail['ARMATURESPACE'][0:3],
-                                None)
-                        bone.type=7
-                        self.__addBone(bone)
-                        bone.parent_index=target.index
-                        target.tail_index=bone.index
+                            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:
@@ -535,6 +525,8 @@ class BoneBuilder(object):
                 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):
@@ -542,10 +534,17 @@ class BoneBuilder(object):
                 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.name, 
                     c.head['ARMATURESPACE'][0:3], 
@@ -589,6 +588,12 @@ class PmdExporter(object):
         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):
         ############################################################
@@ -609,12 +614,14 @@ class PmdExporter(object):
             self.__createOneSkinMesh(child)
 
     def write(self, path):
+        print('write')
         io=pmd.IO()
         io.name=self.name
         io.comment="blender export"
         io.version=1.0
 
         # 頂点
+        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()
@@ -632,6 +639,7 @@ class PmdExporter(object):
             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():
             m=Blender.Material.Get(m)
@@ -667,6 +675,7 @@ class PmdExporter(object):
                 #io.indices.append(indices[i+2])
 
         # 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_"):
@@ -679,20 +688,26 @@ class PmdExporter(object):
             v=englishmap.getUnicodeBoneName(b.name)
             assert(v)
             cp932=v[1].encode('cp932')
-            bone_name="%s\n" % cp932
+            bone_name="%s" % cp932
             assert(len(bone_name)<20)
             bone.name=bone_name
 
-            bone_english_name="%s\n" % b.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
@@ -700,14 +715,16 @@ class PmdExporter(object):
                     bone.tail_index=b.tail_index
             else:
                 bone.tail_index=0
-            # ToDo
-            bone.ik_index=0xFFFF
+
+            bone.ik_index=b.ik_index
+
             # 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]
+            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)
@@ -721,38 +738,62 @@ class PmdExporter(object):
             solver.weight=ik.weight
 
         # 表情
+        print('shape keys')
         for i, m in enumerate(self.oneSkinMesh.morphList):
             # morph
             morph=io.addMorph()
 
-            unicode=englishmap.getUnicodeSkinName(m.name)
-            if unicode:
-                cp932=unicode.encode('cp932')
-            else:
-                cp932=m.name
+            v=englishmap.getUnicodeSkinName(m.name)
+            assert(v)
+            cp932=v[1].encode('cp932')
             morph.name="%s\n" % cp932
 
             morph.english_name="%s\n" % m.name
-            morph.type=m.type
+            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)
 
-            # 表情枠
-            if i>0:
+        # 表情枠
+        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)
 
         # ボーン表示枠
-        boneDisplayName=io.addBoneDisplayName()
-        boneDisplayName.name="bones\n"
-        boneDisplayName.english_name="bones\n"
-        displayIndex=1
+        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]:
-                io.addBoneDisplay(i, displayIndex)
+                continue
+            io.addBoneDisplay(i, getBoneDisplayGroup(b))
 
         # English
+        print('english')
         io.english_name="blender export"
         io.english_coment="blender export"
 
@@ -760,8 +801,60 @@ class PmdExporter(object):
             io.getToonTexture(i).name="toon%02d.bmp\n" % i
 
         # 書き込み
-        return io.write(path.encode(FS_ENCODING))
-
+        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)