OSDN Git Service

export toon textures.
[meshio/meshio.git] / swig / blender / pmd_import.py
index f96d0bf..f2d32e0 100755 (executable)
@@ -7,27 +7,30 @@
  Tooltip: 'Import PMD file for MikuMikuDance.'
 """
 __author__= ["ousttrue"]
-__version__= "1.2"
+__version__= "1.8"
 __url__=()
 __bpydoc__="""
 pmd Importer
 
 This script imports a pmd into Blender for editing.
 
-0.1: 20091126: first implement.
-0.2: 20091209: implement IK.
-0.3: 20091210: implement morph target.
-0.4: 20100305: use english name.
-0.5: 20100408: cleanup not used vertices.
-0.6: 20100416: fix fornt face. texture load fail safe. add progress.
-0.7: 20100506: C extension.
-0.8: 20100521: add shape_key group.
-1.0: 20100530: add invisilbe bone tail(armature layer 2).
-1.1: 20100608: integrate 2.4 and 2.5.
-1.2: 20100616: implement rigid body.
-1.3: 20100619: fix for various models.
-1.4: 20100623: fix constraint name.
-1.5: 20100626: refactoring.
+0.1 20091126: first implement.
+0.2 20091209: implement IK.
+0.3 20091210: implement morph target.
+0.4 20100305: use english name.
+0.5 20100408: cleanup not used vertices.
+0.6 20100416: fix fornt face. texture load fail safe. add progress.
+0.7 20100506: C extension.
+0.8 20100521: add shape_key group.
+1.0 20100530: add invisilbe bone tail(armature layer 2).
+1.1 20100608: integrate 2.4 and 2.5.
+1.2 20100616: implement rigid body.
+1.3 20100619: fix for various models.
+1.4 20100623: fix constraint name.
+1.5 20100626: refactoring.
+1.6 20100629: sphere map.
+1.7 20100703: implement bone group.
+1.8 20100710: implement toon texture.
 """
 
 MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
@@ -52,6 +55,7 @@ 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'
 
 
 ###############################################################################
@@ -78,18 +82,12 @@ if isBlender24():
 
     def createPmdMaterial(m, index):
         material=Blender.Material.New()
-        #material.setRef(1)
-        #material.diffuseSize = 3.14/2
-        #material.setDiffuseSmooth(0)
-        #material.setSpecSize(0)
-        #material.setSpec(0)
-        # shader
+        # fresnelが無いw
         material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
-        material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
-        # diffuse
         material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
         material.setAlpha(m.diffuse.a)
         # specular
+        material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
         material.setSpec(m.shinness*0.1)
         material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
         # ambient
@@ -112,6 +110,15 @@ if isBlender24():
         elif n.startswith("ankle_"):
             b.lockYRot=True
 
+    def setSphereMap(material, index, blend_type='MULTIPLY'):
+        slot=material.textures[index]
+        slot.mapto=Blender.Texture.MapTo.NOR
+        slot.mapping=Blender.Texture.Mappings.SPHERE
+        if blend_type=='MULTIPLY':
+            slot.blendmode=Blender.Texture.BlendModes.MULTIPLY
+        elif blend_type=='ADD':
+            slot.blendmode=Blender.Texture.BlendModes.ADD
+
 else:
     # for 2.5
     import bpy
@@ -125,21 +132,22 @@ else:
 
     def createPmdMaterial(m, index):
         material = bpy.data.materials.new("Material")
-        # set shader
-        material.diffuse_shader='FRESNEL'
-        material.specular_shader='TOON'
         # diffuse
+        material.diffuse_shader='FRESNEL'
         material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
         material.alpha=m.diffuse.a
         # specular
-        material.specular_hardness=int(m.shinness)
+        material.specular_shader='TOON'
         material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
+        material.specular_toon_size=int(m.shinness)
         # ambient
         material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
         # flag
         material.subsurface_scattering.enabled=True if m.flag==1 else False
-        # name
+        # other
         material.name="m_%02d" % index
+        material.preview_render_type='FLAT'
+        material.transparency=True
         return material
 
     def poseBoneLimit(n, b):
@@ -156,11 +164,18 @@ else:
             #b.ik_dof_y=False
             pass
 
+    def setSphereMap(material, index, blend_type='MULTIPLY'):
+        slot=material.texture_slots[index]
+        slot.texture_coordinates='NORMAL'
+        slot.mapping='SPHERE'
+        slot.blend_type=blend_type
+
 
 ###############################################################################
 def VtoV(v):
     return bl.createVector(v.x, v.y, v.z)
 
+
 def convert_coord(pos):
     """
     Left handed y-up to Right handed z-up
@@ -168,10 +183,6 @@ def convert_coord(pos):
     return (pos.x, pos.z, pos.y)
 
 
-def convert_uv(uv):
-    return (uv.x, 1.0 - uv.y)
-
-
 def to_radian(degree):
     return math.pi * degree / 180
 
@@ -188,6 +199,26 @@ def get_bone_name(l, index):
     print('invalid bone index', index)
     return l.bones[0].getName()
 
+
+def get_group_name(g):
+    group_name=englishmap.getEnglishBoneGroupName(g.getName().strip())
+    if not group_name:
+        group_name=g.getName().strip()
+    return group_name
+
+
+def __importToonTextures(io, tex_dir):
+    mesh, meshObject=bl.mesh.create(TOON_TEXTURE_OBJECT)
+    material=bl.material.create(TOON_TEXTURE_OBJECT)
+    bl.mesh.addMaterial(mesh, material)
+    for i in range(10):
+        t=io.getToonTexture(i)
+        path=os.path.join(tex_dir, t.getName())
+        texture, image=bl.texture.create(path)
+        bl.material.addTexture(material, texture, False)
+    return meshObject, material
+
+
 def __importShape(obj, l, vertex_map):
     if len(l.morph_list)==0:
         return
@@ -310,7 +341,8 @@ def __build(armature, b, p, parent):
             else:
                 print('diffurence with parent.tail and head', name)
 
-        bl.bone.setConnected(bone)
+        if b.type!=9:
+            bl.bone.setConnected(bone)
         # armature layer 2
         bl.bone.setLayerMask(bone, [0, 1])
     else:
@@ -329,15 +361,16 @@ def __build(armature, b, p, parent):
         __build(armature, c, b, bone)
 
 
-def __importArmature(scene, l):
-    # build bone
+def __importArmature(l):
     armature, armature_object=bl.armature.create()
+
+    # build bone
     bl.armature.makeEditable(armature_object)
     for b in l.bones:
         if not b.parent:
             __build(armature, b, None, None)
     bl.armature.update(armature)
-    bl.exitEditMode()
+    bl.enterObjectMode()
 
     # IK constraint
     pose = bl.object.getPose(armature_object)
@@ -363,13 +396,37 @@ def __importArmature(scene, l):
 
     bl.armature.makeEditable(armature_object)
     bl.armature.update(armature)
-    bl.exitEditMode()
+    bl.enterObjectMode()
+
+    if isBlender24():
+        pass
+    else:
+        # create bone group
+        for i, g in enumerate(l.bone_group_list):
+            name=get_group_name(g)
+            bl.object.createBoneGroup(armature_object, name, "THEME%02d" % (i+1))
+
+        # assign bone to group
+        for b_index, g_index in l.bone_display_list:
+            # bone
+            b=l.bones[b_index]
+            bone_name=englishmap.getEnglishBoneName(b.getName())
+            if not bone_name:
+                bone_name=b.getName()
+            # group
+            g=l.bone_group_list[g_index-1]
+            group_name=get_group_name(g)
+
+            # assign
+            pose.bones[bone_name].bone_group=pose.bone_groups[group_name]
+
+        bl.enterObjectMode()
 
     return armature_object
         
 
 def __import16MaerialAndMesh(meshObject, l, 
-        material_order, face_map, tex_dir):
+        material_order, face_map, tex_dir, toon_material):
 
     mesh=bl.object.getData(meshObject)
     ############################################################
@@ -389,20 +446,32 @@ def __import16MaerialAndMesh(meshObject, l,
             break
 
         material=createPmdMaterial(m, material_index)
+        toon_index=bl.material.addTexture(
+                material, 
+                bl.material.getTexture(
+                    toon_material, 
+                    0 if m.toon_index==0xFF else m.toon_index
+                    ),
+                False)
 
         texture_name=m.getTexture()
         if texture_name!='':
-            if texture_name in textureMap:
-                texture=textureMap[texture_name]
-            else:
-                try:
-                    texture, image=bl.texture.create(
-                            os.path.join(tex_dir, texture_name))
+            for i, t in enumerate(texture_name.split('*')):
+                if t in textureMap:
+                    texture=textureMap[t]
+                else:
+                    path=os.path.join(tex_dir, t)
+                    texture, image=bl.texture.create(path)
                     textureMap[texture_name]=texture
                     imageMap[material_index]=image
-                except:
-                    continue
-            bl.material.addTexture(material, texture)
+                texture_index=bl.material.addTexture(material, texture)
+                if t.endswith('sph'):
+                    # sphere map
+                    setSphereMap(material, texture_index)
+                elif t.endswith('spa'):
+                    # sphere map
+                    setSphereMap(material, texture_index, 'ADD')
+
         bl.mesh.addMaterial(mesh, material)
         index+=1
 
@@ -524,7 +593,7 @@ def __import16MaerialAndMesh(meshObject, l,
     return vertex_map
 
 
-def __importMesh(scene, io, tex_dir):
+def __importMaterialAndMesh(io, tex_dir, toon_material):
     """
     @param l[in] mmd.PMDLoader
     @param filename[in]
@@ -553,32 +622,34 @@ def __importMesh(scene, io, tex_dir):
             if io.indices[i] in shape_key_used_vertices:
                 return True
 
-    # shapeキーで使われるマテリアルを記録する
-    shape_key_materials=set()
+    material_with_shape=set()
+
     # 各マテリアルの開始頂点インデックスを記録する
     face_map={}
     face_count=0
     for i, m in enumerate(io.materials):
         face_map[i]=face_count
         if isMaterialUsedInShape(face_count, m):
-            shape_key_materials.add(i)
+            material_with_shape.add(i)
         face_count+=m.vertex_count
 
-    # list化
-    material_order=list(shape_key_materials)
+    # shapeキーで使われる頂点のあるマテリアル
+    material_with_shape=list(material_with_shape)
+    material_with_shape.sort()
 
-    # shapeキーに使われていないマテリアルを後ろに追加
+    # shapeキーに使われていないマテリアル
+    material_without_shape=[]
     for i in range(len(io.materials)):
-        if not i in material_order:
-            material_order.append(i)
+        if not i in material_with_shape:
+            material_without_shape.append(i)
 
-    # ã\83\9eã\83\86ã\83ªã\82¢ã\83«16å\80\8bã\81\94ã\81¨ã\81«å\88\86å\89²ã\81\97ã\81\9fã\83¡ã\83\83ã\82·ã\83¥ã\82\92ä½\9cæ\88\90ã\81\99ã\82\8b
-    material_offset=0
-    mesh_objects=[]
-    while material_offset<len(io.materials):
-        mesh, meshObject=bl.mesh.create('mesh')
-        # create object
-        mesh_objects.append(meshObject)
+    # ã\83¡ã\83\83ã\82·ã\83¥ã\81®ç\94\9fæ\88\90
+    def __splitList(l, length):
+        for i in range(0, len(l), length):
+            yield l[i:i+length]
+
+    def __importMeshAndShape(material16, name):
+        mesh, meshObject=bl.mesh.create(name)
 
         # activate object
         bl.object.deselectAll()
@@ -587,20 +658,24 @@ def __importMesh(scene, io, tex_dir):
         # shapeキーで使われる順に並べなおしたマテリアル16個分の
         # メッシュを作成する
         vertex_map=__import16MaerialAndMesh(
-                meshObject, io, 
-                material_order[material_offset:material_offset+16], 
-                face_map, tex_dir)
+                meshObject, io, material16, face_map, tex_dir, toon_material)
 
         # crete shape key
         __importShape(meshObject, io, vertex_map)
 
         mesh.update()
-        material_offset+=16
-
+        return meshObject
+
+    mesh_objects=[__importMeshAndShape(material16, 'with_shape')
+        for material16 in __splitList(material_with_shape, 16)]
+    
+    mesh_objects+=[__importMeshAndShape(material16, 'mesh')
+        for material16 in __splitList(material_without_shape, 16)]
     return mesh_objects
 
 
-def __importConstraints(scene, io):
+def __importConstraints(io):
     if isBlender24():
         return
     print("create constraint")
@@ -622,7 +697,7 @@ def __importConstraints(scene, io):
                 location=(c.pos.x, c.pos.z, c.pos.y),
                 layer=layer
                 )
-        meshObject=scene.objects.active
+        meshObject=bl.object.getActive()
         constraintMeshes.append(meshObject)
         mesh=bl.object.getData(meshObject)
         bl.mesh.addMaterial(mesh, material)
@@ -649,7 +724,7 @@ def __importConstraints(scene, io):
     return container
 
 
-def __importRigidBodies(scene, io):
+def __importRigidBodies(io):
     if isBlender24():
         return
     print("create rigid bodies")
@@ -695,7 +770,7 @@ def __importRigidBodies(scene, io):
         else:
             assert(False)
 
-        meshObject=scene.objects.active
+        meshObject=bl.object.getActive()
         mesh=bl.object.getData(meshObject)
         rigidMeshes.append(meshObject)
         bl.mesh.addMaterial(mesh, material)
@@ -729,7 +804,7 @@ def __importRigidBodies(scene, io):
     return container
 
 
-def __execute(filename, scene):
+def _execute(filename):
     """
     load pmd file to context.
     """
@@ -749,13 +824,18 @@ def __execute(filename, scene):
         model_name=io.getName()
     root=bl.object.createEmpty(model_name)
 
+    # toon textures
+    tex_dir=os.path.dirname(filename)
+    toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
+    bl.object.makeParent(root, toonTextures)
+
     # import mesh
-    mesh_objects=__importMesh(scene, io, os.path.dirname(filename))
+    mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
     for o in mesh_objects:
         bl.object.makeParent(root, o)
 
     # import armature
-    armature_object=__importArmature(scene, io)
+    armature_object=__importArmature(io)
     if armature_object:
         bl.object.makeParent(root, armature_object)
         armature = bl.object.getData(armature_object) 
@@ -769,12 +849,12 @@ def __execute(filename, scene):
             poseBoneLimit(n, b)
 
     # import rigid bodies
-    rigidBodies=__importRigidBodies(scene, io)
+    rigidBodies=__importRigidBodies(io)
     if rigidBodies:
         bl.object.makeParent(root, rigidBodies)
 
     # import constraints
-    constraints=__importConstraints(scene, io)
+    constraints=__importConstraints(io)
     if constraints:
         bl.object.makeParent(root, constraints)
 
@@ -784,11 +864,8 @@ def __execute(filename, scene):
 if isBlender24():
     # for 2.4
     def execute_24(filename):
-        scene=bpy.data.scenes.active
-        bl.initialize('pmd_import', scene)
-        __execute(
-                filename.decode(bl.INTERNAL_ENCODING), 
-                scene)
+        bl.initialize('pmd_import', bpy.data.scenes.active)
+        _execute(filename.decode(bl.INTERNAL_ENCODING))
         bl.finalize()
 
     Blender.Window.FileSelector(
@@ -797,12 +874,6 @@ if isBlender24():
             Blender.sys.makename(ext='.pmd'))
 
 else:
-    # for 2.5
-    def execute_25(filename, scene):
-        bl.initialize('pmd_import', scene)
-        __execute(filename, scene)
-        bl.finalize()
-
     # import operator
     class IMPORT_OT_pmd(bpy.types.Operator):
         bl_idname = "import_scene.pmd"
@@ -823,7 +894,9 @@ else:
                 description="Directory of the file.")
 
         def execute(self, context):
-            execute_25(self.properties.path, context.scene)
+            bl.initialize('pmd_import', context.scene)
+            _execute(self.properties.path)
+            bl.finalize()
             return 'FINISHED'
 
         def invoke(self, context, event):