OSDN Git Service

implement constraint.
[meshio/meshio.git] / swig / blender / pmd_export.py
index 9c0d9b5..641c00b 100644 (file)
@@ -7,7 +7,7 @@
  Tooltip: 'Export PMD file for MikuMikuDance.'
 """
 __author__= ["ousttrue"]
-__version__= "1.0"
+__version__= "1.2"
 __url__=()
 __bpydoc__="""
 pmd Importer
@@ -16,12 +16,32 @@ This script exports a pmd model.
 
 0.1 20100318: first implementation.
 0.2 20100519: refactoring. use C extension.
-1.0 20100530: implement, basic features.
+1.0 20100530: implement basic features.
 1.1 20100612: integrate 2.4 and 2.5.
+1.2 20100616: implement rigid body.
 """
 
 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
 BASE_SHAPE_NAME='Basis'
+RIGID_SHAPE_TYPE='rigid_shape_type'
+RIGID_PROCESS_TYPE='rigid_process_type'
+RIGID_BONE_NAME='rigid_bone_name'
+#RIGID_LOCATION='rigid_loation'
+RIGID_GROUP='ribid_group'
+RIGID_INTERSECTION_GROUP='rigid_intersection_group'
+RIGID_WEIGHT='rigid_weight'
+RIGID_LINEAR_DAMPING='rigid_linear_damping'
+RIGID_ANGULAR_DAMPING='rigid_angular_damping'
+RIGID_RESTITUTION='rigid_restitution'
+RIGID_FRICTION='rigid_friction'
+CONSTRAINT_A='const_a'
+CONSTRAINT_B='const_b'
+CONSTRAINT_POS_MIN='const_pos_min'
+CONSTRAINT_POS_MAX='const_pos_max'
+CONSTRAINT_ROT_MIN='const_rot_min'
+CONSTRAINT_ROT_MAX='const_rot_max'
+CONSTRAINT_SPRING_POS='const_spring_pos'
+CONSTRAINT_SPRING_ROT='const_spring_rot'
 
 
 ###############################################################################
@@ -78,12 +98,15 @@ class VertexKey(object):
     重複頂点の検索キー
     """
     __slots__=[
+            'obj', 'index',
             'x', 'y', 'z', # 位置
             'nx', 'ny', 'nz', # 法線
             'u', 'v', # uv
             ]
 
-    def __init__(self, x, y, z, nx, ny, nz, u, v):
+    def __init__(self, obj, index, x, y, z, nx, ny, nz, u, v):
+        self.obj=obj
+        self.index=index
         self.x=x
         self.y=y
         self.z=z
@@ -104,7 +127,8 @@ class VertexKey(object):
     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)
-        return self.x==rhs.x and self.y==rhs.y and self.z==rhs.z
+        #return self.x==rhs.x and self.y==rhs.y and self.z==rhs.z
+        return self.obj==rhs.obj and self.index==rhs.index
 
 
 class VertexArray(object):
@@ -135,11 +159,12 @@ class VertexArray(object):
                 self.vertices, self.normals, self.uvs,
                 self.b0, self.b1, self.weight)
 
-    def __getIndex(self, base_index, pos, normal, uv, b0, b1, weight0):
+    def __getIndex(self, obj, base_index, pos, normal, uv, b0, b1, weight0):
         """
         頂点属性からその頂点のインデックスを得る
         """
         key=VertexKey(
+                obj, base_index,
                 pos[0], pos[1], pos[2],
                 normal[0], normal[1], normal[2],
                 uv[0], uv[1])
@@ -153,7 +178,7 @@ class VertexArray(object):
             # 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))
+            self.uvs.append((uv[0], uv[1]))
             self.b0.append(b0)
             self.b1.append(b1)
             self.weight.append(weight0)
@@ -170,7 +195,7 @@ class VertexArray(object):
         return self.indexMap[base_index]
 
     def addTriangle(self,
-            material,
+            obj, material,
             base_index0, base_index1, base_index2,
             pos0, pos1, pos2,
             n0, n1, n2,
@@ -182,9 +207,9 @@ class VertexArray(object):
         if not material in self.indexArrays:
             self.indexArrays[material]=[]
 
-        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)
+        index0=self.__getIndex(obj, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
+        index1=self.__getIndex(obj, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
+        index2=self.__getIndex(obj, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
 
         self.indexArrays[material]+=[index0, index1, index2]
 
@@ -200,7 +225,10 @@ class Morph(object):
         self.offsets.append((index, offset))
 
     def sort(self):
-        self.offsets.sort(lambda l, r: l[0]-r[0])
+        if isBlender24():
+            self.offsets.sort(lambda l, r: l[0]-r[0])
+        else:
+            self.offsets.sort(key=lambda e: e[0])
 
     def __str__(self):
         return "<Morph %s>" % self.name
@@ -216,10 +244,17 @@ class IKSolver(object):
 
 
 class OneSkinMesh(object):
-    __slots__=['vertexArray', 'morphList']
-    def __init__(self):
+    __slots__=['obj_index', 'scene', 'vertexArray', 'morphList', 
+            'rigidbodies',
+            'constraints',
+            ]
+    def __init__(self, scene):
         self.vertexArray=VertexArray()
         self.morphList=[]
+        self.rigidbodies=[]
+        self.constraints=[]
+        self.scene=scene
+        self.obj_index=0
 
     def __str__(self):
         return "<OneSkinMesh %s, morph:%d>" % (
@@ -227,41 +262,58 @@ class OneSkinMesh(object):
                 len(self.morphList))
 
     def addMesh(self, obj):
-        if obj.restrictDisplay:
+        if bl.objectIsVisible(obj):
             # 非表示
             return
+        self.__mesh(obj)
+        self.__skin(obj)
+        self.__rigidbody(obj)
+        self.__constraint(obj)
+
+    def __mesh(self, obj):
+        if isBlender24():
+            pass
+        else:
+            if RIGID_SHAPE_TYPE in obj:
+                return
+            if CONSTRAINT_A in obj:
+                return
 
         print("export", obj.name)
-
-        ############################################################
-        # bone weight
-        ############################################################
-        mesh=obj.getData(mesh=True)
+        mesh=bl.objectGetData(obj)
         weightMap={}
         secondWeightMap={}
-        for name in mesh.getVertGroupNames():
-            for i, w in mesh.getVertsFromGroup(name, 1):
-                if w>0:
-                    if i in weightMap:
-                        if i in secondWeightMap:
-                            # 上位2つのweightを採用する
-                            if w<secondWeightMap[i]:
-                                pass
-                            elif w<weightMap[i]:
-                                # 2つ目を入れ替え
-                                secondWeightMap[i]=(name, w)
-                            else:
-                                # 1つ目を入れ替え
-                                weightMap[i]=(name, w)
+        def setWeight(i, name, w):
+            if w>0:
+                if i in weightMap:
+                    if i in secondWeightMap:
+                        # 上位2つのweightを採用する
+                        if w<secondWeightMap[i]:
+                            pass
+                        elif w<weightMap[i]:
+                            # 2つ目を入れ替え
+                            secondWeightMap[i]=(name, w)
                         else:
-                            if w>weightMap[i][1]:
-                                # 多い方をweightMapに
-                                secondWeightMap[i]=weightMap[i]
-                                weightMap[i]=(name, w)
-                            else:
-                                secondWeightMap[i]=(name, w)
+                            # 1つ目を入れ替え
+                            weightMap[i]=(name, w)
                     else:
-                        weightMap[i]=(name, w)
+                        if w>weightMap[i][1]:
+                            # 多い方をweightMapに
+                            secondWeightMap[i]=weightMap[i]
+                            weightMap[i]=(name, w)
+                        else:
+                            secondWeightMap[i]=(name, w)
+                else:
+                    weightMap[i]=(name, w)
+
+        if isBlender24():
+            for name in bl.meshVertexGroupNames(obj):
+                for i, w in mesh.getVertsFromGroup(name, 1):
+                    setWeight(i, name, w)
+        else:
+            for i, v in enumerate(mesh.verts):
+                for g in v.groups:
+                    setWeight(i, obj.vertex_groups[g.group].name, g.weight)
 
         # 合計値が1になるようにする
         for i in xrange(len(mesh.verts)):
@@ -272,137 +324,146 @@ class OneSkinMesh(object):
                 weightMap[i]=(weightMap[i][0], 1.0)
                 secondWeightMap[i]=("", 0)
             else:
-                print "no weight vertex"
+                print("no weight vertex")
                 weightMap[i]=("", 0)
                 secondWeightMap[i]=("", 0)
-                
 
-        ############################################################
-        # faces
-        # 新たにメッシュを生成する
-        ############################################################
-        mesh=Blender.Mesh.New()
-        # not applied modifiers
-        mesh.getFromObject(obj.name, 1)
-        # apply object transform
-        mesh.transform(obj.getMatrix())
-        if len(mesh.verts)==0:
+        # メッシュのコピーを生成してオブジェクトの行列を適用する
+        copyMesh, copyObj=bl.objectDuplicate(self.scene, obj)
+        if len(copyMesh.verts)==0:
             return
 
-        for face in mesh.faces:
-            faceVertexCount=len(face.v)
-            material=mesh.materials[face.mat]
+        for i, face in enumerate(copyMesh.faces):
+            faceVertexCount=bl.faceVertexCount(face)
+            material=copyMesh.materials[bl.faceMaterialIndex(face)]
+            v=[copyMesh.verts[index] for index in bl.faceVertices(face)]
+            uv=bl.meshFaceUv(copyMesh, i, face)
             if faceVertexCount==3:
-                v0=face.v[0]
-                v1=face.v[1]
-                v2=face.v[2]
                 # triangle
                 self.vertexArray.addTriangle(
-                        material.name,
-                        v0.index, v1.index, v2.index,
-                        v0.co, v1.co, v2.co,
+                        self.obj_index, material.name,
+                        v[0].index, v[1].index, v[2].index,
+                        v[0].co, v[1].co, v[2].co,
                         # 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],
-                        weightMap[v2.index][0],
-                        secondWeightMap[v0.index][0],
-                        secondWeightMap[v1.index][0],
-                        secondWeightMap[v2.index][0],
-                        weightMap[v0.index][1],
-                        weightMap[v1.index][1],
-                        weightMap[v2.index][1]
+                        bl.faceNormal(face), 
+                        bl.faceNormal(face), 
+                        bl.faceNormal(face),
+                        uv[0], uv[1], uv[2],
+                        weightMap[v[0].index][0],
+                        weightMap[v[1].index][0],
+                        weightMap[v[2].index][0],
+                        secondWeightMap[v[0].index][0],
+                        secondWeightMap[v[1].index][0],
+                        secondWeightMap[v[2].index][0],
+                        weightMap[v[0].index][1],
+                        weightMap[v[1].index][1],
+                        weightMap[v[2].index][1]
                         )
             elif faceVertexCount==4:
-                v0=face.v[0]
-                v1=face.v[1]
-                v2=face.v[2]
-                v3=face.v[3]
                 # quadrangle
                 self.vertexArray.addTriangle(
-                        material.name,
-                        v0.index, v1.index, v2.index,
-                        v0.co, v1.co, v2.co,
+                        self.obj_index, material.name,
+                        v[0].index, v[1].index, v[2].index,
+                        v[0].co, v[1].co, v[2].co,
                         #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],
-                        weightMap[v2.index][0],
-                        secondWeightMap[v0.index][0],
-                        secondWeightMap[v1.index][0],
-                        secondWeightMap[v2.index][0],
-                        weightMap[v0.index][1],
-                        weightMap[v1.index][1],
-                        weightMap[v2.index][1]
+                        bl.faceNormal(face), 
+                        bl.faceNormal(face), 
+                        bl.faceNormal(face), 
+                        uv[0], uv[1], uv[2],
+                        weightMap[v[0].index][0],
+                        weightMap[v[1].index][0],
+                        weightMap[v[2].index][0],
+                        secondWeightMap[v[0].index][0],
+                        secondWeightMap[v[1].index][0],
+                        secondWeightMap[v[2].index][0],
+                        weightMap[v[0].index][1],
+                        weightMap[v[1].index][1],
+                        weightMap[v[2].index][1]
                         )
                 self.vertexArray.addTriangle(
-                        material.name,
-                        v2.index, v3.index, v0.index,
-                        v2.co, v3.co, v0.co,
+                        self.obj_index, material.name,
+                        v[2].index, v[3].index, v[0].index,
+                        v[2].co, v[3].co, v[0].co,
                         #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],
-                        weightMap[v0.index][0],
-                        secondWeightMap[v2.index][0],
-                        secondWeightMap[v3.index][0],
-                        secondWeightMap[v0.index][0],
-                        weightMap[v2.index][1],
-                        weightMap[v3.index][1],
-                        weightMap[v0.index][1]
+                        bl.faceNormal(face), 
+                        bl.faceNormal(face), 
+                        bl.faceNormal(face), 
+                        uv[2], uv[3], uv[0],
+                        weightMap[v[2].index][0],
+                        weightMap[v[3].index][0],
+                        weightMap[v[0].index][0],
+                        secondWeightMap[v[2].index][0],
+                        secondWeightMap[v[3].index][0],
+                        secondWeightMap[v[0].index][0],
+                        weightMap[v[2].index][1],
+                        weightMap[v[3].index][1],
+                        weightMap[v[0].index][1]
                         )
+        bl.objectDelete(self.scene, copyObj)
+        self.obj_index+=1
+
+    def __skin(self, obj):
+        if not bl.objectHasShapeKey(obj):
+            return
 
-        ############################################################
-        # skin
-        ############################################################
-        # base
         indexRelativeMap={}
-        blenderMesh=obj.getData(mesh=True)
+        blenderMesh=bl.objectGetData(obj)
         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[i]=relativeIndex
-                            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))
+        # shape keys
+        vg=bl.meshVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
+
+        # base
+        used=set()
+        for b in bl.objectShapeKeys(obj):
+            if b.name==BASE_SHAPE_NAME:
+                baseMorph=self.__getOrCreateMorph('base', 0)
+                basis=b
+
+                relativeIndex=0
+                for index in vg:
+                    v=bl.shapeKeyGet(b, index)
+                    pos=[v[0], v[1], v[2]]
+                    indices=self.vertexArray.getMappedIndices(index)
+                    for i in indices:
+                        if i in used:
+                            continue
+                        used.add(i)
+
+                        baseMorph.add(i, pos)
+                        indexRelativeMap[i]=relativeIndex
+                        relativeIndex+=1
+
+                break
+        assert(basis)
+        print(basis.name, len(baseMorph.offsets))
+
+        if len(baseMorph.offsets)==0:
+            return
+
+        # shape keys
+        for b in bl.objectShapeKeys(obj):
+            if b.name==BASE_SHAPE_NAME:
+                continue
+
+            print(b.name)
+            morph=self.__getOrCreateMorph(b.name, 4)
+            used=set()
+            for index, src, dst in zip(
+                    xrange(len(blenderMesh.verts)),
+                    bl.shapeKeys(basis),
+                    bl.shapeKeys(b)):
+                offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
+                if offset[0]==0 and offset[1]==0 and offset[2]==0:
+                    continue
+                if index in vg:
+                    indices=self.vertexArray.getMappedIndices(index)
+                    for i in indices:
+                        if i in used:
+                            continue
+                        used.add(i) 
+                        morph.add(indexRelativeMap[i], offset)
 
         # sort skinmap
         original=self.morphList[:]
@@ -411,7 +472,24 @@ class OneSkinMesh(object):
                 if v[0]==morph.name:
                     return i
             print(morph)
-        self.morphList.sort(lambda l, r: getIndex(l)-getIndex(r))
+        if isBlender24():
+            self.morphList.sort(lambda l, r: getIndex(l)-getIndex(r))
+        else:
+            self.morphList.sort(key=getIndex)
+
+    def __rigidbody(self, obj):
+        if isBlender24():
+            return
+        if not RIGID_SHAPE_TYPE in obj:
+            return
+        self.rigidbodies.append(obj)
+
+    def __constraint(self, obj):
+        if isBlender24():
+            return
+        if not CONSTRAINT_A in obj:
+            return
+        self.constraints.append(obj)
 
     def __getOrCreateMorph(self, name, type):
         for m in self.morphList:
@@ -457,13 +535,13 @@ class BoneBuilder(object):
             return
 
         print("gather bones")
-        armature=armatureObj.getData()
+        armature=bl.objectGetData(armatureObj)
         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])
+                        bl.boneHeadLocal(b),
+                        bl.boneTailLocal(b))
                 self.__addBone(bone)
                 self.__getBone(bone, b)
 
@@ -471,8 +549,8 @@ class BoneBuilder(object):
             if not b.parent and b.name!='center':
                 # root bone
                 bone=Bone(b.name, 
-                        b.head['ARMATURESPACE'][0:3], 
-                        b.tail['ARMATURESPACE'][0:3])
+                        bl.boneHeadLocal(b),
+                        bl.boneTailLocal(b))
                 self.__addBone(bone)
                 self.__getBone(bone, b)
 
@@ -482,17 +560,14 @@ class BoneBuilder(object):
                 self.__checkConnection(b, None)
 
         print("gather ik")
-        pose = armatureObj.getPose()
-        cSetting=Blender.Constraint.Settings
+        pose = bl.objectGetPose(armatureObj)
         for b in pose.bones.values():
             for c in b.constraints:
-                if c.type==Blender.Constraint.Type.IKSOLVER:
+                if bl.constraintIsIKSolver(c):
                     ####################
                     # IK target
                     ####################
-                    assert(c[cSetting.TARGET]==armatureObj)
-                    target=self.__boneByName(
-                            c[Blender.Constraint.Settings.BONE])
+                    target=self.__boneByName(bl.ikTarget(c))
                     target.type=2
 
                     ####################
@@ -504,7 +579,7 @@ class BoneBuilder(object):
 
                     # IK chain
                     e=b.parent
-                    chainLength=c[cSetting.CHAINLEN]
+                    chainLength=bl.ikChainLen(c)
                     for i in range(chainLength):
                         # IK影響下
                         chainBone=self.__boneByName(e.name)
@@ -513,12 +588,12 @@ class BoneBuilder(object):
                         e=e.parent
                     self.ik_list.append(
                             IKSolver(target, link, chainLength, 
-                                int(c[cSetting.ITERATIONS] * 0.1), 
-                                c[cSetting.ROTWEIGHT]
+                                int(bl.ikItration(c) * 0.1), 
+                                bl.ikRotationWeight(c)
                                 ))
 
     def __checkConnection(self, b, p):
-        if Blender.Armature.CONNECTED in b.options:
+        if bl.boneIsConnected(b):
             parent=self.__boneByName(p.name)
             parent.isConnect=True
 
@@ -536,7 +611,11 @@ class BoneBuilder(object):
                     return i
             print(bone)
 
-        self.bones.sort(lambda l, r: getIndex(l)-getIndex(r))
+        if isBlender24():
+            self.bones.sort(lambda l, r: getIndex(l)-getIndex(r))
+        else:
+            self.bones.sort(key=getIndex)
+
         sortMap={}
         for i, b in enumerate(self.bones):
             src=original.index(b)
@@ -569,8 +648,8 @@ class BoneBuilder(object):
 
         for i, c in enumerate(b.children):
             bone=Bone(c.name, 
-                    c.head['ARMATURESPACE'][0:3], 
-                    c.tail['ARMATURESPACE'][0:3])
+                    bl.boneHeadLocal(c),
+                    bl.boneTailLocal(c))
             self.__addBone(bone)
             if parent:
                 bone.parent_index=parent.index
@@ -586,22 +665,25 @@ class BoneBuilder(object):
 
 class PmdExporter(object):
 
-    def __init__(self):
+    def setup(self, scene):
         self.armatureObj=None
+        self.scene=scene
 
-    def setup(self, scene):
         # 木構造を構築する
         object_node_map={}
         for o in scene.objects:
             object_node_map[o]=Node(o)
-        for node in object_node_map.values():
+        for o in scene.objects:
+        #for node in object_node_map.values():
+            node=object_node_map[o]
             if node.o.parent:
                 object_node_map[node.o.parent].children.append(node)
+
         # ルートを得る
         root=object_node_map[scene.objects.active]
 
         # ワンスキンメッシュを作る
-        self.oneSkinMesh=OneSkinMesh()
+        self.oneSkinMesh=OneSkinMesh(scene)
         self.__createOneSkinMesh(root)
         print(self.oneSkinMesh)
         self.name=root.o.name
@@ -615,21 +697,25 @@ class PmdExporter(object):
                 if v[0]==ik.target.name:
                     return i
             return len(englishmap.boneMap)
-        self.builder.ik_list.sort(lambda l, r: getIndex(l)-getIndex(r))
+        if isBlender24():
+            self.builder.ik_list.sort(lambda l, r: getIndex(l)-getIndex(r))
+        else:
+            self.builder.ik_list.sort(key=getIndex)
 
     def __createOneSkinMesh(self, node):
         ############################################################
         # search armature modifier
         ############################################################
         for m in node.o.modifiers:
-            if m.name=="Armature":
-                armatureObj=m[Blender.Modifier.Settings.OBJECT]
+            if bl.modifierIsArmature(m):
+                armatureObj=bl.armatureModifierGetObject(m)
                 if not self.armatureObj:
                     self.armatureObj=armatureObj
                 elif self.armatureObj!=armatureObj:
-                    print "warning! found multiple armature. ignored.", armatureObj.name
+                    print("warning! found multiple armature. ignored.", 
+                            armatureObj.name)
 
-        if node.o.getType()=='Mesh':
+        if node.o.type.upper()=='MESH':
             self.oneSkinMesh.addMesh(node.o)
 
         for child in node.children:
@@ -660,24 +746,39 @@ class PmdExporter(object):
 
         # 面とマテリアル
         vertexCount=self.oneSkinMesh.getVertexCount()
-        for m, indices in self.oneSkinMesh.vertexArray.indexArrays.items():
-            m=Blender.Material.Get(m)
+        for material_name, indices in self.oneSkinMesh.vertexArray.indexArrays.items():
+            m=bl.materialGet(self.scene, material_name)
             # マテリアル
             material=io.addMaterial()
-            material.diffuse.r=m.R
-            material.diffuse.g=m.G
-            material.diffuse.b=m.B
-            material.diffuse.a=m.alpha
-            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.mirR
-            material.ambient.g=m.mirG
-            material.ambient.b=m.mirB
+            if isBlender24():
+                material.diffuse.r=m.R
+                material.diffuse.g=m.G
+                material.diffuse.b=m.B
+                material.diffuse.a=m.alpha
+                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.mirR
+                material.ambient.g=m.mirG
+                material.ambient.b=m.mirB
+                material.flag=1 if m.enableSSS else 0
+            else:
+                material.diffuse.r=m.diffuse_color[0]
+                material.diffuse.g=m.diffuse_color[1]
+                material.diffuse.b=m.diffuse_color[2]
+                material.diffuse.a=m.alpha
+                material.sinness=0 if m.specular_hardness<1e-5 else m.specular_hardness*10
+                material.specular.r=m.specular_color[0]
+                material.specular.g=m.specular_color[1]
+                material.specular.b=m.specular_color[2]
+                material.ambient.r=m.mirror_color[0]
+                material.ambient.g=m.mirror_color[1]
+                material.ambient.b=m.mirror_color[2]
+                material.flag=1 if m.subsurface_scattering.enabled else 0
+
             material.vertex_count=len(indices)
             material.toon_index=0
-            material.flag=1 if m.enableSSS else 0
             # ToDo
             material.texture=""
             # 面
@@ -685,32 +786,25 @@ class PmdExporter(object):
                 assert(i<vertexCount)
             for i in xrange(0, len(indices), 3):
                 # reverse triangle
-                io.indices.append(indices[i+2])
-                io.indices.append(indices[i+1])
                 io.indices.append(indices[i])
-
-                #io.indices.append(indices[i])
-                #io.indices.append(indices[i+1])
-                #io.indices.append(indices[i+2])
+                io.indices.append(indices[i+1])
+                io.indices.append(indices[i+2])
 
         # 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
-
+        boneNameMap={}
+        for i, b in enumerate(self.builder.bones):
             bone=io.addBone()
 
+            # name
+            boneNameMap[b.name]=i
             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
+            assert(len(cp932)<20)
+            bone.setName(cp932)
 
-            bone_english_name="%s" % b.name
+            # english name
+            bone_english_name=b.name
             assert(len(bone_english_name)<20)
             bone.english_name=bone_english_name
 
@@ -762,9 +856,8 @@ class PmdExporter(object):
             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.setName(cp932)
+            morph.setEnglishName(m.name.encode('cp932'))
             m.type=v[2]
             morph.type=v[2]
             for index, offset in m.offsets:
@@ -790,8 +883,12 @@ class PmdExporter(object):
         # ボーン表示枠
         def createBoneDisplayName(name, english):
             boneDisplayName=io.addBoneDisplayName()
-            boneDisplayName.name=name.decode('utf-8').encode('cp932')
-            boneDisplayName.english_name=english
+            if isBlender24():
+                boneDisplayName.name=name.decode('utf-8').encode('cp932')
+                boneDisplayName.english_name=english
+            else:
+                boneDisplayName.setName(name.encode('cp932'))
+                boneDisplayName.setEnglishName(english.encode('cp932'))
         boneDisplayName=createBoneDisplayName("IK\n", "IK\n")
         boneDisplayName=createBoneDisplayName("体(上)\n", "Body[u]\n")
         boneDisplayName=createBoneDisplayName("髪\n", "Hair\n")
@@ -810,11 +907,87 @@ class PmdExporter(object):
         io.english_name="blender export"
         io.english_coment="blender export"
 
+        # toon
         for i in range(10):
             io.getToonTexture(i).name="toon%02d.bmp\n" % i
 
+        # rigid body
+        rigidNameMap={}
+        for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
+            rigidBody=io.addRigidBody()
+            rigidBody.setName(obj.name.encode('cp932'))
+            rigidNameMap[obj.name]=i
+            boneIndex=boneNameMap[obj[RIGID_BONE_NAME]]
+            if boneIndex==0:
+                boneIndex=0xFFFF
+                bone=self.builder.bones[0]
+            else:
+                bone=self.builder.bones[boneIndex]
+            rigidBody.boneIndex=boneIndex
+            #rigidBody.position.x=obj[RIGID_LOCATION][0]
+            #rigidBody.position.y=obj[RIGID_LOCATION][1]
+            #rigidBody.position.z=obj[RIGID_LOCATION][2]
+            rigidBody.position.x=obj.location.x-bone.pos[0]
+            rigidBody.position.y=obj.location.z-bone.pos[2]
+            rigidBody.position.z=obj.location.y-bone.pos[2]
+            rigidBody.rotation.x=-obj.rotation_euler[0]
+            rigidBody.rotation.y=-obj.rotation_euler[2]
+            rigidBody.rotation.z=-obj.rotation_euler[1]
+            rigidBody.processType=obj[RIGID_PROCESS_TYPE]
+            rigidBody.group=obj[RIGID_GROUP]
+            rigidBody.target=obj[RIGID_INTERSECTION_GROUP]
+            rigidBody.weight=obj[RIGID_WEIGHT]
+            rigidBody.linearDamping=obj[RIGID_LINEAR_DAMPING]
+            rigidBody.angularDamping=obj[RIGID_ANGULAR_DAMPING]
+            rigidBody.restitution=obj[RIGID_RESTITUTION]
+            rigidBody.friction=obj[RIGID_FRICTION]
+            if obj[RIGID_SHAPE_TYPE]==0:
+                rigidBody.shapeType=pmd.SHAPE_SPHERE
+                rigidBody.w=obj.scale[0]
+            elif obj[RIGID_SHAPE_TYPE]==1:
+                rigidBody.shapeType=pmd.SHAPE_CAPSULE
+                rigidBody.w=obj.scale[0]
+                rigidBody.h=obj.scale[2]
+            elif obj[RIGID_SHAPE_TYPE]==2:
+                rigidBody.shapeType=pmd.SHAPE_BOX
+                rigidBody.w=obj.scale[0]
+                rigidBody.d=obj.scale[1]
+                rigidBody.h=obj.scale[2]
+
+        # constraint
+        for obj in self.oneSkinMesh.constraints:
+            constraint=io.addConstraint()
+            constraint.setName(obj.name[1:].encode('cp932'))
+            constraint.rigidA=rigidNameMap[obj[CONSTRAINT_A]]
+            constraint.rigidB=rigidNameMap[obj[CONSTRAINT_B]]
+            constraint.pos.x=obj.location[0]
+            constraint.pos.y=obj.location[2]
+            constraint.pos.z=obj.location[1]
+            constraint.rot.x=-obj.rotation_euler[0]
+            constraint.rot.y=-obj.rotation_euler[2]
+            constraint.rot.z=-obj.rotation_euler[1]
+            constraint.constraintPosMin.x=obj[CONSTRAINT_POS_MIN][0]
+            constraint.constraintPosMin.y=obj[CONSTRAINT_POS_MIN][1]
+            constraint.constraintPosMin.z=obj[CONSTRAINT_POS_MIN][2]
+            constraint.constraintPosMax.x=obj[CONSTRAINT_POS_MAX][0]
+            constraint.constraintPosMax.y=obj[CONSTRAINT_POS_MAX][1]
+            constraint.constraintPosMax.z=obj[CONSTRAINT_POS_MAX][2]
+            constraint.constraintRotMin.x=obj[CONSTRAINT_ROT_MIN][0]
+            constraint.constraintRotMin.y=obj[CONSTRAINT_ROT_MIN][1]
+            constraint.constraintRotMin.z=obj[CONSTRAINT_ROT_MIN][2]
+            constraint.constraintRotMax.x=obj[CONSTRAINT_ROT_MAX][0]
+            constraint.constraintRotMax.y=obj[CONSTRAINT_ROT_MAX][1]
+            constraint.constraintRotMax.z=obj[CONSTRAINT_ROT_MAX][2]
+            constraint.springPos.x=obj[CONSTRAINT_SPRING_POS][0]
+            constraint.springPos.y=obj[CONSTRAINT_SPRING_POS][1]
+            constraint.springPos.z=obj[CONSTRAINT_SPRING_POS][2]
+            constraint.springRot.x=obj[CONSTRAINT_SPRING_ROT][0]
+            constraint.springRot.y=obj[CONSTRAINT_SPRING_ROT][1]
+            constraint.springRot.z=obj[CONSTRAINT_SPRING_ROT][2]
+
         # 書き込み
-        return io.write(path.encode(bl.FS_ENCODING))
+        print('write', path)
+        return io.write(path)
 
 
 def getBoneDisplayGroup(bone):
@@ -871,13 +1044,17 @@ def getBoneDisplayGroup(bone):
 
 
 def __execute(filename, scene):
+    if not scene.objects.active:
+        print("abort. no active object.")
+        return
+
     exporter=PmdExporter()
     exporter.setup(scene)
     exporter.write(filename)
 
 
 if isBlender24():
-    # 24
+    # for 2.4
     def execute_24(filename):
         filename=filename.decode(bl.INTERNAL_ENCODING)
         print("pmd exporter: %s" % filename)
@@ -888,7 +1065,7 @@ if isBlender24():
         scene = bpy.data.scenes.active
         __execute(filename, scene)
 
-        print 'finished in %.2f seconds' % (Blender.sys.time()-t) 
+        print('finished in %.2f seconds' % (Blender.sys.time()-t))
         Blender.Redraw()
         Blender.Window.WaitCursor(0) 
 
@@ -898,6 +1075,65 @@ if isBlender24():
              Blender.sys.makename(ext='.pmd'))
 
 else:
-    # 25
-    pass
+    # for 2.5
+    def execute_25(*args):
+        __execute(*args)
+
+    # operator
+    class EXPORT_OT_pmd(bpy.types.Operator):
+        '''Save a Metasequoia PMD file.'''
+        bl_idname = "export_scene.pmd"
+        bl_label = 'Export PMD'
+
+        # List of operator properties, the attributes will be assigned
+        # to the class instance from the operator settings before calling.
+
+        path = StringProperty(
+                name="File Path",
+                description="File path used for exporting the PMD file",
+                maxlen= 1024,
+                default= ""
+                )
+        filename = StringProperty(
+                name="File Name", 
+                description="Name of the file.")
+        directory = StringProperty(
+                name="Directory", 
+                description="Directory of the file.")
+
+        check_existing = BoolProperty(
+                name="Check Existing",
+                description="Check and warn on overwriting existing files",
+                default=True,
+                options=set('HIDDEN'))
+
+        def execute(self, context):
+            execute_25(
+                    self.properties.path, 
+                    context.scene
+                    )
+            return 'FINISHED'
+
+        def invoke(self, context, event):
+            wm=context.manager
+            wm.add_fileselect(self)
+            return 'RUNNING_MODAL'
+
+    # register menu
+    def menu_func(self, context): 
+        #default_path=bpy.data.filename.replace(".blend", ".pmd")
+        self.layout.operator(
+                EXPORT_OT_pmd.bl_idname, 
+                text="Miku Miku Dance Model(.pmd)")#.path=default_path
+
+    def register():
+        bpy.types.register(EXPORT_OT_pmd)
+        bpy.types.INFO_MT_file_export.append(menu_func)
+
+    def unregister():
+        bpy.types.unregister(EXPORT_OT_pmd)
+        bpy.types.INFO_MT_file_export.remove(menu_func)
+
+    if __name__ == "__main__":
+        register()