3 __author__= ['ousttrue']
5 __version__= '20100508 0.1:'
9 This script imports a pmd into Blender for editing.
13 from bpy.props import *
18 from meshio import pmd, englishmap
21 def to_radian(degree):
22 return math.pi * degree / 180
25 def convert_coord(pos):
27 Left handed y-up to Right handed z-up
29 return (pos.x, pos.z, pos.y)
33 return (uv.x, 1.0 - uv.y)
36 def getBoneName(bone):
37 name = englishmap.getEnglishBoneName(bone.getName())
38 return name if name else bone.getName()
41 def create_texture(directory, texture_name):
42 texture=bpy.data.textures.new(texture_name)
44 texture=texture.recast_type()
45 texturePath=os.path.join(directory, texture_name)
46 print('create_texture', texturePath)
47 image=bpy.data.images.load(texturePath)
50 texture.interpolation = True
51 texture.use_alpha = True
57 create default materil
59 material = bpy.data.materials.new("Material")
60 material.diffuse_shader='TOON'
61 material.specular_shader='TOON'
67 def importMesh(scene, l, tex_dir):
69 @param l[in] mmd.PMDLoader
73 ############################################################
74 # shpae
\e$B%-!<$G;H$o$l$k%^%F%j%"%kM%@hE*$KA0$KJB$Y$k
\e(B
75 ############################################################
76 # shape
\e$B%-!<$G;H$o$l$kD:E@%$%s%G%C%/%9$r=8$a$k
\e(B
77 shape_key_used_vertices=set()
78 if len(l.morph_list)>0:
81 for s in l.morph_list:
88 for index in base.indices:
89 shape_key_used_vertices.add(index)
91 #
\e$B%^%F%j%"%k$K4^$^$l$kD:E@$,
\e(Bshape_key
\e$B$K4^$^$l$k$+H]$+!)
\e(B
92 def isMaterialUsedInShape(offset, m):
93 for i in range(offset, offset+m.vertex_count):
94 if l.indices[i] in shape_key_used_vertices:
97 # shape
\e$B%-!<$G;H$o$l$k%^%F%j%"%k$r5-O?$9$k
\e(B
98 shape_key_materials=set()
99 #
\e$B3F%^%F%j%"%k$N3+;OD:E@%$%s%G%C%/%9$r5-O?$9$k
\e(B
102 for i, m in enumerate(l.materials):
103 face_map[i]=face_count
104 if isMaterialUsedInShape(face_count, m):
105 shape_key_materials.add(i)
106 face_count+=m.vertex_count
108 # shape
\e$B%-!<$G;H$o$l$k%^%F%j%"%k$rA0$KJB$Y$k%$%s%G%C%/%9%^%C%W$r:n$k
\e(B
111 not_used_index=len(shape_key_materials)
112 for i, m in enumerate(l.materials):
113 if i in shape_key_materials:
114 material_map[i]=used_index
117 material_map[i]=not_used_index
120 #
\e$B%^%F%j%"%k
\e(B16
\e$B8D$4$H$KJ,3d$7$?%a%C%7%e$r:n@.$9$k
\e(B
123 while material_index<len(l.materials):
124 # shape
\e$B%-!<$G;H$o$l$k=g$KJB$Y$J$*$7$?%^%F%j%"%k
\e(B16
\e$B8DJ,$N
\e(B
125 #
\e$B%a%C%7%e$r:n@.$9$k
\e(B
126 meshObject, used_vertices=import16MaerialAndMesh(l,
127 material_index, material_map, face_map, tex_dir)
128 scene.objects.link(meshObject)
130 mesh_objects.append(meshObject)
133 bpy.ops.object.select_all(action='DESELECT')
134 meshObject.selected=True
135 scene.objects.active=meshObject
136 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
139 importShape(meshObject, l)
141 ############################################################
142 # clean up not used vertices
143 ############################################################
144 #progress_print('clean up vertices not used')
147 for i, v in enumerate(mesh.verts):
148 if i in used_vertices:
149 vertex_map[i]=len(vertex_map)
153 assert(mesh.verts[i].selected)
154 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
155 print("%d vertices selected" % mesh.total_vert_sel)
156 print("used %d/%d" % (len(vertex_map), len(mesh.verts)))
157 bpy.ops.mesh.delete(type='VERT')
159 ############################################################
161 ############################################################
162 bpy.ops.mesh.select_all(action='SELECT')
163 bpy.ops.mesh.flip_normals()
164 bpy.ops.mesh.select_all(action='DESELECT')
167 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
175 def import16MaerialAndMesh(l,
176 material_offset, material_map, face_map, tex_dir):
178 mesh=bpy.data.meshes.new("Mesh")
181 meshObject= bpy.data.objects.new("Mesh", mesh)
182 meshObject.layers[0]=True
184 ############################################################
186 ############################################################
187 #progress_print('create materials')
193 for i in range(material_offset, material_offset+16):
195 material_index=material_map[i]
196 m=l.materials[material_index]
197 mesh_material_map[material_index]=index
201 material=createMaterial()
202 material.diffuse_color=([m.diffuse.r, m.diffuse.g, m.diffuse.b])
203 material.alpha=m.diffuse.a
204 material.specular_hardness=int(m.shinness)
205 material.specular_color=([m.specular.r, m.specular.g, m.specular.b])
206 material.mirror_color=([m.ambient.r, m.ambient.g, m.ambient.b])
207 texture_name=m.getTexture()
209 if texture_name in textureMap:
210 texture=textureMap[texture_name]
212 texture=create_texture(tex_dir, texture_name)
213 textureMap[texture_name]=texture
214 imageMap[material_index]=texture.image
215 #material.add_texture(texture, "UV", {"COLOR", "ALPHA"})
216 material.add_texture(texture, "UV", "COLOR")
218 materials.append(material)
219 mesh.add_material(material)
220 # lookup table for assign
223 ############################################################
225 ############################################################
226 #progress_print('create vertices')
230 for v in l.each_vertex():
231 unpackedVertices.extend(
232 convert_coord(v.pos))
233 vertex_groups[v.bone0]=True
234 vertex_groups[v.bone1]=True
236 ############################################################
238 ############################################################
239 #progress_print('create faces')
242 mesh_face_materials=[]
244 for i in range(material_offset, material_offset+16):
246 material_index=material_map[i]
249 face_offset=face_map[material_index]
250 m=l.materials[material_index]
251 material_faces=l.indices[face_offset:face_offset+m.vertex_count]
252 for j in range(0, len(material_faces), 3):
254 i1=material_faces[j+1]
255 i2=material_faces[j+2]
257 mesh_face_indices.extend([i2, i0, i1, 0])
259 mesh_face_indices.extend([i0, i1, i2, 0])
260 mesh_face_materials.append(material_index)
261 used_vertices.add(i0)
262 used_vertices.add(i1)
263 used_vertices.add(i2)
265 ############################################################
266 # create vertices & faces
267 ############################################################
269 int(len(unpackedVertices)/3), 0, int(len(mesh_face_indices)/4))
270 mesh.verts.foreach_set("co", unpackedVertices)
271 mesh.faces.foreach_set("verts_raw", mesh_face_indices)
272 assert(len(l.vertices)==len(mesh.verts))
274 ############################################################
276 ############################################################
278 mesh.add_uv_texture()
280 for face, uv_face, material_index in zip(mesh.faces,
281 mesh.uv_textures[0].data,
285 index=mesh_material_map[material_index]
286 except KeyError as message:
287 print(message, mesh_material_map, m)
289 face.material_index=index
290 material=mesh.materials[index]
292 if material.texture_slots[0]:
293 #texture=material.texture_slots[0].texture
294 #face.image=texture.image
295 #texture.imageFlags|=Blender.Texture.ImageFlags.USEALPHA
296 uv=l.getUV(face.verts[0])
297 uv_face.uv1=[uv.x, 1.0-uv.y]
299 uv=l.getUV(face.verts[1])
300 uv_face.uv2=[uv.x, 1.0-uv.y]
302 uv=l.getUV(face.verts[2])
303 uv_face.uv3=[uv.x, 1.0-uv.y]
304 if face.material_index in imageMap:
305 uv_face.image=imageMap[face.material_index]
311 ############################################################
313 ############################################################
314 # create vertex group
315 for i in vertex_groups.keys():
316 meshObject.add_vertex_group(getBoneName(l.bones[i]))
319 for i, v, mvert in zip(range(len(l.vertices)),
320 l.each_vertex(), mesh.verts):
321 mvert.normal=mathutils.Vector(convert_coord(v.normal))
322 #mvert.uvco=convert_uv(v.uv)
323 w1=float(v.weight0)/100.0
326 meshObject.add_vertex_to_group(i,
327 meshObject.vertex_groups[getBoneName(l.bones[v.bone0])], w1, 'ADD')
328 meshObject.add_vertex_to_group(i,
329 meshObject.vertex_groups[getBoneName(l.bones[v.bone1])], w2, 'ADD')
332 #progress_print('%s created' % mesh.name)
333 return meshObject, used_vertices
336 def build_bone(armature, b, parent=None):
341 bone = armature.edit_bones.new(name if name else b.getName())
343 bone.head = mathutils.Vector(convert_coord(b.pos))
345 bone.connected=True if parent.tail==bone.head else False
346 bone.tail = mathutils.Vector(convert_coord(b.tail))
347 if bone.head==bone.tail:
348 bone.tail=bone.head-mathutils.Vector((0, 1, 0))
349 elif b.__class__ is pmd.BONE_IK:
350 bone.head = mathutils.Vector(convert_coord(b.pos))
351 bone.tail = mathutils.Vector(convert_coord(b.tail))
354 tail=mathutils.Vector(convert_coord(b.pos))
356 bone.head = tail-mathutils.Vector((0, 1, 0))
358 for child in b.children:
359 build_bone(armature, child, bone)
362 def importArmature(scene, l):
364 armature = bpy.data.armatures.new('Armature')
366 armature_object=bpy.data.objects.new('Armature', armature)
367 scene.objects.link(armature_object)
368 armature_object.x_ray=True
371 armature.drawtype='OCTAHEDRAL'
372 armature.deform_envelope=False
373 armature.deform_vertexgroups=True
374 armature.x_axis_mirror=True
377 #act = Blender.Armature.NLA.NewAction()
378 #act.setActive(armature_object)
380 # select only armature object and set edit mode
381 scene.objects.active=armature_object
382 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
383 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
388 build_bone(armature, b)
390 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
391 bpy.ops.object.select_all(action='DESELECT')
393 ############################################################
395 ############################################################
396 pose = armature_object.pose
398 effector=l.bones[ik.target]
399 parent=l.bones[effector.parent_index]
400 name=getBoneName(parent)
401 p_bone = pose.bones[name]
403 print('not found', name)
405 if len(ik.children) >= 16:
406 print('over MAX_CHAINLEN', ik, len(ik.children))
409 ik_const = p_bone.constraints.new('IK')
410 ik_const.chain_length=len(ik.children)
411 ik_const.target=armature_object
412 ik_const.subtarget=getBoneName(l.bones[ik.index])
414 rot_const = p_bone.constraints.new('LIMIT_ROTATION')
415 rot_const.influence = ik.weight
416 rot_const.owner_space = 'LOCAL'
417 rot_const.use_limit_x=True
418 rot_const.use_limit_z=True
419 rot_const.minimum_x=to_radian(ik.iterations)
420 rot_const.maximum_x=to_radian(180)
421 rot_const.minimum_z=to_radian(180 - ik.iterations)
422 rot_const.maximum_z=to_radian(0)
424 return armature_object
427 def importShape(meshObject, l):
428 if len(l.morph_list)==0:
433 for s in l.morph_list:
441 baseblock=meshObject.add_shape_key("Basis")
447 for s in l.morph_list:
448 if s.getName()==base.name:
453 #for v, base_pos in zip(mesh.verts, baseblock.data):
458 name=englishmap.getEnglishSkinName(s.getName())
461 new_shape_key=meshObject.add_shape_key(name)
462 #new_shape_key.value=1.0
465 for i, offset in zip(s.indices, s.pos_list):
467 vertex_index=base.indices[i]
468 new_shape_key.data[vertex_index].co=[p+o for p, o in zip(
469 mesh.verts[vertex_index].co, convert_coord(offset))]
470 except IndexError as msg:
471 print(IndexError, msg)
472 print(i, len(base.indices))
473 print(vertex_index, len(mesh.verts))
474 print(base.indices[i])
477 #print 'this mesh not has shape vertices'
481 #icu=ipo.addCurve(name)
482 #icu.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
483 #icu.append( (0.0, 0.0) )
486 def load(filename, context):
488 load pmd file to context.
491 if not io.read(filename):
492 print("fail to read", filename)
498 root=bpy.data.objects.new(os.path.basename(filename), None)
499 scene.objects.link(root)
502 mesh_objects=importMesh(scene, io, os.path.dirname(filename))
503 for o in mesh_objects:
507 armature_object=importArmature(scene, io)
509 armature_object.parent=root
510 armature = armature_object.data
511 armature.draw_names=True
513 # add armature modifier
514 for o in mesh_objects:
515 mod=o.modifiers.new("Modifier", "ARMATURE")
516 mod.object = armature_object
517 mod.use_bone_envelopes=False
523 for o in mesh_objects:
525 armature_object.selected=True
533 ###############################################################################
535 ###############################################################################
536 class IMPORT_OT_pmd(bpy.types.Operator):
537 bl_idname = "import_scene.pmd"
538 bl_label = 'Import PMD'
540 # List of operator properties, the attributes will be assigned
541 # to the class instance from the operator settings before calling.
543 path = StringProperty(
545 description="File path used for importing the PMD file",
546 maxlen= 1024, default= "")
547 filename = StringProperty(
549 description="Name of the file.")
550 directory = StringProperty(
552 description="Directory of the file.")
554 def execute(self, context):
555 load(self.properties.path, context)
558 def invoke(self, context, event):
560 wm.add_fileselect(self)
561 return {'RUNNING_MODAL'}
564 ###############################################################################
566 ###############################################################################
567 def menu_func(self, context):
568 self.layout.operator(IMPORT_OT_pmd.bl_idname,
569 text="MikuMikuDance model (.pmd)")
572 bpy.types.register(IMPORT_OT_pmd)
573 bpy.types.INFO_MT_file_import.append(menu_func)
576 bpy.types.unregister(IMPORT_OT_pmd)
577 bpy.types.INFO_MT_file_import.remove(menu_func)
580 if __name__=="__main__":