from Blender import Mathutils
-# \e$B%U%!%$%k%7%9%F%`$NJ8;z%3!<%I\e(B
-# \e$B2~B$HG$H$N6&MQ$N$?$a\e(B
+# ファイルシステムの文字コード
+# 改造版との共用のため
FS_ENCODING=sys.getfilesystemencoding()
if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
INTERNAL_ENCODING='utf-8'
INTERNAL_ENCODING=FS_ENCODING
+###############################################################################
+# Writer
+###############################################################################
class Writer(object):
def __init__(self, path, encoding):
self.io=open(path, "wb")
self.io.close()
-def createEmptyObject(scene, name):
- empty=scene.objects.new("Empty")
- empty.setName(name)
- return empty
-
-
-def createMqoMaterial(m):
- material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))
- material.mode |= Blender.Material.Modes.SHADELESS
- material.rgbCol = [m.color.r, m.color.g, m.color.b]
- material.alpha = m.color.a
- material.amb = m.ambient
- material.spec = m.specular
- material.hard = int(255 * m.power)
- return material
-
-
-def createTexture(path):
- image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))
- texture = Blender.Texture.New(path.encode(INTERNAL_ENCODING))
- texture.type = Blender.Texture.Types.IMAGE
- texture.image = image
- return texture, image
-
-
-def materialAddTexture(material, texture):
- material.mode = material.mode | Blender.Material.Modes.TEXFACE
- material.setTexture(0, texture, Blender.Texture.TexCo.UV)
-
-
-def createMesh(scene, name):
- mesh = Blender.Mesh.New()
- mesh_object=scene.objects.new(mesh, name.encode(INTERNAL_ENCODING))
- return mesh, mesh_object
-
-
-def objectMakeParent(parent, child):
- parent.makeParent([child])
-
-
-def meshAddMqoGeometry(mesh, o, materials, imageMap, scale):
- # add vertices
- mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy
- mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])
- # add faces
- mesh_faces=[]
- for face in o.faces:
- face_indices=[]
- for i in xrange(face.index_count):
- face_indices.append(face.getIndex(i)+1)
- mesh_faces.append(face_indices)
- #new_faces=mesh.faces.extend([face.indices for face in o.faces],
- new_faces=mesh.faces.extend(mesh_faces,
- #ignoreDups=True,
- indexList=True)
- mesh.update()
-
- # gather used materials
- materialMap = {}
- if new_faces:
- for i in new_faces:
- if type(i) is int:
- materialMap[o.faces[i].material_index]=True
-
- # blender limits 16 materials per mesh
- # separate mesh ?
- for i, material_index in enumerate(materialMap.keys()):
- if i>=16:
- print("over 16 materials!")
- break
- mesh.materials+=[materials[material_index]]
- materialMap[material_index]=i
-
- # set face params
- for i, f in enumerate(o.faces):
- if not type(new_faces[i]) is int:
- continue
-
- face=mesh.faces[new_faces[i]]
-
- uv_array=[]
- for i in xrange(f.index_count):
- uv_array.append(Blender.Mathutils.Vector(
- f.getUV(i).x,
- 1.0-f.getUV(i).y)
- )
- try:
- face.uv=uv_array
- except Exception as msg:
- #print msg
- #print face.index, uv_array
- pass
-
- if f.material_index in materialMap:
- face.mat = materialMap[f.material_index]
-
- face.smooth = 1
-
- # rmeove dummy 0 vertex
- mesh.verts.delete(0)
-
- mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH
- mesh.maxSmoothAngle = int(o.smoothing)
- mesh.smooth()
- mesh.calcNormals()
- mesh.flipNormals()
- mesh.update()
+###############################################################################
+# ProgressBar
+###############################################################################
+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(FS_ENCODING)
+ 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)
- # mirror modifier
- if o.mirror:
- mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)
###############################################################################
# for mqo mikoto bone.
class TrianglePlane(object):
"""
- mikoto\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#\e(B
- (\e$BIT40A4\e(B)
+ mikoto方式ボーンのアンカーウェイト計算用。
+ (不完全)
"""
__slots__=['normal',
'v0', 'v1', 'v2',
class MikotoAnchor(object):
"""
- mikoto\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#\e(B
+ mikoto方式スケルトンのアンカー。
"""
__slots__=[
"triangles", "bbox",
mesh.update()
###############################################################################
+def createEmptyObject(scene, name):
+ empty=scene.objects.new("Empty")
+ empty.setName(name)
+ return empty
+
+
+def createMqoMaterial(m):
+ material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))
+ material.mode |= Blender.Material.Modes.SHADELESS
+ material.rgbCol = [m.color.r, m.color.g, m.color.b]
+ material.alpha = m.color.a
+ material.amb = m.ambient
+ material.spec = m.specular
+ material.hard = int(255 * m.power)
+ return material
+
+
+def createTexture(path):
+ image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))
+ texture = Blender.Texture.New(path.encode(INTERNAL_ENCODING))
+ texture.type = Blender.Texture.Types.IMAGE
+ texture.image = image
+ return texture, image
+
+
+def materialAddTexture(material, texture):
+ material.mode = material.mode | Blender.Material.Modes.TEXFACE
+ material.setTexture(0, texture, Blender.Texture.TexCo.UV)
+
+
+def createMesh(scene, name):
+ mesh = Blender.Mesh.New()
+ mesh_object=scene.objects.new(mesh, name.encode(INTERNAL_ENCODING))
+ return mesh, mesh_object
+
+
+def objectMakeParent(parent, child):
+ parent.makeParent([child])
+
+
+def meshAddMqoGeometry(mesh, o, materials, imageMap, scale):
+ # add vertices
+ mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy
+ mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])
+ # add faces
+ mesh_faces=[]
+ for face in o.faces:
+ face_indices=[]
+ for i in xrange(face.index_count):
+ face_indices.append(face.getIndex(i)+1)
+ mesh_faces.append(face_indices)
+ #new_faces=mesh.faces.extend([face.indices for face in o.faces],
+ new_faces=mesh.faces.extend(mesh_faces,
+ #ignoreDups=True,
+ indexList=True)
+ mesh.update()
+
+ # gather used materials
+ materialMap = {}
+ if new_faces:
+ for i in new_faces:
+ if type(i) is int:
+ materialMap[o.faces[i].material_index]=True
+
+ # blender limits 16 materials per mesh
+ # separate mesh ?
+ for i, material_index in enumerate(materialMap.keys()):
+ if i>=16:
+ print("over 16 materials!")
+ break
+ mesh.materials+=[materials[material_index]]
+ materialMap[material_index]=i
+
+ # set face params
+ for i, f in enumerate(o.faces):
+ if not type(new_faces[i]) is int:
+ continue
+
+ face=mesh.faces[new_faces[i]]
+
+ uv_array=[]
+ for i in xrange(f.index_count):
+ uv_array.append(Blender.Mathutils.Vector(
+ f.getUV(i).x,
+ 1.0-f.getUV(i).y)
+ )
+ try:
+ face.uv=uv_array
+ except Exception as msg:
+ #print msg
+ #print face.index, uv_array
+ pass
+
+ if f.material_index in materialMap:
+ face.mat = materialMap[f.material_index]
+
+ face.smooth = 1
+
+ # rmeove dummy 0 vertex
+ mesh.verts.delete(0)
+
+ mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH
+ mesh.maxSmoothAngle = int(o.smoothing)
+ mesh.smooth()
+ mesh.calcNormals()
+ mesh.flipNormals()
+ mesh.update()
+
+ # mirror modifier
+ if o.mirror:
+ mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)
+
def getTexture(m, dirname):
tex=""
aplane=""
continue
imagePath=Blender.sys.expandpath(image.getFilename())
if len(dirname)>0 and imagePath.startswith(dirname):
- # \e$BAjBP%Q%9$KJQ49$9$k\e(B
+ # 相対パスに変換する
imagePath=imagePath[len(dirname)+1:len(imagePath)]
if texture.mtCol>0:
tex=" tex(\"%s\")" % imagePath
def faceMaterialIndex(face):
return face.mat
+def objectGetData(o):
+ return o.getData(mesh=True)
+
+def objectAddArmatureModifier(o, armature_object):
+ mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
+ mod[Blender.Modifier.Settings.OBJECT] = armature_object
+ mod[Blender.Modifier.Settings.ENVELOPES] = False
+
+def objectSelect(o):
+ o.select(True)
+
+def objectGetPose(o):
+ return o.getPose()
+
+def poseBoneLimit(n, b):
+ if n.endswith("_t"):
+ return
+ if n.startswith("knee_"):
+ b.lockYRot=True
+ b.lockZRot=True
+ b.limitX=True
+ b.limitMin=[0, 0, 0]
+ b.limitMax=[180, 0, 0]
+ elif n.startswith("ankle_"):
+ b.lockYRot=True
+
--- /dev/null
+#!BPY
+# coding:utf-8
+"""
+ Name: 'MikuMikuDance model (.pmd)...'
+ Blender: 248
+ Group: 'Import'
+ Tooltip: 'Import PMD file for MikuMikuDance.'
+"""
+__author__= ["ousttrue"]
+__version__= "1.1"
+__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.
+"""
+
+MMD_SHAPE_GROUP_NAME='_MMD_SHAPE'
+
+
+###############################################################################
+# import
+###############################################################################
+import os
+import sys
+import re
+import math
+
+# C extension
+from meshio import pmd, englishmap
+
+def isBlender24():
+ return sys.version_info[0]<3
+
+if isBlender24():
+ # for 2.4
+ import Blender
+ from Blender import Mathutils
+ import bpy
+
+ # wrapper
+ import bl24 as bl
+else:
+ # for 2.5
+ import bpy
+ from bpy.props import *
+ import mathutils
+
+ # wrapper
+ import bl25 as bl
+
+
+###############################################################################
+# progress bar
+###############################################################################
+def progress_start(base):
+ global progressBar
+ progressBar=bl.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)
+
+
+if isBlender24():
+ # functions
+ 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().encode(bl.INTERNAL_ENCODING)
+
+
+ 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
+
+ # list化
+ material_order=list(shape_key_materials)
+
+ # shapeキーに使われていないマテリアルを後ろに追加
+ for i in range(len(l.materials)):
+ if not i in material_order:
+ material_order.append(i)
+
+ # マテリアル16個ごとに分割したメッシュを作成する
+ material_offset=0
+ mesh_objects=[]
+ while material_offset<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_order[material_offset:material_offset+16],
+ face_map, tex_dir)
+
+ # crete shape key
+ importShape(obj, l, vertex_map)
+
+ mesh.update()
+ material_offset+=16
+
+ return mesh_objects
+
+
+ def import16MaerialAndMesh(mesh, l, material_order, face_map, tex_dir):
+ ############################################################
+ # material
+ ############################################################
+ progress_print('create materials')
+ mesh_material_map={}
+ materials=[]
+ index=0
+ for material_index in material_order:
+ try:
+ 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.setSpec(m.shinness*0.1)
+ material.setSpecCol([m.specular.r, m.specular.g, m.specular.b])
+ material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
+ material.enableSSS=True if m.flag==1 else False
+ # set texture
+ if m.getTexture()!='':
+ tex_file=re.compile('\*.*.spa$').sub('', m.getTexture())
+ tex_path = os.path.join(tex_dir, tex_file).encode(
+ bl.INTERNAL_ENCODING)
+ tex = Blender.Texture.New()
+ tex.setType("Image")
+ try:
+ tex.image = Blender.Image.Load(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()
+
+ def degenerate(i0, i1, i2):
+ return i0==i1 or i1==i2 or i2==i0
+
+ for material_index in material_order:
+ 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]
+ triangle=[i0, i1, i2]
+ if degenerate(*triangle):
+ continue
+ mesh_face_indices.append(triangle)
+ 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)
+
+ # 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 as 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
+
+
+ class Builder(object):
+ def __init__(self):
+ self.boneMap={}
+
+ def build(self, armature, bones):
+ for b in bones:
+ if not b.parent:
+ self.__build(armature, b, None, None)
+ armature.update()
+
+ def __build(self, armature, b, p, parent):
+ name=englishmap.getEnglishBoneName(b.getName())
+ if not name:
+ name=b.getName().encode(bl.INTERNAL_ENCODING)
+ self.boneMap[name]=b
+
+ bone=Blender.Armature.Editbone()
+ bone.name=name
+ armature.bones[name]=bone
+
+ if b.tail_index==0:
+ # 先端
+ assert(b.type==6 or b.type==7)
+ bone.head = Mathutils.Vector(*convert_coord(b.pos))
+ bone.tail=bone.head+Mathutils.Vector(0, 1, 0)
+ assert(parent)
+ bone.parent=parent
+ if bone.name=="center_t":
+ # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
+ parent.tail=parent.head+Mathutils.Vector(0, 1, 0)
+ bone.head=parent.tail
+ bone.tail=bone.head+Mathutils.Vector(0, 1, 0)
+ else:
+ assert(parent.tail==bone.head)
+ bone.options=[Blender.Armature.CONNECTED]
+ # armature layer 2
+ bone.layerMask = (1<<1)
+ else:
+ bone.head = Mathutils.Vector(*convert_coord(b.pos))
+ bone.tail = Mathutils.Vector(*convert_coord(b.tail))
+ if parent:
+ bone.parent=parent
+ if parent.tail==bone.head:
+ bone.options=[Blender.Armature.CONNECTED]
+
+ if bone.head==bone.tail:
+ bone.tail=bone.head+Mathutils.Vector(0, 1, 0)
+
+ for c in b.children:
+ self.__build(armature, c, b, 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.drawNames=True
+ armature.envelopes = False
+ armature.vertexGroups = True
+ armature.mirrorEdit = True
+
+ # create armature
+ armature.makeEditable()
+
+ ############################################################
+ # build bone
+ ############################################################
+ builder=Builder()
+ builder.build(armature, l.bones)
+
+ ############################################################
+ # IK
+ ############################################################
+ pose = armature_object.getPose()
+ cSetting = Blender.Constraint.Settings
+ for ik in l.ik_list:
+ # IKtarget->parent(=IK).name
+ target=l.bones[ik.target]
+ name = englishmap.getEnglishBoneName(target.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 solver
+ ik_solver = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER)
+ ik_solver[cSetting.CHAINLEN]=len(ik.children)
+ ik_solver[cSetting.TARGET]=armature_object
+ ik_solver[cSetting.USETIP]=False
+
+ effector_name=englishmap.getEnglishBoneName(
+ l.bones[ik.index].getName())
+ if not effector_name:
+ effector_name=l.bones[ik.index].getName()
+
+ ik_solver[cSetting.BONE]=effector_name
+ #ik_solver.influence=ik.weight
+ # not used. place folder when export.
+ ik_solver[cSetting.ROTWEIGHT]=ik.weight
+ ik_solver[cSetting.ITERATIONS]=ik.iterations * 10
+
+ armature.makeEditable()
+ armature.update()
+
+ return armature_object
+
+
+ def importShape(obj, l, vertex_map):
+ if len(l.morph_list)==0:
+ return
+ obj.pinShape=True
+ mesh=obj.getData(mesh=True)
+
+ # find base
+ base=None
+ for s in l.morph_list:
+ if s.type==0:
+ base=s
+
+ # create vertex group
+ mesh.addVertGroup(MMD_SHAPE_GROUP_NAME)
+ indices=[]
+ hasShape=False
+ for i in s.indices:
+ if i in vertex_map:
+ hasShape=True
+ indices.append(vertex_map[i])
+ mesh.assignVertsToGroup(MMD_SHAPE_GROUP_NAME, indices, 0,
+ Blender.Mesh.AssignModes.ADD)
+ if not hasShape:
+ return
+
+ # create base key
+ mesh.insertKey()
+ assert(len(mesh.key.blocks)==1)
+ baseShapeIndex=0
+ baseShapeBlock=mesh.key.blocks[baseShapeIndex]
+ baseShapeBlock.name='Basis'
+ obj.activeShape=baseShapeIndex
+ mesh.update()
+ break
+
+ assert(base)
+
+ # each skin
+ for s in l.morph_list:
+ if s.name==base.name:
+ continue
+
+ 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 IndexErrora as msg:
+ print(msg)
+ print(index, len(base.indices), len(vertex_map))
+ print(len(mesh.verts))
+ print(base.indices[index])
+ print(vertex_index)
+ break
+ except KeyError:
+ #print 'this mesh not has shape vertices'
+ break
+
+ # get skin name
+ name=englishmap.getEnglishSkinName(s.getName())
+ if not name:
+ name=s.getName().encode(bl.INTERNAL_ENCODING)
+ print(name)
+
+ # create shapekey block
+ mesh.insertKey()
+ shapeIndex=len(mesh.key.blocks)-1
+ keyBlock=mesh.key.blocks[shapeIndex]
+ keyBlock.name=name
+
+ # copy vertex to shape key
+ mesh.update()
+
+ # restore
+ for mv, v in zip(mesh.verts, baseShapeBlock.getData()):
+ mv.co[0] = v[0]
+ mv.co[1] = v[1]
+ mv.co[2] = v[2]
+ mesh.update()
+
+ # select base shape
+ obj.activeShape=baseShapeIndex
+
+else:
+ # for 2.5
+ 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 getBoneName(bone):
+ name = englishmap.getEnglishBoneName(bone.getName())
+ return name if name else bone.getName()
+
+
+ def create_texture(directory, texture_name):
+ texture=bpy.data.textures.new(texture_name)
+ texture.type='IMAGE'
+ texture=texture.recast_type()
+ texturePath=os.path.join(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キーで使われるマテリアル優先的に前に並べる
+ ############################################################
+ # 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 range(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):
+ # shapeキーで使われる順に並べなおしたマテリアル16個分の
+ # メッシュを作成する
+ 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(getBoneName(l.bones[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[getBoneName(l.bones[v.bone0])], w1, 'ADD')
+ meshObject.add_vertex_to_group(i,
+ meshObject.vertex_groups[getBoneName(l.bones[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=getBoneName(b)
+ 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.draw_names=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=getBoneName(parent)
+ 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=getBoneName(l.bones[ik.index])
+ # 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 __execute(filename, scene):
+ """
+ load pmd file to context.
+ """
+ # load pmd
+ progress_set('load %s' % filename, 0.0)
+
+ io=pmd.IO()
+ if not io.read(filename):
+ print("fail to load %s" % filename)
+ return
+ progress_set('loaded %s' % filename, 0.1)
+
+ # create root object
+ root=bl.createEmptyObject(scene,
+ io.english_name if len(io.english_name)>0 else io.getName().encode(bl.INTERNAL_ENCODING))
+
+ # import mesh
+ mesh_objects=importMesh(scene, io, os.path.dirname(filename))
+ for o in mesh_objects:
+ bl.objectMakeParent(root, o)
+
+ # import armature
+ armature_object=importArmature(scene, io)
+ if armature_object:
+ bl.objectMakeParent(root, armature_object)
+ armature = bl.objectGetData(armature_object)
+
+ # add armature modifier
+ for o in mesh_objects:
+ bl.objectAddArmatureModifier(o, armature_object)
+
+ # Limitation
+ for n, b in bl.objectGetPose(armature_object).bones.items():
+ bl.poseBoneLimit(n, b)
+
+ # select objects
+ bl.objectSelect(root)
+ for o in mesh_objects:
+ bl.objectSelect(o)
+ bl.objectSelect(armature_object)
+
+
+if isBlender24():
+ # for 2.4
+ def execute_24(filename):
+ """
+ @param filename
+ """
+ filename=filename.decode(bl.INTERNAL_ENCODING)
+ print(bl.INTERNAL_ENCODING, bl.FS_ENCODING)
+
+ # set object mode
+ mode_edit = Blender.Window.EditMode()
+ if mode_edit:
+ Blender.Window.EditMode(0)
+
+ progress_start('pmd_import')
+ scene = bpy.data.scenes.active
+ __execute(filename, scene)
+ scene.update(0)
+ progress_finish()
+
+ # restore edit mode
+ if mode_edit:
+ Blender.Window.EditMode(1)
+ Blender.Window.RedrawAll()
+
+ Blender.Window.FileSelector(
+ execute_24,
+ 'Import PMD file',
+ Blender.sys.makename(ext='.pmd'))
+
+else:
+ # for 2.5
+ def execute_25(*args):
+ progress_start('pmd_import')
+ __execute(*args)
+ progress_finish()
+
+ # import operator
+ class IMPORT_OT_pmd(bpy.types.Operator):
+ 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):
+ 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):
+ 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()
+