OSDN Git Service

implement pmdbuilder
[meshio/pymeshio.git] / blender25-meshio / export_pmd.py
index 8fb08a5..7a40cdd 100644 (file)
@@ -7,7 +7,7 @@
  Tooltip: 'Export PMD file for MikuMikuDance.'
 """
 __author__= ["ousttrue"]
-__version__= "2.4"
+__version__= "2.5"
 __url__=()
 __bpydoc__="""
 pmd Importer
@@ -29,6 +29,7 @@ This script exports a pmd model.
 2.2 20101005: update for Blender2.54.
 2.3 20101228: update for Blender2.55.
 2.4 20110429: update for Blender2.57b.
+2.5 20110522: implement RigidBody and Constraint.
 """
 
 bl_addon_info = {
@@ -44,34 +45,6 @@ bl_addon_info = {
         'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
         }
 
-MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
-MMD_MB_NAME='mb_name'
-MMD_MB_COMMENT='mb_comment'
-MMD_COMMENT='comment'
-BASE_SHAPE_NAME='Basis'
-RIGID_NAME='rigid_name'
-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_NAME='constraint_name'
-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'
-TOON_TEXTURE_OBJECT='ToonTextures'
-
 
 ###############################################################################
 # import
@@ -81,7 +54,7 @@ import sys
 
 try:
     # C extension
-    from meshio import pmd, englishmap
+    from .meshio import pmd, englishmap
     print('use meshio C module')
 except ImportError:
     # full python
@@ -116,7 +89,7 @@ def setMaterialParams(material, m):
     # flag
     material.flag=1 if m.subsurface_scattering.use else 0
     # toon
-    material.toon_index=7
+    material.toon_index=0
 
 def toCP932(s):
     return s.encode('cp932')
@@ -303,6 +276,29 @@ class IKSolver(object):
         self.weight=weight
 
 
+class SSS(object):
+    def __init__(self):
+        self.use=1
+
+
+class DefaultMatrial(object):
+    def __init__(self):
+        self.name='default'
+        # diffuse
+        self.diffuse_color=[1, 1, 1]
+        self.alpha=1
+        # specular
+        self.specular_toon_size=0
+        self.specular_hardness=5
+        self.specular_color=[1, 1, 1]
+        # ambient
+        self.mirror_color=[1, 1, 1]
+        # flag
+        self.subsurface_scattering=SSS()
+        # texture
+        self.texture_slots=[]
+
+
 class OneSkinMesh(object):
     __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', ]
     def __init__(self):
@@ -357,7 +353,11 @@ class OneSkinMesh(object):
                 for g in v.groups:
                     setWeight(i, obj.vertex_groups[g.group].name, g.weight)
             else:
-                setWeight(i, obj.vertex_groups[0].name, 1)
+                try:
+                    setWeight(i, obj.vertex_groups[0].name, 1)
+                except:
+                    # no vertex_groups
+                    pass
 
         # 合計値が1になるようにする
         for i in xrange(len(mesh.vertices)):
@@ -374,10 +374,14 @@ class OneSkinMesh(object):
         return weightMap, secondWeightMap
 
     def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap):
+        default_material=DefaultMatrial()
         # 各面の処理
         for i, face in enumerate(mesh.faces):
             faceVertexCount=bl.face.getVertexCount(face)
-            material=mesh.materials[bl.face.getMaterialIndex(face)]
+            try:
+                material=mesh.materials[bl.face.getMaterialIndex(face)]
+            except IndexError as e:
+                material=default_material
             v=[mesh.vertices[index] for index in bl.face.getVertices(face)]
             uv=bl.mesh.getFaceUV(
                     mesh, i, face, bl.face.getVertexCount(face))
@@ -460,26 +464,37 @@ class OneSkinMesh(object):
                         )
 
     def __mesh(self, obj):
-        if RIGID_SHAPE_TYPE in obj:
+        if bl.RIGID_SHAPE_TYPE in obj:
             return
-        if CONSTRAINT_A in obj:
+        if bl.CONSTRAINT_A in obj:
             return
 
-        #if not bl.modifier.hasType(obj, 'ARMATURE'):
-        #    return
-
         bl.message("export: %s" % obj.name)
 
         # メッシュのコピーを生成してオブジェクトの行列を適用する
         copyMesh, copyObj=bl.object.duplicate(obj)
         if len(copyMesh.vertices)>0:
             # apply transform
-            copyObj.scale=obj.scale
-            bpy.ops.object.transform_apply(scale=True)
-            copyObj.rotation_euler=obj.rotation_euler
-            bpy.ops.object.transform_apply(rotation=True)
-            copyObj.location=obj.location
-            bpy.ops.object.transform_apply(location=True)
+            """
+            try:
+                # svn 36722
+                copyObj.scale=obj.scale
+                bpy.ops.object.transform_apply(scale=True)
+                copyObj.rotation_euler=obj.rotation_euler
+                bpy.ops.object.transform_apply(rotation=True)
+                copyObj.location=obj.location
+                bpy.ops.object.transform_apply(location=True)
+            except AttributeError as e:
+                # 2.57b
+                copyObj.scale=obj.scale
+                bpy.ops.object.scale_apply()
+                copyObj.rotation_euler=obj.rotation_euler
+                bpy.ops.object.rotation_apply()
+                copyObj.location=obj.location
+                bpy.ops.object.location_apply()
+            """
+            copyMesh.transform(obj.matrix_world)
+
             # apply modifier
             for m in [m for m in copyObj.modifiers]:
                 if m.type=='SOLIDFY':
@@ -507,12 +522,12 @@ class OneSkinMesh(object):
         baseMorph=None
 
         # shape keys
-        vg=bl.object.getVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
+        vg=bl.object.getVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME)
 
         # base
         used=set()
         for b in bl.object.getShapeKeys(obj):
-            if b.name==BASE_SHAPE_NAME:
+            if b.name==bl.BASE_SHAPE_NAME:
                 baseMorph=self.__getOrCreateMorph('base', 0)
                 basis=b
 
@@ -533,17 +548,17 @@ class OneSkinMesh(object):
 
                 break
         assert(basis)
-        print(basis.name, len(baseMorph.offsets))
+        #print(basis.name, len(baseMorph.offsets))
 
         if len(baseMorph.offsets)==0:
             return
 
         # shape keys
         for b in bl.object.getShapeKeys(obj):
-            if b.name==BASE_SHAPE_NAME:
+            if b.name==bl.BASE_SHAPE_NAME:
                 continue
 
-            print(b.name)
+            #print(b.name)
             morph=self.__getOrCreateMorph(b.name, 4)
             used=set()
             for index, src, dst in zip(
@@ -568,17 +583,17 @@ class OneSkinMesh(object):
             for i, v in enumerate(englishmap.skinMap):
                 if v[0]==morph.name:
                     return i
-            print(morph)
+            #print(morph)
             return len(englishmap.skinMap)
         self.morphList.sort(key=getIndex)
 
     def __rigidbody(self, obj):
-        if not RIGID_SHAPE_TYPE in obj:
+        if not bl.RIGID_SHAPE_TYPE in obj:
             return
         self.rigidbodies.append(obj)
 
     def __constraint(self, obj):
-        if not CONSTRAINT_A in obj:
+        if not bl.CONSTRAINT_A in obj:
             return
         self.constraints.append(obj)
 
@@ -785,11 +800,14 @@ class BoneBuilder(object):
         if name=='':
             return 0
         else:
-            return self.getIndex(self.__boneByName(name))
+            try:
+                return self.getIndex(self.__boneByName(name))
+            except:
+                return 0
 
     def __boneByName(self, name):
         return self.boneMap[name]
-                    
+
     def __getBone(self, parent, b):
         if len(b.children)==0:
             parent.type=7
@@ -841,9 +859,9 @@ class PmdExporter(object):
         root=object_node_map[bl.object.getActive()]
         o=root.o
         self.englishName=o.name
-        self.englishComment=o[MMD_COMMENT] if MMD_COMMENT in o else 'blender export\n'
-        self.name=o[MMD_MB_NAME] if MMD_MB_NAME in o else 'Blenderエクスポート'
-        self.comment=o[MMD_MB_COMMENT] if MMD_MB_COMMENT in o else 'Blnderエクスポート\n'
+        self.englishComment=o[bl.MMD_COMMENT] if bl.MMD_COMMENT in o else 'blender export\n'
+        self.name=o[bl.MMD_MB_NAME] if bl.MMD_MB_NAME in o else 'Blenderエクスポート'
+        self.comment=o[bl.MMD_MB_COMMENT] if bl.MMD_MB_COMMENT in o else 'Blnderエクスポート\n'
 
         # ワンスキンメッシュを作る
         self.oneSkinMesh=OneSkinMesh()
@@ -889,9 +907,10 @@ class PmdExporter(object):
             v.pos.x=pos[0]
             v.pos.y=pos[2]
             v.pos.z=pos[1]
+            # convert right-handed z-up to left-handed y-up
             v.normal.x=attribute.nx
-            v.normal.y=attribute.ny
-            v.normal.z=attribute.nz
+            v.normal.y=attribute.nz
+            v.normal.z=attribute.ny
             v.uv.x=attribute.u
             v.uv.y=1.0-attribute.v # reverse vertical
             v.bone0=self.skeleton.indexByName(b0)
@@ -903,15 +922,24 @@ class PmdExporter(object):
         vertexCount=self.oneSkinMesh.getVertexCount()
         for material_name, indices in self.oneSkinMesh.vertexArray.each():
             #print('material:', material_name)
-            m=bl.material.get(material_name)
+            try:
+                m=bl.material.get(material_name)
+            except KeyError as e:
+                m=DefaultMatrial()
             # マテリアル
             material=io.addMaterial()
             setMaterialParams(material, m)
 
             material.vertex_count=len(indices)
-            material.toon_index=0
-            textures=[os.path.basename(path) 
+            def get_texture_name(texture):
+                pos=texture.replace("\\", "/").rfind("/")
+                if pos==-1:
+                    return texture
+                else:
+                    return texture[pos+1:]
+            textures=[get_texture_name(path)
                 for path in bl.material.eachEnalbeTexturePath(m)]
+            print(textures)
             if len(textures)>0:
                 material.texture='*'.join(textures)
             else:
@@ -940,7 +968,9 @@ class PmdExporter(object):
 
             # english name
             bone_english_name=toCP932(b.name)
-            assert(len(bone_english_name)<20)
+            if len(bone_english_name)>=20:
+                print(bone_english_name)
+                #assert(len(bone_english_name)<20)
             bone.english_name=bone_english_name
 
             if len(v)>=3:
@@ -1036,7 +1066,7 @@ class PmdExporter(object):
         toonMeshObject=None
         for o in bl.object.each():
             try:
-                if o.name.startswith(TOON_TEXTURE_OBJECT):
+                if o.name.startswith(bl.TOON_TEXTURE_OBJECT):
                     toonMeshObject=o
             except:
                 p(o.name)
@@ -1047,55 +1077,52 @@ class PmdExporter(object):
             for i in range(10):
                 t=bl.material.getTexture(toonMaterial, i)
                 if t:
-                    io.toon_textures[i]=pmd.encode_string(t.name)
+                    io.toon_textures[i]="%s" % t.name
                 else:
-                    io.toon_textures[i]=pmd.encode_string("toon%02d.bmp\n" % i)
+                    io.toon_textures[i]="toon%02d.bmp" % (i+1)
         else:
             for i in range(10):
-                io.toon_textures[i]=pmd.encode_string("toon%02d.bmp\n" % i)
+                io.toon_textures[i]="toon%02d.bmp" % (i+1)
 
         # rigid body
         rigidNameMap={}
         for i, obj in enumerate(self.oneSkinMesh.rigidbodies):
-            name=obj[RIGID_NAME] if RIGID_NAME in obj else obj.name
-            #print(name)
+            name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name
+            print(name)
             rigidBody=pmd.RigidBody(name)
             rigidNameMap[name]=i
-            boneIndex=boneNameMap[obj[RIGID_BONE_NAME]]
+            boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]]
             if boneIndex==0:
                 boneIndex=0xFFFF
                 bone=self.skeleton.bones[0]
             else:
                 bone=self.skeleton.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[1]
             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.processType=obj[bl.RIGID_PROCESS_TYPE]
+            rigidBody.group=obj[bl.RIGID_GROUP]
+            rigidBody.target=obj[bl.RIGID_INTERSECTION_GROUP]
+            rigidBody.weight=obj[bl.RIGID_WEIGHT]
+            rigidBody.linearDamping=obj[bl.RIGID_LINEAR_DAMPING]
+            rigidBody.angularDamping=obj[bl.RIGID_ANGULAR_DAMPING]
+            rigidBody.restitution=obj[bl.RIGID_RESTITUTION]
+            rigidBody.friction=obj[bl.RIGID_FRICTION]
+            if obj[bl.RIGID_SHAPE_TYPE]==0:
                 rigidBody.shapeType=pmd.SHAPE_SPHERE
                 rigidBody.w=obj.scale[0]
                 rigidBody.d=0
                 rigidBody.h=0
-            elif obj[RIGID_SHAPE_TYPE]==1:
+            elif obj[bl.RIGID_SHAPE_TYPE]==1:
                 rigidBody.shapeType=pmd.SHAPE_BOX
                 rigidBody.w=obj.scale[0]
                 rigidBody.d=obj.scale[1]
                 rigidBody.h=obj.scale[2]
-            elif obj[RIGID_SHAPE_TYPE]==2:
+            elif obj[bl.RIGID_SHAPE_TYPE]==2:
                 rigidBody.shapeType=pmd.SHAPE_CAPSULE
                 rigidBody.w=obj.scale[0]
                 rigidBody.h=obj.scale[2]
@@ -1104,33 +1131,34 @@ class PmdExporter(object):
 
         # constraint
         for obj in self.oneSkinMesh.constraints:
-            constraint=pmd.Constraint(obj[CONSTRAINT_NAME])
-            constraint.rigidA=rigidNameMap[obj[CONSTRAINT_A]]
-            constraint.rigidB=rigidNameMap[obj[CONSTRAINT_B]]
+            print(obj)
+            constraint=pmd.Constraint(obj[bl.CONSTRAINT_NAME])
+            constraint.rigidA=rigidNameMap[obj[bl.CONSTRAINT_A]]
+            constraint.rigidB=rigidNameMap[obj[bl.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]
+            constraint.constraintPosMin.x=obj[bl.CONSTRAINT_POS_MIN][0]
+            constraint.constraintPosMin.y=obj[bl.CONSTRAINT_POS_MIN][1]
+            constraint.constraintPosMin.z=obj[bl.CONSTRAINT_POS_MIN][2]
+            constraint.constraintPosMax.x=obj[bl.CONSTRAINT_POS_MAX][0]
+            constraint.constraintPosMax.y=obj[bl.CONSTRAINT_POS_MAX][1]
+            constraint.constraintPosMax.z=obj[bl.CONSTRAINT_POS_MAX][2]
+            constraint.constraintRotMin.x=obj[bl.CONSTRAINT_ROT_MIN][0]
+            constraint.constraintRotMin.y=obj[bl.CONSTRAINT_ROT_MIN][1]
+            constraint.constraintRotMin.z=obj[bl.CONSTRAINT_ROT_MIN][2]
+            constraint.constraintRotMax.x=obj[bl.CONSTRAINT_ROT_MAX][0]
+            constraint.constraintRotMax.y=obj[bl.CONSTRAINT_ROT_MAX][1]
+            constraint.constraintRotMax.z=obj[bl.CONSTRAINT_ROT_MAX][2]
+            constraint.springPos.x=obj[bl.CONSTRAINT_SPRING_POS][0]
+            constraint.springPos.y=obj[bl.CONSTRAINT_SPRING_POS][1]
+            constraint.springPos.z=obj[bl.CONSTRAINT_SPRING_POS][2]
+            constraint.springRot.x=obj[bl.CONSTRAINT_SPRING_ROT][0]
+            constraint.springRot.y=obj[bl.CONSTRAINT_SPRING_ROT][1]
+            constraint.springRot.z=obj[bl.CONSTRAINT_SPRING_ROT][2]
             io.constraints.append(constraint)
 
         # 書き込み