OSDN Git Service

update package.
[meshio/meshio.git] / swig / blender25 / import_scene_pmd.py
1 # coding: utf-8
2
3 __author__= ['ousttrue']
4 __url__ = ("")
5 __version__= '20100508 0.1:'
6 __bpydoc__= '''\
7 pmd Importer
8
9 This script imports a pmd into Blender for editing.
10 '''
11
12 import bpy
13 from bpy.props import *
14 import mathutils
15 import sys
16 import os
17 import math
18 from meshio import pmd, englishmap
19
20
21 def to_radian(degree):
22     return math.pi * degree / 180
23
24
25 def convert_coord(pos):
26     """
27     Left handed y-up to Right handed z-up
28     """
29     return (pos.x, pos.z, pos.y)
30
31
32 def convert_uv(uv):
33     return (uv.x, 1.0 - uv.y)
34
35
36 def getBoneName(bone):
37     name = englishmap.getEnglishBoneName(bone.getName())
38     return name if name else bone.getName()
39
40
41 def create_texture(directory, texture_name):
42     texture=bpy.data.textures.new(texture_name)
43     texture.type='IMAGE'
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)
48     texture.image=image
49     texture.mipmap = True
50     texture.interpolation = True
51     texture.use_alpha = True
52     return texture
53
54
55 def createMaterial():
56     """
57     create default materil 
58     """
59     material = bpy.data.materials.new("Material")
60     material.diffuse_shader='TOON'
61     material.specular_shader='TOON'
62     # temporary
63     material.emit=1.0
64     return material
65
66
67 def importMesh(scene, l, tex_dir):
68     """
69     @param l[in] mmd.PMDLoader
70     @param filename[in]
71     """
72
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:
79         # base 
80         base=None
81         for s in l.morph_list:
82             if s.type!=0:
83                 continue
84             base=s
85             break
86         assert(base)
87
88         for index in base.indices:
89             shape_key_used_vertices.add(index)
90
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:
95                 return True
96
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
100     face_map={}
101     face_count=0
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
107
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
109     material_map={}
110     used_index=0
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
115             used_index+=1
116         else:
117             material_map[i]=not_used_index
118             not_used_index+=1
119
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
121     material_index=0
122     mesh_objects=[]
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)
129         scene.update()
130         mesh_objects.append(meshObject)
131
132         # enter Edit Mode
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)
137  
138         # crete shape key
139         importShape(meshObject, l)
140
141         ############################################################
142         # clean up not used vertices
143         ############################################################
144         #progress_print('clean up vertices not used')
145         vertex_map={}
146         mesh=meshObject.data
147         for i, v in enumerate(mesh.verts):
148             if i in used_vertices:
149                 vertex_map[i]=len(vertex_map)
150                 v.selected=False
151             else:
152                 v.selected=True
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')
158
159         ############################################################
160         # flip face
161         ############################################################
162         bpy.ops.mesh.select_all(action='SELECT')
163         bpy.ops.mesh.flip_normals()
164         bpy.ops.mesh.select_all(action='DESELECT')
165
166         # exit Edit Mode
167         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
168
169         mesh.update()
170         material_index+=16
171
172     return mesh_objects
173
174
175 def import16MaerialAndMesh(l, 
176         material_offset, material_map, face_map, tex_dir):
177     # create mesh
178     mesh=bpy.data.meshes.new("Mesh")
179
180     # create object
181     meshObject= bpy.data.objects.new("Mesh", mesh)
182     meshObject.layers[0]=True
183
184     ############################################################
185     # material
186     ############################################################
187     #progress_print('create materials')
188     mesh_material_map={}
189     materials=[]
190     textureMap={}
191     imageMap={}
192     index=0
193     for i in range(material_offset, material_offset+16):
194         try:
195             material_index=material_map[i]
196             m=l.materials[material_index]
197             mesh_material_map[material_index]=index
198         except KeyError:
199             break
200
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()
208         if texture_name!='':
209             if texture_name in textureMap:
210                 texture=textureMap[texture_name]
211             else:
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")
217
218         materials.append(material)
219         mesh.add_material(material)
220         # lookup table for assign
221         index+=1
222
223     ############################################################
224     # vertex
225     ############################################################
226     #progress_print('create vertices')
227     # create vertices
228     vertex_groups={}
229     unpackedVertices=[]
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
235
236     ############################################################
237     # face
238     ############################################################
239     #progress_print('create faces')
240     # create faces
241     mesh_face_indices=[]
242     mesh_face_materials=[]
243     used_vertices=set()
244     for i in range(material_offset, material_offset+16):
245         try:
246             material_index=material_map[i]
247         except KeyError:
248             break
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):
253             i0=material_faces[j]
254             i1=material_faces[j+1]
255             i2=material_faces[j+2]
256             if i2==0:
257                 mesh_face_indices.extend([i2, i0, i1, 0])
258             else:
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)
264
265     ############################################################
266     # create vertices & faces
267     ############################################################
268     mesh.add_geometry(
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))
273
274     ############################################################
275     # face params
276     ############################################################
277     used_map={}
278     mesh.add_uv_texture()
279
280     for face, uv_face, material_index in zip(mesh.faces, 
281             mesh.uv_textures[0].data,
282             mesh_face_materials,
283             ):
284         try:
285             index=mesh_material_map[material_index]
286         except KeyError as message:
287             print(message, mesh_material_map, m)
288             assert(False)
289         face.material_index=index
290         material=mesh.materials[index]
291         used_map[index]=True
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]
298
299             uv=l.getUV(face.verts[1])
300             uv_face.uv2=[uv.x, 1.0-uv.y]
301
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]
306             uv_face.tex=True
307
308         # set smooth
309         face.smooth = 1
310
311     ############################################################
312     # vertex weight
313     ############################################################
314     # create vertex group
315     for i in vertex_groups.keys():
316         meshObject.add_vertex_group(getBoneName(l.bones[i]))
317
318     # vertex params
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
324         w2=1.0-w1
325
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')
330     mesh.update()
331
332     #progress_print('%s created' % mesh.name)
333     return meshObject, used_vertices
334
335
336 def build_bone(armature, b, parent=None):
337     if b.tail_index==0:
338         return
339
340     name=getBoneName(b)
341     bone = armature.edit_bones.new(name if name else b.getName())
342     if parent:
343         bone.head = mathutils.Vector(convert_coord(b.pos))
344         bone.parent=parent
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))
352     else:
353         # center
354         tail=mathutils.Vector(convert_coord(b.pos))
355         bone.tail = tail
356         bone.head = tail-mathutils.Vector((0, 1, 0))
357
358     for child in b.children:
359         build_bone(armature, child, bone)
360
361
362 def importArmature(scene, l):
363     # create armature
364     armature = bpy.data.armatures.new('Armature')
365     # link to object
366     armature_object=bpy.data.objects.new('Armature', armature)
367     scene.objects.link(armature_object)
368     armature_object.x_ray=True
369
370     # armature settings
371     armature.drawtype='OCTAHEDRAL'
372     armature.deform_envelope=False
373     armature.deform_vertexgroups=True
374     armature.x_axis_mirror=True
375
376     # create action
377     #act = Blender.Armature.NLA.NewAction()
378     #act.setActive(armature_object)
379
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)
384
385     # create armature
386     for b in l.bones:
387         if not b.parent:
388             build_bone(armature, b)
389
390     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
391     bpy.ops.object.select_all(action='DESELECT')
392
393     ############################################################
394     # IK
395     ############################################################
396     pose = armature_object.pose
397     for ik in l.ik_list:
398         effector=l.bones[ik.target]
399         parent=l.bones[effector.parent_index]
400         name=getBoneName(parent)
401         p_bone = pose.bones[name]
402         if not p_bone:
403             print('not found', name)
404             continue
405         if len(ik.children) >= 16:
406             print('over MAX_CHAINLEN', ik, len(ik.children))
407             continue
408         # IK
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])
413         # ROT
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)
423
424     return armature_object
425     
426
427 def importShape(meshObject, l):
428     if len(l.morph_list)==0:
429         return
430
431     # base 
432     base=None
433     for s in l.morph_list:
434         if s.type!=0:
435             continue
436         base=s
437         break
438     assert(base)
439
440     # create base key
441     baseblock=meshObject.add_shape_key("Basis")
442
443     # mesh
444     mesh=meshObject.data
445
446     # each skin
447     for s in l.morph_list:
448         if s.getName()==base.name:
449             # skip base
450             continue
451
452         # restore
453         #for v, base_pos in zip(mesh.verts, baseblock.data):
454         #    v.co=base_pos.co
455         #mesh.update()
456
457         # name
458         name=englishmap.getEnglishSkinName(s.getName())
459         if not name:
460             name=s.getName()
461         new_shape_key=meshObject.add_shape_key(name)
462         #new_shape_key.value=1.0
463
464         # morph
465         for i, offset in zip(s.indices, s.pos_list):
466             try:
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])
475                 break
476             except KeyError:
477                 #print 'this mesh not has shape vertices'
478                 break
479         
480         # set ipo curve
481         #icu=ipo.addCurve(name)
482         #icu.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
483         #icu.append( (0.0, 0.0) )
484         
485
486 def load(filename, context):
487     """
488     load pmd file to context.
489     """
490     io=pmd.IO()
491     if not io.read(filename):
492         print("fail to read", filename)
493         return
494
495     scene=context.scene
496
497     # create root object
498     root=bpy.data.objects.new(os.path.basename(filename), None)
499     scene.objects.link(root)
500
501     # import mesh
502     mesh_objects=importMesh(scene, io, os.path.dirname(filename))
503     for o in mesh_objects:
504         o.parent=root
505
506     # import armature
507     armature_object=importArmature(scene, io)
508     if armature_object:
509         armature_object.parent=root
510         armature = armature_object.data
511         armature.draw_names=True
512
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
518             #o.makeDisplayList()
519
520
521     # select objects
522     root.selected=True
523     for o in mesh_objects:
524         o.selected=True
525     armature_object.selected=True
526  
527     # redraw
528     scene.update()
529
530     print("finised")
531
532
533 ###############################################################################
534 # import operator
535 ###############################################################################
536 class IMPORT_OT_pmd(bpy.types.Operator):
537     bl_idname = "import_scene.pmd"
538     bl_label = 'Import PMD'
539
540     # List of operator properties, the attributes will be assigned
541     # to the class instance from the operator settings before calling.
542
543     path = StringProperty(
544             name="File Path", 
545             description="File path used for importing the PMD file", 
546             maxlen= 1024, default= "")
547     filename = StringProperty(
548             name="File Name", 
549             description="Name of the file.")
550     directory = StringProperty(
551             name="Directory", 
552             description="Directory of the file.")
553
554     def execute(self, context):
555         load(self.properties.path, context)
556         return {'FINISHED'}
557
558     def invoke(self, context, event):
559         wm = context.manager
560         wm.add_fileselect(self)
561         return {'RUNNING_MODAL'}
562
563
564 ###############################################################################
565 # register menu
566 ###############################################################################
567 def menu_func(self, context): 
568     self.layout.operator(IMPORT_OT_pmd.bl_idname, 
569             text="MikuMikuDance model (.pmd)")
570
571 def register():
572     bpy.types.register(IMPORT_OT_pmd)
573     bpy.types.INFO_MT_file_import.append(menu_func)
574
575 def unregister():
576     bpy.types.unregister(IMPORT_OT_pmd)
577     bpy.types.INFO_MT_file_import.remove(menu_func)
578
579
580 if __name__=="__main__":
581     register()
582