+++ /dev/null
-#!BPY
-# coding:utf-8
-"""
- Name: 'MikuMikuDance model (.pmd)...'
- Blender: 248
- Group: 'Import'
- Tooltip: 'Import PMD file for MikuMikuDance.'
-"""
-__author__= ["ousttrue"]
-__version__= "0.7"
-__url__=()
-__bpydoc__="""
-0.1: 20091126
-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.
-"""
-import Blender
-from Blender import Mathutils
-import bpy
-
-import os
-import sys
-
-# extension
-from meshio import pmd, englishmap
-
-
-if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
- FS_ENCODING='utf-8'
-else:
- FS_ENCODING=sys.getfilesystemencoding()
-print FS_ENCODING
-
-
-def cp932_to_utf8(cp932):
- return str(cp932).decode('cp932').encode('utf-8')
-
-
-class ProgressBar(object):
- def __init__(self, base):
- print "#### %s ####" % base
- self.base=base
- self.start=Blender.sys.time()
- self.set('<start>', 0)
-
- def advance(self, message, progress):
- self.progress+=float(progress)
- self._print(message)
-
- def set(self, message, progress):
- self.progress=float(progress)
- self._print(message)
-
- def _print(self, message):
- print message
- message="%s: %s" % (self.base, message)
- if message.__class__ is unicode:
- message=message.encode('utf-8')
- Blender.Window.DrawProgressBar(self.progress, message)
-
- def finish(self):
- self.progress=1.0
- message='finished in %.2f sec' % (Blender.sys.time()-self.start)
- self.set(message, 1.0)
-
-def progress_start(base):
- global progressBar
- progressBar=ProgressBar(base)
-
-def progress_finish():
- global progressBar
- progressBar.finish()
-
-def progress_print(message, progress=0.05):
- global progressBar
- progressBar.advance(message, progress)
-
-def progress_set(message, progress):
- global progressBar
- progressBar.set(message, progress)
-
-
-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].name.decode('cp932'))
- return name if name else cp932_to_utf8(l.bones[index].name)
-
-
-def createMaterial():
- """
- create default materil
- """
- material=Blender.Material.New()
- material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
- material.setRef(1)
- material.diffuseSize = 3.14/2
- material.setDiffuseSmooth(0)
- material.setSpecShader(Blender.Material.Shaders.SPEC_TOON)
- material.setSpecSize(0)
- material.setSpec(0)
- return material
-
-
-def importMesh(scene, l, tex_dir):
- """
- @param l[in] mmd.PMDLoader
- @param filename[in]
- """
-
- ############################################################
- # shpaeキーで使われるマテリアル優先的に前に並べる
- ############################################################
- # shapeキーで使われる頂点インデックスを集める
- 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)
-
- # マテリアルに含まれる頂点がshape_keyに含まれるか否か?
- def isMaterialUsedInShape(offset, m):
- for i in xrange(offset, offset+m.vertex_count):
- if l.indices[i] in shape_key_used_vertices:
- return True
-
- # shapeキーで使われるマテリアルを記録する
- shape_key_materials=set()
- # 各マテリアルの開始頂点インデックスを記録する
- 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キーで使われるマテリアルを前に並べるインデックスマップを作る
- 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
-
- # マテリアル16個ごとに分割したメッシュを作成する
- material_index=0
- mesh_objects=[]
- while material_index<len(l.materials):
- # create mesh
- mesh = Blender.Mesh.New()
- mesh.vertexUV = 1
- # create object
- obj = scene.objects.new(mesh)
- obj.layers = [1]
- mesh_objects.append(obj)
-
- # shapeキーで使われる順に並べなおしたマテリアル16個分の
- # メッシュを作成する
- vertex_map=import16MaerialAndMesh(
- mesh, l,
- material_index, material_map, face_map, tex_dir)
-
- # crete shape key
- importShape(mesh, l, vertex_map)
-
- mesh.update()
- material_index+=16
-
- return mesh_objects
-
-
-def import16MaerialAndMesh(mesh, l,
- material_offset, material_map, face_map, tex_dir):
- ############################################################
- # material
- ############################################################
- progress_print('create materials')
- mesh_material_map={}
- materials=[]
- index=0
- for i in xrange(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.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b])
- material.setAlpha(m.diffuse.a)
- material.setHardness(int(m.shinness))
- material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
- material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
- # set texture
- if m.texture!='':
- if not tex_dir.endswith("\\") and not tex_dir.endswith("/"):
- tex_dir+="/"
- tex_path = tex_dir+m.texture
- tex = Blender.Texture.New()
- tex.setType("Image")
- try:
- tex.image = Blender.Image.Load(cp932_to_utf8(tex_path))
- material.setTexture(0, tex)
- material.getTextures()[0].texco = Blender.Texture.TexCo.UV
- except IOError:
- print material.name, "fail to load", tex_path
- materials.append(material)
- # lookup table for assign
- index+=1
- mesh.materials=materials
-
- ############################################################
- # vertex
- ############################################################
- progress_print('create vertices')
- # create vertices
- vertex_groups={}
- vertices=[]
- for v in l.each_vertex():
- vertices.append(convert_coord(v.pos))
- vertex_groups[v.bone0]=True
- vertex_groups[v.bone1]=True
- mesh.verts.extend(vertices)
-
- # create vertex group
- for i in vertex_groups.keys():
- mesh.addVertGroup(get_bone_name(l, i))
-
- # vertex params
- for i, v, mvert in zip(xrange(len(l.vertices)), l.each_vertex(), mesh.verts):
- mvert.no=Mathutils.Vector(*convert_coord(v.normal))
- mvert.uvco=convert_uv(v.uv)
- w1=float(v.weight0)/100.0
- w2=1.0-w1
- mesh.assignVertsToGroup(get_bone_name(l, v.bone0), [i], w1,
- Blender.Mesh.AssignModes.ADD)
- mesh.assignVertsToGroup(get_bone_name(l, v.bone1), [i], w2,
- Blender.Mesh.AssignModes.ADD)
-
- ############################################################
- # face
- ############################################################
- progress_print('create faces')
- # create faces
- mesh_face_indices=[]
- mesh_face_materials=[]
- used_vertices=set()
- for i in xrange(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 xrange(0, len(material_faces), 3):
- i0=material_faces[j]
- i1=material_faces[j+1]
- i2=material_faces[j+2]
- mesh_face_indices.append([i0, i1, i2])
- mesh_face_materials.append(material_index)
- used_vertices.add(i0)
- used_vertices.add(i1)
- used_vertices.add(i2)
- mesh.faces.extend(mesh_face_indices, ignoreDups=True)
- assert(len(mesh.faces)==len(mesh_face_indices))
-
- # face params
- used_map={}
- mesh.addUVLayer('NewUV')
- for face, material_index in zip(mesh.faces, mesh_face_materials):
- try:
- index=mesh_material_map[material_index]
- except KeyError, message:
- print message, mesh_material_map, m
- assert(False)
- face.mat=index
- material=mesh.materials[index]
- texture=material.getTextures()[0]
- used_map[index]=True
- if texture:
- face.image=texture.tex.image
- texture.tex.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
- face.uv=[face.verts[0].uvco, face.verts[1].uvco, face.verts[2].uvco]
- # set smooth
- face.smooth = 1
- # flip
- mesh.flipNormals()
-
- ############################################################
- # clean up not used vertices
- ############################################################
- progress_print('clean up vertices not used')
- remove_vertices=[]
- vertex_map={}
- for i, v in enumerate(l.each_vertex()):
- if i in used_vertices:
- vertex_map[i]=len(vertex_map)
- else:
- remove_vertices.append(i)
- mesh.verts.delete(remove_vertices)
-
- progress_print('%s created' % mesh.name)
- return vertex_map
-
-
-def build_bone(armature, b, parent=None):
- if b.tail_index==0:
- return
-
- bone = Blender.Armature.Editbone()
- name=englishmap.getEnglishBoneName(b.name.decode('cp932'))
- bone.name = name if name else cp932_to_utf8(b.name)
- armature.bones[bone.name] = bone
- if parent:
- bone.head = Mathutils.Vector(*convert_coord(b.pos))
- bone.parent=parent
- options=[]
- if parent.tail==bone.head:
- options.append(Blender.Armature.CONNECTED)
- bone.options=options
- 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 = Blender.Armature.New()
- # link to object
- armature_object = scene.objects.new(armature)
- # create action
- act = Blender.Armature.NLA.NewAction()
- act.setActive(armature_object)
- # set XRAY
- armature_object.drawMode = (
- armature_object.drawMode | Blender.Object.DrawModes.XRAY)
- # armature settings
- armature.drawType = Blender.Armature.OCTAHEDRON
- armature.envelopes = False
- armature.vertexGroups = True
- armature.mirrorEdit = True
-
- # create armature
- armature.makeEditable()
- for b in l.bones:
- if not b.parent:
- build_bone(armature, b)
- armature.update()
-
- ############################################################
- # IK
- ############################################################
- pose = armature_object.getPose()
- cSetting = Blender.Constraint.Settings
- for ik in l.ik_list:
- # IKtarget->parent(=IK).name
- target=l.bones[ik.target]
- parent=l.bones[target.parent_index]
- name = englishmap.getEnglishBoneName(parent.name.decode('cp932'))
- 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_const = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
- ik_const[cSetting.CHAINLEN] = len(ik.children)
- ik_const[cSetting.TARGET] = armature_object
- ik_const[cSetting.BONE] = englishmap.getEnglishBoneName(
- l.bones[ik.index].name.decode('cp932'))
- lrot_const = p_bone.constraints.append(Blender.Constraint.Type.LIMITROT)
- lrot_const.influence = ik.weight
- lrot_const[cSetting.OWNERSPACE] = cSetting.SPACE_LOCAL
- lrot_const[cSetting.LIMIT] = (cSetting.LIMIT_XROT | cSetting.LIMIT_ZROT)
- lrot_const[cSetting.XMIN] = ik.iterations
- lrot_const[cSetting.XMAX] = 180
- lrot_const[cSetting.ZMIN] = 180 - ik.iterations
- lrot_const[cSetting.ZMAX] = 0
-
- armature.makeEditable()
- armature.update()
-
- return armature_object
-
-
-def importShape(mesh, l, vertex_map):
- 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)
- mesh.insertKey()
- baseblock=mesh.key.blocks[-1]
- ipo=Blender.Ipo.New('Key', 'pmd')
- mesh.key.ipo=ipo
-
- # each skin
- for s in l.morph_list:
- if s.name==base.name:
- continue
- name=englishmap.getEnglishSkinName(s.name.decode('cp932'))
- if not name:
- name=cp932_to_utf8(s.name)
- for index, offset in zip(s.indices, s.pos_list):
- try:
- vertex_index=vertex_map[base.indices[index]]
- v=mesh.verts[vertex_index].co
- offset=convert_coord(offset)
- v[0]+=offset[0]
- v[1]+=offset[1]
- v[2]+=offset[2]
- except IndexError, msg:
- print IndexError, msg
- print index, len(base.indices)
- print vertex_index, len(mesh.verts)
- print base.indices[index]
- break
- except KeyError:
- #print 'this mesh not has shape vertices'
- break
-
- # set shapekey block
- mesh.update()
- mesh.insertKey()
- mesh.key.blocks[-1].name=name
-
- # set ipo curve
- icu=ipo.addCurve(name)
- icu.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
- icu.append( (0.0, 0.0) )
-
- # restore
- for mv, v in zip(mesh.verts, baseblock.getData()):
- mv.co[0] = v[0]
- mv.co[1] = v[1]
- mv.co[2] = v[2]
- mesh.update()
-
-
-def run(filename):
- """
- @param filename
- """
- filename=filename.decode(FS_ENCODING)
- tex_dir=os.path.dirname(filename)
-
- # progress
- progress_start('pmd_import')
-
- # load pmd
- progress_set('load %s' % filename, 0.0)
-
- import locale
- locale.setlocale(locale.LC_ALL, '')
-
- l=pmd.IO()
- if not l.read(filename.encode('cp932')):
- print "fail to load %s" % filename
- return
- progress_set('loaded %s' % filename, 0.1)
-
- # set object mode
- mode_edit = Blender.Window.EditMode()
- if mode_edit:
- Blender.Window.EditMode(0)
-
- scene = bpy.data.scenes.active
-
- # import objects container
- root=scene.objects.new("Empty")
- root.setName(l.english_model_name)
-
- # import mesh
- mesh_objects=importMesh(scene, l, tex_dir)
- root.makeParent(mesh_objects)
-
- # import armature
- armature_object=importArmature(scene, l)
- if armature_object:
- armature = armature_object.getData()
- armature.drawNames=True
- root.makeParent([armature_object])
-
- # add armature modifier
- for o in mesh_objects:
- mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
- mod[Blender.Modifier.Settings.OBJECT] = armature_object
- mod[Blender.Modifier.Settings.ENVELOPES] = False
- o.makeDisplayList()
-
- # redraw
- scene.update(0)
-
- # restore edit mode
- if mode_edit:
- Blender.Window.EditMode(1)
-
- progress_finish()
- Blender.Window.RedrawAll()
-
-
-if __name__=="__main__":
- Blender.Window.FileSelector(
- run,
- 'Import PMD file',
- Blender.sys.makename(ext='.pmd'))
-