OSDN Git Service

fix material.
[meshio/meshio.git] / swig / blender24 / pmd_export.py
index 5cbf3b7..d70361b 100644 (file)
@@ -29,6 +29,7 @@ else:
     INTERNAL_ENCODING=FS_ENCODING
 
 
+MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
 EXTENSION = u'.pmd'
 
 
@@ -67,11 +68,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 +99,7 @@ class VertexArray(object):
         self.weight=[]
 
         self.vertexMap={}
+        self.indexMap={}
 
     def __str__(self):
         return "<VertexArray %d vertices, %d indexArrays>" % (
@@ -101,7 +110,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 +118,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,24 +157,45 @@ 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 Morph(object):
+    __slots__=['name', 'type', 'indices', 'offsets']
+    def __init__(self, name, type):
+        self.name=name
+        self.type=type
+        self.indices=[]
+        self.offsets=[]
+
+
+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
+
+
 class OneSkinMesh(object):
-    __slots__=['armatureObj', 'vertexArray']
+    __slots__=['armatureObj', 'vertexArray', 'morphList']
     def __init__(self, root):
         self.armatureObj=None
         self.vertexArray=VertexArray()
+        self.morphList=[]
         self.__create(root)
 
     def __str__(self):
-        return "<OneSkinMesh armature:%s, %s>" % (
+        return "<OneSkinMesh armature:%s, %s, morph:%d>" % (
                 self.armatureObj.name if self.armatureObj else None, 
-                self.vertexArray)
+                self.vertexArray,
+                len(self.morphList))
 
     def __create(self, node):
         if node.o.getType()=='Mesh':
@@ -242,8 +288,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 +312,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 +329,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,26 +345,83 @@ class OneSkinMesh(object):
                         weightMap[v0.index][1]
                         )
 
+        ############################################################
+        # skin
+        ############################################################
+        # base
+        indexRelativeMap={}
+        blenderMesh=obj.getData(mesh=True)
+        if blenderMesh.key:
+            for b in blenderMesh.key.blocks:
+                if b.name=='Basis':
+                    print(b.name)
+                    morph=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:
+                            morph.indices.append(i)
+                            morph.offsets.append(pos)
+                            indexRelativeMap[i]=relativeIndex
+                            relativeIndex+=1
+                    break
+
+            assert(basis)
+
+            # shape keys
+            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 in obj.getData(mesh=True).getVertsFromGroup(
+                        MMD_SHAPE_GROUP_NAME):
+                    #offset=[d-s for d, s in zip(
+                    #    b.data[index], basis.data[index])]
+                    src=basis.data[index]
+                    dst=b.data[index]
+                    offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
+                    indices=self.vertexArray.getMappedIndices(index)
+                    for i in indices:
+                        morph.indices.append(indexRelativeMap[i])
+                        morph.offsets.append(offset)
+
+    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']
+    __slots__=['index', 'name', 'pos', 'parent_index', 'tail_index', 'type']
+    def __init__(self, name, pos):
+        self.index=-1
+        self.name=name
         self.pos=[pos.x, pos.y, pos.z]
         self.parent_index=None
         self.tail_index=None
+        self.type=0
 
     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:
@@ -319,24 +429,79 @@ class BoneBuilder(object):
             for b in armature.bones.values():
                 if not b.parent:
                     # root bone
-                    bone=Bone(b)
-                    self.addBone(bone)
-                    self.getBone(bone, b)
+                    bone=Bone(b.name, b.head['ARMATURESPACE'])
+                    self.__addBone(bone)
+                    self.__getBone(bone, b)
+
+        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 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
+
+                    # IK chain
+                    e=b
+                    chainLength=c[cSetting.CHAINLEN]
+                    for i in range(chainLength):
+                        # IK影響下
+                        self.__boneByName(e.name).type=4
+                        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))
+
+    def getIndex(self, bone):
+        for i, b in enumerate(self.bones):
+            if b==bone:
+                return i
+        assert(false)
+
+    def __boneByName(self, name):
+        return self.bones[self.boneMap[name]]
+                    
+    def __getBone(self, parent, b):
+        if not Blender.Armature.CONNECTED in b.options:
+            #print b, b.options
+            pass
 
-    def getBone(self, parent, b):
         if len(b.children)==0:
+            # 末端の非表示ボーン
+            bone=Bone(b.name+'_t', b.tail['ARMATURESPACE'])
+            bone.type=7
+            self.__addBone(bone)
+            assert(parent)
+            bone.parent_index=parent.index
+            parent.tail_index=bone.index
             return
 
         for i, c in enumerate(b.children):
-            bone=Bone(c)
-            self.addBone(bone)
+            bone=Bone(c.name, c.head['ARMATURESPACE'])
+            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
@@ -366,21 +531,9 @@ class PmdExporter(object):
         io.comment="blender export"
         io.version=1.0
 
-        # bones
+        # skeleton
         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]
 
         # 頂点
         for pos, normal, uv, b0, b1, weight in self.oneSkinMesh.vertexArray.zip():
@@ -402,7 +555,6 @@ class PmdExporter(object):
         # 面とマテリアル
         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 +562,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,6 +587,85 @@ class PmdExporter(object):
                 #io.indices.append(indices[i+1])
                 #io.indices.append(indices[i+2])
 
+        # bones
+        for b in builder.bones:
+            bone=io.addBone()
+
+            unicode=englishmap.getUnicodeBoneName(b.name)
+            if unicode:
+                cp932=unicode.encode('cp932')
+            else:
+                cp932=b.name
+            bone_name="%s\n" % cp932
+            assert(len(bone_name)<20)
+            bone.name=bone_name
+
+            bone_english_name="%s\n" % b.name
+            assert(len(bone_english_name)<20)
+            bone.english_name=bone_english_name
+
+            bone.type=b.type
+            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 0
+            # 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]
+
+        # IK
+        for ik in builder.ik_list:
+            solver=io.addIK()
+            solver.index=builder.getIndex(ik.target)
+            solver.target=builder.getIndex(ik.effector)
+            solver.length=ik.length
+            b=builder.bones[ik.effector.parent_index]
+            for i in xrange(solver.length):
+                solver.children.append(builder.getIndex(b))
+                b=builder.bones[b.parent_index]
+            solver.iterations=ik.iterations
+            solver.weight=ik.weight
+
+        # 表情
+        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
+            morph.name="%s\n" % cp932
+
+            morph.english_name="%s\n" % m.name
+            morph.type=m.type
+            for index, offset in zip(m.indices, 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.indices)
+
+            # 表情枠
+            if i>0:
+                io.face_list.append(i)
+
+        # ボーン表示枠
+        boneDisplayName=io.addBoneDisplayName()
+        boneDisplayName.name="bones\n"
+        boneDisplayName.english_name="bones\n"
+        displayIndex=1
+        for i, b in enumerate(builder.bones):
+            if b.type in [6, 7]:
+                io.addBoneDisplay(i, displayIndex)
+
+        # English
+        io.english_name="blender export model"
+        io.english_coment="blender export"
+
+        for i in range(10):
+            io.getToonTexture(i).name="toon%02d.bmp" % i
+
         # 書き込み
         return io.write(path.encode(FS_ENCODING))