--- /dev/null
+# coding: utf-8
+import os
+import sys
+import time
+import functools
+
+try:
+ import bpy
+ import mathutils
+except:
+ pass
+
+FS_ENCODING=sys.getfilesystemencoding()
+if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
+ INTERNAL_ENCODING='utf-8'
+else:
+ INTERNAL_ENCODING=FS_ENCODING
+
+def register():
+ pass
+
+def unregister():
+ pass
+
+SCENE=None
+def initialize(name, scene):
+ global SCENE
+ SCENE=scene
+ progress_start(name)
+
+def finalize():
+ scene.update(SCENE)
+ progress_finish()
+
+def message(msg):
+ print(msg)
+
+def enterEditMode():
+ bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+
+def enterObjectMode():
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+
+def enterPoseMode():
+ bpy.ops.object.mode_set(mode='POSE', toggle=False)
+
+def createVector(x, y, z):
+ return mathutils.Vector([x, y, z])
+
+
+class Writer(object):
+ '''
+ io wrapper
+ '''
+ def __init__(self, path, encoding):
+ self.io=open(path, "wb")
+ self.encoding=encoding
+
+ def write(self, s):
+ self.io.write(s.encode(self.encoding))
+
+ def flush(self):
+ self.io.flush()
+
+ def close(self):
+ self.io.close()
+
+
+class ProgressBar(object):
+ '''
+ progress bar wrapper
+ '''
+ def __init__(self, base):
+ print("#### %s ####" % base)
+ self.base=base
+ self.start=time.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)
+ #Blender.Window.DrawProgressBar(self.progress, message)
+
+ def finish(self):
+ self.progress=1.0
+ message='finished in %.2f sec' % (time.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)
+
+
+class scene:
+ @staticmethod
+ def update(scene):
+ scene.update()
+
+
+class object:
+ @staticmethod
+ def createEmpty(name):
+ global SCENE
+ empty=bpy.data.objects.new(name, None)
+ SCENE.objects.link(empty)
+ return empty
+
+ @staticmethod
+ def each():
+ for o in SCENE.objects:
+ yield o
+
+ @staticmethod
+ def makeParent(parent, child):
+ child.parent=parent
+
+ @staticmethod
+ def duplicate(o):
+ global SCENE
+ bpy.ops.object.select_all(action='DESELECT')
+ o.select=True
+ SCENE.objects.active=o
+ bpy.ops.object.duplicate()
+ dumy=SCENE.objects.active
+ #bpy.ops.object.rotation_apply()
+ #bpy.ops.object.scale_apply()
+ #bpy.ops.object.location_apply()
+ return dumy.data, dumy
+
+ @staticmethod
+ def delete(o):
+ global SCENE
+ SCENE.objects.unlink(o)
+
+ @staticmethod
+ def getData(o):
+ return o.data
+
+ @staticmethod
+ def select(o):
+ o.select=True
+
+ @staticmethod
+ def activate(o):
+ global SCENE
+ o.select=True
+ SCENE.objects.active=o
+
+ @staticmethod
+ def getActive():
+ global SCENE
+ return SCENE.objects.active
+
+ @staticmethod
+ def deselectAll():
+ bpy.ops.object.select_all(action='DESELECT')
+
+ @staticmethod
+ def setLayerMask(object, layers):
+ layer=[]
+ for i in range(20):
+ try:
+ layer.append(True if layers[i]!=0 else False)
+ except IndexError:
+ layer.append(False)
+ object.layers=layer
+
+ @staticmethod
+ def isVisible(o):
+ return False if o.hide else True
+
+ @staticmethod
+ def getShapeKeys(o):
+ return o.data.shape_keys.keys
+
+ @staticmethod
+ def addShapeKey(o, name):
+ try:
+ return o.shape_key_add(name)
+ except:
+ return o.add_shape_key(name)
+
+ @staticmethod
+ def hasShapeKey(o):
+ return o.data.shape_keys
+
+ @staticmethod
+ def pinShape(o, enable):
+ o.show_only_shape_key=enable
+
+ @staticmethod
+ def setActivateShapeKey(o, index):
+ o.active_shape_key_index=index
+
+ @staticmethod
+ def getPose(o):
+ return o.pose
+
+ @staticmethod
+ def getVertexGroup(o, name):
+ indices=[]
+ for i, v in enumerate(o.data.vertices):
+ for g in v.groups:
+ if o.vertex_groups[g.group].name==name:
+ indices.append(i)
+ return indices
+
+ @staticmethod
+ def getVertexGroupNames(o):
+ for g in o.vertex_groups:
+ yield g.name
+
+ @staticmethod
+ def addVertexGroup(o, name):
+ o.vertex_groups.new(name)
+
+ @staticmethod
+ def assignVertexGroup(o, name, index, weight):
+ g=o.vertex_groups[name]
+ o.vertex_groups.assign([index], g, weight, 'ADD')
+
+ @staticmethod
+ def createBoneGroup(o, name, color_set='DEFAULT'):
+ # create group
+ object.activate(o)
+ enterPoseMode()
+ bpy.ops.pose.group_add()
+ # set name
+ pose=object.getPose(o)
+ g=pose.bone_groups.active
+ g.name=name
+ g.color_set=color_set
+
+ @staticmethod
+ def boneGroups(o):
+ return object.getPose(o).bone_groups
+
+
+class modifier:
+ @staticmethod
+ def addMirror(mesh_object):
+ return mesh_object.modifiers.new("Modifier", "MIRROR")
+
+ @staticmethod
+ def addArmature(mesh_object, armature_object):
+ mod=mesh_object.modifiers.new("Modifier", "ARMATURE")
+ mod.object = armature_object
+ mod.use_bone_envelopes=False
+
+ @staticmethod
+ def hasType(mesh_object, type_name):
+ for mod in mesh_object.modifiers:
+ if mod.type==type_name.upper():
+ return True
+
+ @staticmethod
+ def isType(m, type_name):
+ return m.type==type_name.upper()
+
+ @staticmethod
+ def getArmatureObject(m):
+ return m.object
+
+
+class shapekey:
+ @staticmethod
+ def assign(shapeKey, index, pos):
+ shapeKey.data[index].co=pos
+
+ @staticmethod
+ def getByIndex(b, index):
+ return b.data[index].co
+
+ @staticmethod
+ def get(b):
+ for k in b.data:
+ yield k.co
+
+
+class texture:
+ @staticmethod
+ def create(path):
+ texture=bpy.data.textures.new(os.path.basename(path), 'IMAGE')
+ texture.use_mipmap=True
+ texture.use_interpolation=True
+ texture.use_alpha=True
+ try:
+ image=bpy.data.images.load(path)
+ except SystemError:
+ image=bpy.data.images.new('Image')
+ texture.image=image
+ return texture, image
+
+ @staticmethod
+ def getPath(t):
+ if t.type=="IMAGE":
+ image=t.image
+ if image:
+ return image.filepath
+
+
+class material:
+ @staticmethod
+ def create(name):
+ return bpy.data.materials.new(name)
+
+ @staticmethod
+ def get(material_name):
+ return bpy.data.materials[material_name]
+
+ @staticmethod
+ def addTexture(material, texture, enable=True):
+ # search free slot
+ index=None
+ for i, slot in enumerate(material.texture_slots):
+ if not slot:
+ index=i
+ break
+ if index==None:
+ return
+ #
+ #material.add_texture(texture, "UV", "COLOR")
+ #slot=material.texture_slots.add()
+ slot=material.texture_slots.create(index)
+ slot.texture=texture
+ slot.texture_coords='UV'
+ slot.blend_type='MULTIPLY'
+ slot.use_map_alpha=True
+ slot.use=enable
+ return index
+
+ @staticmethod
+ def getTexture(m, index):
+ return m.texture_slots[index].texture
+
+ @staticmethod
+ def hasTexture(m):
+ return m.texture_slots[0]
+
+ @staticmethod
+ def setUseTexture(m, index, enable):
+ m.use_textures[index]=enable
+
+ @staticmethod
+ def eachTexturePath(m):
+ for slot in m.texture_slots:
+ if slot and slot.texture:
+ texture=slot.texture
+ if texture.type=="IMAGE":
+ image=texture.image
+ if not image:
+ continue
+ yield image.filepath
+
+ @staticmethod
+ def eachEnalbeTexturePath(m):
+ for i, slot in enumerate(m.texture_slots):
+ if m.use_textures[i] and slot and slot.texture:
+ texture=slot.texture
+ if texture.type=="IMAGE":
+ image=texture.image
+ if not image:
+ continue
+ yield image.filepath
+
+
+class mesh:
+ @staticmethod
+ def create(name):
+ global SCENE
+ mesh=bpy.data.meshes.new("Mesh")
+ mesh_object= bpy.data.objects.new(name, mesh)
+ SCENE.objects.link(mesh_object)
+ return mesh, mesh_object
+
+ @staticmethod
+ def addGeometry(mesh, vertices, faces):
+ mesh.from_pydata(vertices, [], faces)
+ """
+ mesh.add_geometry(len(vertices), 0, len(faces))
+ # add vertex
+ unpackedVertices=[]
+ for v in vertices:
+ unpackedVertices.extend(v)
+ mesh.vertices.foreach_set("co", unpackedVertices)
+ # add face
+ unpackedFaces = []
+ for face in faces:
+ if len(face) == 4:
+ if face[3] == 0:
+ # rotate indices if the 4th is 0
+ face = [face[3], face[0], face[1], face[2]]
+ elif len(face) == 3:
+ if face[2] == 0:
+ # rotate indices if the 3rd is 0
+ face = [face[2], face[0], face[1], 0]
+ else:
+ face.append(0)
+ unpackedFaces.extend(face)
+ mesh.faces.foreach_set("verts_raw", unpackedFaces)
+ """
+ assert(len(vertices)==len(mesh.vertices))
+ assert(len(faces)==len(mesh.faces))
+
+ @staticmethod
+ def hasUV(mesh):
+ return mesh.active_uv_texture
+
+ @staticmethod
+ def useVertexUV(mesh):
+ pass
+
+ @staticmethod
+ def addUV(mesh):
+ mesh.uv_textures.new()
+
+ @staticmethod
+ def hasFaceUV(mesh, i, face):
+ return mesh.active_uv_texture and mesh.active_uv_texture.data[i]
+
+ @staticmethod
+ def getFaceUV(mesh, i, faces, count=3):
+ if mesh.active_uv_texture and mesh.active_uv_texture.data[i]:
+ uvFace=mesh.active_uv_texture.data[i]
+ if count==3:
+ return (uvFace.uv1, uvFace.uv2, uvFace.uv3)
+ elif count==4:
+ return (uvFace.uv1, uvFace.uv2, uvFace.uv3, uvFace.uv4)
+ else:
+ print(count)
+ assert(False)
+ else:
+ return ((0, 0), (0, 0), (0, 0), (0, 0))
+
+ @staticmethod
+ def setFaceUV(m, i, face, uv_array, image):
+ uv_face=m.uv_textures[0].data[i]
+ uv_face.uv=uv_array
+ if image:
+ uv_face.image=image
+ uv_face.use_image=True
+
+ @staticmethod
+ def vertsDelete(m, remove_vertices):
+ enterEditMode()
+ bpy.ops.mesh.select_all(action='DESELECT')
+ enterObjectMode()
+
+ for i in remove_vertices:
+ m.vertices[i].select=True
+
+ enterEditMode()
+ bpy.ops.mesh.delete(type='VERT')
+ enterObjectMode()
+
+ @staticmethod
+ def setSmooth(m, smoothing):
+ m.auto_smooth_angle=int(smoothing)
+ m.use_auto_smooth=True
+
+ @staticmethod
+ def recalcNormals(mesh_object):
+ bpy.ops.object.select_all(action='DESELECT')
+ object.activate(mesh_object)
+ enterEditMode()
+ bpy.ops.mesh.normals_make_consistent()
+ enterObjectMode()
+
+ @staticmethod
+ def flipNormals(m):
+ m.flipNormals()
+
+ @staticmethod
+ def addMaterial(m, material):
+ m.materials.append(material)
+
+ @staticmethod
+ def getMaterial(m, index):
+ return m.materials[index]
+
+
+class vertex:
+ @staticmethod
+ def setNormal(v, normal):
+ v.normal=mathutils.Vector(normal)
+
+ @staticmethod
+ def getNormal(v):
+ return v.normal
+
+ @staticmethod
+ def setUv(v, uv):
+ # sticky ?
+ pass
+
+
+class face:
+ @staticmethod
+ def getVertexCount(face):
+ return len(face.vertices)
+
+ @staticmethod
+ def getVertices(face):
+ return face.vertices[:]
+
+ @staticmethod
+ def getIndices(face, count=3):
+ if count==3:
+ return [face.vertices[0], face.vertices[1], face.vertices[2]]
+ elif count==4:
+ return [face.vertices[0], face.vertices[1], face.vertices[2], face.vertices[3]]
+ else:
+ assert(False)
+
+ @staticmethod
+ def setMaterial(face, material_index):
+ face.material_index=material_index
+
+ @staticmethod
+ def getMaterialIndex(face):
+ return face.material_index
+
+ @staticmethod
+ def setNormal(face, normal):
+ face.normal=normal
+
+ @staticmethod
+ def getNormal(face):
+ return face.normal
+
+ @staticmethod
+ def setSmooth(face, isSmooth):
+ face.use_smooth=True if isSmooth else False
+
+
+class armature:
+ @staticmethod
+ def create():
+ global SCENE
+ armature = bpy.data.armatures.new('Armature')
+ armature_object=bpy.data.objects.new('Armature', armature)
+ SCENE.objects.link(armature_object)
+
+ armature_object.show_x_ray=True
+ armature.show_names=True
+ #armature.draw_type='OCTAHEDRAL'
+ armature.draw_type='STICK'
+ armature.use_deform_envelopes=False
+ armature.use_deform_vertex_groups=True
+ armature.use_mirror_x=True
+
+ return armature, armature_object
+
+ @staticmethod
+ def makeEditable(armature_object):
+ global SCENE
+ # 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)
+
+ @staticmethod
+ def createIkConstraint(armature_object, p_bone, effector_name, ik):
+ constraint = p_bone.constraints.new('IK')
+ constraint.chain_count=len(ik.children)
+ constraint.target=armature_object
+ constraint.subtarget=effector_name
+ constraint.use_tail=False
+ # not used. place folder when export.
+ constraint.weight=ik.weight
+ constraint.iterations=ik.iterations * 10
+ return constraint
+
+ @staticmethod
+ def createBone(armature, name):
+ return armature.edit_bones.new(name)
+
+ @staticmethod
+ def update(armature):
+ pass
+
+
+class bone:
+ @staticmethod
+ def setConnected(bone):
+ bone.use_connect=True
+
+ @staticmethod
+ def isConnected(b):
+ return b.connected
+
+ @staticmethod
+ def setLayerMask(bone, layers):
+ layer=[]
+ for i in range(32):
+ try:
+ layer.append(True if layers[i]!=0 else False)
+ except IndexError:
+ layer.append(False)
+ bone.layers=layer
+
+ @staticmethod
+ def getHeadLocal(b):
+ return b.head_local[0:3]
+
+ @staticmethod
+ def getTailLocal(b):
+ return b.tail_local[0:3]
+
+
+class constraint:
+ @staticmethod
+ def ikChainLen(c):
+ return c.chain_length
+
+ @staticmethod
+ def ikTarget(c):
+ return c.subtarget
+
+ @staticmethod
+ def ikItration(c):
+ return c.iterations
+
+ @staticmethod
+ def ikRotationWeight(c):
+ return c.weight
+
+ @staticmethod
+ def isIKSolver(c):
+ return c.type=='IK'
+
--- /dev/null
+#!BPY
+# coding: utf-8
+
+"""
+Name: 'Metasequoia (.mqo)...'
+Blender: 245
+Group: 'Export'
+Tooltip: 'Save as Metasequoia MQO File'
+"""
+__author__= 'ousttrue'
+__url__ = ["http://gunload.web.fc2.com/blender/"]
+__version__= '2.2'
+__bpydoc__ = """\
+This script is an exporter to MQO file format.
+
+Usage:
+
+Run this script from "File->Export" menu.
+
+0.1 20080128:
+0.2 20100518: refactoring.
+0.3 20100606: integrate 2.4 and 2.5.
+0.4 20100626: refactoring.
+0.5 20100710: add [apply_modifier] option(2.5 only).
+0.6 20100714: remove shape_key when apply_modifier. fix material.
+2.0 20100724: update for Blender2.53.
+2.1 20101005: update for Blender2.54.
+2.2 20101228: update for Blender2.55.
+"""
+
+bl_addon_info = {
+ 'category': 'Import/Export',
+ 'name': 'Export: Metasequioa Model Format (.mqo)',
+ 'author': 'ousttrue',
+ 'version': (2, 1),
+ 'blender': (2, 5, 3),
+ 'location': 'File > Export',
+ 'description': 'Export to the Metasequioa Model Format (.mqo)',
+ 'warning': '', # used for warning icon and text in addons panel
+ 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
+ 'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
+ }
+
+import os
+import sys
+
+def isBlender24():
+ return sys.version_info[0]<3
+
+
+class MQOMaterial(object):
+ __slots__=[
+ 'name', 'shader', 'r', 'g', 'b', 'a',
+ 'dif', 'amb', 'emi',
+ ]
+ def __init__(self, name, shader=3):
+ self.name=name
+ self.shader=shader
+ self.r=0.5
+ self.g=0.5
+ self.b=0.5
+ self.a=1
+ self.dif=1
+ self.amb=0
+ self.emi=0
+
+ def __str__(self):
+ return "\"%s\" shader(%d) col(%f %f %f %f) dif(%f) amb(%f) emi(%f)" % (
+ self.name, self.shader, self.r, self.g, self.b, self.a,
+ self.dif, self.amb, self.emi
+ )
+
+
+if isBlender24():
+ # for 2.4
+ import Blender
+ from Blender import Mathutils
+ import bpy
+
+ # wrapper
+ import bl24 as bl
+
+ def materialToMqo(m):
+ material=MQOMaterial(m.name, 3)
+ material.r=m.rgbCol[0]
+ material.g=m.rgbCol[1]
+ material.b=m.rgbCol[2]
+ material.a=m.alpha
+ return material
+
+else:
+ # for 2.5
+ import bpy
+
+ # wrapper
+ import bl25 as bl
+
+ def materialToMqo(m):
+ material=MQOMaterial(m.name, 3)
+ material.r=m.diffuse_color[0]
+ material.g=m.diffuse_color[1]
+ material.b=m.diffuse_color[2]
+ material.a=m.alpha
+ material.amb=m.ambient
+ material.emi=m.emit
+ return material
+
+def apply_transform(vec, matrix):
+ x, y, z = vec
+ xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]
+ return x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc,\
+ x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc,\
+ x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc
+
+def convert_to_mqo(vec):
+ return vec.x, vec.z, -vec.y
+
+
+class OutlineNode(object):
+ __slots__=['o', 'children']
+ def __init__(self, o):
+ self.o=o
+ self.children=[]
+
+ def __str__(self):
+ return "<Node %s>" % self.o
+
+
+class ObjectInfo(object):
+ __slots__=['object', 'depth', 'material_map']
+ def __init__(self, o, depth):
+ self.object=o
+ self.depth=depth
+ self.material_map={}
+
+ def __str__(self):
+ return "<ObjectInfo %d %s>" % (self.depth, self.object)
+
+
+class MqoExporter(object):
+ __slots__=["materials", "objects", 'scale', 'apply_modifier',]
+ def __init__(self, scale, apply_modifier):
+ self.objects=[]
+ self.materials=[]
+ self.scale=scale
+ self.apply_modifier=apply_modifier
+
+ def setup(self, scene):
+ # 木構造を構築する
+ object_node_map={}
+ for o in scene.objects:
+ object_node_map[o]=OutlineNode(o)
+ for node in object_node_map.values():
+ if node.o.parent:
+ object_node_map[node.o.parent].children.append(node)
+
+ # ルートを得る
+ root=object_node_map[scene.objects.active]
+
+ # 情報を集める
+ if root.o.type.upper()=='EMPTY':
+ # depth調整
+ for node in root.children:
+ self.__setup(node)
+ else:
+ self.__setup(root)
+
+ def __setup(self, node, depth=0):
+ info=ObjectInfo(node.o, depth)
+ self.objects.append(info)
+ if node.o.type.upper()=='MESH':
+ # set material index
+ for i, m in enumerate(node.o.data.materials):
+ info.material_map[i]=self.__getOrAddMaterial(m)
+ # recursive
+ for child in node.children:
+ self.__setup(child, depth+1)
+
+ def __getOrAddMaterial(self, material):
+ for i, m in enumerate(self.materials):
+ if m==material:
+ return i
+ index=len(self.materials)
+ self.materials.append(material)
+ return index
+
+ def write(self, path):
+ bl.message("open: "+path)
+ io=bl.Writer(path, 'cp932')
+ self.__write_header(io)
+ self.__write_scene(io)
+ print("Writing MaterialChunk")
+ self.__write_materials(io, os.path.dirname(path))
+ print("Writing ObjectChunk")
+ for info in self.objects:
+ self.__write_object(io, info)
+ io.write("Eof\r\n")
+ io.flush()
+ io.close()
+
+ def __write_header(self, io):
+ io.write("Metasequoia Document\r\n")
+ io.write("Format Text Ver 1.0\r\n")
+ io.write("\r\n")
+
+ def __write_scene(self, io):
+ print("Writing SceneChunk")
+ io.write("Scene {\r\n")
+ io.write("}\r\n")
+
+ def __write_materials(self, io, dirname):
+ # each material
+ io.write("Material %d {\r\n" % (len(self.materials)))
+ for m in self.materials:
+ io.write(str(materialToMqo(m)))
+ # ToDo separated alpha texture
+ for filename in bl.material.eachTexturePath(m):
+ if len(dirname)>0 and filename.startswith(dirname):
+ # 相対パスに変換する
+ filename=filename[len(dirname)+1:]
+ io.write(" tex(\"%s\")" % filename)
+ break
+ io.write("\r\n")
+ # end of chunk
+ io.write("}\r\n")
+
+ def __write_object(self, io, info):
+ print(info)
+
+ obj=info.object
+ if obj.type.upper()=='MESH' or obj.type.upper()=='EMPTY':
+ pass
+ else:
+ print(obj.type)
+ return
+
+ io.write("Object \""+obj.name+"\" {\r\n")
+
+ # depth
+ io.write("\tdepth %d\r\n" % info.depth)
+
+ # mirror
+ if not self.apply_modifier:
+ if bl.modifier.hasType(obj, 'MIRROR'):
+ io.write("\tmirror 1\r\n")
+ io.write("\tmirror_axis 1\r\n")
+
+ if obj.type.upper()=='MESH':
+ # duplicate and applyMatrix
+ copyMesh, copyObj=bl.object.duplicate(obj)
+ # apply transform
+ 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()
+ # apply modifier
+ if self.apply_modifier:
+ # remove shape key
+ while bl.object.hasShapeKey(copyObj):
+ bpy.ops.object.shape_key_remove()
+ for m in [m for m in copyObj.modifiers]:
+ if m.type=='SOLIDFY':
+ continue
+ elif m.type=='ARMATURE':
+ bpy.ops.object.modifier_apply(modifier=m.name)
+ elif m.type=='MIRROR':
+ bpy.ops.object.modifier_apply(modifier=m.name)
+ else:
+ print(m.type)
+ # write mesh
+ self.__write_mesh(io, copyMesh, info.material_map)
+ bl.object.delete(copyObj)
+
+ io.write("}\r\n") # end of object
+
+ def __write_mesh(self, io, mesh, material_map):
+ # vertices
+ io.write("\tvertex %d {\r\n" % len(mesh.verts))
+ for vert in mesh.verts:
+ x, y, z = convert_to_mqo(vert.co)
+ io.write("\t\t%f %f %f\r\n" %
+ (x*self.scale, y*self.scale, z*self.scale)) # rotate to y-up
+ io.write("\t}\r\n")
+
+ # faces
+ io.write("\tface %d {\r\n" % len(mesh.faces))
+ for i, face in enumerate(mesh.faces):
+ count=bl.face.getVertexCount(face)
+ # V
+ io.write("\t\t%d V(" % count)
+ for j in reversed(bl.face.getVertices(face)):
+ io.write("%d " % j)
+ io.write(")")
+ # mat
+ if len(mesh.materials):
+ io.write(" M(%d)" %
+ material_map[bl.face.getMaterialIndex(face)])
+ # UV
+ if bl.mesh.hasUV(mesh) and bl.mesh.hasFaceUV(mesh, i, face):
+ io.write(" UV(")
+ for uv in reversed(bl.mesh.getFaceUV(mesh, i, face, count)):
+ # reverse vertical value
+ io.write("%f %f " % (uv[0], 1.0-uv[1]))
+ io.write(")")
+ io.write("\r\n")
+ io.write("\t}\r\n") # end of faces
+
+
+def __execute(filename, scene, scale=10, apply_modifier=False):
+ if scene.objects.active:
+ exporter=MqoExporter(scale, apply_modifier)
+ exporter.setup(scene)
+ exporter.write(filename)
+ else:
+ bl.message('no active object !')
+
+
+if isBlender24():
+ # for 2.4
+ def execute_24(filename):
+ scene=Blender.Scene.GetCurrent()
+ bl.initialize('mqo_export', scene)
+ __execute(
+ filename.decode(bl.INTERNAL_ENCODING),
+ scene, False)
+ bl.finalize()
+
+ # execute
+ Blender.Window.FileSelector(
+ execute_24,
+ 'Export Metasequoia MQO',
+ Blender.sys.makename(ext='.mqo'))
+
+else:
+ # for 2.5
+ def execute_25(path, scene, scale, apply_modifier):
+ bl.initialize('mqo_export', scene)
+ __execute(path, scene, scale, apply_modifier)
+ bl.finalize()
+
+ # operator
+ class EXPORT_OT_mqo(bpy.types.Operator):
+ '''Save a Metasequoia MQO file.'''
+ bl_idname = "export_scene.mqo"
+ bl_label = 'Export MQO'
+
+ # List of operator properties, the attributes will be assigned
+ # to the class instance from the operator settings before calling.
+ filepath = bpy.props.StringProperty()
+ filename = bpy.props.StringProperty()
+ directory = bpy.props.StringProperty()
+
+ scale = bpy.props.FloatProperty(
+ name="Scale",
+ description="Scale the MQO by this value",
+ min=0.0001, max=1000000.0,
+ soft_min=0.001, soft_max=100.0, default=10.0)
+
+ apply_modifier = bpy.props.BoolProperty(
+ name="ApplyModifier",
+ description="Would apply modifiers",
+ default=False)
+
+ def execute(self, context):
+ execute_25(
+ self.properties.filepath,
+ context.scene,
+ self.properties.scale,
+ self.properties.apply_modifier)
+ return 'FINISHED'
+
+ def invoke(self, context, event):
+ wm=context.window_manager
+ try:
+ wm.fileselect_add(self)
+ except:
+ wm.add_fileselect(self)
+ return 'RUNNING_MODAL'
+
+ # register menu
+ def menu_func(self, context):
+ default_path=bpy.data.filepath.replace(".blend", ".mqo")
+ self.layout.operator(
+ EXPORT_OT_mqo.bl_idname,
+ text="Metasequoia (.mqo)",
+ icon='PLUGIN'
+ ).filepath=default_path
+
+ def register():
+ bpy.types.INFO_MT_file_export.append(menu_func)
+
+ def unregister():
+ bpy.types.INFO_MT_file_export.remove(menu_func)
+
+ if __name__ == "__main__":
+ register()
+
--- /dev/null
+#!BPY
+# coding: utf-8
+"""
+ Name: 'MikuMikuDance model (.pmd)...'
+ Blender: 248
+ Group: 'Export'
+ Tooltip: 'Export PMD file for MikuMikuDance.'
+"""
+__author__= ["ousttrue"]
+__version__= "2.2"
+__url__=()
+__bpydoc__="""
+pmd Importer
+
+This script exports a pmd model.
+
+0.1 20100318: first implementation.
+0.2 20100519: refactoring. use C extension.
+1.0 20100530: implement basic features.
+1.1 20100612: integrate 2.4 and 2.5.
+1.2 20100616: implement rigid body.
+1.3 20100619: fix rigid body, bone weight.
+1.4 20100626: refactoring.
+1.5 20100629: sphere map.
+1.6 20100710: toon texture & bone group.
+1.7 20100711: separate vertex with normal or uv.
+2.0 20100724: update for Blender2.53.
+2.1 20100731: add full python module.
+2.2 20101005: update for Blender2.54.
+2.3 20101228: update for Blender2.55.
+"""
+
+bl_addon_info = {
+ 'category': 'Import/Export',
+ 'name': 'Export: MikuMikuDance Model Format (.pmd)',
+ 'author': 'ousttrue',
+ 'version': (2, 2),
+ 'blender': (2, 5, 3),
+ 'location': 'File > Export',
+ 'description': 'Export to the MikuMikuDance Model Format (.pmd)',
+ 'warning': '', # used for warning icon and text in addons panel
+ 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
+ '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
+###############################################################################
+import os
+import sys
+
+try:
+ # C extension
+ from meshio import pmd, englishmap
+ print('use meshio C module')
+except ImportError:
+ # full python
+ from pymeshio import englishmap
+ from pymeshio import mmd as pmd
+ pmd.IO=pmd.PMDLoader
+
+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
+
+ def setMaterialParams(material, m):
+ # diffuse
+ material.diffuse.r=m.R
+ material.diffuse.g=m.G
+ material.diffuse.b=m.B
+ material.diffuse.a=m.alpha
+ # specular
+ material.shinness=0 if m.spec<1e-5 else m.spec*10
+ material.specular.r=m.specR
+ material.specular.g=m.specG
+ material.specular.b=m.specB
+ # ambient
+ material.ambient.r=m.mirR
+ material.ambient.g=m.mirG
+ material.ambient.b=m.mirB
+ # flag
+ material.flag=1 if m.enableSSS else 0
+
+ def toCP932(s):
+ return s
+
+
+else:
+ # for 2.5
+ import bpy
+ import mathutils
+
+ # wrapper
+ import bl25 as bl
+
+ xrange=range
+
+ def setMaterialParams(material, m):
+ # diffuse
+ material.diffuse.r=m.diffuse_color[0]
+ material.diffuse.g=m.diffuse_color[1]
+ material.diffuse.b=m.diffuse_color[2]
+ material.diffuse.a=m.alpha
+ # specular
+ material.shinness=0 if m.specular_toon_size<1e-5 else m.specular_hardness*10
+ material.specular.r=m.specular_color[0]
+ material.specular.g=m.specular_color[1]
+ material.specular.b=m.specular_color[2]
+ # ambient
+ material.ambient.r=m.mirror_color[0]
+ material.ambient.g=m.mirror_color[1]
+ material.ambient.b=m.mirror_color[2]
+ # flag
+ material.flag=1 if m.subsurface_scattering.enabled else 0
+ # toon
+ material.toon_index=7
+
+ def toCP932(s):
+ return s.encode('cp932')
+
+
+class Node(object):
+ __slots__=['o', 'children']
+ def __init__(self, o):
+ self.o=o
+ self.children=[]
+
+
+###############################################################################
+# Blenderのメッシュをワンスキンメッシュ化する
+###############################################################################
+def near(x, y, EPSILON=1e-5):
+ d=x-y
+ return d>=-EPSILON and d<=EPSILON
+
+
+class VertexAttribute(object):
+ __slots__=[
+ 'nx', 'ny', 'nz', # normal
+ 'u', 'v', # uv
+ ]
+ def __init__(self, nx, ny, nz, u, v):
+ self.nx=nx
+ self.ny=ny
+ self.nz=nz
+ self.u=u
+ self.v=v
+
+ def __str__(self):
+ return "<vkey: %f, %f, %f, %f, %f>" % (
+ self.nx, self.ny, self.nz, self.u, self.v)
+
+ def __hash__(self):
+ return self.nx + self.ny + self.nz + self.u + self.v
+
+ def __eq__(self, rhs):
+ return self.nx==rhs.nx and self.ny==rhs.ny and self.nz==rhs.nz and self.u==rhs.u and self.v==rhs.v
+
+
+class VertexKey(object):
+ __slots__=[
+ 'obj_index', 'index',
+ ]
+
+ def __init__(self, obj_index, index):
+ self.obj_index=obj_index
+ self.index=index
+
+ def __str__(self):
+ return "<vkey: %d, %d>" % (self.obj_index, self.index)
+
+ def __hash__(self):
+ return self.index*100+self.obj_index
+
+ def __eq__(self, rhs):
+ return self.obj_index==rhs.obj_index and self.index==rhs.index
+
+
+class VertexArray(object):
+ """
+ 頂点配列
+ """
+ __slots__=[
+ 'indexArrays',
+ 'positions',
+ 'attributes', # normal and uv
+ 'b0', 'b1', 'weight',
+ 'vertexMap',
+ 'objectMap',
+ ]
+ def __init__(self):
+ # indexArrays split with each material
+ self.indexArrays={}
+
+ self.positions=[]
+ self.attributes=[]
+ self.b0=[]
+ self.b1=[]
+ self.weight=[]
+
+ self.vertexMap={}
+ self.objectMap={}
+
+ def __str__(self):
+ return "<VertexArray %d positions, %d indexArrays>" % (
+ len(self.positions), len(self.indexArrays))
+
+ def zip(self):
+ return zip(
+ self.positions, self.attributes,
+ self.b0, self.b1, self.weight)
+
+ def each(self):
+ keys=[key for key in self.indexArrays.keys()]
+ keys.sort()
+ for key in keys:
+ yield(key, self.indexArrays[key])
+
+ def __addOrGetIndex(self, obj_index, base_index, pos, normal, uv, b0, b1, weight0):
+ key=VertexKey(obj_index, base_index)
+ attribute=VertexAttribute(
+ normal[0], normal[1], normal[2],
+ uv[0], uv[1])
+ if key in self.vertexMap:
+ if attribute in self.vertexMap[key]:
+ return self.vertexMap[key][attribute]
+ else:
+ return self.__addVertex(self.vertexMap[key],
+ pos, attribute, b0, b1, weight0)
+ else:
+ vertexMapKey={}
+ self.vertexMap[key]=vertexMapKey
+ return self.__addVertex(vertexMapKey,
+ pos, attribute, b0, b1, weight0)
+
+ def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0):
+ index=len(self.positions)
+ vertexMapKey[attribute]=index
+ # position
+ self.positions.append((pos.x, pos.y, pos.z))
+ # unique attribute
+ self.attributes.append(attribute)
+ # shared attribute
+ self.b0.append(b0)
+ self.b1.append(b1)
+ self.weight.append(weight0)
+ assert(index<=65535)
+ return index
+
+ def getMappedIndex(self, obj_name, base_index):
+ return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)]
+
+ def addTriangle(self,
+ object_name, material,
+ base_index0, base_index1, base_index2,
+ pos0, pos1, pos2,
+ n0, n1, n2,
+ uv0, uv1, uv2,
+ b0_0, b0_1, b0_2,
+ b1_0, b1_1, b1_2,
+ weight0, weight1, weight2
+ ):
+ if object_name in self.objectMap:
+ obj_index=self.objectMap[object_name]
+ else:
+ obj_index=len(self.objectMap)
+ self.objectMap[object_name]=obj_index
+ index0=self.__addOrGetIndex(obj_index, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0)
+ index1=self.__addOrGetIndex(obj_index, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1)
+ index2=self.__addOrGetIndex(obj_index, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2)
+
+ if not material in self.indexArrays:
+ self.indexArrays[material]=[]
+ self.indexArrays[material]+=[index0, index1, index2]
+
+
+class Morph(object):
+ __slots__=['name', 'type', 'offsets']
+ def __init__(self, name, type):
+ self.name=name
+ self.type=type
+ self.offsets=[]
+
+ def add(self, index, offset):
+ self.offsets.append((index, offset))
+
+ def sort(self):
+ if isBlender24():
+ self.offsets.sort(lambda l, r: l[0]-r[0])
+ else:
+ self.offsets.sort(key=lambda e: e[0])
+
+ def __str__(self):
+ return "<Morph %s>" % self.name
+
+class IKSolver(object):
+ __slots__=['target', 'effector', 'length', 'iterations', 'weight']
+ def __init__(self, target, effector, length, iterations, weight):
+ self.target=target
+ self.effector=effector
+ self.length=length
+ self.iterations=iterations
+ self.weight=weight
+
+
+class OneSkinMesh(object):
+ __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', ]
+ def __init__(self):
+ self.vertexArray=VertexArray()
+ self.morphList=[]
+ self.rigidbodies=[]
+ self.constraints=[]
+
+ def __str__(self):
+ return "<OneSkinMesh %s, morph:%d>" % (
+ self.vertexArray,
+ len(self.morphList))
+
+ def addMesh(self, obj):
+ if not bl.object.isVisible(obj):
+ return
+ self.__mesh(obj)
+ self.__skin(obj)
+ self.__rigidbody(obj)
+ self.__constraint(obj)
+
+ def __getWeightMap(self, obj, mesh):
+ # bone weight
+ weightMap={}
+ secondWeightMap={}
+ def setWeight(i, name, w):
+ if w>0:
+ if i in weightMap:
+ if i in secondWeightMap:
+ # 上位2つのweightを採用する
+ if w<secondWeightMap[i][1]:
+ pass
+ elif w<weightMap[i][1]:
+ # 2つ目を入れ替え
+ secondWeightMap[i]=(name, w)
+ else:
+ # 1つ目を入れ替え
+ weightMap[i]=(name, w)
+ else:
+ if w>weightMap[i][1]:
+ # 多い方をweightMapに
+ secondWeightMap[i]=weightMap[i]
+ weightMap[i]=(name, w)
+ else:
+ secondWeightMap[i]=(name, w)
+ else:
+ weightMap[i]=(name, w)
+
+ # ToDo bone weightと関係ないvertex groupを除外する
+ if isBlender24():
+ for name in bl.object.getVertexGroupNames(obj):
+ for i, w in mesh.getVertsFromGroup(name, 1):
+ setWeight(i, name, w)
+ else:
+ for i, v in enumerate(mesh.verts):
+ if len(v.groups)>0:
+ for g in v.groups:
+ setWeight(i, obj.vertex_groups[g.group].name, g.weight)
+ else:
+ setWeight(i, obj.vertex_groups[0].name, 1)
+
+ # 合計値が1になるようにする
+ for i in xrange(len(mesh.verts)):
+ if i in secondWeightMap:
+ secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1])
+ elif i in weightMap:
+ weightMap[i]=(weightMap[i][0], 1.0)
+ secondWeightMap[i]=("", 0)
+ else:
+ print("no weight vertex")
+ weightMap[i]=("", 0)
+ secondWeightMap[i]=("", 0)
+
+ return weightMap, secondWeightMap
+
+ def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap):
+ # 各面の処理
+ for i, face in enumerate(mesh.faces):
+ faceVertexCount=bl.face.getVertexCount(face)
+ material=mesh.materials[bl.face.getMaterialIndex(face)]
+ v=[mesh.verts[index] for index in bl.face.getVertices(face)]
+ uv=bl.mesh.getFaceUV(
+ mesh, i, face, bl.face.getVertexCount(face))
+ # flip triangle
+ if faceVertexCount==3:
+ # triangle
+ self.vertexArray.addTriangle(
+ obj_name, material.name,
+ v[2].index,
+ v[1].index,
+ v[0].index,
+ v[2].co,
+ v[1].co,
+ v[0].co,
+ bl.vertex.getNormal(v[2]),
+ bl.vertex.getNormal(v[1]),
+ bl.vertex.getNormal(v[0]),
+ uv[2],
+ uv[1],
+ uv[0],
+ weightMap[v[2].index][0],
+ weightMap[v[1].index][0],
+ weightMap[v[0].index][0],
+ secondWeightMap[v[2].index][0],
+ secondWeightMap[v[1].index][0],
+ secondWeightMap[v[0].index][0],
+ weightMap[v[2].index][1],
+ weightMap[v[1].index][1],
+ weightMap[v[0].index][1]
+ )
+ elif faceVertexCount==4:
+ # quadrangle
+ self.vertexArray.addTriangle(
+ obj_name, material.name,
+ v[2].index,
+ v[1].index,
+ v[0].index,
+ v[2].co,
+ v[1].co,
+ v[0].co,
+ bl.vertex.getNormal(v[2]),
+ bl.vertex.getNormal(v[1]),
+ bl.vertex.getNormal(v[0]),
+ uv[2],
+ uv[1],
+ uv[0],
+ weightMap[v[2].index][0],
+ weightMap[v[1].index][0],
+ weightMap[v[0].index][0],
+ secondWeightMap[v[2].index][0],
+ secondWeightMap[v[1].index][0],
+ secondWeightMap[v[0].index][0],
+ weightMap[v[2].index][1],
+ weightMap[v[1].index][1],
+ weightMap[v[0].index][1]
+ )
+ self.vertexArray.addTriangle(
+ obj_name, material.name,
+ v[0].index,
+ v[3].index,
+ v[2].index,
+ v[0].co,
+ v[3].co,
+ v[2].co,
+ bl.vertex.getNormal(v[0]),
+ bl.vertex.getNormal(v[3]),
+ bl.vertex.getNormal(v[2]),
+ uv[0],
+ uv[3],
+ uv[2],
+ weightMap[v[0].index][0],
+ weightMap[v[3].index][0],
+ weightMap[v[2].index][0],
+ secondWeightMap[v[0].index][0],
+ secondWeightMap[v[3].index][0],
+ secondWeightMap[v[2].index][0],
+ weightMap[v[0].index][1],
+ weightMap[v[3].index][1],
+ weightMap[v[2].index][1]
+ )
+
+ def __mesh(self, obj):
+ if isBlender24():
+ pass
+ else:
+ if RIGID_SHAPE_TYPE in obj:
+ return
+ if 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.verts)>0:
+ # apply transform
+ 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()
+ # apply modifier
+ for m in [m for m in copyObj.modifiers]:
+ if m.type=='SOLIDFY':
+ continue
+ elif m.type=='ARMATURE':
+ continue
+ elif m.type=='MIRROR':
+ bpy.ops.object.modifier_apply(modifier=m.name)
+ else:
+ print(m.type)
+
+ weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh)
+ self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap)
+ bl.object.delete(copyObj)
+
+ def createEmptyBasicSkin(self):
+ self.__getOrCreateMorph('base', 0)
+
+ def __skin(self, obj):
+ if not bl.object.hasShapeKey(obj):
+ return
+
+ indexRelativeMap={}
+ blenderMesh=bl.object.getData(obj)
+ baseMorph=None
+
+ # shape keys
+ vg=bl.object.getVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
+
+ # base
+ used=set()
+ for b in bl.object.getShapeKeys(obj):
+ if b.name==BASE_SHAPE_NAME:
+ baseMorph=self.__getOrCreateMorph('base', 0)
+ basis=b
+
+ relativeIndex=0
+ for index in vg:
+ v=bl.shapekey.getByIndex(b, index)
+ pos=[v[0], v[1], v[2]]
+
+ indices=self.vertexArray.getMappedIndex(obj.name, index)
+ for attribute, i in indices.items():
+ if i in used:
+ continue
+ used.add(i)
+
+ baseMorph.add(i, pos)
+ indexRelativeMap[i]=relativeIndex
+ relativeIndex+=1
+
+ break
+ assert(basis)
+ 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:
+ continue
+
+ print(b.name)
+ morph=self.__getOrCreateMorph(b.name, 4)
+ used=set()
+ for index, src, dst in zip(
+ xrange(len(blenderMesh.verts)),
+ bl.shapekey.get(basis),
+ bl.shapekey.get(b)):
+ offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]]
+ if offset[0]==0 and offset[1]==0 and offset[2]==0:
+ continue
+ if index in vg:
+ indices=self.vertexArray.getMappedIndex(obj.name, index)
+ for attribute, i in indices.items():
+ if i in used:
+ continue
+ used.add(i)
+ morph.add(indexRelativeMap[i], offset)
+ assert(len(morph.offsets)<len(baseMorph.offsets))
+
+ # sort skinmap
+ original=self.morphList[:]
+ def getIndex(morph):
+ for i, v in enumerate(englishmap.skinMap):
+ if v[0]==morph.name:
+ return i
+ print(morph)
+ return len(englishmap.skinMap)
+ if isBlender24():
+ self.morphList.sort(lambda l, r: getIndex(l)-getIndex(r))
+ else:
+ self.morphList.sort(key=getIndex)
+
+ def __rigidbody(self, obj):
+ if isBlender24():
+ return
+ if not RIGID_SHAPE_TYPE in obj:
+ return
+ self.rigidbodies.append(obj)
+
+ def __constraint(self, obj):
+ if isBlender24():
+ return
+ if not CONSTRAINT_A in obj:
+ return
+ self.constraints.append(obj)
+
+ def __getOrCreateMorph(self, name, type):
+ for m in self.morphList:
+ if m.name==name:
+ return m
+ m=Morph(name, type)
+ self.morphList.append(m)
+ return m
+
+ def getVertexCount(self):
+ return len(self.vertexArray.positions)
+
+
+class Bone(object):
+ __slots__=['index', 'name', 'ik_index',
+ 'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
+ def __init__(self, name, pos, tail, isConnect):
+ self.index=-1
+ self.name=name
+ self.pos=pos
+ self.tail=tail
+ self.parent_index=None
+ self.tail_index=None
+ self.type=0
+ self.isConnect=isConnect
+ self.ik_index=0
+
+ def __eq__(self, rhs):
+ return self.index==rhs.index
+
+ def __str__(self):
+ return "<Bone %s %d>" % (self.name, self.type)
+
+class BoneBuilder(object):
+ __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',]
+ def __init__(self):
+ self.bones=[]
+ self.boneMap={}
+ self.ik_list=[]
+ self.bone_groups=[]
+
+ def getBoneGroup(self, bone):
+ for i, g in enumerate(self.bone_groups):
+ for b in g[1]:
+ if b==bone.name:
+ return i+1
+ print('no gorup', bone)
+ return 0
+
+ def build(self, armatureObj):
+ if not armatureObj:
+ return
+
+ bl.message("build skeleton")
+ armature=bl.object.getData(armatureObj)
+
+ ####################
+ # bone group
+ ####################
+ for g in bl.object.boneGroups(armatureObj):
+ self.bone_groups.append((g.name, []))
+
+ ####################
+ # get bones
+ ####################
+ for b in armature.bones.values():
+ if not b.parent:
+ # root bone
+ bone=Bone(b.name,
+ bl.bone.getHeadLocal(b),
+ bl.bone.getTailLocal(b),
+ False)
+ self.__addBone(bone)
+ self.__getBone(bone, b)
+
+ for b in armature.bones.values():
+ if not b.parent:
+ self.__checkConnection(b, None)
+
+ ####################
+ # get IK
+ ####################
+ pose = bl.object.getPose(armatureObj)
+ for b in pose.bones.values():
+ ####################
+ # assing bone group
+ ####################
+ self.__assignBoneGroup(b, b.bone_group)
+ for c in b.constraints:
+ if bl.constraint.isIKSolver(c):
+ ####################
+ # IK target
+ ####################
+ target=self.__boneByName(bl.constraint.ikTarget(c))
+ target.type=2
+
+ ####################
+ # IK effector
+ ####################
+ # IK 接続先
+ link=self.__boneByName(b.name)
+ link.type=6
+
+ # IK chain
+ e=b.parent
+ chainLength=bl.constraint.ikChainLen(c)
+ for i in range(chainLength):
+ # IK影響下
+ chainBone=self.__boneByName(e.name)
+ chainBone.type=4
+ chainBone.ik_index=target.index
+ e=e.parent
+ self.ik_list.append(
+ IKSolver(target, link, chainLength,
+ int(bl.constraint.ikItration(c) * 0.1),
+ bl.constraint.ikRotationWeight(c)
+ ))
+
+ ####################
+
+ # boneのsort
+ self._sortBy()
+ self._fix()
+ # IKのsort
+ def getIndex(ik):
+ for i, v in enumerate(englishmap.boneMap):
+ if v[0]==ik.target.name:
+ return i
+ return len(englishmap.boneMap)
+ if isBlender24():
+ self.ik_list.sort(lambda l, r: getIndex(l)-getIndex(r))
+ else:
+ self.ik_list.sort(key=getIndex)
+
+ def __assignBoneGroup(self, poseBone, boneGroup):
+ if boneGroup:
+ for g in self.bone_groups:
+ if g[0]==boneGroup.name:
+ g[1].append(poseBone.name)
+
+ def __checkConnection(self, b, p):
+ if bl.bone.isConnected(b):
+ parent=self.__boneByName(p.name)
+ parent.isConnect=True
+
+ for c in b.children:
+ self.__checkConnection(c, b)
+
+ def _sortBy(self):
+ """
+ boneMap順に並べ替える
+ """
+ boneMap=englishmap.boneMap
+ original=self.bones[:]
+ def getIndex(bone):
+ for i, k_v in enumerate(boneMap):
+ if k_v[0]==bone.name:
+ return i
+ print(bone)
+ return len(boneMap)
+
+ if isBlender24():
+ self.bones.sort(lambda l, r: getIndex(l)-getIndex(r))
+ else:
+ self.bones.sort(key=getIndex)
+
+ sortMap={}
+ for i, b in enumerate(self.bones):
+ src=original.index(b)
+ sortMap[src]=i
+ for b in self.bones:
+ b.index=sortMap[b.index]
+ if b.parent_index:
+ b.parent_index=sortMap[b.parent_index]
+ if b.tail_index:
+ b.tail_index=sortMap[b.tail_index]
+ if b.ik_index>0:
+ b.ik_index=sortMap[b.ik_index]
+
+ def _fix(self):
+ """
+ 調整
+ """
+ for b in self.bones:
+ # parent index
+ if b.parent_index==None:
+ b.parent_index=0xFFFF
+ else:
+ if b.type==6 or b.type==7:
+ # fix tail bone
+ parent=self.bones[b.parent_index]
+ #print('parnet', parent.name)
+ parent.tail_index=b.index
+
+ for b in self.bones:
+ if b.tail_index==None:
+ b.tail_index=0
+ elif b.type==9:
+ b.tail_index==0
+
+ def getIndex(self, bone):
+ for i, b in enumerate(self.bones):
+ if b==bone:
+ return i
+ assert(false)
+
+ def indexByName(self, name):
+ if name=='':
+ return 0
+ else:
+ return self.getIndex(self.__boneByName(name))
+
+ def __boneByName(self, name):
+ return self.boneMap[name]
+
+ def __getBone(self, parent, b):
+ if len(b.children)==0:
+ parent.type=7
+ return
+
+ for i, c in enumerate(b.children):
+ bone=Bone(c.name,
+ bl.bone.getHeadLocal(c),
+ bl.bone.getTailLocal(c),
+ bl.bone.isConnected(c))
+ self.__addBone(bone)
+ if parent:
+ bone.parent_index=parent.index
+ #if i==0:
+ if bone.isConnect or (not parent.tail_index and parent.tail==bone.pos):
+ parent.tail_index=bone.index
+ self.__getBone(bone, c)
+
+ def __addBone(self, bone):
+ bone.index=len(self.bones)
+ self.bones.append(bone)
+ self.boneMap[bone.name]=bone
+
+
+class PmdExporter(object):
+
+ __slots__=[
+ 'armatureObj',
+ 'oneSkinMesh',
+ 'englishName',
+ 'englishComment',
+ 'name',
+ 'comment',
+ 'skeleton',
+ ]
+ def setup(self):
+ self.armatureObj=None
+
+ # 木構造を構築する
+ object_node_map={}
+ for o in bl.object.each():
+ object_node_map[o]=Node(o)
+ for o in bl.object.each():
+ node=object_node_map[o]
+ if node.o.parent:
+ object_node_map[node.o.parent].children.append(node)
+
+ # ルートを得る
+ 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.oneSkinMesh=OneSkinMesh()
+ self.__createOneSkinMesh(root)
+ bl.message(self.oneSkinMesh)
+ if len(self.oneSkinMesh.morphList)==0:
+ # create emtpy skin
+ self.oneSkinMesh.createEmptyBasicSkin()
+
+ # skeleton
+ self.skeleton=BoneBuilder()
+ self.skeleton.build(self.armatureObj)
+
+ def __createOneSkinMesh(self, node):
+ ############################################################
+ # search armature modifier
+ ############################################################
+ for m in node.o.modifiers:
+ if bl.modifier.isType(m, 'ARMATURE'):
+ armatureObj=bl.modifier.getArmatureObject(m)
+ if not self.armatureObj:
+ self.armatureObj=armatureObj
+ elif self.armatureObj!=armatureObj:
+ print("warning! found multiple armature. ignored.",
+ armatureObj.name)
+
+ if node.o.type.upper()=='MESH':
+ self.oneSkinMesh.addMesh(node.o)
+
+ for child in node.children:
+ self.__createOneSkinMesh(child)
+
+ def write(self, path):
+ io=pmd.IO()
+ io.setName(toCP932(self.name))
+ io.setComment(toCP932(self.comment))
+ io.version=1.0
+
+ # 頂点
+ for pos, attribute, b0, b1, weight in self.oneSkinMesh.vertexArray.zip():
+ # convert right-handed z-up to left-handed y-up
+ v=io.addVertex()
+ v.pos.x=pos[0]
+ v.pos.y=pos[2]
+ v.pos.z=pos[1]
+ v.normal.x=attribute.nx
+ v.normal.y=attribute.ny
+ v.normal.z=attribute.nz
+ v.uv.x=attribute.u
+ v.uv.y=1.0-attribute.v # reverse vertical
+ v.bone0=self.skeleton.indexByName(b0)
+ v.bone1=self.skeleton.indexByName(b1)
+ v.weight0=int(100*weight)
+ v.edge_flag=0 # edge flag, 0: enable edge, 1: not edge
+
+ # 面とマテリアル
+ vertexCount=self.oneSkinMesh.getVertexCount()
+ for material_name, indices in self.oneSkinMesh.vertexArray.each():
+ #print('material:', material_name)
+ m=bl.material.get(material_name)
+ # マテリアル
+ material=io.addMaterial()
+ setMaterialParams(material, m)
+
+ material.vertex_count=len(indices)
+ material.toon_index=0
+ textures=[os.path.basename(path)
+ for path in bl.material.eachEnalbeTexturePath(m)]
+ if len(textures)>0:
+ material.setTexture(toCP932('*'.join(textures)))
+ else:
+ material.setTexture(toCP932(""))
+ # 面
+ for i in indices:
+ assert(i<vertexCount)
+ for i in xrange(0, len(indices), 3):
+ # reverse triangle
+ io.indices.append(indices[i])
+ io.indices.append(indices[i+1])
+ io.indices.append(indices[i+2])
+
+ # bones
+ boneNameMap={}
+ for i, b in enumerate(self.skeleton.bones):
+ bone=io.addBone()
+
+ # name
+ boneNameMap[b.name]=i
+ v=englishmap.getUnicodeBoneName(b.name)
+ if not v:
+ v=[b.name, b.name]
+ assert(v)
+ cp932=v[1].encode('cp932')
+ assert(len(cp932)<20)
+ bone.setName(cp932)
+
+ # english name
+ bone_english_name=toCP932(b.name)
+ assert(len(bone_english_name)<20)
+ bone.setEnglishName(bone_english_name)
+
+ if len(v)>=3:
+ # has type
+ if v[2]==5:
+ b.ik_index=self.skeleton.indexByName('eyes')
+ bone.type=v[2]
+ else:
+ bone.type=b.type
+
+ bone.parent_index=b.parent_index
+ bone.tail_index=b.tail_index
+ bone.ik_index=b.ik_index
+
+ # convert right-handed z-up to left-handed y-up
+ bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0
+ bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0
+ bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0
+
+ # IK
+ for ik in self.skeleton.ik_list:
+ solver=io.addIK()
+ solver.index=self.skeleton.getIndex(ik.target)
+ solver.target=self.skeleton.getIndex(ik.effector)
+ solver.length=ik.length
+ b=self.skeleton.bones[ik.effector.parent_index]
+ for i in xrange(solver.length):
+ solver.children.append(self.skeleton.getIndex(b))
+ b=self.skeleton.bones[b.parent_index]
+ solver.iterations=ik.iterations
+ solver.weight=ik.weight
+
+ # 表情
+ for i, m in enumerate(self.oneSkinMesh.morphList):
+ # morph
+ morph=io.addMorph()
+
+ v=englishmap.getUnicodeSkinName(m.name)
+ if not v:
+ v=[m.name, m.name, 0]
+ assert(v)
+ cp932=v[1].encode('cp932')
+ morph.setName(cp932)
+ morph.setEnglishName(m.name.encode('cp932'))
+ m.type=v[2]
+ morph.type=v[2]
+ for index, offset in m.offsets:
+ # convert right-handed z-up to left-handed y-up
+ morph.append(index, offset[0], offset[2], offset[1])
+ morph.vertex_count=len(m.offsets)
+
+ # 表情枠
+ # type==0はbase
+ for i, m in enumerate(self.oneSkinMesh.morphList):
+ if m.type==3:
+ io.face_list.append(i)
+ for i, m in enumerate(self.oneSkinMesh.morphList):
+ if m.type==2:
+ io.face_list.append(i)
+ for i, m in enumerate(self.oneSkinMesh.morphList):
+ if m.type==1:
+ io.face_list.append(i)
+ for i, m in enumerate(self.oneSkinMesh.morphList):
+ if m.type==4:
+ io.face_list.append(i)
+
+ # ボーングループ
+ for g in self.skeleton.bone_groups:
+ boneDisplayName=io.addBoneGroup()
+ # name
+ name=englishmap.getUnicodeBoneGroupName(g[0])
+ if not name:
+ name=g[0]
+ boneDisplayName.setName(toCP932(name+'\n'))
+ # english
+ englishName=g[0]
+ boneDisplayName.setEnglishName(toCP932(englishName+'\n'))
+
+ # ボーングループメンバー
+ for i, b in enumerate(self.skeleton.bones):
+ if i==0:
+ continue
+ if b.type in [6, 7]:
+ continue
+ io.addBoneDisplay(i, self.skeleton.getBoneGroup(b))
+
+ #assert(len(io.bones)==len(io.bone_display_list)+1)
+
+ # English
+ io.setEnglishName(toCP932(self.englishName))
+ io.setEnglishComment(toCP932(self.englishComment))
+
+ # toon
+ toonMeshObject=None
+ for o in bl.object.each():
+ try:
+ if o.name.startswith(TOON_TEXTURE_OBJECT):
+ toonMeshObject=o
+ except:
+ p(o.name)
+ break
+ if toonMeshObject:
+ toonMesh=bl.object.getData(toonMeshObject)
+ toonMaterial=bl.mesh.getMaterial(toonMesh, 0)
+ for i in range(10):
+ t=bl.material.getTexture(toonMaterial, i)
+ if t:
+ io.getToonTexture(i).setName(toCP932(t.name))
+ else:
+ io.getToonTexture(i).setName(toCP932("toon%02d.bmp\n" % i))
+ else:
+ for i in range(10):
+ io.getToonTexture(i).setName(toCP932("toon%02d.bmp\n" % i))
+
+ # 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)
+ rigidBody=io.addRigidBody()
+ rigidBody.setName(name.encode('cp932'))
+ rigidNameMap[name]=i
+ boneIndex=boneNameMap[obj[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.shapeType=pmd.SHAPE_SPHERE
+ rigidBody.w=obj.scale[0]
+ elif obj[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:
+ rigidBody.shapeType=pmd.SHAPE_CAPSULE
+ rigidBody.w=obj.scale[0]
+ rigidBody.h=obj.scale[2]
+
+ # constraint
+ for obj in self.oneSkinMesh.constraints:
+ constraint=io.addConstraint()
+ constraint.setName(obj[CONSTRAINT_NAME].encode('cp932'))
+ constraint.rigidA=rigidNameMap[obj[CONSTRAINT_A]]
+ constraint.rigidB=rigidNameMap[obj[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]
+
+ # 書き込み
+ bl.message('write: %s' % path)
+ return io.write(path)
+
+
+def __execute(filename):
+ active=bl.object.getActive()
+ if not active:
+ print("abort. no active object.")
+ return
+ exporter=PmdExporter()
+ exporter.setup()
+ print(exporter)
+ exporter.write(filename)
+ bl.object.activate(active)
+
+
+if isBlender24():
+ # for 2.4
+ def execute_24(filename):
+ bl.initialize('pmd_export', bpy.data.scenes.active)
+ __execute(filename.decode(bl.INTERNAL_ENCODING))
+ bl.finalize()
+
+ Blender.Window.FileSelector(
+ execute_24,
+ 'Export Metasequoia PMD',
+ Blender.sys.makename(ext='.pmd'))
+
+else:
+ # for 2.5
+ def execute_25(filename, scene):
+ bl.initialize('pmd_export', scene)
+ __execute(filename)
+ bl.finalize()
+
+ # operator
+ class EXPORT_OT_pmd(bpy.types.Operator):
+ '''Save a Metasequoia PMD file.'''
+ bl_idname = "export_scene.pmd"
+ bl_label = 'Export PMD'
+
+ # List of operator properties, the attributes will be assigned
+ # to the class instance from the operator settings before calling.
+ filepath = bpy.props.StringProperty()
+ filename = bpy.props.StringProperty()
+ directory = bpy.props.StringProperty()
+
+ def execute(self, context):
+ execute_25(
+ self.properties.filepath,
+ context.scene
+ )
+ return 'FINISHED'
+
+ def invoke(self, context, event):
+ wm=context.window_manager
+ try:
+ wm.fileselect_add(self)
+ except:
+ wm.add_fileselect(self)
+ return 'RUNNING_MODAL'
+
+ # register menu
+ def menu_func(self, context):
+ default_path=bpy.data.filepath.replace(".blend", ".pmd")
+ self.layout.operator(
+ EXPORT_OT_pmd.bl_idname,
+ text="Miku Miku Dance Model(.pmd)",
+ icon='PLUGIN'
+ ).filepath=default_path
+
+ def register():
+ bpy.types.INFO_MT_file_export.append(menu_func)
+
+ def unregister():
+ bpy.types.INFO_MT_file_export.remove(menu_func)
+
+ if __name__ == "__main__":
+ register()
+
--- /dev/null
+#!BPY\r
+# coding: utf-8\r
+""" \r
+Name: 'Metasequoia(.mqo)...'\r
+Blender: 245\r
+Group: 'Import'\r
+Tooltip: 'Import from Metasequoia file format (.mqo)'\r
+"""\r
+__author__=['ousttrue']\r
+__url__ = ["http://gunload.web.fc2.com/blender/"]\r
+__version__= '2.2'\r
+__bpydoc__= '''\\r
+\r
+MQO Importer\r
+\r
+This script imports a mqo into Blender for editing.\r
+\r
+0.2 20080123: update.\r
+0.3 20091125: modify for linux.\r
+0.4 20100310: rewrite.\r
+0.5 20100311: create armature from mikoto bone.\r
+0.6 20100505: C extension.\r
+0.7 20100606: integrate 2.4 and 2.5.\r
+0.8 20100619: fix multibyte object name.\r
+0.9 20100626: refactoring.\r
+2.0 20100724: update for Blender2.53.\r
+2.1 20100731: add full python module.\r
+2.2 20101005: update for Blender2.54.\r
+2.3 20101228: update for Blender2.55.\r
+'''\r
+\r
+bl_addon_info = {\r
+ 'category': 'Import/Export',\r
+ 'name': 'Import: Metasequioa Model Format (.mqo)',\r
+ 'author': 'ousttrue',\r
+ 'version': (2, 0),\r
+ 'blender': (2, 5, 3),\r
+ 'location': 'File > Import',\r
+ 'description': 'Import from the Metasequioa Model Format (.mqo)',\r
+ 'warning': '', # used for warning icon and text in addons panel\r
+ 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',\r
+ 'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',\r
+ }\r
+\r
+import os\r
+import sys\r
+\r
+try:\r
+ # C extension\r
+ from meshio import mqo\r
+ print('use meshio C module')\r
+except ImportError:\r
+ # full python\r
+ from pymeshio import mqo\r
+\r
+def isBlender24():\r
+ return sys.version_info[0]<3\r
+\r
+if isBlender24():\r
+ # for 2.4\r
+ import Blender\r
+ from Blender import Mathutils\r
+ import bpy\r
+\r
+ # wrapper\r
+ import bl24 as bl\r
+\r
+ def createMqoMaterial(m):\r
+ material = Blender.Material.New(\r
+ m.getName().encode(bl.INTERNAL_ENCODING))\r
+ #material.mode |= Blender.Material.Modes.SHADELESS\r
+ # diffuse\r
+ material.rgbCol = [m.color.r, m.color.g, m.color.b]\r
+ material.alpha = m.color.a\r
+ # other\r
+ material.amb=m.ambient\r
+ material.spec=m.specular\r
+ material.hard=int(255 * m.power)\r
+ material.emit=m.emit\r
+ return material\r
+\r
+else:\r
+ # for 2.5\r
+ import bpy\r
+\r
+ # wrapper\r
+ import bl25 as bl\r
+\r
+ def createMqoMaterial(m):\r
+ material = bpy.data.materials.new(m.getName())\r
+ # shader\r
+ if m.shader==1:\r
+ material.diffuse_shader='FRESNEL'\r
+ else:\r
+ material.diffuse_shader='LAMBERT'\r
+ # diffuse\r
+ material.diffuse_color=[m.color.r, m.color.g, m.color.b]\r
+ material.diffuse_intensity=m.diffuse\r
+ material.alpha=m.color.a\r
+ # other\r
+ material.ambient = m.ambient\r
+ #material.specular = m.specular\r
+ material.emit=m.emit\r
+ material.use_shadeless=True\r
+ return material\r
+\r
+\r
+def has_mikoto(mqo):\r
+ #for o in mqo.objects:\r
+ # if o.getName().startswith('bone'):\r
+ # return True\r
+ # if o.getName().startswith('sdef'):\r
+ # return True\r
+ # if o.getName().startswith('anchor'):\r
+ # return True\r
+ return False\r
+\r
+\r
+def __createMaterials(mqo, directory):\r
+ """\r
+ create blender materials and renturn material list.\r
+ """\r
+ materials = []\r
+ textureMap={}\r
+ imageMap={}\r
+ if len(mqo.materials)>0:\r
+ for material_index, m in enumerate(mqo.materials):\r
+ # material\r
+ material=createMqoMaterial(m)\r
+ materials.append(material)\r
+ # texture\r
+ texture_name=m.getTexture()\r
+ if texture_name!='':\r
+ if texture_name in textureMap:\r
+ texture=textureMap[texture_name]\r
+ else:\r
+ # load texture image\r
+ if os.path.isabs(texture_name):\r
+ # absolute\r
+ path = texture_name\r
+ else:\r
+ # relative\r
+ path = os.path.join(directory, texture_name)\r
+ # texture\r
+ path=path.replace("\\", "/")\r
+ if os.path.exists(path):\r
+ print("create texture:", path)\r
+ texture, image=bl.texture.create(path)\r
+ textureMap[texture_name]=texture\r
+ imageMap[material_index]=image\r
+ else:\r
+ print("%s not exits" % path)\r
+ continue\r
+ bl.material.addTexture(material, texture)\r
+ else:\r
+ # default material\r
+ pass\r
+ return materials, imageMap\r
+\r
+\r
+def __createObjects(mqo, root, materials, imageMap, scale):\r
+ """\r
+ create blender mesh objects.\r
+ """\r
+ # tree stack\r
+ stack=[root] \r
+ objects=[]\r
+ for o in mqo.objects:\r
+ mesh, mesh_object=bl.mesh.create(o.getName())\r
+\r
+ # add hierarchy\r
+ stack_depth=len(stack)-1\r
+ #print(o.depth, stack_depth)\r
+ if o.depth<stack_depth:\r
+ for i in range(stack_depth-o.depth):\r
+ stack.pop()\r
+ bl.object.makeParent(stack[-1], mesh_object)\r
+ stack.append(mesh_object)\r
+\r
+ if o.getName().startswith('sdef'):\r
+ objects.append(mesh_object)\r
+ elif o.getName().startswith('anchor'):\r
+ bl.object.setLayerMask(mesh_object, [0, 1])\r
+ elif o.getName().startswith('bone'):\r
+ bl.object.setLayerMask(mesh_object, [0, 1])\r
+\r
+ # geometry\r
+ vertices=[(v.x * scale, -v.z * scale, v.y * scale) for v in o.vertices]\r
+ faces=[]\r
+ materialMap={}\r
+ for f in o.faces:\r
+ face_indices=[]\r
+ # flip face\r
+ for i in reversed(range(f.index_count)):\r
+ face_indices.append(f.getIndex(i))\r
+ faces.append(face_indices)\r
+ materialMap[f.material_index]=True\r
+ bl.mesh.addGeometry(mesh, vertices, faces)\r
+\r
+ # blender limits 16 materials per mesh\r
+ for i, material_index in enumerate(materialMap.keys()):\r
+ if i>=16:\r
+ # split a mesh ?\r
+ print("over 16 materials!")\r
+ break\r
+ bl.mesh.addMaterial(mesh, materials[material_index])\r
+ materialMap[material_index]=i\r
+ \r
+ # set face params\r
+ assert(len(o.faces)==len(mesh.faces))\r
+ bl.mesh.addUV(mesh)\r
+ for i, (f, face) in enumerate(zip(o.faces, mesh.faces)):\r
+ uv_array=[]\r
+ # ToDo FIX\r
+ # flip face\r
+ for j in reversed(range(f.index_count)):\r
+ uv_array.append((f.getUV(j).x, 1.0-f.getUV(j).y))\r
+ bl.mesh.setFaceUV(mesh, i, face, uv_array, \r
+ imageMap.get(f.material_index, None))\r
+ if f.material_index in materialMap:\r
+ bl.face.setMaterial(face, materialMap[f.material_index])\r
+ bl.face.setSmooth(face, True)\r
+\r
+ # mirror modifier\r
+ if o.mirror:\r
+ bl.modifier.addMirror(mesh_object)\r
+\r
+ # set smoothing\r
+ bl.mesh.setSmooth(mesh, o.smoothing)\r
+\r
+ # calc normal\r
+ bl.mesh.recalcNormals(mesh_object)\r
+\r
+ return objects\r
+\r
+\r
+###############################################################################\r
+# for mqo mikoto bone.\r
+###############################################################################\r
+class MikotoBone(object):\r
+ __slots__=[\r
+ 'name',\r
+ 'iHead', 'iTail', 'iUp',\r
+ 'vHead', 'vTail', 'vUp',\r
+ 'parent', 'isFloating',\r
+ 'children',\r
+ ]\r
+ def __init__(self, face=None, vertices=None, materials=None):\r
+ self.parent=None\r
+ self.isFloating=False\r
+ self.children=[]\r
+ if not face:\r
+ self.name='root'\r
+ return\r
+\r
+ self.name=materials[face.material_index].name.encode('utf-8')\r
+\r
+ i0=face.getIndex(0)\r
+ i1=face.getIndex(1)\r
+ i2=face.getIndex(2)\r
+ v0=vertices[i0]\r
+ v1=vertices[i1]\r
+ v2=vertices[i2]\r
+ e01=v1-v0\r
+ e12=v2-v1\r
+ e20=v0-v2\r
+ sqNorm0=e01.getSqNorm()\r
+ sqNorm1=e12.getSqNorm()\r
+ sqNorm2=e20.getSqNorm()\r
+ if sqNorm0>sqNorm1:\r
+ if sqNorm1>sqNorm2:\r
+ # e01 > e12 > e20\r
+ self.iHead=i2\r
+ self.iTail=i1\r
+ self.iUp=i0\r
+ else:\r
+ if sqNorm0>sqNorm2:\r
+ # e01 > e20 > e12\r
+ self.iHead=i2\r
+ self.iTail=i0\r
+ self.iUp=i1\r
+ else:\r
+ # e20 > e01 > e12\r
+ self.iHead=i1\r
+ self.iTail=i0\r
+ self.iUp=i2\r
+ else:\r
+ # 0 < 1\r
+ if sqNorm1<sqNorm2:\r
+ # e20 > e12 > e01\r
+ self.iHead=i1\r
+ self.iTail=i2\r
+ self.iUp=i0\r
+ else:\r
+ if sqNorm0<sqNorm2:\r
+ # e12 > e20 > e01\r
+ self.iHead=i0\r
+ self.iTail=i2\r
+ self.iUp=i1\r
+ else:\r
+ # e12 > e01 > e20\r
+ self.iHead=i0\r
+ self.iTail=i1\r
+ self.iUp=i2\r
+ self.vHead=vertices[self.iHead]\r
+ self.vTail=vertices[self.iTail]\r
+ self.vUp=vertices[self.iUp]\r
+\r
+ if self.name.endswith('[]'):\r
+ basename=self.name[0:-2]\r
+ # expand LR name\r
+ if self.vTail.x>0:\r
+ self.name="%s_L" % basename\r
+ else:\r
+ self.name="%s_R" % basename\r
+\r
+\r
+ def setParent(self, parent, floating=False):\r
+ if floating:\r
+ self.isFloating=True\r
+ self.parent=parent\r
+ parent.children.append(self)\r
+\r
+ def printTree(self, indent=''):\r
+ print("%s%s" % (indent, self.name))\r
+ for child in self.children:\r
+ child.printTree(indent+' ')\r
+\r
+\r
+def build_armature(armature, mikotoBone, parent=None):\r
+ """\r
+ create a armature bone.\r
+ """\r
+ bone = Armature.Editbone()\r
+ bone.name = mikotoBone.name.encode('utf-8')\r
+ armature.bones[bone.name] = bone\r
+\r
+ bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())\r
+ bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())\r
+ if parent:\r
+ bone.parent=parent\r
+ if mikotoBone.isFloating:\r
+ pass\r
+ else:\r
+ bone.options=[Armature.CONNECTED]\r
+\r
+ for child in mikotoBone.children:\r
+ build_armature(armature, child, bone)\r
+\r
+\r
+def create_armature(mqo):\r
+ """\r
+ create armature\r
+ """\r
+ boneObject=None\r
+ for o in mqo.objects:\r
+ if o.name.startswith('bone'):\r
+ boneObject=o\r
+ break\r
+ if not boneObject:\r
+ return\r
+\r
+ tailMap={}\r
+ for f in boneObject.faces:\r
+ if f.index_count!=3:\r
+ print("invalid index_count: %d" % f.index_count)\r
+ continue\r
+ b=MikotoBone(f, boneObject.vertices, mqo.materials)\r
+ tailMap[b.iTail]=b\r
+\r
+ #################### \r
+ # build mikoto bone tree\r
+ #################### \r
+ mikotoRoot=MikotoBone()\r
+\r
+ for b in tailMap.values():\r
+ # each bone has unique parent or is root bone.\r
+ if b.iHead in tailMap:\r
+ b.setParent(tailMap[b.iHead])\r
+ else: \r
+ isFloating=False\r
+ for e in boneObject.edges:\r
+ if b.iHead==e.indices[0]:\r
+ # floating bone\r
+ if e.indices[1] in tailMap:\r
+ b.setParent(tailMap[e.indices[1]], True)\r
+ isFloating=True\r
+ break\r
+ elif b.iHead==e.indices[1]:\r
+ # floating bone\r
+ if e.indices[0] in tailMap:\r
+ b.setParent(tailMap[e.indices[0]], True)\r
+ isFloating=True\r
+ break\r
+ if isFloating:\r
+ continue\r
+\r
+ # no parent bone\r
+ b.setParent(mikotoRoot, True)\r
+\r
+ if len(mikotoRoot.children)==0:\r
+ print("no root bone")\r
+ return\r
+\r
+ if len(mikotoRoot.children)==1:\r
+ # single root\r
+ mikotoRoot=mikotoRoot.children[0]\r
+ mikotoRoot.parent=None\r
+ else:\r
+ mikotoRoot.vHead=Vector3(0, 10, 0)\r
+ mikotoRoot.vTail=Vector3(0, 0, 0)\r
+\r
+ #################### \r
+ # create armature\r
+ #################### \r
+ armature = Armature.New()\r
+ # link to object\r
+ armature_object = scene.objects.new(armature)\r
+ # create action\r
+ act = Armature.NLA.NewAction()\r
+ act.setActive(armature_object)\r
+ # set XRAY\r
+ armature_object.drawMode |= Object.DrawModes.XRAY\r
+ # armature settings\r
+ armature.drawType = Armature.OCTAHEDRON\r
+ armature.envelopes = False\r
+ armature.vertexGroups = True\r
+ armature.mirrorEdit = True\r
+ armature.drawNames=True\r
+\r
+ # edit bones\r
+ armature.makeEditable()\r
+ build_armature(armature, mikotoRoot)\r
+ armature.update()\r
+\r
+ return armature_object\r
+ \r
+\r
+class TrianglePlane(object):\r
+ """\r
+ mikoto\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#\e(B\r
+ (\e$BIT40A4\e(B)\r
+ """\r
+ __slots__=['normal', \r
+ 'v0', 'v1', 'v2',\r
+ ]\r
+ def __init__(self, v0, v1, v2):\r
+ self.v0=v0\r
+ self.v1=v1\r
+ self.v2=v2\r
+\r
+ def isInsideXY(self, p):\r
+ v0=Vector2(self.v0.x, self.v0.y)\r
+ v1=Vector2(self.v1.x, self.v1.y)\r
+ v2=Vector2(self.v2.x, self.v2.y)\r
+ e01=v1-v0\r
+ e12=v2-v1\r
+ e20=v0-v2\r
+ c0=Vector2.cross(e01, p-v0)\r
+ c1=Vector2.cross(e12, p-v1)\r
+ c2=Vector2.cross(e20, p-v2)\r
+ if c0>=0 and c1>=0 and c2>=0:\r
+ return True\r
+ if c0<=0 and c1<=0 and c2<=0:\r
+ return True\r
+\r
+ def isInsideYZ(self, p):\r
+ v0=Vector2(self.v0.y, self.v0.z)\r
+ v1=Vector2(self.v1.y, self.v1.z)\r
+ v2=Vector2(self.v2.y, self.v2.z)\r
+ e01=v1-v0\r
+ e12=v2-v1\r
+ e20=v0-v2\r
+ c0=Vector2.cross(e01, p-v0)\r
+ c1=Vector2.cross(e12, p-v1)\r
+ c2=Vector2.cross(e20, p-v2)\r
+ if c0>=0 and c1>=0 and c2>=0:\r
+ return True\r
+ if c0<=0 and c1<=0 and c2<=0:\r
+ return True\r
+\r
+ def isInsideZX(self, p):\r
+ v0=Vector2(self.v0.z, self.v0.x)\r
+ v1=Vector2(self.v1.z, self.v1.x)\r
+ v2=Vector2(self.v2.z, self.v2.x)\r
+ e01=v1-v0\r
+ e12=v2-v1\r
+ e20=v0-v2\r
+ c0=Vector2.cross(e01, p-v0)\r
+ c1=Vector2.cross(e12, p-v1)\r
+ c2=Vector2.cross(e20, p-v2)\r
+ if c0>=0 and c1>=0 and c2>=0:\r
+ return True\r
+ if c0<=0 and c1<=0 and c2<=0:\r
+ return True\r
+\r
+\r
+class MikotoAnchor(object):\r
+ """\r
+ mikoto\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#\e(B\r
+ """\r
+ __slots__=[\r
+ "triangles", "bbox",\r
+ ]\r
+ def __init__(self):\r
+ self.triangles=[]\r
+ self.bbox=None\r
+\r
+ def push(self, face, vertices):\r
+ if face.index_count==3:\r
+ self.triangles.append(TrianglePlane(\r
+ vertices[face.indices[0]],\r
+ vertices[face.indices[1]],\r
+ vertices[face.indices[2]]\r
+ ))\r
+ elif face.index_count==4:\r
+ self.triangles.append(TrianglePlane(\r
+ vertices[face.indices[0]],\r
+ vertices[face.indices[1]],\r
+ vertices[face.indices[2]]\r
+ ))\r
+ self.triangles.append(TrianglePlane(\r
+ vertices[face.indices[2]],\r
+ vertices[face.indices[3]],\r
+ vertices[face.indices[0]]\r
+ ))\r
+ # bounding box\r
+ if not self.bbox:\r
+ self.bbox=BoundingBox(vertices[face.indices[0]])\r
+ for i in face.indices:\r
+ self.bbox.expand(vertices[i])\r
+\r
+\r
+ def calcWeight(self, v):\r
+ if not self.bbox.isInside(v):\r
+ return 0\r
+\r
+ if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):\r
+ return 1.0\r
+ else:\r
+ return 0\r
+ \r
+ def anyXY(self, x, y):\r
+ for t in self.triangles:\r
+ if t.isInsideXY(Vector2(x, y)):\r
+ return True\r
+ return False\r
+\r
+ def anyYZ(self, y, z):\r
+ for t in self.triangles:\r
+ if t.isInsideYZ(Vector2(y, z)):\r
+ return True\r
+ return False\r
+\r
+ def anyZX(self, z, x):\r
+ for t in self.triangles:\r
+ if t.isInsideZX(Vector2(z, x)):\r
+ return True\r
+ return False\r
+\r
+\r
+def create_bone_weight(scene, mqo, armature_object, objects):\r
+ """\r
+ create mikoto bone weight.\r
+ """\r
+ anchorMap={}\r
+ # setup mikoto anchors\r
+ for o in mqo.objects:\r
+ if o.name.startswith("anchor"):\r
+ for f in o.faces:\r
+ name=mqo.materials[f.material_index].name\r
+ if name.endswith('[]'):\r
+ basename=name[0:-2]\r
+ v=o.vertices[f.indices[0]]\r
+ if(v.x>0):\r
+ # L\r
+ name_L=basename+'_L'\r
+ if not name_L in anchorMap:\r
+ anchorMap[name_L]=MikotoAnchor()\r
+ anchorMap[name_L].push(f, o.vertices)\r
+ elif(v.x<0):\r
+ # R\r
+ name_R=basename+'_R'\r
+ if not name_R in anchorMap:\r
+ anchorMap[name_R]=MikotoAnchor()\r
+ anchorMap[name_R].push(f, o.vertices)\r
+ else:\r
+ print("no side", v)\r
+ else:\r
+ if not name in anchorMap:\r
+ anchorMap[name]=MikotoAnchor()\r
+ anchorMap[name].push(f, o.vertices)\r
+\r
+ for o in objects:\r
+ # add armature modifier\r
+ mod=o.modifiers.append(Modifier.Types.ARMATURE)\r
+ mod[Modifier.Settings.OBJECT] = armature_object\r
+ mod[Modifier.Settings.ENVELOPES] = False\r
+ o.makeDisplayList()\r
+ # create vertex group\r
+ mesh=o.getData(mesh=True)\r
+ for name in anchorMap.keys():\r
+ mesh.addVertGroup(name)\r
+ mesh.update()\r
+ \r
+ # assing vertices to vertex group\r
+ for o in objects:\r
+ mesh=o.getData(mesh=True)\r
+ for i, mvert in enumerate(mesh.verts):\r
+ hasWeight=False\r
+ for name, anchor in anchorMap.items():\r
+ weight=anchor.calcWeight(mvert.co)\r
+ if weight>0:\r
+ mesh.assignVertsToGroup(\r
+ name, [i], weight, Mesh.AssignModes.ADD)\r
+ hasWeight=True\r
+ if not hasWeight:\r
+ # debug orphan vertex\r
+ print('orphan', mvert)\r
+ mesh.update()\r
+\r
+\r
+def __execute(filename, scene, scale=0.1):\r
+ # parse file\r
+ io=mqo.IO()\r
+ if not io.read(filename):\r
+ bl.message("fail to load %s" % filename)\r
+ return\r
+\r
+ # create materials\r
+ materials, imageMap=__createMaterials(io, os.path.dirname(filename))\r
+ if len(materials)==0:\r
+ materials.append(bl.material.create('default'))\r
+\r
+ # create objects\r
+ root=bl.object.createEmpty(os.path.basename(filename))\r
+ objects=__createObjects(io, root, materials, imageMap, scale)\r
+\r
+ if has_mikoto(io):\r
+ # create mikoto bone\r
+ armature_object=create_armature(io)\r
+ if armature_object:\r
+ root.makeParent([armature_object])\r
+\r
+ # create bone weight\r
+ create_bone_weight(io, armature_object, objects)\r
+\r
+ \r
+###############################################################################\r
+# register\r
+###############################################################################\r
+if isBlender24():\r
+ # for 2.4\r
+ def execute_24(filename):\r
+ scene=Blender.Scene.GetCurrent()\r
+ bl.initialize('mqo_import', scene)\r
+ __execute(\r
+ filename.decode(bl.INTERNAL_ENCODING), \r
+ scene)\r
+ bl.finalize()\r
+\r
+ # execute\r
+ Blender.Window.FileSelector(execute_24, 'Import MQO', '*.mqo')\r
+\r
+else:\r
+ # for 2.5\r
+ def execute_25(filename, scene, scale):\r
+ bl.initialize('mqo_import', scene)\r
+ __execute(filename, scene, scale)\r
+ bl.finalize()\r
+\r
+ # operator\r
+ class IMPORT_OT_mqo(bpy.types.Operator):\r
+ '''Import from Metasequoia file format (.mqo)'''\r
+ bl_idname = "import_scene.mqo"\r
+ bl_label = 'Import MQO'\r
+\r
+ # List of operator properties, the attributes will be assigned\r
+ # to the class instance from the operator settings before calling.\r
+ filepath = bpy.props.StringProperty()\r
+ filename = bpy.props.StringProperty()\r
+ directory = bpy.props.StringProperty()\r
+\r
+ scale = bpy.props.FloatProperty(\r
+ name="Scale", \r
+ description="Scale the MQO by this value", \r
+ min=0.0001, max=1000000.0, \r
+ soft_min=0.001, soft_max=100.0, default=0.1)\r
+\r
+ def execute(self, context):\r
+ execute_25(\r
+ self.properties.filepath, \r
+ context.scene, \r
+ self.properties.scale)\r
+ return 'FINISHED'\r
+\r
+ def invoke(self, context, event):\r
+ wm=context.window_manager\r
+ try:\r
+ wm.fileselect_add(self)\r
+ except:\r
+ wm.add_fileselect(self)\r
+ return 'RUNNING_MODAL'\r
+\r
+\r
+ # register menu\r
+ def menu_func(self, context): \r
+ self.layout.operator(\r
+ IMPORT_OT_mqo.bl_idname, \r
+ text="Metasequoia (.mqo)",\r
+ icon='PLUGIN'\r
+ )\r
+\r
+ def register():\r
+ bpy.types.INFO_MT_file_import.append(menu_func)\r
+\r
+ def unregister():\r
+ bpy.types.INFO_MT_file_import.remove(menu_func)\r
+\r
+ if __name__=="__main__":\r
+ register()\r
+\r
--- /dev/null
+#!BPY
+# coding:utf-8
+"""
+ Name: 'MikuMikuDance model (.pmd)...'
+ Blender: 248
+ Group: 'Import'
+ Tooltip: 'Import PMD file for MikuMikuDance.'
+"""
+__author__= ["ousttrue"]
+__version__= "2.2"
+__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.
+1.6 20100629: sphere map.
+1.7 20100703: implement bone group.
+1.8 20100710: implement toon texture.
+1.9 20100718: keep model name, comment.
+2.0 20100724: update for Blender2.53.
+2.1 20100731: add full python module.
+2.2 20101005: update for Blender2.54.
+2.3 20101228: update for Blender2.55.
+"""
+bl_addon_info = {
+ 'category': 'Import/Export',
+ 'name': 'Import: MikuMikuDance Model Format (.pmd)',
+ 'author': 'ousttrue',
+ 'version': (2, 2),
+ 'blender': (2, 5, 3),
+ 'location': 'File > Import',
+ 'description': 'Import from the MikuMikuDance Model Format (.pmd)',
+ 'warning': '', # used for warning icon and text in addons panel
+ 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
+ '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
+###############################################################################
+import os
+import sys
+import math
+
+try:
+ # C extension
+ from meshio import pmd, englishmap
+ print('use meshio C module')
+except ImportError:
+ # full python
+ from pymeshio import englishmap
+ from pymeshio import mmd as pmd
+ pmd.IO=pmd.PMDLoader
+
+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
+
+ def createPmdMaterial(m, index):
+ material=Blender.Material.New()
+ # fresnelが無いw
+ material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON)
+ 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
+ material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b])
+ # flag
+ material.enableSSS=True if m.flag==1 else False
+ # name
+ material.name="m_%02d" % index
+ return material
+
+ 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
+
+ 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
+ import mathutils
+
+ # wrapper
+ import bl25 as bl
+
+ xrange=range
+
+ def createPmdMaterial(m, index):
+ material = bpy.data.materials.new("Material")
+ # 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_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.use=True if m.flag==1 else False
+ # other
+ material.name="m_%02d" % index
+ material.preview_render_type='FLAT'
+ material.use_transparency=True
+ return material
+
+ def poseBoneLimit(n, b):
+ if n.endswith("_t"):
+ return
+ if n.startswith("knee_"):
+ b.lock_ik_y=True
+ b.lock_ik_z=True
+ b.lock_ik_x=False
+ # IK limit
+ b.use_ik_limit_x=True
+ b.ik_min_x=0
+ b.ik_max_x=180
+ elif n.startswith("ankle_"):
+ #b.ik_dof_y=False
+ pass
+
+ def setSphereMap(material, index, blend_type='MULTIPLY'):
+ slot=material.texture_slots[index]
+ slot.texture_coords='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
+ """
+ return (pos.x, pos.z, pos.y)
+
+
+def to_radian(degree):
+ return math.pi * degree / 180
+
+
+def get_bone_name(l, index):
+ if index==0xFFFF:
+ return l.bones[0].getName()
+
+ if index < len(l.bones):
+ name=englishmap.getEnglishBoneName(l.bones[index].getName())
+ if name:
+ return name
+ return l.bones[index].getName()
+ 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
+
+ # set shape_key pin
+ bl.object.pinShape(obj, True)
+
+ # find base
+ base=None
+ for s in l.morph_list:
+ if s.type==0:
+ base=s
+
+ # create vertex group
+ bl.object.addVertexGroup(obj, MMD_SHAPE_GROUP_NAME)
+ hasShape=False
+ for i in s.indices:
+ if i in vertex_map:
+ hasShape=True
+ bl.object.assignVertexGroup(
+ obj, MMD_SHAPE_GROUP_NAME, vertex_map[i], 0)
+ if not hasShape:
+ return
+ assert(base)
+
+ # create base key
+ baseShapeBlock=bl.object.addShapeKey(obj, BASE_SHAPE_NAME)
+ # mesh
+ mesh=bl.object.getData(obj)
+ mesh.update()
+
+ # each skin
+ for s in l.morph_list:
+ if s.type==0:
+ continue
+
+ # name
+ name=englishmap.getEnglishSkinName(s.getName())
+ if not name:
+ name=s.getName()
+
+ if isBlender24():
+ # 24
+ for index, offset in zip(s.indices, s.pos_list):
+ try:
+ vertex_index=vertex_map[base.indices[index]]
+ v=mesh.vertices[vertex_index].co
+ offset=convert_coord(offset)
+ v[0]+=offset[0]
+ v[1]+=offset[1]
+ v[2]+=offset[2]
+ except IndexError as msg:
+ print(msg)
+ print(index, len(base.indices), len(vertex_map))
+ print(len(mesh.vertices))
+ print(base.indices[index])
+ print(vertex_index)
+ break
+ except KeyError:
+ #print 'this mesh not has shape vertices'
+ break
+
+ # create shapekey block
+ new_shape_key=bl.object.addShapeKey(obj, name)
+
+ # copy vertex to shape key
+ mesh.update()
+
+ # restore
+ for mv, v in zip(mesh.vertices, baseShapeBlock.getData()):
+ mv.co[0] = v[0]
+ mv.co[1] = v[1]
+ mv.co[2] = v[2]
+ mesh.update()
+
+ else:
+ # 25
+ new_shape_key=bl.object.addShapeKey(obj, name)
+
+ for index, offset in zip(s.indices, s.pos_list):
+ try:
+ vertex_index=vertex_map[base.indices[index]]
+ bl.shapekey.assign(new_shape_key, vertex_index,
+ mesh.vertices[vertex_index].co+
+ bl.createVector(*convert_coord(offset)))
+ except IndexError as msg:
+ print(msg)
+ print(index, len(base.indices), len(vertex_map))
+ print(len(mesh.vertices))
+ print(base.indices[index])
+ print(vertex_index)
+ break
+ except KeyError:
+ #print 'this mesh not has shape vertices'
+ break
+
+ # select base shape
+ bl.object.setActivateShapeKey(obj, 0)
+
+
+def __build(armature, b, p, parent):
+ name=englishmap.getEnglishBoneName(b.getName())
+ if not name:
+ name=b.getName()
+
+ bone=bl.armature.createBone(armature, name)
+
+ if parent and (b.tail_index==0 or b.type==6 or b.type==7 or b.type==9):
+ # 先端ボーン
+ bone.head = bl.createVector(*convert_coord(b.pos))
+ bone.tail=bone.head+bl.createVector(0, 1, 0)
+ bone.parent=parent
+ if bone.name=="center_t":
+ # センターボーンは(0, 1, 0)の方向を向いていないと具合が悪い
+ parent.tail=parent.head+bl.createVector(0, 1, 0)
+ bone.head=parent.tail
+ bone.tail=bone.head+bl.createVector(0, 1, 0)
+ else:
+ if parent.tail==bone.head:
+ pass
+ else:
+ print('diffurence with parent.tail and head', name)
+
+ if b.type!=9:
+ bl.bone.setConnected(bone)
+ # armature layer 2
+ bl.bone.setLayerMask(bone, [0, 1])
+ else:
+ # 通常ボーン
+ bone.head = bl.createVector(*convert_coord(b.pos))
+ bone.tail = bl.createVector(*convert_coord(b.tail))
+ if parent:
+ bone.parent=parent
+ if parent.tail==bone.head:
+ bl.bone.setConnected(bone)
+
+ if bone.head==bone.tail:
+ bone.tail=bone.head+bl.createVector(0, 1, 0)
+
+ for c in b.children:
+ __build(armature, c, b, 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.enterObjectMode()
+
+ # IK constraint
+ pose = bl.object.getPose(armature_object)
+ for ik in l.ik_list:
+ target=l.bones[ik.target]
+ name = englishmap.getEnglishBoneName(target.getName())
+ if not name:
+ name=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
+ effector_name=englishmap.getEnglishBoneName(
+ l.bones[ik.index].getName())
+ if not effector_name:
+ effector_name=l.bones[ik.index].getName()
+
+ constraint=bl.armature.createIkConstraint(armature_object,
+ p_bone, effector_name, ik)
+
+ bl.armature.makeEditable(armature_object)
+ bl.armature.update(armature)
+ 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, toon_material):
+
+ mesh=bl.object.getData(meshObject)
+ ############################################################
+ # material
+ ############################################################
+ bl.progress_print('create materials')
+ mesh_material_map={}
+ textureMap={}
+ imageMap={}
+ index=0
+
+ for material_index in material_order:
+ try:
+ m=l.materials[material_index]
+ mesh_material_map[material_index]=index
+ except KeyError:
+ break
+
+ material=createPmdMaterial(m, material_index)
+
+ # main texture
+ texture_name=m.getTexture()
+ if 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
+ 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')
+
+ # toon texture
+ toon_index=bl.material.addTexture(
+ material,
+ bl.material.getTexture(
+ toon_material,
+ 0 if m.toon_index==0xFF else m.toon_index
+ ),
+ False)
+
+ bl.mesh.addMaterial(mesh, material)
+
+ index+=1
+
+ ############################################################
+ # vertex
+ ############################################################
+ bl.progress_print('create vertices')
+ # create vertices
+ vertices=[]
+ for v in l.each_vertex():
+ vertices.append(convert_coord(v.pos))
+
+ ############################################################
+ # face
+ ############################################################
+ bl.progress_print('create faces')
+ # create faces
+ mesh_face_indices=[]
+ mesh_face_materials=[]
+ used_vertices=set()
+
+ 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]
+
+ def degenerate(i0, i1, i2):
+ """
+ 縮退しているか?
+ """
+ return i0==i1 or i1==i2 or i2==i0
+
+ for j in xrange(0, len(material_faces), 3):
+ i0=material_faces[j]
+ i1=material_faces[j+1]
+ i2=material_faces[j+2]
+ # flip
+ triangle=[i2, i1, i0]
+ if degenerate(*triangle):
+ continue
+ mesh_face_indices.append(triangle[0:3])
+ mesh_face_materials.append(material_index)
+ used_vertices.add(i0)
+ used_vertices.add(i1)
+ used_vertices.add(i2)
+
+ ############################################################
+ # create vertices & faces
+ ############################################################
+ bl.mesh.addGeometry(mesh, vertices, mesh_face_indices)
+
+ ############################################################
+ # vertex bone weight
+ ############################################################
+ # create vertex group
+ vertex_groups={}
+ for v in l.each_vertex():
+ vertex_groups[v.bone0]=True
+ vertex_groups[v.bone1]=True
+ for i in vertex_groups.keys():
+ bl.object.addVertexGroup(meshObject, get_bone_name(l, i))
+
+ # vertex params
+ bl.mesh.useVertexUV(mesh)
+ for i, v, mvert in zip(xrange(len(l.vertices)),
+ l.each_vertex(), mesh.vertices):
+ # normal, uv
+ bl.vertex.setNormal(mvert, convert_coord(v.normal))
+ # bone weight
+ w1=float(v.weight0)/100.0
+ w2=1.0-w1
+ bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone0),
+ i, w1)
+ bl.object.assignVertexGroup(meshObject, get_bone_name(l, v.bone1),
+ i, w2)
+
+ ############################################################
+ # face params
+ ############################################################
+ used_map={}
+ bl.mesh.addUV(mesh)
+ for i, (face, material_index) in enumerate(
+ 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)
+ bl.face.setMaterial(face, index)
+ material=mesh.materials[index]
+ used_map[index]=True
+ if bl.material.hasTexture(material):
+ uv_array=[l.getUV(i) for i in bl.face.getIndices(face)]
+ bl.mesh.setFaceUV(mesh, i, face,
+ # fix uv
+ [(uv.x, 1.0-uv.y) for uv in uv_array],
+ imageMap.get(index, None))
+
+ # set smooth
+ bl.face.setSmooth(face, True)
+
+ mesh.update()
+
+ ############################################################
+ # clean up not used vertices
+ ############################################################
+ bl.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)
+
+ bl.mesh.vertsDelete(mesh, remove_vertices)
+
+ bl.progress_print('%s created' % mesh.name)
+ return vertex_map
+
+
+def __importMaterialAndMesh(io, tex_dir, toon_material):
+ """
+ @param l[in] mmd.PMDLoader
+ @param filename[in]
+ """
+ ############################################################
+ # shpaeキーで使われるマテリアル優先的に前に並べる
+ ############################################################
+ # shapeキーで使われる頂点インデックスを集める
+ shape_key_used_vertices=set()
+ if len(io.morph_list)>0:
+ # base
+ base=None
+ for s in io.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 io.indices[i] in shape_key_used_vertices:
+ return True
+
+ 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):
+ material_with_shape.add(i)
+ face_count+=m.vertex_count
+
+ # shapeキーで使われる頂点のあるマテリアル
+ material_with_shape=list(material_with_shape)
+ material_with_shape.sort()
+
+ # shapeキーに使われていないマテリアル
+ material_without_shape=[]
+ for i in range(len(io.materials)):
+ if not i in material_with_shape:
+ material_without_shape.append(i)
+
+ # メッシュの生成
+ 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()
+ bl.object.activate(meshObject)
+
+ # shapeキーで使われる順に並べなおしたマテリアル16個分の
+ # メッシュを作成する
+ vertex_map=__import16MaerialAndMesh(
+ meshObject, io, material16, face_map, tex_dir, toon_material)
+
+ # crete shape key
+ __importShape(meshObject, io, vertex_map)
+
+ mesh.update()
+ 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(io):
+ if isBlender24():
+ return
+ print("create constraint")
+ container=bl.object.createEmpty('Constraints')
+ layer=[
+ True, False, False, False, False, False, False, False, False, False,
+ False, False, False, False, False, False, False, False, False, False,
+ ]
+ material=bl.material.create('constraint')
+ material.diffuse_color=(1, 0, 0)
+ constraintMeshes=[]
+ for i, c in enumerate(io.constraints):
+ bpy.ops.mesh.primitive_uv_sphere_add(
+ segments=8,
+ rings=4,
+ size=0.1,
+ location=(c.pos.x, c.pos.z, c.pos.y),
+ layer=layer
+ )
+ meshObject=bl.object.getActive()
+ constraintMeshes.append(meshObject)
+ mesh=bl.object.getData(meshObject)
+ bl.mesh.addMaterial(mesh, material)
+ meshObject.name='c_%d' % i
+ #meshObject.draw_transparent=True
+ #meshObject.draw_wire=True
+ meshObject.max_draw_type='SOLID'
+ rot=c.rot
+ meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
+
+ meshObject[CONSTRAINT_NAME]=c.getName()
+ meshObject[CONSTRAINT_A]=io.rigidbodies[c.rigidA].getName()
+ meshObject[CONSTRAINT_B]=io.rigidbodies[c.rigidB].getName()
+ meshObject[CONSTRAINT_POS_MIN]=VtoV(c.constraintPosMin)
+ meshObject[CONSTRAINT_POS_MAX]=VtoV(c.constraintPosMax)
+ meshObject[CONSTRAINT_ROT_MIN]=VtoV(c.constraintRotMin)
+ meshObject[CONSTRAINT_ROT_MAX]=VtoV(c.constraintRotMax)
+ meshObject[CONSTRAINT_SPRING_POS]=VtoV(c.springPos)
+ meshObject[CONSTRAINT_SPRING_ROT]=VtoV(c.springRot)
+
+ for meshObject in reversed(constraintMeshes):
+ bl.object.makeParent(container, meshObject)
+
+ return container
+
+
+def __importRigidBodies(io):
+ if isBlender24():
+ return
+ print("create rigid bodies")
+
+ container=bl.object.createEmpty('RigidBodies')
+ layer=[
+ True, False, False, False, False, False, False, False, False, False,
+ False, False, False, False, False, False, False, False, False, False,
+ ]
+ material=bl.material.create('rigidBody')
+ rigidMeshes=[]
+ for i, rigid in enumerate(io.rigidbodies):
+ if rigid.boneIndex==0xFFFF:
+ # no reference bone
+ bone=io.bones[0]
+ else:
+ bone=io.bones[rigid.boneIndex]
+ pos=bone.pos+rigid.position
+
+ if rigid.shapeType==pmd.SHAPE_SPHERE:
+ bpy.ops.mesh.primitive_ico_sphere_add(
+ location=(pos.x, pos.z, pos.y),
+ layer=layer
+ )
+ bpy.ops.transform.resize(
+ value=(rigid.w, rigid.w, rigid.w))
+ elif rigid.shapeType==pmd.SHAPE_BOX:
+ bpy.ops.mesh.primitive_cube_add(
+ location=(pos.x, pos.z, pos.y),
+ layer=layer
+ )
+ bpy.ops.transform.resize(
+ value=(rigid.w, rigid.d, rigid.h))
+ elif rigid.shapeType==pmd.SHAPE_CAPSULE:
+ bpy.ops.mesh.primitive_tube_add(
+ location=(pos.x, pos.z, pos.y),
+ layer=layer
+ )
+ bpy.ops.transform.resize(
+ value=(rigid.w, rigid.w, rigid.h))
+ else:
+ assert(False)
+
+ meshObject=bl.object.getActive()
+ mesh=bl.object.getData(meshObject)
+ rigidMeshes.append(meshObject)
+ bl.mesh.addMaterial(mesh, material)
+ meshObject.name='r_%d' % i
+ meshObject[RIGID_NAME]=rigid.getName()
+ #meshObject.draw_transparent=True
+ #meshObject.draw_wire=True
+ meshObject.max_draw_type='WIRE'
+ rot=rigid.rotation
+ meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
+
+ # custom properties
+ meshObject[RIGID_SHAPE_TYPE]=rigid.shapeType
+ meshObject[RIGID_PROCESS_TYPE]=rigid.processType
+
+ bone_name = englishmap.getEnglishBoneName(bone.getName())
+ if not bone_name:
+ bone_name=bone.getName()
+ meshObject[RIGID_BONE_NAME]=bone_name
+
+ meshObject[RIGID_GROUP]=rigid.group
+ meshObject[RIGID_INTERSECTION_GROUP]=rigid.target
+ meshObject[RIGID_WEIGHT]=rigid.weight
+ meshObject[RIGID_LINEAR_DAMPING]=rigid.linearDamping
+ meshObject[RIGID_ANGULAR_DAMPING]=rigid.angularDamping
+ meshObject[RIGID_RESTITUTION]=rigid.restitution
+ meshObject[RIGID_FRICTION]=rigid.friction
+
+ for meshObject in reversed(rigidMeshes):
+ bl.object.makeParent(container, meshObject)
+
+ return container
+
+
+def _execute(filename):
+ """
+ load pmd file to context.
+ """
+
+ # load pmd
+ bl.progress_set('load %s' % filename, 0.0)
+
+ io=pmd.IO()
+ if not io.read(filename):
+ bl.message("fail to load %s" % filename)
+ return
+ bl.progress_set('loaded', 0.1)
+
+ # create root object
+ model_name=io.getEnglishName()
+ if len(model_name)==0:
+ model_name=io.getName()
+ root=bl.object.createEmpty(model_name)
+ root[MMD_MB_NAME]=io.getName()
+ root[MMD_MB_COMMENT]=io.getComment()
+ root[MMD_COMMENT]=io.getEnglishComment()
+
+ # toon textures
+ tex_dir=os.path.dirname(filename)
+ toonTextures, toonMaterial=__importToonTextures(io, tex_dir)
+ bl.object.makeParent(root, toonTextures)
+
+ # import mesh
+ mesh_objects=__importMaterialAndMesh(io, tex_dir, toonMaterial)
+ for o in mesh_objects:
+ bl.object.makeParent(root, o)
+
+ # import armature
+ armature_object=__importArmature(io)
+ if armature_object:
+ bl.object.makeParent(root, armature_object)
+ armature = bl.object.getData(armature_object)
+
+ # add armature modifier
+ for o in mesh_objects:
+ bl.modifier.addArmature(o, armature_object)
+
+ # Limitation
+ for n, b in bl.object.getPose(armature_object).bones.items():
+ poseBoneLimit(n, b)
+
+ # import rigid bodies
+ rigidBodies=__importRigidBodies(io)
+ if rigidBodies:
+ bl.object.makeParent(root, rigidBodies)
+
+ # import constraints
+ constraints=__importConstraints(io)
+ if constraints:
+ bl.object.makeParent(root, constraints)
+
+ bl.object.activate(root)
+
+
+if isBlender24():
+ # for 2.4
+ def execute_24(filename):
+ bl.initialize('pmd_import', bpy.data.scenes.active)
+ _execute(filename.decode(bl.INTERNAL_ENCODING))
+ bl.finalize()
+
+ Blender.Window.FileSelector(
+ execute_24,
+ 'Import PMD file',
+ Blender.sys.makename(ext='.pmd'))
+
+else:
+ # 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.
+ filepath = bpy.props.StringProperty()
+ filename = bpy.props.StringProperty()
+ directory = bpy.props.StringProperty()
+
+ def execute(self, context):
+ bl.initialize('pmd_import', context.scene)
+ _execute(self.properties.filepath)
+ bl.finalize()
+ return 'FINISHED'
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ try:
+ wm.fileselect_add(self)
+ except:
+ 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)",
+ icon='PLUGIN'
+ )
+
+ def register():
+ bpy.types.INFO_MT_file_import.append(menu_func)
+
+ def unregister():
+ bpy.types.INFO_MT_file_import.remove(menu_func)
+
+ if __name__=="__main__":
+ register()
+
--- /dev/null
+#!/usr/bin/env python
+# coding: utf8
+"""
+日本語名との変換マップ
+"""
+import sys
+
+"""
+ボーン名変換
+"""
+boneMap=[
+("center", "センター", 1),
+("upper body", "上半身"),
+("neck", "首"),
+("head", "頭"),
+("eye_L", "左目", 5),
+("eye_R", "右目", 5),
+("necktie1", "ネクタイ1"),
+("necktie2", "ネクタイ2"),
+("necktie3", "ネクタイ3"),
+("lower body", "下半身"),
+("waist accessory", "腰飾り"),
+("hair1_L", "左髪1"),
+("hair2_L", "左髪2"),
+("hair3_L", "左髪3"),
+("hair4_L", "左髪4"),
+("hair5_L", "左髪5"),
+("hair6_L", "左髪6"),
+("shoulder_L", "左肩"),
+("arm_L", "左腕"),
+("arm twist_L", "左腕捩", 8),
+("elbow_L", "左ひじ"),
+("wrist twist_L", "左手捩", 8),
+("wrist_L", "左手首"),
+("sleeve_L", "左袖", 1),
+("thumb1_L", "左親指1"),
+("thumb2_L", "左親指2"),
+("fore1_L", "左人指1"),
+("fore2_L", "左人指2"),
+("fore3_L", "左人指3"),
+("middle1_L", "左中指1"),
+("middle2_L", "左中指2"),
+("middle3_L", "左中指3"),
+("third1_L", "左薬指1"),
+("third2_L", "左薬指2"),
+("third3_L", "左薬指3"),
+("little1_L", "左小指1"),
+("little2_L", "左小指2"),
+("little3_L", "左小指3"),
+("front skirt_L", "左スカート前"),
+("back skirt_L", "左スカート後"),
+("leg_L", "左足"),
+("knee_L", "左ひざ"),
+("ankle_L", "左足首"),
+("hair1_R", "右髪1"),
+("hair2_R", "右髪2"),
+("hair3_R", "右髪3"),
+("hair4_R", "右髪4"),
+("hair5_R", "右髪5"),
+("hair6_R", "右髪6"),
+("shoulder_R", "右肩"),
+("arm_R", "右腕"),
+("arm twist_R", "右腕捩", 8),
+("elbow_R", "右ひじ"),
+("wrist twist_R", "右手捩", 8),
+("wrist_R", "右手首"),
+("sleeve_R", "右袖", 1),
+("thumb1_R", "右親指1"),
+("thumb2_R", "右親指2"),
+("fore1_R", "右人指1"),
+("fore2_R", "右人指2"),
+("fore3_R", "右人指3"),
+("middle1_R", "右中指1"),
+("middle2_R", "右中指2"),
+("middle3_R", "右中指3"),
+("third1_R", "右薬指1"),
+("third2_R", "右薬指2"),
+("third3_R", "右薬指3"),
+("little1_R", "右小指1"),
+("little2_R", "右小指2"),
+("little3_R", "右小指3"),
+("front skirt_R", "右スカート前"),
+("back skirt_R", "右スカート後"),
+("leg_R", "右足"),
+("knee_R", "右ひざ"),
+("ankle_R", "右足首"),
+("eyes", "両目"),
+("front hair1", "前髪1"),
+("front hair2", "前髪2"),
+("front hair3", "前髪3"),
+("eyelight_L", "左目光"),
+("eyelight_R", "右目光"),
+("necktie3_t", "ネクタイ4"),
+("hair6_L_t", "左髪7"),
+("hair6_R_t", "右髪7"),
+("ankle_L_t", "左つま先"),
+("ankle_R_t", "右つま先"),
+("necktie IK", "ネクタイIK"),
+("hair IK_L", "左髪IK"),
+("hair IK_R", "右髪IK"),
+("leg IK_L", "左足IK"),
+("leg IK_R", "右足IK"),
+("toe IK_L", "左つま先IK"),
+("toe IK_R", "右つま先IK"),
+
+("lower body_t", "下半身先"),
+("head_t", "頭先"),
+("eye_L_t", "左目先"),
+("eye_R_t", "右目先"),
+("waist accessory_t", "腰飾り先"),
+
+("sleeve_L_t", "左袖先"),
+("wrist_L_t", "左手先"),
+("thumb2_L_t", "左親指先"),
+("fore3_L_t", "左人差指先"),
+("middle3_L_t", "左中指先"),
+("third3_L_t", "左薬指先"),
+("little3_L_t", "左小指先"),
+("front skirt_L_t", "左スカート前先"),
+("back skirt_L_t", "左スカート後先"),
+
+("sleeve_R_t", "右袖先"),
+("wrist_R_t", "右手先"),
+("thumb2_R_t", "右親指先"),
+("fore3_R_t", "右人差指先"),
+("middle3_R_t", "右中指先"),
+("third3_R_t", "右薬指先"),
+("little3_R_t", "右小指先"),
+("front skirt_R_t", "右スカート前先"),
+("back skirt_R_t", "右スカート後先"),
+
+("center_t", "センター先"),
+("eyes_t", "両目先"),
+("necktie IK_t", "ネクタイIK先"),
+("hair IK_L_t", "左髪IK先"),
+("hair IK_R_t", "右髪IK先"),
+("leg IK_L_t", "左足IK先"),
+("leg IK_R_t", "右足IK先"),
+("toe IK_L_t", "左つま先IK先"),
+("toe IK_R_t", "右つま先IK先"),
+("front hair1_t", "前髪1先"),
+("front hair2_t", "前髪2先"),
+("front hair3_t", "前髪3先"),
+("eyelight_L_t", "左目光先"),
+("eyelight_R_t", "右目光先"),
+("arm twist_L_t", "左腕捩先"),
+("wrist twist_L_t", "左手捩先"),
+("arm twist_R_t", "右腕捩先"),
+("wrist twist_R_t", "右手捩先"),
+("arm twist1_L", "左腕捩1", 9),
+("arm twist2_L", "左腕捩2", 9),
+("arm twist3_L", "左腕捩3", 9),
+("arm twist1_R", "右腕捩1", 9),
+("arm twist2_R", "右腕捩2", 9),
+("arm twist3_R", "右腕捩3", 9),
+#
+("arm twist1_L_t", "左腕捩1先"),
+("arm twist2_L_t", "左腕捩2先"),
+("arm twist3_L_t", "左腕捩3先"),
+("arm twist1_R_t", "右腕捩1先"),
+("arm twist2_R_t", "右腕捩2先"),
+("arm twist3_R_t", "右腕捩3先"),
+
+# 追加ボーン
+("root", "全ての親"),
+("root_t", "全ての親先"),
+("group", "グループ"),
+("group_t", "グループ先"),
+("front_shirt_L", "左シャツ前"),
+("front_shirt_R", "右シャツ前"),
+("back_shirt_L", "左シャツ後"),
+("back_shirt_R", "右シャツ後"),
+]
+def getEnglishBoneName(name):
+ for v in boneMap:
+ if v[1]==name:
+ return v[0]
+
+def getIndexByEnglish(name):
+ for i, v in enumerate(boneMap):
+ if v[0]==name:
+ return i
+
+def getUnicodeBoneName(name):
+ for v in boneMap:
+ if v[0]==name:
+ return v
+
+"""
+モーフ名変換
+"""
+skinMap=[
+("base", "base", 0),
+("serious", "真面目", 1),
+("sadness", "困る", 1),
+("cheerful", "にこり", 1),
+("anger", "怒り", 1),
+("go up", "上", 1),
+("go down", "下", 1),
+("blink", "まばたき", 2),
+("smile", "笑い", 2),
+("wink", "ウィンク", 2),
+("wink2", "ウィンク2", 2),
+("wink_R", "ウィンク右", 2),
+("wink2_R", "ウィンク2右", 2),
+("close><", "はぅ", 2),
+("calm", "なごみ", 2),
+("surprise", "びっくり", 2),
+("doubt", "じと目", 2),
+("confuse", "なぬ!", 2),
+("pupil", "瞳小", 4),
+("a", "あ", 3),
+("i", "い", 3),
+("u", "う", 3),
+("o", "お", 3),
+("triangle", "▲", 3),
+("regret", "∧", 3),
+("omega", "ω", 3),
+("omegabox", "ω□", 3),
+("fool", "はんっ!", 3),
+("tongue", "ぺろっ", 4),
+("e-", "えー", 3),
+("grin", "にやり", 3),
+]
+def getEnglishSkinName(name):
+ for v in skinMap:
+ if v[1]==name:
+ return v[0]
+
+def getUnicodeSkinName(name):
+ for v in skinMap:
+ if v[0]==name:
+ return v
+
+"""
+ボーングループ名変換
+"""
+boneGroupMap=[
+ ("IK", "IK"),
+ ("Body[u]", "体(上)"),
+ ("Hair", "髪"),
+ ("Arms", "腕"),
+ ("Fingers", "指"),
+ ("Body[l]", "体(下)"),
+ ("Legs", "足"),
+ ]
+def getEnglishBoneGroupName(name):
+ for v in boneGroupMap:
+ if v[1]==name:
+ return v[0]
+
+def getUnicodeBoneGroupName(name):
+ for v in boneGroupMap:
+ if v[0]==name:
+ return v[1]
+
+
+###############################################################################
+# blender2.4 str to unicode
+###############################################################################
+if sys.version_info[0]<3:
+ print('convert boneMap and skinMap to unicode...')
+ # python2.x
+ # unicodeに変換
+ for i, l in enumerate(boneMap):
+ replace=[]
+ for j, m in enumerate(l):
+ if j==1:
+ replace.append(m.decode('utf-8'))
+ else:
+ replace.append(m)
+ boneMap[i]=replace
+
+ for i, l in enumerate(skinMap):
+ replace=[]
+ for j, m in enumerate(l):
+ if j==1:
+ replace.append(m.decode('utf-8'))
+ else:
+ replace.append(m)
+ skinMap[i]=replace
+ print('done')
+
--- /dev/null
+#!/usr/bin/python
+# coding: utf-8
+"""
+20091202: VPD読み込みを追加
+20100318: PMD書き込みを追加
+20100731: meshioと互換になるように改造
+
+VMDの読み込み
+http://yumin3123.at.webry.info/200810/article_4.html
+http://atupdate.web.fc2.com/vmd_format.htm
+
+PMDの読み込み
+http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
+
+VPDの読み込み
+
+ToDo:
+ rigdid bodies
+ constraints
+"""
+import sys
+import codecs
+import os.path
+import struct
+import math
+import re
+#import numpy
+from decimal import *
+
+ENCODING='cp932'
+
+if sys.version_info[0]>=3:
+ xrange=range
+
+###############################################################################
+# utility
+###############################################################################
+def truncate_zero(src):
+ """
+ 0x00以降を捨てる
+ """
+ pos = src.find(b"\x00")
+ assert(type(src)==bytes)
+ if pos >= 0:
+ return src[:pos]
+ else:
+ return src
+
+def radian_to_degree(x):
+ return x/math.pi * 180.0
+
+
+###############################################################################
+# geometry
+###############################################################################
+class Vector2(object):
+ __slots__=['x', 'y']
+ def __init__(self, x=0, y=0):
+ self.x=x
+ self.y=y
+
+ def __str__(self):
+ return "<%f %f>" % (self.x, self.y)
+
+ def __getitem__(self, key):
+ if key==0:
+ return self.x
+ elif key==1:
+ return self.y
+ else:
+ assert(False)
+
+ def to_tuple(self):
+ return (self.x, self.y)
+
+
+class Vector3(object):
+ __slots__=['x', 'y', 'z']
+ def __init__(self, x=0, y=0, z=0):
+ self.x=x
+ self.y=y
+ self.z=z
+
+ def __str__(self):
+ return "<%f %f %f>" % (self.x, self.y, self.z)
+
+ def __getitem__(self, key):
+ if key==0:
+ return self.x
+ elif key==1:
+ return self.y
+ elif key==2:
+ return self.z
+ else:
+ assert(False)
+
+ def to_tuple(self):
+ return (self.x, self.y, self.z)
+
+class Quaternion(object):
+ __slots__=['x', 'y', 'z', 'w']
+ def __init__(self, x=0, y=0, z=0, w=1):
+ self.x=x
+ self.y=y
+ self.z=z
+ self.w=w
+
+ def __str__(self):
+ return "<%f %f %f %f>" % (self.x, self.y, self.z, self.w)
+
+ def __mul__(self, rhs):
+ u=numpy.array([self.x, self.y, self.z], 'f')
+ v=numpy.array([rhs.x, rhs.y, rhs.z], 'f')
+ xyz=self.w*v+rhs.w*u+numpy.cross(u, v)
+ q=Quaternion(xyz[0], xyz[1], xyz[2], self.w*rhs.w-numpy.dot(u, v))
+ return q
+
+ def dot(self, rhs):
+ return self.x*rhs.x+self.y*rhs.y+self.z*rhs.z+self.w*rhs.w
+
+ def getMatrix(self):
+ sqX=self.x*self.x
+ sqY=self.y*self.y
+ sqZ=self.z*self.z
+ xy=self.x*self.y
+ xz=self.x*self.z
+ yz=self.y*self.z
+ wx=self.w*self.x
+ wy=self.w*self.y
+ wz=self.w*self.z
+ return numpy.array([
+ # 1
+ [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
+ # 2
+ [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
+ # 3
+ [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
+ # 4
+ [0, 0, 0, 1]],
+ 'f')
+
+ def getRHMatrix(self):
+ x=-self.x
+ y=-self.y
+ z=self.z
+ w=self.w
+ sqX=x*x
+ sqY=y*y
+ sqZ=z*z
+ xy=x*y
+ xz=x*z
+ yz=y*z
+ wx=w*x
+ wy=w*y
+ wz=w*z
+ return numpy.array([
+ # 1
+ [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
+ # 2
+ [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
+ # 3
+ [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
+ # 4
+ [0, 0, 0, 1]],
+ 'f')
+
+ def getRollPitchYaw(self):
+ m=self.getMatrix()
+
+ roll = math.atan2(m[0, 1], m[1, 1])
+ pitch = math.asin(-m[2, 1])
+ yaw = math.atan2(m[2, 0], m[2, 2])
+
+ if math.fabs(math.cos(pitch)) < 1.0e-6:
+ roll += m[0, 1] > math.pi if 0.0 else -math.pi
+ yaw += m[2, 0] > math.pi if 0.0 else -math.pi
+
+ return roll, pitch, yaw
+
+ def getSqNorm(self):
+ return self.x*self.x+self.y*self.y+self.z*self.z+self.w*self.w
+
+ def getNormalized(self):
+ f=1.0/self.getSqNorm()
+ q=Quaternion(self.x*f, self.y*f, self.z*f, self.w*f)
+ return q
+
+ def getRightHanded(self):
+ "swap y and z axis"
+ return Quaternion(-self.x, -self.z, -self.y, self.w)
+
+ @staticmethod
+ def createFromAxisAngle(axis, rad):
+ q=Quaternion()
+ half_rad=rad/2.0
+ c=math.cos(half_rad)
+ s=math.sin(half_rad)
+ return Quaternion(axis[0]*s, axis[1]*s, axis[2]*s, c)
+
+
+class RGBA(object):
+ __slots__=['r', 'g', 'b', 'a']
+ def __init__(self, r=0, g=0, b=0, a=1):
+ self.r=r
+ self.g=g
+ self.b=b
+ self.a=a
+
+ def __getitem__(self, key):
+ if key==0:
+ return self.r
+ elif key==1:
+ return self.g
+ elif key==2:
+ return self.b
+ elif key==3:
+ return self.a
+ else:
+ assert(False)
+
+
+###############################################################################
+# VMD
+###############################################################################
+class ShapeData(object):
+ __slots__=['name', 'frame', 'ratio']
+ def __init__(self, name):
+ self.name=name
+ self.frame=-1
+ self.ratio=0
+
+ def __cmp__(self, other):
+ return cmp(self.frame, other.frame)
+
+class MotionData(object):
+ __slots__=['name', 'frame', 'pos', 'q', 'complement']
+ def __init__(self, name):
+ self.name=name
+ self.frame=-1
+ self.pos=Vector3()
+ self.q=Quaternion()
+
+ def __cmp__(self, other):
+ return cmp(self.frame, other.frame)
+
+ def __str__(self):
+ return '<MotionData "%s" %d %s%s>' % (self.name, self.frame, self.pos, self.q)
+
+class VMDLoader(object):
+ __slots__=['io', 'end', 'signature',
+ 'model_name', 'last_frame',
+ 'motions', 'shapes', 'cameras', 'lights',
+ ]
+ def __init__(self):
+ self.model_name=''
+ self.motions=[]
+ self.shapes=[]
+ self.cameras=[]
+ self.lights=[]
+ self.last_frame=0
+
+ def __str__(self):
+ return '<VMDLoader model: "%s", motion: %d, shape: %d, camera: %d, light: %d>' % (
+ self.model_name, len(self.motions), len(self.shapes),
+ len(self.cameras), len(self.lights))
+
+ def load(self, path, io, end):
+ self.io=io
+ self.end=end
+
+ # signature
+ self.signature=truncate_zero(self.io.read(30))
+ version=self.validate_signature(self.signature)
+ if not version:
+ print("invalid signature", self.signature)
+ return False
+
+ if version==1:
+ if not self.load_verstion_1():
+ return False
+ elif version==2:
+ if not self.load_verstion_2():
+ return False
+ else:
+ raise Exception("unknown version")
+
+ # post process
+ motions=self.motions
+ self.motions={}
+ for m in motions:
+ if not m.name in self.motions:
+ self.motions[m.name]=[]
+ self.motions[m.name].append(m)
+ for name in self.motions.keys():
+ self.motions[name].sort()
+
+ shapes=self.shapes
+ self.shapes={}
+ for s in shapes:
+ if not s.name in self.shapes:
+ self.shapes[s.name]=[]
+ self.shapes[s.name].append(s)
+ for name in self.shapes.keys():
+ self.shapes[name].sort()
+
+ return True
+
+ def getMotionCount(self):
+ count=0
+ for v in self.motions.values():
+ count+=len(v)
+ return count
+
+ def getShapeCount(self):
+ count=0
+ for v in self.shapes.values():
+ count+=len(v)
+ return count
+
+ def load_verstion_1(self):
+ # model name
+ self.model_name=truncate_zero(self.io.read(10))
+ if not self.loadMotion_1():
+ return False
+ return True
+
+ def loadMotion_1(self):
+ count=struct.unpack('H', self.io.read(2))[0]
+ self.io.read(2)
+ for i in xrange(0, count):
+ self.loadFrameData()
+ return True
+
+ ############################################################
+ def load_verstion_2(self):
+ # model name
+ self.model_name=truncate_zero(self.io.read(20))
+
+ if not self.loadMotion():
+ return False
+ if not self.loadShape():
+ return False
+ if not self.loadCamera():
+ return False
+ if not self.loadLight():
+ return False
+ #assert(self.io.tell()==self.end)
+ #self.motions.sort(lambda l, r: l.name<r.name)
+
+ return True
+
+ def validate_signature(self, signature):
+ if self.signature == "Vocaloid Motion Data 0002":
+ return 2
+ if self.signature == "Vocaloid Motion Data file":
+ return 1
+ else:
+ return None
+
+ def loadMotion(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ self.loadFrameData()
+ return True
+
+ def loadShape(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ self.loadShapeData()
+ return True
+
+ def loadCamera(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ # not implemented
+ assert(False)
+ pass
+ return True
+
+ def loadLight(self):
+ count=struct.unpack('I', self.io.read(4))[0]
+ for i in xrange(0, count):
+ # not implemented
+ assert(False)
+ pass
+ return True
+
+ def loadFrameData(self):
+ """
+ フレームひとつ分を読み込む
+ """
+ data=MotionData(truncate_zero(self.io.read(15)))
+ (data.frame, data.pos.x, data.pos.y, data.pos.z,
+ data.q.x, data.q.y, data.q.z, data.q.w) = struct.unpack(
+ 'I7f', self.io.read(32))
+ # complement data
+ data.complement=''.join(
+ ['%x' % x for x in struct.unpack('64B', self.io.read(64))])
+ self.motions.append(data)
+ if data.frame>self.last_frame:
+ self.last_frame=data.frame
+
+ def loadShapeData(self):
+ """
+ モーフデータひとつ分を読み込む
+ """
+ data=ShapeData(truncate_zero(self.io.read(15)))
+ (data.frame, data.ratio)=struct.unpack('If', self.io.read(8))
+ self.shapes.append(data)
+ if data.frame>self.last_frame:
+ self.last_frame=data.frame
+
+ # vmd -> csv
+ ############################################################
+ def create_csv_line(m):
+ # quaternion -> euler angle
+ (roll, pitch, yaw)=m.q.getRollPitchYaw()
+ return '%s,%d,%g,%g,%g,%g,%g,%g,0x%s\n' % (
+ m.name, m.frame, m.pos.x, m.pos.y, m.pos.z,
+ to_degree(pitch), to_degree(yaw), to_degree(roll), m.complement
+ )
+
+ def write_csv(l, path):
+ sys.setdefaultencoding('cp932')
+ csv=open(path, "w")
+ csv.write('%s,0\n' % l.signature)
+ csv.write('%s\n' % l.model_name)
+ # motion
+ csv.write('%d\n' % len(l.motions))
+ for m in l.motions:
+ csv.write(create_csv_line(m))
+ # shape
+ csv.write('%d\n' % len(l.shapes))
+ for s in l.shapes:
+ csv.write('%s,%d,%f\n' % ( s.name, s.frame, s.ratio))
+ # camera
+ csv.write('%d\n' % len(l.cameras))
+ for camera in l.cameras:
+ assert(False)
+ # light
+ csv.write('%d\n' % len(l.lights))
+ for light in l.lights:
+ assert(False)
+
+
+###############################################################################
+# PMD
+###############################################################################
+class Vertex(object):
+ __slots__=['pos', 'normal', 'uv', 'bone0', 'bone1', 'weight0', 'edge_flag']
+ def __init__(self, x=0, y=0, z=0, nx=0, ny=0, nz=0, u=0, v=0,
+ bone0=0, bone1=0, weight0=0, edge_flag=0):
+ self.pos=Vector3(x, y, z)
+ self.normal=Vector3(nx, ny, nz)
+ self.uv=Vector2(u, v)
+ self.bone0=bone0
+ self.bone1=bone1
+ self.weight0=weight0
+ self.edge_flag=edge_flag
+
+ def __str__(self):
+ return "<%s %s %s, (%d, %d, %d)>" % (str(self.pos), str(self.normal), str(self.uv), self.bone0, self.bone1, self.weight0)
+
+ def __getitem__(self, key):
+ if key==0:
+ return self.pos.x
+ elif key==1:
+ return self.pos.y
+ elif key==2:
+ return self.pos.z
+ else:
+ assert(False)
+
+class Material(object):
+ __slots__=[
+ 'diffuse', 'shinness', 'specular',
+ 'ambient', 'vertex_count', 'texture', 'toon_index', 'flag',
+ ]
+
+ def __init__(self, dr=0, dg=0, db=0, alpha=1,
+ specular=0, sr=0, sg=0, sb=0, ar=0, ag=0, ab=0):
+ self.diffuse=RGBA(dr, dg, db, alpha)
+ self.specular=RGBA(sr, sg, sb)
+ self.shinness=specular
+ self.ambient=RGBA(ar, ag, ab)
+ self.vertex_count=0
+ self.texture=''
+ self.toon_index=0
+ self.flag=0
+
+ def __str__(self):
+ return "<Material [%f, %f, %f, %f]>" % (
+ self.diffuse[0], self.diffuse[1],
+ self.diffuse[2], self.diffuse[3],
+ )
+
+ def getTexture(self): return self.texture.decode('cp932')
+ def setTexture(self, u): self.texture=u
+
+# @return 各マテリアルについて、そのマテリアルが保持する面の回数だけ
+# マテリアル自身を返す
+def material_per_face(materials):
+ for m in materials:
+ for x in xrange(int(m.vertex_count/3)):
+ yield m
+
+class Bone(object):
+ # kinds
+ ROTATE = 0
+ ROTATE_MOVE = 1
+ IK = 2
+ IK_ROTATE_INFL = 4
+ ROTATE_INFL = 5
+ IK_TARGET = 6
+ UNVISIBLE = 7
+ # since v4.0
+ ROLLING=8 # ?
+ TWEAK=9
+ __slots__=['name', 'index', 'type', 'parent', 'ik', 'pos',
+ 'children', 'english_name', 'ik_index',
+ 'parent_index', 'tail_index', 'tail',
+ ]
+ def __init__(self, name='bone', type=0):
+ self.name=name
+ self.index=0
+ self.type=type
+ self.parent_index=0xFFFF
+ self.tail_index=0
+ self.tail=Vector3(0, 0, 0)
+ self.parent=None
+ self.ik_index=0xFFFF
+ self.pos=Vector3(0, 0, 0)
+ self.children=[]
+ self.english_name=''
+
+ def getName(self): return self.name.decode('cp932')
+ def setName(self, u): self.name=u
+ def setEnglishName(self, u): self.english_name=u
+
+ def hasParent(self):
+ return self.parent_index!=0xFFFF
+
+ def hasChild(self):
+ return self.tail_index!=0
+
+ def display(self, indent=[]):
+ if len(indent)>0:
+ prefix=''
+ for i, is_end in enumerate(indent):
+ if i==len(indent)-1:
+ break
+ else:
+ prefix+=' ' if is_end else ' |'
+ uni='%s +%s(%s)' % (prefix, unicode(self), self.english_name)
+ print(uni.encode(ENCODING))
+ else:
+ uni='%s(%s)' % (unicode(self), self.english_name)
+ print(uni.encode(ENCODING))
+
+ child_count=len(self.children)
+ for i in xrange(child_count):
+ child=self.children[i]
+ if i<child_count-1:
+ child.display(indent+[False])
+ else:
+ # last
+ child.display(indent+[True])
+
+# 0
+class Bone_Rotate(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_Rotate, self).__init__(name, 0)
+ def __str__(self):
+ return '<ROTATE %s>' % (self.name)
+# 1
+class Bone_RotateMove(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_RotateMove, self).__init__(name, 1)
+ def __str__(self):
+ return '<ROTATE_MOVE %s>' % (self.name)
+# 2
+class Bone_IK(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_IK, self).__init__(name, 2)
+ def __str__(self):
+ return '<IK %s>' % (self.name)
+# 4
+class Bone_IKRotateInfl(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_IKRotateInfl, self).__init__(name, 4)
+ def __str__(self):
+ return '<IK_ROTATE_INFL %s>' % (self.name)
+# 5
+class Bone_RotateInfl(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_RotateInfl, self).__init__(name, 5)
+ def __str__(self):
+ return '<ROTATE_INFL %s>' % (self.name)
+# 6
+class Bone_IKTarget(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_IKTarget, self).__init__(name, 6)
+ def __str__(self):
+ return '<IK_TARGET %s>' % (self.name)
+# 7
+class Bone_Unvisible(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_Unvisible, self).__init__(name, 7)
+ def __str__(self):
+ return '<UNVISIBLE %s>' % (self.name)
+# 8
+class Bone_Rolling(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_Rolling, self).__init__(name, 8)
+ def __str__(self):
+ return '<ROLLING %s>' % (self.name)
+# 9
+class Bone_Tweak(Bone):
+ __slots__=[]
+ def __init__(self, name):
+ super(Bone_Tweak, self).__init__(name, 9)
+ def __str__(self):
+ return '<TWEAK %s>' % (self.name)
+
+
+def createBone(name, type):
+ if type==0:
+ return Bone_Rotate(name)
+ elif type==1:
+ return Bone_RotateMove(name)
+ elif type==2:
+ return Bone_IK(name)
+ elif type==3:
+ raise Exception("no used bone type: 3(%s)" % name)
+ elif type==4:
+ return Bone_IKRotateInfl(name)
+ elif type==5:
+ return Bone_RotateInfl(name)
+ elif type==6:
+ return Bone_IKTarget(name)
+ elif type==7:
+ return Bone_Unvisible(name)
+ elif type==8:
+ return Bone_Rolling(name)
+ elif type==9:
+ return Bone_Tweak(name)
+ else:
+ raise Exception("unknown bone type: %d(%s)", type, name)
+
+
+class IK(object):
+ __slots__=['index', 'target', 'iterations', 'weight', 'length', 'children']
+ def __init__(self, index=0, target=0):
+ self.index=index
+ self.target=target
+ self.iterations=None
+ self.weight=None
+ self.children=[]
+
+ def __str__(self):
+ return "<IK index: %d, target: %d, iterations: %d, weight: %f, children: %s(%d)>" %(self.index, self.target, self.iterations, self.weight, '-'.join([str(i) for i in self.children]), len(self.children))
+
+class Skin(object):
+ __slots__=['name', 'type', 'indices', 'pos_list', 'english_name',
+ 'vertex_count']
+ def __init__(self, name='skin'):
+ self.name=name
+ self.type=None
+ self.indices=[]
+ self.pos_list=[]
+ self.english_name=''
+ self.vertex_count=0
+
+ def getName(self): return self.name.decode('cp932')
+ def setName(self, u): self.name=u
+ def setEnglishName(self, u): self.english_name=u
+
+ def append(self, index, x, y, z):
+ self.indices.append(index)
+ self.pos_list.append(Vector3(x, y, z))
+
+ def __str__(self):
+ return '<Skin name: "%s", type: %d, vertex: %d>' % (
+ self.name, self.type, len(self.indices))
+
+class ToonTexture(object):
+ __slots__=['name']
+ def __init__(self, name): self.name=name
+ def getName(self): return self.name.decode('cp932')
+ def setName(self, u): self.name=u
+
+class BoneGroup(object):
+ __slots__=['name', 'english_name']
+ def __init__(self, name='group'): self.name=name; self.english_name='center'
+ def getName(self): return self.name.decode('cp932')
+ def setName(self, u): self.name=u
+ def getEnglishName(self): return self.english_name.decode('cp932')
+ def setEnglishName(self, u): self.english_name=u
+
+class PMDLoader(object):
+ __slots__=['io', 'end', 'pos',
+ 'version', 'model_name', 'comment',
+ 'english_model_name', 'english_comment',
+ 'vertices', 'indices', 'materials', 'bones',
+ 'ik_list', 'morph_list',
+ 'face_list', 'bone_group_list', 'bone_display_list',
+ 'toon_textures',
+ 'no_parent_bones',
+ 'rigidbodies', 'constraints',
+ ]
+ def __init__(self):
+ self.version=1.0
+ self.model_name=b"default"
+ self.comment=b"default"
+ self.english_model_name=b'default'
+ self.english_comment=b'default'
+ self.vertices=[]
+ self.indices=[]
+ self.materials=[]
+ self.bones=[]
+ self.ik_list=[]
+ self.morph_list=[]
+
+ self.face_list=[]
+ self.bone_group_list=[]
+ self.bone_display_list=[]
+
+ self.toon_textures=[
+ ToonTexture(b'toon'), ToonTexture(b'toon'),
+ ToonTexture(b'toon'), ToonTexture(b'toon'),
+ ToonTexture(b'toon'), ToonTexture(b'toon'),
+ ToonTexture(b'toon'), ToonTexture(b'toon'),
+ ToonTexture(b'toon'), ToonTexture(b'toon'),
+ ]
+
+ self.no_parent_bones=[]
+
+ self.rigidbodies=[]
+ self.constraints=[]
+
+ def getName(self): return self.model_name.decode('cp932')
+ def setName(self, u): self.model_name=u
+ def getComment(self): return self.comment.decode('cp932')
+ def setComment(self, u): self.comment=u
+ def getEnglishName(self): return self.english_model_name.decode('cp932')
+ def setEnglishName(self, u): self.english_model_name=u
+ def getEnglishComment(self): return self.english_comment.decode('cp932')
+ def setEnglishComment(self, u): self.english_comment=u
+
+ def getToonTexture(self, i): return self.toon_textures[i]
+ def each_vertex(self): return self.vertices
+ def getUV(self, i): return self.vertices[i].uv
+ def addVertex(self):
+ v=Vertex()
+ self.vertices.append(v)
+ return v
+ def addMaterial(self):
+ m=Material()
+ self.materials.append(m)
+ return m
+ def addBone(self):
+ b=Bone()
+ self.bones.append(b)
+ return b
+ def addIK(self):
+ ik=IK()
+ self.ik_list.append(ik)
+ return ik
+ def addMorph(self):
+ s=Skin()
+ self.morph_list.append(s)
+ return s
+ def addBoneGroup(self):
+ g=BoneGroup()
+ self.bone_group_list.append(g)
+ return g
+ def addBoneDisplay(self, b, g):
+ self.bone_display_list.append((b, g))
+
+ def __str__(self):
+ return '<PMDLoader version: %g, model: "%s", vertex: %d, face: %d, material: %d, bone: %d ik: %d, skin: %d>' % (
+ self.version, self.model_name, len(self.vertices), len(self.indices),
+ len(self.materials), len(self.bones), len(self.ik_list), len(self.morph_list))
+
+ def _check_position(self):
+ """
+ if self.pos:
+ print(self.pos, self.io.tell()-self.pos)
+ """
+ self.pos=self.io.tell()
+ pass
+
+ def read(self, path):
+ size=os.path.getsize(path)
+ f=open(path, "rb")
+ return self.load(path, f, size)
+
+ def load(self, path, io, end):
+ self.io=io
+ self.pos=self.io.tell()
+ self.end=end
+ self._check_position()
+
+ if not self._loadHeader():
+ return False
+ self._check_position()
+
+ if not self._loadVertex():
+ return False
+ self._check_position()
+
+ if not self._loadFace():
+ return False
+ self._check_position()
+
+ if not self._loadMaterial():
+ return False
+ self._check_position()
+
+ if not self._loadBone():
+ return False
+ self._check_position()
+
+ if not self._loadIK():
+ return False
+ self._check_position()
+
+ if not self._loadSkin():
+ return False
+ self._check_position()
+
+ if not self._loadSkinIndex():
+ return False
+ self._check_position()
+
+ if not self._loadBoneName():
+ return False
+ self._check_position()
+
+ if not self._loadBoneIndex():
+ return False
+ self._check_position()
+
+ if not self._loadExtend():
+ print('fail to loadExtend')
+ return False
+
+ # 終端
+ if self.io.tell()!=self.end:
+ print("can not reach eof.")
+ print("current: %d, end: %d, remain: %d" % (
+ self.io.tell(), self.end, self.end-self.io.tell()))
+
+ # build bone tree
+ for i, child in enumerate(self.bones):
+ if child.parent_index==0xFFFF:
+ # no parent
+ self.no_parent_bones.append(child)
+ child.parent=None
+ else:
+ # has parent
+ parent=self.bones[child.parent_index]
+ child.parent=parent
+ parent.children.append(child)
+ # 後位置
+ if child.hasChild():
+ child.tail=self.bones[child.tail_index].pos
+
+ return True
+
+ def write(self, path):
+ io=open(path, 'wb')
+ if not io:
+ return False
+ # Header
+ io.write(b"Pmd")
+ io.write(struct.pack("f", self.version))
+ io.write(struct.pack("20s", self.model_name))
+ io.write(struct.pack("256s", self.comment))
+
+ # Vertices
+ io.write(struct.pack("I", len(self.vertices)))
+ sVertex=struct.Struct("=8f2H2B") # 38byte
+ assert(sVertex.size==38)
+ for v in self.vertices:
+ data=sVertex.pack(
+ v.pos[0], v.pos[1], v.pos[2],
+ v.normal[0], v.normal[1], v.normal[2],
+ v.uv[0], v.uv[1],
+ v.bone0, v.bone1, v.weight0, v.edge_flag)
+ io.write(data)
+
+ # Faces
+ io.write(struct.pack("I", len(self.indices)))
+ io.write(struct.pack("=%dH" % len(self.indices), *self.indices))
+
+ # material
+ io.write(struct.pack("I", len(self.materials)))
+ sMaterial=struct.Struct("=3fff3f3fBBI20s") # 70byte
+ assert(sMaterial.size==70)
+ for m in self.materials:
+ io.write(sMaterial.pack(
+ m.diffuse[0], m.diffuse[1], m.diffuse[2], m.diffuse[3],
+ m.shinness,
+ m.specular[0], m.specular[1], m.specular[2],
+ m.ambient[0], m.ambient[1], m.ambient[2],
+ m.toon_index, m.flag,
+ m.vertex_count,
+ m.texture
+ ))
+
+ # bone
+ io.write(struct.pack("H", len(self.bones)))
+ sBone=struct.Struct("=20sHHBH3f")
+ assert(sBone.size==39)
+ for b in self.bones:
+ io.write(sBone.pack(
+ b.name,
+ b.parent_index, b.tail_index, b.type, b.ik_index,
+ b.pos[0], b.pos[1], b.pos[2]))
+
+ # IK
+ io.write(struct.pack("H", len(self.ik_list)))
+ for ik in self.ik_list:
+ io.write(struct.pack("=2HBHf",
+ ik.index, ik.target, ik.length, ik.iterations, ik.weight
+ ))
+ for c in ik.children:
+ io.write(struct.pack("H", c))
+
+ # skin
+ io.write(struct.pack("H", len(self.morph_list)))
+ for s in self.morph_list:
+ io.write(struct.pack("20sIB",
+ s.name, len(s.indices), s.type))
+ for i, v in zip(s.indices, s.pos_list):
+ io.write(struct.pack("I3f", i, v[0], v[1], v[2]))
+
+ # skin list
+ io.write(struct.pack("B", len(self.face_list)))
+ for i in self.face_list:
+ io.write(struct.pack("H", i))
+
+ # bone name
+ io.write(struct.pack("B", len(self.bone_group_list)))
+ for g in self.bone_group_list:
+ io.write(struct.pack("50s", g.name))
+
+ # bone list
+ io.write(struct.pack("I", len(self.bone_display_list)))
+ for l in self.bone_display_list:
+ io.write(struct.pack("=HB", *l))
+
+ # ToDo
+ # Extend Data
+
+ return True
+
+
+ def _loadExtend(self):
+ ############################################################
+ # extend1: english name
+ ############################################################
+ if self.io.tell()>=self.end:
+ return True
+ if struct.unpack("B", self.io.read(1))[0]==1:
+ if not self.loadEnglishName():
+ return False
+ self._check_position()
+
+ ############################################################
+ # extend2: toon texture list
+ ############################################################
+ if self.io.tell()>=self.end:
+ return True
+ if not self.loadToonTexture():
+ return False
+ self._check_position()
+
+ ############################################################
+ # extend3: physics
+ ############################################################
+ if self.io.tell()>=self.end:
+ return True
+ #if not self.loadPhysics():
+ # return False
+ self._check_position()
+
+ return True
+
+ def _loadHeader(self):
+ signature=struct.unpack("3s", self.io.read(3))[0]
+ print(signature)
+ if signature!=b"Pmd":
+ print("invalid signature", signature)
+ return False
+ self.version=struct.unpack("f", self.io.read(4))[0]
+ self.model_name = truncate_zero(struct.unpack("20s", self.io.read(20))[0])
+ self.comment = truncate_zero(
+ struct.unpack("256s", self.io.read(256))[0])
+ return True
+
+ def _loadVertex(self):
+ count = struct.unpack("I", self.io.read(4))[0]
+ for i in xrange(count):
+ self.vertices.append(Vertex(*struct.unpack("8f2H2B", self.io.read(38))))
+ return True
+
+ def _loadFace(self):
+ count = struct.unpack("I", self.io.read(4))[0]
+ for i in xrange(0, count, 3):
+ self.indices+=struct.unpack("HHH", self.io.read(6))
+ return True
+
+ def _loadMaterial(self):
+ count = struct.unpack("I", self.io.read(4))[0]
+ for i in xrange(count):
+ material=Material(*struct.unpack("4ff3f3f", self.io.read(44)))
+ material.toon_index=struct.unpack("B", self.io.read(1))[0]
+ material.flag=struct.unpack("B", self.io.read(1))[0]
+ material.vertex_count=struct.unpack("I", self.io.read(4))[0]
+ texture=truncate_zero(struct.unpack("20s", self.io.read(20))[0])
+ # todo sphere map
+ #material.texture=texture.split('*')[0]
+ material.texture=texture
+ self.materials.append(material)
+ return True
+
+ def _loadBone(self):
+ size = struct.unpack("H", self.io.read(2))[0]
+ for i in xrange(size):
+ name=truncate_zero(struct.unpack("20s", self.io.read(20))[0])
+ parent_index, tail_index = struct.unpack("HH", self.io.read(4))
+ type = struct.unpack("B", self.io.read(1))[0]
+ bone=createBone(name, type)
+ bone.parent_index=parent_index
+ bone.tail_index=tail_index
+ bone.ik_index = struct.unpack("H", self.io.read(2))[0]
+ bone.pos = Vector3(*struct.unpack("3f", self.io.read(12)))
+ bone.english_name="bone%03d" % len(self.bones)
+ self.bones.append(bone)
+ return True
+
+ def _loadIK(self):
+ size = struct.unpack("H", self.io.read(2))[0]
+ for i in xrange(size):
+ ik=IK(*struct.unpack("2H", self.io.read(4)))
+ ik.length = struct.unpack("B", self.io.read(1))[0]
+ ik.iterations = struct.unpack("H", self.io.read(2))[0]
+ ik.weight = struct.unpack("f", self.io.read(4))[0]
+ for j in xrange(ik.length):
+ ik.children.append(struct.unpack("H", self.io.read(2))[0])
+ self.ik_list.append(ik)
+ return True
+
+ def _loadSkin(self):
+ size = struct.unpack("H", self.io.read(2))[0]
+ for i in xrange(size):
+ skin=Skin(truncate_zero(struct.unpack("20s", self.io.read(20))[0]))
+ skin_size = struct.unpack("I", self.io.read(4))[0]
+ skin.type = struct.unpack("B", self.io.read(1))[0]
+ for j in xrange(skin_size):
+ skin.indices.append(struct.unpack("I", self.io.read(4))[0])
+ skin.pos_list.append(
+ Vector3(*struct.unpack("3f", self.io.read(12))))
+ skin.english_name="skin%03d" % len(self.morph_list)
+ self.morph_list.append(skin)
+ return True
+
+ def _loadSkinIndex(self):
+ size = struct.unpack("B", self.io.read(1))[0]
+ for i in xrange(size):
+ self.face_list.append(struct.unpack("H", self.io.read(2))[0])
+ return True
+
+ def _loadBoneName(self):
+ size = struct.unpack("B", self.io.read(1))[0]
+ for i in xrange(size):
+ self.bone_group_list.append(BoneGroup(
+ truncate_zero(struct.unpack("50s", self.io.read(50))[0])))
+ return True
+
+ def _loadBoneIndex(self):
+ size = struct.unpack("I", self.io.read(4))[0]
+ for i in xrange(size):
+ self.bone_display_list.append(struct.unpack("HB", self.io.read(3)))
+ return True
+
+ def loadToonTexture(self):
+ """
+ 100bytex10
+ """
+ for i in xrange(10):
+ self.toon_textures.append(ToonTexture(
+ truncate_zero(struct.unpack("100s", self.io.read(100))[0])))
+ return True
+
+ def loadEnglishName(self):
+ # english name
+ self.english_model_name=truncate_zero(
+ struct.unpack("20s", self.io.read(20))[0])
+ self.english_comment=truncate_zero(
+ struct.unpack("256s", self.io.read(256))[0])
+ # english bone list
+ for bone in self.bones:
+ english_name=truncate_zero(
+ struct.unpack("20s", self.io.read(20))[0])
+ if english_name!=bone.name:
+ bone.english_name=english_name
+ # english skin list
+ #for index in self.face_list:
+ for skin in self.morph_list:
+ if skin.name=='base':
+ continue
+ english_name=truncate_zero(
+ struct.unpack("20s", self.io.read(20))[0])
+ #skin=self.morph_list[index]
+ if english_name!=skin.name:
+ skin.english_name=english_name
+ # english bone list
+ for i in xrange(0, len(self.bone_group_list)):
+ self.bone_group_list[i].english_name=truncate_zero(
+ struct.unpack("50s", self.io.read(50))[0])
+ return True
+
+ def loadPhysics(self):
+ # 剛体リスト
+ count = struct.unpack("I", self.io.read(4))[0]
+ for i in xrange(count):
+ struct.unpack("83s", self.io.read(83))[0]
+ # ジョイントリスト
+ count = struct.unpack("I", self.io.read(4))[0]
+ for i in xrange(count):
+ struct.unpack("124s", self.io.read(124))[0]
+ return True
+
+
+###############################################################################
+# VPD
+###############################################################################
+class LineLoader(object):
+ """
+ 行指向の汎用ローダ
+ """
+ __slots__=['path', 'io', 'end']
+ def __str__(self):
+ return "<%s current:%d, end:%d>" % (
+ self.__class__, self.getPos(), self.getEnd())
+
+ def getPos(self):
+ return self.io.tell()
+
+ def getEnd(self):
+ return self.end
+
+ def readline(self):
+ return (self.io.readline()).strip()
+
+ def isEnd(self):
+ return self.io.tell()>=self.end
+
+ def load(self, path, io, end):
+ self.path=path
+ self.io=io
+ self.end=end
+ return self.process()
+
+ def process(self):
+ """
+ dummy. read to end.
+ """
+ while not self.isEnd():
+ self.io.readline()
+ return True
+
+
+class VPDLoader(LineLoader):
+ __slots__=['pose']
+ def __init__(self):
+ super(VPDLoader, self).__init__()
+ self.pose=[]
+
+ def __str__(self):
+ return "<VPD poses:%d>" % len(self.pose)
+
+ def process(self):
+ if self.readline()!="Vocaloid Pose Data file":
+ return
+
+ RE_OPEN=re.compile('^(\w+){(.*)')
+ RE_OSM=re.compile('^\w+\.osm;')
+ RE_COUNT=re.compile('^(\d+);')
+
+ bone_count=-1
+ while not self.isEnd():
+ line=self.readline()
+ if line=='':
+ continue
+ m=RE_OPEN.match(line)
+ if m:
+ if not self.parseBone(m.group(2)):
+ raise Exception("invalid bone")
+ continue
+
+ m=RE_OSM.match(line)
+ if m:
+ continue
+
+ m=RE_COUNT.match(line)
+ if m:
+ bone_count=int(m.group(1))
+ continue
+
+ return len(self.pose)==bone_count
+
+ def parseBone(self, name):
+ bone=MotionData(name)
+ self.pose.append(bone)
+ bone.pos=Vector3(*[float(token) for token in self.readline().split(';')[0].split(',')])
+ bone.q=Quaternion(*[float(token) for token in self.readline().split(';')[0].split(',')])
+ return self.readline()=="}"
+
+
+###############################################################################
+# interface
+###############################################################################
+def load_pmd(path):
+ size=os.path.getsize(path)
+ f=open(path, "rb")
+ l=PMDLoader()
+ if l.load(path, f, size):
+ return l
+
+def load_vmd(path):
+ size=os.path.getsize(path)
+ f=open(path, "rb")
+ l=VMDLoader()
+ if l.load(path, f, size):
+ return l
+
+def load_vpd(path):
+ f=open(path, 'rb')
+ if not f:
+ return;
+ size=os.path.getsize(path)
+ l=VPDLoader()
+ if l.load(path, f, size):
+ return l
+
+
+###############################################################################
+# debug
+###############################################################################
+def debug_pmd(path):
+ l=load_pmd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+
+ print(unicode(l).encode(ENCODING))
+ print(l.comment.encode(ENCODING))
+ print("<ボーン>".decode('utf-8').encode(ENCODING))
+ for bone in l.no_parent_bones:
+ print(bone.name.encode(ENCODING))
+ bone.display()
+ #for bone in l.bones:
+ # uni="%s:%s" % (bone.english_name, bone.name)
+ # print uni.encode(ENCODING)
+ #for skin in l.morph_list:
+ # uni="%s:%s" % (skin.english_name, skin.name)
+ # print uni.encode(ENCODING)
+ #for i, v in enumerate(l.vertices):
+ # print i, v
+ #for i, f in enumerate(l.indices):
+ # print i, f
+ for m in l.materials:
+ print(m)
+
+def debug_pmd_write(path, out):
+ l=load_pmd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+
+ if not l.write(out):
+ print("fail to write")
+ sys.exit()
+
+def debug_vmd(path):
+ l=load_vmd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+ print(unicode(l).encode(ENCODING))
+
+ #for m in l.motions[u'センター']:
+ # print m.frame, m.pos
+ for n, m in l.shapes.items():
+ print(unicode(n).encode(ENCODING), getEnglishSkinName(n))
+
+def debug_vpd(path):
+ l=load_vpd(path)
+ if not l:
+ print("fail to load")
+ sys.exit()
+ for bone in l.pose:
+ print(unicode(bone).encode(ENCODING))
+
+if __name__=="__main__":
+ if len(sys.argv)<2:
+ print("usage: %s {pmd file}" % sys.argv[0])
+ print("usage: %s {vmd file}" % sys.argv[0])
+ print("usage: %s {vpd file}" % sys.argv[0])
+ print("usage: %s {pmd file} {export pmdfile}" % sys.argv[0])
+ sys.exit()
+
+ path=sys.argv[1]
+ if not os.path.exists(path):
+ print("no such file: %s" % path)
+
+ if path.lower().endswith('.pmd'):
+ if len(sys.argv)==2:
+ debug_pmd(path)
+ else:
+ debug_pmd_write(path, sys.argv[2])
+ elif path.lower().endswith('.vmd'):
+ debug_vmd(path)
+ elif path.lower().endswith('.vpd'):
+ debug_vpd(path)
+ else:
+ print("unknown file type: %s" % path)
+ sys.exit()
+
--- /dev/null
+#!BPY\r
+""" \r
+Name: 'Metasequoia(.mqo)...'\r
+Blender: 245\r
+Group: 'Import'\r
+Tooltip: 'Import from Metasequoia file format (.mqo)'\r
+"""\r
+__author__= 'ousttrue'\r
+__url__ = ["http://gunload.web.fc2.com/blender/"]\r
+__version__= '0.4 2009/11/25'\r
+__bpydoc__= '''\\r
+\r
+MQO Importer\r
+\r
+This script imports a mqo file.\r
+\r
+0.2 20080123: update.\r
+0.3 20091125: modify for linux.\r
+0.4 20100310: rewrite.\r
+0.5 20100311: create armature from mikoto bone.\r
+'''\r
+\r
+import os\r
+import sys\r
+import math\r
+\r
+\r
+class RGBA(object):\r
+ __slots__=['r', 'g', 'b', 'a']\r
+ def __init__(self, r=0, g=0, b=0, a=0):\r
+ self.r=r\r
+ self.g=g\r
+ self.b=b\r
+ self.a=a\r
+\r
+class Vector3(object):\r
+ __slots__=['x', 'y', 'z']\r
+ def __init__(self, x=0, y=0, z=0):\r
+ self.x=x\r
+ self.y=y\r
+ self.z=z\r
+\r
+ def __str__(self):\r
+ return "[%f, %f, %f]" % (self.x, self.y, self.z)\r
+\r
+ def __sub__(self, rhs):\r
+ return Vector3(self.x-rhs.x, self.y-rhs.y, self.z-rhs.z)\r
+\r
+ def getSqNorm(self):\r
+ return self.x*self.x + self.y*self.y + self.z*self.z\r
+\r
+ def getNorm(self):\r
+ return math.sqrt(self.getSqNorm())\r
+\r
+ def normalize(self):\r
+ factor=1.0/self.getNorm()\r
+ self.x*=factor\r
+ self.y*=factor\r
+ self.z*=factor\r
+ return self\r
+\r
+ def to_a(self):\r
+ return [self.x, self.y, self.z]\r
+\r
+ @staticmethod\r
+ def dot(lhs, rhs):\r
+ return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z\r
+\r
+ @staticmethod\r
+ def cross(lhs, rhs):\r
+ return Vector3(\r
+ lhs.y*rhs.z - rhs.y*lhs.z,\r
+ lhs.z*rhs.x - rhs.z*lhs.x,\r
+ lhs.x*rhs.y - rhs.x*lhs.y,\r
+ )\r
+\r
+\r
+class Vector2(object):\r
+ __slots__=['x', 'y']\r
+ def __init__(self, x=0, y=0):\r
+ self.x=x\r
+ self.y=y\r
+\r
+ def __str__(self):\r
+ return "[%f, %f]" % (self.x, self.y)\r
+\r
+ def __sub__(self, rhs):\r
+ return Vector3(self.x-rhs.x, self.y-rhs.y)\r
+\r
+ @staticmethod\r
+ def cross(lhs, rhs):\r
+ return lhs.x*rhs.y-lhs.y*rhs.x\r
+\r
+\r
+###############################################################################\r
+# MQO loader\r
+###############################################################################\r
+class Material(object):\r
+ __slots__=[\r
+ "name", "shader", "color", "diffuse", \r
+ "ambient", "emit", "specular", "power",\r
+ "tex",\r
+ ]\r
+ def __init__(self, name):\r
+ self.name=name\r
+ self.shader=3\r
+ self.color=RGBA(0.5, 0.5, 0.5, 1.0)\r
+ self.diffuse=1.0\r
+ self.ambient=0.0\r
+ self.emit=0.0\r
+ self.specular=0.0\r
+ self.power=5.0\r
+ self.tex=""\r
+\r
+ def getName(self): return self.name\r
+ def getTexture(self): return self.tex\r
+\r
+ def parse(self, line):\r
+ offset=0\r
+ while True:\r
+ leftParenthesis=line.find("(", offset)\r
+ if leftParenthesis==-1:\r
+ break\r
+ key=line[offset:leftParenthesis]\r
+ rightParenthesis=line.find(")", leftParenthesis+1)\r
+ if rightParenthesis==-1:\r
+ raise ValueError("assert")\r
+\r
+ param=line[leftParenthesis+1:rightParenthesis]\r
+ if key=="shader":\r
+ self.shader=int(param)\r
+ elif key=="col":\r
+ self.color=RGBA(*[float(e) for e in param.split()])\r
+ elif key=="dif":\r
+ self.diffuse=float(param)\r
+ elif key=="amb":\r
+ self.ambient=float(param)\r
+ elif key=="emi":\r
+ self.emit=float(param)\r
+ elif key=="spc":\r
+ self.specular=float(param)\r
+ elif key=="power":\r
+ self.power=float(param)\r
+ elif key=="tex":\r
+ self.tex=param[1:-1]\r
+ else:\r
+ print(\r
+ "%s#parse" % self.name, \r
+ "unknown key: %s" % key\r
+ )\r
+\r
+ offset=rightParenthesis+2\r
+\r
+ def __str__(self):\r
+ return "<Material %s shader: %d [%f, %f, %f, %f] %f>" % (\r
+ self.name, self.shader,\r
+ self.color[0], self.color[1], self.color[2], self.color[3],\r
+ self.diffuse)\r
+\r
+\r
+class Obj(object):\r
+ __slots__=["name", "depth", "folding", \r
+ "scale", "rotation", "translation",\r
+ "visible", "locking", "shading", "facet",\r
+ "color", "color_type", "mirror", "mirror_axis",\r
+ "vertices", "faces", "edges", "smoothing",\r
+ ]\r
+\r
+ def __init__(self, name):\r
+ self.name=name\r
+ self.vertices=[]\r
+ self.faces=[]\r
+ self.edges=[]\r
+ self.depth=0\r
+ self.folding=0\r
+ self.scale=[1, 1, 1]\r
+ self.rotation=[0, 0, 0]\r
+ self.translation=[0, 0, 0]\r
+ self.visible=15\r
+ self.locking=0\r
+ self.shading=0\r
+ self.facet=59.5\r
+ self.color=[1, 1, 1]\r
+ self.color_type=0\r
+ self.mirror=0\r
+ self.smoothing=0\r
+\r
+ def getName(self): return self.name\r
+\r
+ def addVertex(self, x, y, z):\r
+ self.vertices.append(Vector3(x, y, z))\r
+\r
+ def addFace(self, face):\r
+ if face.index_count==2:\r
+ self.edges.append(face)\r
+ else:\r
+ self.faces.append(face)\r
+\r
+ def __str__(self):\r
+ return "<Object %s, %d vertices, %d faces>" % (\r
+ self.name, len(self.vertices), len(self.faces))\r
+\r
+\r
+class Face(object):\r
+ __slots__=[\r
+ "index_count",\r
+ "indices", "material_index", "col", "uv",\r
+ ]\r
+ def __init__(self, index_count, line):\r
+ if index_count<2 or index_count>4:\r
+ raise ValueError("invalid vertex count: %d" % index_count)\r
+ self.material_index=0\r
+ self.col=[]\r
+ self.uv=[Vector2(0, 0)]*4\r
+ self.index_count=index_count\r
+ offset=0\r
+ while True:\r
+ leftParenthesis=line.find("(", offset)\r
+ if leftParenthesis==-1:\r
+ break\r
+ key=line[offset:leftParenthesis]\r
+ rightParenthesis=line.find(")", leftParenthesis+1)\r
+ if rightParenthesis==-1:\r
+ raise ValueError("assert")\r
+ params=line[leftParenthesis+1:rightParenthesis].split()\r
+ if key=="V":\r
+ self.indices=[int(e) for e in params]\r
+ elif key=="M":\r
+ self.material_index=int(params[0])\r
+ elif key=="UV":\r
+ uv_list=[float(e) for e in params]\r
+ self.uv=[]\r
+ for i in range(0, len(uv_list), 2):\r
+ self.uv.append(Vector2(uv_list[i], uv_list[i+1]))\r
+ elif key=="COL":\r
+ for n in params:\r
+ d=int(n)\r
+ # R\r
+ d, m=divmod(d, 256)\r
+ self.col.append(m)\r
+ # G\r
+ d, m=divmod(d, 256)\r
+ self.col.append(m)\r
+ # B\r
+ d, m=divmod(d, 256)\r
+ self.col.append(m)\r
+ # A\r
+ d, m=divmod(d, 256)\r
+ self.col.append(m)\r
+ else:\r
+ print("Face#__init__:unknown key: %s" % key)\r
+\r
+ offset=rightParenthesis+2\r
+\r
+ def getIndex(self, i): return self.indices[i]\r
+ def getUV(self, i): return self.uv[i] if i<len(self.uv) else Vector2(0, 0)\r
+\r
+\r
+def withio(method):\r
+ def new(self, path):\r
+ self.io=open(path, encoding='cp932')\r
+ result=method(self)\r
+ self.io=None\r
+ return result\r
+ return new\r
+\r
+\r
+class IO(object):\r
+ __slots__=[\r
+ "has_mikoto",\r
+ "eof", "io", "lines",\r
+ "materials", "objects",\r
+ ]\r
+ def __init__(self):\r
+ self.has_mikoto=False\r
+ self.eof=False\r
+ self.io=None\r
+ self.lines=0\r
+ self.materials=[]\r
+ self.objects=[]\r
+\r
+ def __str__(self):\r
+ return "<MQO %d lines, %d materials, %d objects>" % (\r
+ self.lines, len(self.materials), len(self.objects))\r
+\r
+ def getline(self):\r
+ line=self.io.readline()\r
+ self.lines+=1\r
+ if line=="":\r
+ self.eof=True\r
+ return None\r
+ return line.strip()\r
+\r
+ def printError(self, method, msg):\r
+ print("%s:%s:%d" % (method, msg, self.lines))\r
+\r
+ @withio\r
+ def read(self):\r
+ line=self.getline()\r
+ if line!="Metasequoia Document":\r
+ print("invalid signature")\r
+ return False\r
+\r
+ line=self.getline()\r
+ if line!="Format Text Ver 1.0":\r
+ print("unknown version: %s" % line)\r
+\r
+ while True:\r
+ line=self.getline()\r
+ if line==None:\r
+ # eof\r
+ break;\r
+ if line=="":\r
+ # empty line\r
+ continue\r
+\r
+ tokens=line.split()\r
+ key=tokens[0]\r
+\r
+ if key=="Eof":\r
+ return True\r
+ elif key=="Scene":\r
+ if not self.readChunk():\r
+ return False\r
+ elif key=="Material":\r
+ if not self.readMaterial():\r
+ return False\r
+ elif key=="Object":\r
+ firstQuote=line.find('"')\r
+ secondQuote=line.find('"', firstQuote+1)\r
+ if not self.readObject(line[firstQuote+1:secondQuote]):\r
+ return False\r
+ elif key=="BackImage":\r
+ if not self.readChunk():\r
+ return False\r
+ elif key=="IncludeXml":\r
+ firstQuote=line.find('"')\r
+ secondQuote=line.find('"', firstQuote+1)\r
+ print("IncludeXml", line[firstQuote+1:secondQuote])\r
+ else:\r
+ print("unknown key: %s" % key)\r
+ if not self.readChunk():\r
+ return False\r
+\r
+ self.printError("parse", "invalid eof")\r
+ return False\r
+\r
+ def readObject(self, name):\r
+ obj=Obj(name)\r
+ if name.startswith('bone'):\r
+ self.has_mikoto=True\r
+ self.objects.append(obj)\r
+ while(True):\r
+ line=self.getline()\r
+ if line==None:\r
+ # eof\r
+ break;\r
+ if line=="":\r
+ # empty line\r
+ continue\r
+\r
+ if line=="}":\r
+ return True\r
+ else:\r
+ tokens=line.split()\r
+ key=tokens[0]\r
+ if key=="vertex":\r
+ if not self.readVertex(obj):\r
+ return False\r
+ elif key=="face":\r
+ if not self.readFace(obj):\r
+ return False\r
+ elif key=="depth":\r
+ obj.depth=int(tokens[1])\r
+ else:\r
+ print(\r
+ "%s#readObject" % name,\r
+ "unknown key: %s" % name\r
+ )\r
+\r
+ self.printError("readObject", "invalid eof")\r
+ return False\r
+\r
+ def readFace(self, obj):\r
+ while(True):\r
+ line=self.getline()\r
+ if line==None:\r
+ # eof\r
+ break;\r
+ if line=="":\r
+ # empty line\r
+ continue\r
+\r
+ if line=="}":\r
+ return True\r
+ else:\r
+ # face\r
+ tokens=line.split(' ', 1)\r
+ try:\r
+ obj.addFace(Face(int(tokens[0]), tokens[1]))\r
+ except ValueError as ex:\r
+ self.printError("readFace", ex)\r
+ #return False\r
+\r
+ self.printError("readFace", "invalid eof")\r
+ return False\r
+\r
+ def readVertex(self, obj):\r
+ while(True):\r
+ line=self.getline()\r
+ if line==None:\r
+ # eof\r
+ break;\r
+ if line=="":\r
+ # empty line\r
+ continue\r
+\r
+ if line=="}":\r
+ return True\r
+ else:\r
+ # vertex\r
+ obj.addVertex(*[float(v) for v in line.split()])\r
+\r
+ self.printError("readVertex", "invalid eof")\r
+ return False\r
+\r
+ def readMaterial(self):\r
+ while(True):\r
+ line=self.getline()\r
+ if line==None:\r
+ # eof\r
+ break;\r
+ if line=="":\r
+ # empty line\r
+ continue\r
+\r
+ if line=="}":\r
+ return True\r
+ else:\r
+ # material\r
+ secondQuaote=line.find('"', 1) \r
+ material=Material(line[1:secondQuaote])\r
+ try:\r
+ material.parse(line[secondQuaote+2:])\r
+ except ValueError as ex:\r
+ self.printError("readMaterial", ex)\r
+\r
+ self.materials.append(material)\r
+\r
+ self.printError("readMaterial", "invalid eof")\r
+ return False\r
+\r
+ def readChunk(self):\r
+ level=1\r
+ while(True):\r
+ line=self.getline()\r
+ if line==None:\r
+ # eof\r
+ break;\r
+ if line=="":\r
+ # empty line\r
+ continue\r
+\r
+ if line=="}":\r
+ level-=1\r
+ if level==0:\r
+ return True\r
+ elif line.find("{")!=-1:\r
+ level+=1\r
+\r
+ self.printError("readChunk", "invalid eof")\r
+ return False\r
+\r