OSDN Git Service

6f2ff29a02a27f17f51f4fb9d4e56afb0e50e68c
[meshio/pymeshio.git] / blender26-meshio / import_pmx.py
1 # coding: utf-8
2 """
3 PMXモデルをインポートする。
4
5 1マテリアル、1オブジェクトで作成する。
6 """
7 import os
8 import bpy
9 from . import bl
10 from .pymeshio import pmx
11 from .pymeshio.pmx import reader
12
13
14 def convert_coord(pos):
15     """
16     Left handed y-up to Right handed z-up
17     """
18     return (pos.x, pos.z, pos.y)
19
20 def VtoV(v):
21     return bl.createVector(v.x, v.y, v.z)
22
23 # マテリアル毎にメッシュを作成する
24 def get_object_name(fmt, index, name):
25     """
26     object名を作る。最大21バイト
27     """
28     len_list=[len(name[:i].encode('utf-8')) for i in range(1, len(name)+1, 1)]
29     letter_count=0
30     prefix=fmt.format(index)
31     max_length=21-len(prefix)
32     for str_len in len_list:
33         if str_len>max_length:
34             break
35         letter_count+=1
36     name=prefix+name[:letter_count]
37     print("%s(%d)" % (name, letter_count))
38     return name
39
40 def __import_joints(joints, rigidbodies):
41     print("create joints")
42     container=bl.object.createEmpty('Joints')
43     layers=[
44         True, False, False, False, False, False, False, False, False, False,
45         False, False, False, False, False, False, False, False, False, False,
46             ]
47     material=bl.material.create('joint')
48     material.diffuse_color=(1, 0, 0)
49     constraintMeshes=[]
50     for i, c in enumerate(joints):
51         bpy.ops.mesh.primitive_uv_sphere_add(
52                 segments=8,
53                 ring_count=4,
54                 size=0.1,
55                 location=(c.position.x, c.position.z, c.position.y),
56                 layers=layers
57                 )
58         meshObject=bl.object.getActive()
59         constraintMeshes.append(meshObject)
60         mesh=bl.object.getData(meshObject)
61         bl.mesh.addMaterial(mesh, material)
62         meshObject.name=get_object_name("j{0:02}:", i, c.name)
63         #meshObject.draw_transparent=True
64         #meshObject.draw_wire=True
65         meshObject.draw_type='SOLID'
66         rot=c.rotation
67         meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
68
69         meshObject[bl.CONSTRAINT_NAME]=c.name
70         meshObject[bl.CONSTRAINT_A]=rigidbodies[c.rigidbody_index_a].name
71         meshObject[bl.CONSTRAINT_B]=rigidbodies[c.rigidbody_index_b].name
72         meshObject[bl.CONSTRAINT_POS_MIN]=VtoV(c.translation_limit_min)
73         meshObject[bl.CONSTRAINT_POS_MAX]=VtoV(c.translation_limit_max)
74         meshObject[bl.CONSTRAINT_ROT_MIN]=VtoV(c.rotation_limit_min)
75         meshObject[bl.CONSTRAINT_ROT_MAX]=VtoV(c.rotation_limit_max)
76         meshObject[bl.CONSTRAINT_SPRING_POS]=VtoV(c.spring_constant_translation)
77         meshObject[bl.CONSTRAINT_SPRING_ROT]=VtoV(c.spring_constant_rotation)
78
79     for meshObject in reversed(constraintMeshes):
80         bl.object.makeParent(container, meshObject)
81
82     return container
83
84 def __importRigidBodies(rigidbodies, bones):
85     print("create rigid bodies")
86
87     container=bl.object.createEmpty('RigidBodies')
88     layers=[
89         True, False, False, False, False, False, False, False, False, False,
90         False, False, False, False, False, False, False, False, False, False,
91             ]
92     material=bl.material.create('rigidBody')
93     rigidMeshes=[]
94     for i, rigid in enumerate(rigidbodies):
95         if rigid.bone_index==-1:
96             # no reference bone
97             bone=bones[0]
98         else:
99             bone=bones[rigid.bone_index]
100         pos=rigid.shape_position
101         size=rigid.shape_size
102
103         if rigid.shape_type==0:
104             bpy.ops.mesh.primitive_ico_sphere_add(
105                     location=(pos.x, pos.z, pos.y),
106                     layers=layers
107                     )
108             bpy.ops.transform.resize(
109                     value=(size.x, size.x, size.x))
110         elif rigid.shape_type==1:
111             bpy.ops.mesh.primitive_cube_add(
112                     location=(pos.x, pos.z, pos.y),
113                     layers=layers
114                     )
115             bpy.ops.transform.resize(
116                     value=(size.x, size.z, size.y))
117         elif rigid.shape_type==2:
118             bpy.ops.mesh.primitive_cylinder_add(
119                     location=(pos.x, pos.z, pos.y),
120                     layers=layers
121                     )
122             bpy.ops.transform.resize(
123                     value=(size.x, size.x, size.y))
124         else:
125             assert(False)
126
127         meshObject=bl.object.getActive()
128         mesh=bl.object.getData(meshObject)
129         rigidMeshes.append(meshObject)
130         bl.mesh.addMaterial(mesh, material)
131         meshObject.name=get_object_name("r{0:02}:", i, rigid.name)
132         #meshObject.draw_transparent=True
133         #meshObject.draw_wire=True
134         meshObject.draw_type='WIRE'
135         rot=rigid.shape_rotation
136         meshObject.rotation_euler=(-rot.x, -rot.z, -rot.y)
137
138         meshObject[bl.RIGID_NAME]=rigid.name
139         meshObject[bl.RIGID_SHAPE_TYPE]=rigid.shape_type
140         meshObject[bl.RIGID_PROCESS_TYPE]=rigid.mode
141         meshObject[bl.RIGID_BONE_NAME]=bone.name
142         meshObject[bl.RIGID_GROUP]=rigid.collision_group
143         meshObject[bl.RIGID_INTERSECTION_GROUP]=rigid.no_collision_group
144         meshObject[bl.RIGID_WEIGHT]=rigid.param.mass
145         meshObject[bl.RIGID_LINEAR_DAMPING]=rigid.param.linear_damping
146         meshObject[bl.RIGID_ANGULAR_DAMPING]=rigid.param.angular_damping
147         meshObject[bl.RIGID_RESTITUTION]=rigid.param.restitution
148         meshObject[bl.RIGID_FRICTION]=rigid.param.friction
149
150     for meshObject in reversed(rigidMeshes):
151         bl.object.makeParent(container, meshObject)
152
153     return container
154
155 def __create_a_material(m, name, textures_and_images):
156     """
157     materialを作成する
158
159     :Params:
160         m
161             pymeshio.pmx.Material
162         name
163             material name
164         textures_and_images
165             list of (texture, image)
166     """
167     material = bl.material.create(name)
168     # diffuse
169     material.diffuse_shader='FRESNEL'
170     material.diffuse_color=[m.diffuse_color.r, m.diffuse_color.g, m.diffuse_color.b]
171     material.alpha=m.alpha
172     # specular
173     material.specular_shader='TOON'
174     material.specular_color=[m.specular_color.r, m.specular_color.g, m.specular_color.b]
175     material.specular_toon_size=int(m.specular_factor)
176     # ambient
177     material.mirror_color=[m.ambient_color.r, m.ambient_color.g, m.ambient_color.b]
178     # todo
179     # flag
180     # edge_color
181     # edge_size
182     # other
183     material.preview_render_type='FLAT'
184     material.use_transparency=True
185     # texture
186     if m.texture_index!=-1:
187         bl.material.addTexture(material, textures_and_images[m.texture_index][0])
188     return material
189
190 def __create_armature(bones, display_slots):
191     """
192     armatureを作成する
193
194     :Params:
195         bones
196             list of pymeshio.pmx.Bone
197     """
198     armature, armature_object=bl.armature.create()
199
200     # create bones
201     bl.armature.makeEditable(armature_object)
202     def create_bone(b):
203         bone=bl.armature.createBone(armature, b.name)
204         # bone position
205         bone.head=bl.createVector(*convert_coord(b.position))
206         if not b.getConnectionFlag():
207             bone.tail=bl.createVector(*convert_coord(b.position))
208         elif not b.getVisibleFlag():
209             bone.tail=bone.head+bl.createVector(0, 1, 0)
210
211         return bone
212     bl_bones=[create_bone(b) for b in bones]
213
214     # build skeleton
215     for b, bone in zip(bones, bl_bones):
216         assert(b.name==bone.name)
217         if b.parent_index!=-1:
218             #print("%s -> %s" % (bones[b.parent_index].name, b.name))
219             parent_bone=bl_bones[b.parent_index]
220             bone.parent=parent_bone
221             if b.getConnectionFlag() and b.tail_index!=-1:
222                 assert(b.tail_index!=0)
223                 tail_bone=bl_bones[b.tail_index]
224                 bone.tail=tail_bone.head
225                 bl.bone.setConnected(tail_bone)
226         else:
227             print("no parent %s" % b.name)
228     bl.armature.update(armature)
229
230     # create ik constraint
231     bl.enterObjectMode()
232     pose = bl.object.getPose(armature_object)
233     for b, bone in zip(bones, bl_bones):
234         if b.getIkFlag():
235             ik=b.ik
236             assert(len(ik.link)<16)
237             p_bone=pose.bones[bones[ik.target_index].name]
238             assert(p_bone)
239             constraint=bl.armature.createIkConstraint(
240                     armature_object, p_bone, bone.name,
241                     ik.link, ik.limit_radian, ik.loop)
242     bl.armature.makeEditable(armature_object)
243     bl.armature.update(armature)
244
245     # create bone group
246     bl.enterObjectMode()
247     pose = bl.object.getPose(armature_object)
248     for i, ds in enumerate(display_slots):
249         print(ds)
250         g=bl.object.createBoneGroup(armature_object, ds.name, "THEME%02d" % (i+1))
251         for t, index in ds.references:
252             if t==0:
253                 name=bones[index].name
254                 try:
255                     pose.bones[name].bone_group=g
256                 except KeyError as e:
257                     print("pose %s is not found" % name)
258
259     bl.enterObjectMode()
260     return armature_object
261
262 def _execute(filepath):
263     """
264     importerr 本体
265     """
266     print(filepath)
267
268     model=reader.read_from_file(filepath)
269     if not model:
270         print("fail to load %s" % filepath)
271         return
272     print(model)
273
274     # メッシュをまとめるエンプティオブジェクト
275     model_name=model.english_name
276     if len(model_name)==0:
277         model_name=os.path.basename(filepath)
278     root_object=bl.object.createEmpty(model_name)
279     root_object[bl.MMD_MB_NAME]=model.name
280     root_object[bl.MMD_MB_COMMENT]=model.comment
281     root_object[bl.MMD_COMMENT]=model.english_comment
282
283     # armatureを作る
284     armature_object=__create_armature(model.bones, model.display_slots)
285     if armature_object:
286         bl.object.makeParent(root_object, armature_object)
287
288     # テクスチャを作る
289     texture_dir=os.path.dirname(filepath)
290     textures_and_images=[bl.texture.create(os.path.join(texture_dir, t))
291             for t in model.textures]
292     print(textures_and_images)
293
294     index_generator=(i for i in model.indices)
295     # 頂点配列。(Left handed y-up) to (Right handed z-up)
296     vertices=[convert_coord(pos)
297             for pos in (v.position for v in model.vertices)]
298
299     for i, m in enumerate(model.materials):
300         ####################
301         # material
302         ####################
303         material=__create_a_material(m, m.name, textures_and_images)
304
305         ####################
306         # mesh object
307         ####################
308         # object名はutf-8で21byteまで
309         mesh, mesh_object=bl.mesh.create(get_object_name("{0:02}:", i, m.name))
310         bl.mesh.addMaterial(mesh, material)
311         # activate object
312         bl.object.deselectAll()
313         bl.object.activate(mesh_object)
314         bl.object.makeParent(root_object, mesh_object)
315
316         ####################
317         # vertices & faces
318         ####################
319         indices=[next(index_generator)
320                     for _ in range(m.vertex_count)]
321         used_indices=set(indices)
322         bl.mesh.addGeometry(mesh, vertices,
323                 [(indices[i], indices[i+1], indices[i+2])
324                     for i in range(0, len(indices), 3)])
325         assert(len(model.vertices), len(mesh.vertices))
326
327         # assign material
328         bl.mesh.addUV(mesh)
329         hasTexture=bl.material.hasTexture(material)
330         if hasTexture:
331             index_gen=(i for i in indices)
332             image=(textures_and_images.get[m.texture_index] 
333                     if m.texture_index in textures_and_images
334                     else None)
335         for i, face in enumerate(mesh.faces):
336             bl.face.setMaterial(face, 0)
337             if hasTexture:
338                 uv0=model.vertices[next(index_gen)].uv
339                 uv1=model.vertices[next(index_gen)].uv
340                 uv2=model.vertices[next(index_gen)].uv
341                 bl.mesh.setFaceUV(mesh, i, face, [# fix uv
342                     (uv0.x, 1.0-uv0.y),
343                     (uv1.x, 1.0-uv1.y),
344                     (uv2.x, 1.0-uv2.y)
345                     ],
346                     image)
347
348         ####################
349         # armature
350         ####################
351         if armature_object:
352             # armature modifirer
353             bl.modifier.addArmature(mesh_object, armature_object)
354             # set vertex attributes(normal, bone weights)
355             bl.mesh.useVertexUV(mesh)
356             for i, (v,  mvert) in enumerate(zip(model.vertices, mesh.vertices)):
357                 bl.vertex.setNormal(mvert, convert_coord(v.normal))
358                 if isinstance(v.deform, pmx.Bdef1):
359                     bl.object.assignVertexGroup(mesh_object,
360                             model.bones[v.deform.index0].name, i, 1.0)
361                 elif isinstance(v.deform, pmx.Bdef2):
362                     bl.object.assignVertexGroup(mesh_object,
363                             model.bones[v.deform.index0].name, i, v.deform.weight0)
364                     bl.object.assignVertexGroup(mesh_object,
365                             model.bones[v.deform.index1].name, i, 1.0-v.deform.weight0)
366                 else:
367                     raise Exception("unknown deform: %s" % v.deform)
368
369         ####################
370         # shape keys
371         ####################
372         # set shape_key pin
373         bl.object.pinShape(mesh_object, True)
374         # create base key
375         baseShapeBlock=bl.object.addShapeKey(mesh_object, bl.BASE_SHAPE_NAME)
376         mesh.update()
377         for m in model.morphs:
378             new_shape_key=bl.object.addShapeKey(mesh_object, m.name)
379             for o in m.offsets:
380                 if isinstance(o, pmx.VertexMorphOffset):
381                     bl.shapekey.assign(new_shape_key, 
382                             o.vertex_index, 
383                             mesh.vertices[o.vertex_index].co+
384                             bl.createVector(*convert_coord(o.position_offset)))
385                 else:
386                     raise Exception("unknown morph type: %s" % o)
387         # select base shape
388         bl.object.setActivateShapeKey(mesh_object, 0)
389
390         #############################
391         # clean up not used vertices
392         # in the material.
393         #############################
394         bl.mesh.vertsDelete(mesh, [i for i in range(len(mesh.vertices))
395             if i not in used_indices])
396
397     # import rigid bodies
398     rigidbody_object=__importRigidBodies(model.rigidbodies, model.bones)
399     if rigidbody_object:
400         bl.object.makeParent(root_object, rigidbody_object)
401
402     # import joints
403     joint_object=__import_joints(model.joints, model.rigidbodies)
404     if joint_object:
405         bl.object.makeParent(root_object, joint_object)
406
407     return {'FINISHED'}
408