--- /dev/null
+# coding: utf-8
+
+__author__= ['ousttrue']
+__url__ = ("")
+__version__= '20100508 0.1:'
+__bpydoc__= '''\
+pmd Importer
+
+This script imports a pmd into Blender for editing.
+'''
+
+import bpy
+from bpy.props import *
+import mathutils
+import sys
+import os
+import math
+from meshio import pmd, englishmap
+
+
+def to_radian(degree):
+ return math.pi * degree / 180
+
+
+def convert_coord(pos):
+ """
+ Left handed y-up to Right handed z-up
+ """
+ return (pos.x, pos.z, pos.y)
+
+
+def convert_uv(uv):
+ return (uv.x, 1.0 - uv.y)
+
+
+def get_bone_name(l, index):
+ name=englishmap.getEnglishBoneName(l.bones[index].getName())
+ return name if name else l.bones[index].getName()
+
+
+def create_texture(directory, texture_name):
+ texture=bpy.data.textures.new(texture_name)
+ texture.type='IMAGE'
+ texture=texture.recast_type()
+ texturePath="%s/%s" % (directory, texture_name)
+ print('create_texture', texturePath)
+ image=bpy.data.images.load(texturePath)
+ texture.image=image
+ texture.mipmap = True
+ texture.interpolation = True
+ texture.use_alpha = True
+ return texture
+
+
+def createMaterial():
+ """
+ create default materil
+ """
+ material = bpy.data.materials.new("Material")
+ material.diffuse_shader='TOON'
+ material.specular_shader='TOON'
+ # temporary
+ material.emit=1.0
+ return material
+
+
+def importMesh(scene, l, tex_dir):
+ """
+ @param l[in] mmd.PMDLoader
+ @param filename[in]
+ """
+
+ ############################################################
+ # shpae\e$B%-!<$G;H$o$l$k%^%F%j%"%kM%@hE*$KA0$KJB$Y$k\e(B
+ ############################################################
+ # shape\e$B%-!<$G;H$o$l$kD:E@%$%s%G%C%/%9$r=8$a$k\e(B
+ shape_key_used_vertices=set()
+ if len(l.morph_list)>0:
+ # base
+ base=None
+ for s in l.morph_list:
+ if s.type!=0:
+ continue
+ base=s
+ break
+ assert(base)
+
+ for index in base.indices:
+ shape_key_used_vertices.add(index)
+
+ # \e$B%^%F%j%"%k$K4^$^$l$kD:E@$,\e(Bshape_key\e$B$K4^$^$l$k$+H]$+!)\e(B
+ def isMaterialUsedInShape(offset, m):
+ for i in range(offset, offset+m.vertex_count):
+ if l.indices[i] in shape_key_used_vertices:
+ return True
+
+ # shape\e$B%-!<$G;H$o$l$k%^%F%j%"%k$r5-O?$9$k\e(B
+ shape_key_materials=set()
+ # \e$B3F%^%F%j%"%k$N3+;OD:E@%$%s%G%C%/%9$r5-O?$9$k\e(B
+ face_map={}
+ face_count=0
+ for i, m in enumerate(l.materials):
+ face_map[i]=face_count
+ if isMaterialUsedInShape(face_count, m):
+ shape_key_materials.add(i)
+ face_count+=m.vertex_count
+
+ # shape\e$B%-!<$G;H$o$l$k%^%F%j%"%k$rA0$KJB$Y$k%$%s%G%C%/%9%^%C%W$r:n$k\e(B
+ material_map={}
+ used_index=0
+ not_used_index=len(shape_key_materials)
+ for i, m in enumerate(l.materials):
+ if i in shape_key_materials:
+ material_map[i]=used_index
+ used_index+=1
+ else:
+ material_map[i]=not_used_index
+ not_used_index+=1
+
+ # \e$B%^%F%j%"%k\e(B16\e$B8D$4$H$KJ,3d$7$?%a%C%7%e$r:n@.$9$k\e(B
+ material_index=0
+ mesh_objects=[]
+ while material_index<len(l.materials):
+ # shape\e$B%-!<$G;H$o$l$k=g$KJB$Y$J$*$7$?%^%F%j%"%k\e(B16\e$B8DJ,$N\e(B
+ # \e$B%a%C%7%e$r:n@.$9$k\e(B
+ meshObject, used_vertices=import16MaerialAndMesh(l,
+ material_index, material_map, face_map, tex_dir)
+ scene.objects.link(meshObject)
+ scene.update()
+ mesh_objects.append(meshObject)
+
+ # enter Edit Mode
+ bpy.ops.object.select_all(action='DESELECT')
+ meshObject.selected=True
+ scene.objects.active=meshObject
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+
+ # crete shape key
+ importShape(meshObject, l)
+
+ ############################################################
+ # clean up not used vertices
+ ############################################################
+ #progress_print('clean up vertices not used')
+ vertex_map={}
+ mesh=meshObject.data
+ for i, v in enumerate(mesh.verts):
+ if i in used_vertices:
+ vertex_map[i]=len(vertex_map)
+ v.selected=False
+ else:
+ v.selected=True
+ assert(mesh.verts[i].selected)
+ bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ print("%d vertices selected" % mesh.total_vert_sel)
+ print("used %d/%d" % (len(vertex_map), len(mesh.verts)))
+ bpy.ops.mesh.delete(type='VERT')
+
+ ############################################################
+ # flip face
+ ############################################################
+ bpy.ops.mesh.select_all(action='SELECT')
+ bpy.ops.mesh.flip_normals()
+ bpy.ops.mesh.select_all(action='DESELECT')
+
+ # exit Edit Mode
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+
+ mesh.update()
+ material_index+=16
+
+ return mesh_objects
+
+
+def import16MaerialAndMesh(l,
+ material_offset, material_map, face_map, tex_dir):
+ # create mesh
+ mesh=bpy.data.meshes.new("Mesh")
+
+ # create object
+ meshObject= bpy.data.objects.new("Mesh", mesh)
+ meshObject.layers[0]=True
+
+ ############################################################
+ # material
+ ############################################################
+ #progress_print('create materials')
+ mesh_material_map={}
+ materials=[]
+ textureMap={}
+ imageMap={}
+ index=0
+ for i in range(material_offset, material_offset+16):
+ try:
+ material_index=material_map[i]
+ m=l.materials[material_index]
+ mesh_material_map[material_index]=index
+ except KeyError:
+ break
+
+ material=createMaterial()
+ material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
+ material.alpha=m.diffuse.a
+ material.specular_hardness=int(m.shinness)
+ material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
+ material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
+ texture_name=m.getTexture()
+ if texture_name!='':
+ if texture_name in textureMap:
+ texture=textureMap[texture_name]
+ else:
+ texture=create_texture(tex_dir, texture_name)
+ textureMap[texture_name]=texture
+ imageMap[material_index]=texture.image
+ #material.add_texture(texture, "UV", {"COLOR", "ALPHA"})
+ material.add_texture(texture, "UV", "COLOR")
+
+ materials.append(material)
+ mesh.add_material(material)
+ # lookup table for assign
+ index+=1
+
+ ############################################################
+ # vertex
+ ############################################################
+ #progress_print('create vertices')
+ # create vertices
+ vertex_groups={}
+ unpackedVertices=[]
+ for v in l.each_vertex():
+ unpackedVertices.extend(
+ convert_coord(v.pos))
+ vertex_groups[v.bone0]=True
+ vertex_groups[v.bone1]=True
+
+ ############################################################
+ # face
+ ############################################################
+ #progress_print('create faces')
+ # create faces
+ mesh_face_indices=[]
+ mesh_face_materials=[]
+ used_vertices=set()
+ for i in range(material_offset, material_offset+16):
+ try:
+ material_index=material_map[i]
+ except KeyError:
+ break
+ face_offset=face_map[material_index]
+ m=l.materials[material_index]
+ material_faces=l.indices[face_offset:face_offset+m.vertex_count]
+ for j in range(0, len(material_faces), 3):
+ i0=material_faces[j]
+ i1=material_faces[j+1]
+ i2=material_faces[j+2]
+ if i2==0:
+ mesh_face_indices.extend([i2, i0, i1, 0])
+ else:
+ mesh_face_indices.extend([i0, i1, i2, 0])
+ mesh_face_materials.append(material_index)
+ used_vertices.add(i0)
+ used_vertices.add(i1)
+ used_vertices.add(i2)
+
+ ############################################################
+ # create vertices & faces
+ ############################################################
+ mesh.add_geometry(
+ int(len(unpackedVertices)/3), 0, int(len(mesh_face_indices)/4))
+ mesh.verts.foreach_set("co", unpackedVertices)
+ mesh.faces.foreach_set("verts_raw", mesh_face_indices)
+ assert(len(l.vertices)==len(mesh.verts))
+
+ ############################################################
+ # face params
+ ############################################################
+ used_map={}
+ mesh.add_uv_texture()
+
+ for face, uv_face, material_index in zip(mesh.faces,
+ mesh.uv_textures[0].data,
+ mesh_face_materials,
+ ):
+ try:
+ index=mesh_material_map[material_index]
+ except KeyError as message:
+ print(message, mesh_material_map, m)
+ assert(False)
+ face.material_index=index
+ material=mesh.materials[index]
+ used_map[index]=True
+ if material.texture_slots[0]:
+ #texture=material.texture_slots[0].texture
+ #face.image=texture.image
+ #texture.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
+ uv=l.getUV(face.verts[0])
+ uv_face.uv1=[uv.x, 1.0-uv.y]
+
+ uv=l.getUV(face.verts[1])
+ uv_face.uv2=[uv.x, 1.0-uv.y]
+
+ uv=l.getUV(face.verts[2])
+ uv_face.uv3=[uv.x, 1.0-uv.y]
+ if face.material_index in imageMap:
+ uv_face.image=imageMap[face.material_index]
+ uv_face.tex=True
+
+ # set smooth
+ face.smooth = 1
+
+ ############################################################
+ # vertex weight
+ ############################################################
+ # create vertex group
+ for i in vertex_groups.keys():
+ meshObject.add_vertex_group(get_bone_name(l, i))
+
+ # vertex params
+ for i, v, mvert in zip(range(len(l.vertices)),
+ l.each_vertex(), mesh.verts):
+ mvert.normal=mathutils.Vector(convert_coord(v.normal))
+ #mvert.uvco=convert_uv(v.uv)
+ w1=float(v.weight0)/100.0
+ w2=1.0-w1
+
+ meshObject.add_vertex_to_group(i,
+ meshObject.vertex_groups[get_bone_name(l, v.bone0)], w1, 'ADD')
+ meshObject.add_vertex_to_group(i,
+ meshObject.vertex_groups[get_bone_name(l, v.bone1)], w2, 'ADD')
+ mesh.update()
+
+ #progress_print('%s created' % mesh.name)
+ return meshObject, used_vertices
+
+
+def build_bone(armature, b, parent=None):
+ if b.tail_index==0:
+ return
+
+ name=englishmap.getEnglishBoneName(b.getName())
+ bone = armature.edit_bones.new(name if name else b.getName())
+ if parent:
+ bone.head = mathutils.Vector(convert_coord(b.pos))
+ bone.parent=parent
+ bone.connected=True if parent.tail==bone.head else False
+ bone.tail = mathutils.Vector(convert_coord(b.tail))
+ if bone.head==bone.tail:
+ bone.tail=bone.head-mathutils.Vector((0, 1, 0))
+ elif b.__class__ is pmd.BONE_IK:
+ bone.head = mathutils.Vector(convert_coord(b.pos))
+ bone.tail = mathutils.Vector(convert_coord(b.tail))
+ else:
+ # center
+ tail=mathutils.Vector(convert_coord(b.pos))
+ bone.tail = tail
+ bone.head = tail-mathutils.Vector((0, 1, 0))
+
+ for child in b.children:
+ build_bone(armature, child, bone)
+
+
+def importArmature(scene, l):
+ # create armature
+ armature = bpy.data.armatures.new('Armature')
+ # link to object
+ armature_object=bpy.data.objects.new('Armature', armature)
+ scene.objects.link(armature_object)
+ armature_object.x_ray=True
+
+ # armature settings
+ armature.drawtype='OCTAHEDRAL'
+ armature.deform_envelope=False
+ armature.deform_vertexgroups=True
+ armature.x_axis_mirror=True
+
+ # create action
+ #act = Blender.Armature.NLA.NewAction()
+ #act.setActive(armature_object)
+
+ # select only armature object and set edit mode
+ scene.objects.active=armature_object
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+ bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+
+ # create armature
+ for b in l.bones:
+ if not b.parent:
+ build_bone(armature, b)
+
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+ bpy.ops.object.select_all(action='DESELECT')
+
+ ############################################################
+ # IK
+ ############################################################
+ pose = armature_object.pose
+ for ik in l.ik_list:
+ effector=l.bones[ik.target]
+ parent=l.bones[effector.parent_index]
+ name = englishmap.getEnglishBoneName(parent.getName())
+ p_bone = pose.bones[name]
+ if not p_bone:
+ print('not found', name)
+ continue
+ if len(ik.children) >= 16:
+ print('over MAX_CHAINLEN', ik, len(ik.children))
+ continue
+ # IK
+ ik_const = p_bone.constraints.new('IK')
+ ik_const.chain_length=len(ik.children)
+ ik_const.target=armature_object
+ ik_const.subtarget=englishmap.getEnglishBoneName(l.bones[ik.index].getName())
+ # ROT
+ rot_const = p_bone.constraints.new('LIMIT_ROTATION')
+ rot_const.influence = ik.weight
+ rot_const.owner_space = 'LOCAL'
+ rot_const.use_limit_x=True
+ rot_const.use_limit_z=True
+ rot_const.minimum_x=to_radian(ik.iterations)
+ rot_const.maximum_x=to_radian(180)
+ rot_const.minimum_z=to_radian(180 - ik.iterations)
+ rot_const.maximum_z=to_radian(0)
+
+ return armature_object
+
+
+def importShape(meshObject, l):
+ if len(l.morph_list)==0:
+ return
+
+ # base
+ base=None
+ for s in l.morph_list:
+ if s.type!=0:
+ continue
+ base=s
+ break
+ assert(base)
+
+ # create base key
+ baseblock=meshObject.add_shape_key("Basis")
+
+ # mesh
+ mesh=meshObject.data
+
+ # each skin
+ for s in l.morph_list:
+ if s.getName()==base.name:
+ # skip base
+ continue
+
+ # restore
+ #for v, base_pos in zip(mesh.verts, baseblock.data):
+ # v.co=base_pos.co
+ #mesh.update()
+
+ # name
+ name=englishmap.getEnglishSkinName(s.getName())
+ if not name:
+ name=s.getName()
+ new_shape_key=meshObject.add_shape_key(name)
+ #new_shape_key.value=1.0
+
+ # morph
+ for i, offset in zip(s.indices, s.pos_list):
+ try:
+ vertex_index=base.indices[i]
+ new_shape_key.data[vertex_index].co=[p+o for p, o in zip(
+ mesh.verts[vertex_index].co, convert_coord(offset))]
+ except IndexError as msg:
+ print(IndexError, msg)
+ print(i, len(base.indices))
+ print(vertex_index, len(mesh.verts))
+ print(base.indices[i])
+ break
+ except KeyError:
+ #print 'this mesh not has shape vertices'
+ break
+
+ # set ipo curve
+ #icu=ipo.addCurve(name)
+ #icu.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
+ #icu.append( (0.0, 0.0) )
+
+
+def load(filename, context):
+ """
+ load pmd file to context.
+ """
+ io=pmd.IO()
+ if not io.read(filename):
+ print("fail to read", filename)
+ return
+
+ scene=context.scene
+
+ # create root object
+ root=bpy.data.objects.new(os.path.basename(filename), None)
+ scene.objects.link(root)
+
+ # import mesh
+ mesh_objects=importMesh(scene, io, os.path.dirname(filename))
+ for o in mesh_objects:
+ o.parent=root
+
+ # import armature
+ armature_object=importArmature(scene, io)
+ if armature_object:
+ armature_object.parent=root
+ armature = armature_object.data
+ armature.draw_names=True
+
+ # add armature modifier
+ for o in mesh_objects:
+ mod=o.modifiers.new("Modifier", "ARMATURE")
+ mod.object = armature_object
+ mod.use_bone_envelopes=False
+ #o.makeDisplayList()
+
+ # redraw
+ scene.update()
+
+ print("finised")
+
+
+###############################################################################
+# import operator
+###############################################################################
+class IMPORT_OT_pmd(bpy.types.Operator):
+ '''Import from Metasequoia file format (.pmd)'''
+ bl_idname = "import_scene.pmd"
+ bl_label = 'Import 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 importing 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.")
+
+ def execute(self, context):
+ load(self.properties.path, context)
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ wm = context.manager
+ wm.add_fileselect(self)
+ return {'RUNNING_MODAL'}
+
+
+###############################################################################
+# register menu
+###############################################################################
+menu_func = lambda self, context: self.layout.operator(
+ IMPORT_OT_pmd.bl_idname, text="MikuMikuDance model (.pmd)")
+
+
+def register():
+ bpy.types.register(IMPORT_OT_pmd)
+ bpy.types.INFO_MT_file_import.append(menu_func)
+
+def unregister():
+ bpy.types.unregister(IMPORT_OT_pmd)
+ bpy.types.INFO_MT_file_import.remove(menu_func)
+
+
+if __name__=="__main__":
+ register()
+