align columns (for multi-drag)
[blender-addons.git] / io_scene_ms3d / ms3d_import.py
blobfa3a8462b7dfbb104252d825a75add430e4d5181
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 ###############################################################################
22 #234567890123456789012345678901234567890123456789012345678901234567890123456789
23 #--------1---------2---------3---------4---------5---------6---------7---------
26 # ##### BEGIN COPYRIGHT BLOCK #####
28 # initial script copyright (c)2011-2013 Alexander Nussbaumer
30 # ##### END COPYRIGHT BLOCK #####
33 #import python stuff
34 import io
35 from mathutils import (
36 Vector,
37 Matrix,
39 from os import (
40 path,
42 from sys import (
43 exc_info,
45 from time import (
46 time,
50 # import io_scene_ms3d stuff
51 from io_scene_ms3d.ms3d_strings import (
52 ms3d_str,
54 from io_scene_ms3d.ms3d_spec import (
55 Ms3dSpec,
56 Ms3dModel,
57 Ms3dVertexEx2,
58 Ms3dVertexEx3,
59 Ms3dHeader,
61 from io_scene_ms3d.ms3d_utils import (
62 select_all,
63 enable_pose_mode,
64 enable_edit_mode,
65 pre_setup_environment,
66 post_setup_environment,
67 get_edge_split_modifier_add_if,
69 from io_scene_ms3d.ms3d_ui import (
70 Ms3dUi,
74 #import blender stuff
75 from bpy import (
76 ops,
78 import bmesh
79 from bpy_extras.image_utils import (
80 load_image,
84 ###############################################################################
85 FORMAT_GROUP = "{}.g"
86 FORMAT_IMAGE = "{}.i"
87 FORMAT_TEXTURE = "{}.tex"
88 # keep material name like it is (prevent name "snakes" on re-import)
89 #FORMAT_MATERIAL = "{}.mat"
90 FORMAT_MATERIAL = "{}"
91 FORMAT_ACTION = "{}.act"
92 FORMAT_MESH = "{}.m"
93 FORMAT_MESH_OBJECT = "{}.mo"
94 FORMAT_EMPTY_OBJECT = "{}.eo"
95 FORMAT_ARMATURE = "{}.a"
96 FORMAT_ARMATURE_OBJECT = "{}.ao"
97 FORMAT_ARMATURE_NLA = "{}.an"
99 ###############################################################################
100 class Ms3dImporter():
102 Load a MilkShape3D MS3D File
104 def __init__(self,
105 report,
106 verbose='NONE',
107 use_extended_normal_handling=False,
108 use_animation=True,
109 use_quaternion_rotation=False,
110 use_joint_size=False,
111 joint_size=1.0,
112 use_joint_to_bones=False,
114 self.report = report
115 self.options_verbose = verbose
116 self.options_use_extended_normal_handling = use_extended_normal_handling
117 self.options_use_animation = use_animation
118 self.options_use_quaternion_rotation = use_quaternion_rotation
119 self.options_use_joint_size = use_joint_size
120 self.options_joint_size = joint_size
121 self.options_use_joint_to_bones = use_joint_to_bones
122 self.directory_name = ""
123 self.file_name = ""
124 pass
126 ###########################################################################
127 # create empty blender ms3d_model
128 # read ms3d file
129 # fill blender with ms3d_model content
130 def read(self, blender_context, filepath):
131 """ read ms3d file and convert ms3d content to bender content """
132 t1 = time()
133 t2 = None
134 self.has_textures = False
136 try:
137 # setup environment
138 pre_setup_environment(self, blender_context)
140 # inject splitted filepath
141 self.directory_name, self.file_name = path.split(filepath)
143 # create an empty ms3d template
144 ms3d_model = Ms3dModel(self.file_name)
146 try:
147 # open ms3d file
148 with io.FileIO(filepath, 'rb') as raw_io:
149 # read and inject ms3d data from disk to internal structure
150 debug_out = ms3d_model.read(raw_io)
151 raw_io.close()
153 if self.options_verbose in Ms3dUi.VERBOSE_MAXIMAL:
154 print(debug_out)
155 finally:
156 pass
158 # if option is set, this time will enlarges the io time
159 if self.options_verbose in Ms3dUi.VERBOSE_MAXIMAL:
160 ms3d_model.print_internal()
162 t2 = time()
164 is_valid, statistics = ms3d_model.is_valid()
166 if is_valid:
167 # inject ms3d data to blender
168 self.to_blender(blender_context, ms3d_model)
170 post_setup_environment(self, blender_context)
172 if self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
173 print()
174 print("##########################################################")
175 print("Import from MS3D to Blender")
176 print(statistics)
177 print("##########################################################")
179 except Ms3dHeader.HeaderError:
180 msg = "read - invalid file format."
181 if self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
182 print(msg)
183 if self.report:
184 self.report({'WARNING', 'ERROR', }, msg)
186 return False
188 except Exception:
189 type, value, traceback = exc_info()
190 if self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
191 print("read - exception in try block\n type: '{0}'\n"
192 " value: '{1}'".format(type, value, traceback))
193 if self.report:
194 self.report({'WARNING', 'ERROR', }, "read - exception.")
196 if t2 is None:
197 t2 = time()
199 return False
201 else:
202 pass
204 t3 = time()
206 if self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
207 print(ms3d_str['SUMMARY_IMPORT'].format(
208 (t3 - t1), (t2 - t1), (t3 - t2)))
210 return True
213 ###########################################################################
214 def to_blender(self, blender_context, ms3d_model):
215 blender_mesh_object = self.create_geometry(blender_context, ms3d_model)
216 blender_armature_object = self.create_animation(
217 blender_context, ms3d_model, blender_mesh_object)
219 blender_empty_object = self.organize_objects(
220 blender_context, ms3d_model,
221 [blender_mesh_object, blender_armature_object])
223 return blender_empty_object, blender_mesh_object
226 ###########################################################################
227 def organize_objects(self, blender_context, ms3d_model, blender_objects):
228 ##########################
229 # blender_armature_object to blender_mesh_object
230 # that has bad side effects to the armature
231 # and causes cyclic dependencies
232 ###blender_armature_object.parent = blender_mesh_object
233 ###blender_mesh_object.parent = blender_armature_object
235 blender_scene = blender_context.scene
237 blender_group = blender_context.blend_data.groups.new(
238 FORMAT_GROUP.format(ms3d_model.name))
239 blender_empty_object = blender_context.blend_data.objects.new(
240 FORMAT_EMPTY_OBJECT.format(ms3d_model.name), None)
241 blender_empty_object.location = blender_scene.cursor_location
242 blender_scene.objects.link(blender_empty_object)
243 blender_group.objects.link(blender_empty_object)
245 for blender_object in blender_objects:
246 if blender_object is not None:
247 blender_group.objects.link(blender_object)
248 blender_object.parent = blender_empty_object
250 return blender_empty_object
253 ###########################################################################
254 def create_geometry(self, blender_context, ms3d_model):
255 ##########################
256 # blender stuff:
257 # create a blender Mesh
258 blender_mesh = blender_context.blend_data.meshes.new(
259 FORMAT_MESH.format(ms3d_model.name))
260 blender_mesh.ms3d.name = ms3d_model.name
262 ms3d_comment = ms3d_model.comment_object
263 if ms3d_comment is not None:
264 blender_mesh.ms3d.comment = ms3d_comment.comment
265 ms3d_model_ex = ms3d_model.model_ex_object
266 if ms3d_model_ex is not None:
267 blender_mesh.ms3d.joint_size = ms3d_model_ex.joint_size
268 blender_mesh.ms3d.alpha_ref = ms3d_model_ex.alpha_ref
269 blender_mesh.ms3d.transparency_mode \
270 = Ms3dUi.transparency_mode_from_ms3d(
271 ms3d_model_ex.transparency_mode)
273 ##########################
274 # blender stuff:
275 # link to blender object
276 blender_mesh_object = blender_context.blend_data.objects.new(
277 FORMAT_MESH_OBJECT.format(ms3d_model.name), blender_mesh)
279 ##########################
280 # blender stuff:
281 # create edge split modifier, to make sharp edges visible
282 blender_modifier = get_edge_split_modifier_add_if(blender_mesh_object)
284 ##########################
285 # blender stuff:
286 # link to blender scene
287 blender_scene = blender_context.scene
288 blender_scene.objects.link(blender_mesh_object)
289 #blender_mesh_object.location = blender_scene.cursor_location
290 enable_edit_mode(False, blender_context)
291 select_all(False)
292 blender_mesh_object.select = True
293 blender_scene.objects.active = blender_mesh_object
295 ##########################
296 # take this as active object after import
297 self.active_object = blender_mesh_object
299 ##########################
300 # blender stuff:
301 # create all (ms3d) groups
302 ms3d_to_blender_group_index = {}
303 blender_group_manager = blender_mesh.ms3d
304 for ms3d_group_index, ms3d_group in enumerate(ms3d_model.groups):
305 blender_group = blender_group_manager.create_group()
306 blender_group.name = ms3d_group.name
307 blender_group.flags = Ms3dUi.flags_from_ms3d(ms3d_group.flags)
308 blender_group.material_index = ms3d_group.material_index
310 ms3d_comment = ms3d_group.comment_object
311 if ms3d_comment is not None:
312 blender_group.comment = ms3d_comment.comment
314 # translation dictionary
315 ms3d_to_blender_group_index[ms3d_group_index] = blender_group.id
317 ####################################################
318 # begin BMesh stuff
321 ##########################
322 # BMesh stuff:
323 # create an empty BMesh
324 bm = bmesh.new()
326 ##########################
327 # BMesh stuff:
328 # create new Layers for custom data per "mesh face"
329 layer_texture = bm.faces.layers.tex.get(
330 ms3d_str['OBJECT_LAYER_TEXTURE'])
331 if layer_texture is None:
332 layer_texture = bm.faces.layers.tex.new(
333 ms3d_str['OBJECT_LAYER_TEXTURE'])
335 layer_smoothing_group = bm.faces.layers.int.get(
336 ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
337 if layer_smoothing_group is None:
338 layer_smoothing_group = bm.faces.layers.int.new(
339 ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
341 layer_group = bm.faces.layers.int.get(
342 ms3d_str['OBJECT_LAYER_GROUP'])
343 if layer_group is None:
344 layer_group = bm.faces.layers.int.new(
345 ms3d_str['OBJECT_LAYER_GROUP'])
347 ##########################
348 # BMesh stuff:
349 # create new Layers for custom data per "face vertex"
350 layer_uv = bm.loops.layers.uv.get(ms3d_str['OBJECT_LAYER_UV'])
351 if layer_uv is None:
352 layer_uv = bm.loops.layers.uv.new(ms3d_str['OBJECT_LAYER_UV'])
354 ##########################
355 # BMesh stuff:
356 # create new Layers for custom data per "vertex"
357 layer_extra = bm.verts.layers.int.get(ms3d_str['OBJECT_LAYER_EXTRA'])
358 if layer_extra is None:
359 layer_extra = bm.verts.layers.int.new(ms3d_str['OBJECT_LAYER_EXTRA'])
361 ##########################
362 # BMesh stuff:
363 # create all vertices
364 for ms3d_vertex_index, ms3d_vertex in enumerate(ms3d_model.vertices):
365 bmv = bm.verts.new(self.geometry_correction(ms3d_vertex.vertex))
366 bmv.index = ms3d_vertex_index
368 if layer_extra and ms3d_vertex.vertex_ex_object and \
369 (isinstance(ms3d_vertex.vertex_ex_object, Ms3dVertexEx2) \
370 or isinstance(ms3d_vertex.vertex_ex_object, Ms3dVertexEx3)):
372 #bmv[layer_extra] = ms3d_vertex.vertex_ex_object.extra
373 # bm.verts.layers.int does only support signed int32
374 # convert unsigned int32 to signed int32 (little-endian)
375 unsigned_int32 = ms3d_vertex.vertex_ex_object.extra
376 bytes_int32 = unsigned_int32.to_bytes(
377 4, byteorder='little', signed=False)
378 signed_int32 = int.from_bytes(
379 bytes_int32, byteorder='little', signed=True)
380 bmv[layer_extra] = signed_int32
382 ##########################
383 # blender stuff (uses BMesh stuff):
384 # create all materials / image textures
385 ms3d_to_blender_material = {}
386 for ms3d_material_index, ms3d_material in enumerate(
387 ms3d_model.materials):
388 blender_material = blender_context.blend_data.materials.new(
389 FORMAT_MATERIAL.format(ms3d_material.name))
391 # custom data
392 blender_material.ms3d.name = ms3d_material.name
393 blender_material.ms3d.ambient = ms3d_material.ambient
394 blender_material.ms3d.diffuse = ms3d_material.diffuse
395 blender_material.ms3d.specular = ms3d_material.specular
396 blender_material.ms3d.emissive = ms3d_material.emissive
397 blender_material.ms3d.shininess = ms3d_material.shininess
398 blender_material.ms3d.transparency = ms3d_material.transparency
399 blender_material.ms3d.mode = Ms3dUi.texture_mode_from_ms3d(
400 ms3d_material.mode)
402 if ms3d_material.texture:
403 blender_material.ms3d.texture = ms3d_material.texture
405 if ms3d_material.alphamap:
406 blender_material.ms3d.alphamap = ms3d_material.alphamap
408 ms3d_comment = ms3d_material.comment_object
409 if ms3d_comment is not None:
410 blender_material.ms3d.comment = ms3d_comment.comment
412 # blender data
413 blender_material.ambient = (
414 (ms3d_material.ambient[0]
415 + ms3d_material.ambient[1]
416 + ms3d_material.ambient[2]) / 3.0)
418 blender_material.diffuse_color[0] = ms3d_material.diffuse[0]
419 blender_material.diffuse_color[1] = ms3d_material.diffuse[1]
420 blender_material.diffuse_color[2] = ms3d_material.diffuse[2]
422 blender_material.specular_color[0] = ms3d_material.specular[0]
423 blender_material.specular_color[1] = ms3d_material.specular[1]
424 blender_material.specular_color[2] = ms3d_material.specular[2]
426 blender_material.emit = (
427 (ms3d_material.emissive[0]
428 + ms3d_material.emissive[1]
429 + ms3d_material.emissive[2]) / 3.0)
431 blender_material.specular_hardness = ms3d_material.shininess * 4.0
432 blender_material.alpha = 1.0 - ms3d_material.transparency
434 # diffuse texture
435 if ms3d_material.texture:
436 dir_name_diffuse = self.directory_name
437 file_name_diffuse = path.split(ms3d_material.texture)[1]
438 blender_image_diffuse = load_image(
439 file_name_diffuse, dir_name_diffuse)
440 name_diffuse = path.splitext(file_name_diffuse)[0]
441 if blender_image_diffuse:
442 blender_image_diffuse.name = FORMAT_IMAGE.format(name_diffuse)
443 blender_texture_diffuse = \
444 blender_context.blend_data.textures.new(
445 name=FORMAT_TEXTURE.format(name_diffuse),
446 type='IMAGE')
447 blender_texture_diffuse.image = blender_image_diffuse
448 blender_texture_slot_diffuse \
449 = blender_material.texture_slots.add()
450 blender_texture_slot_diffuse.texture = blender_texture_diffuse
451 blender_texture_slot_diffuse.texture_coords = 'UV'
452 blender_texture_slot_diffuse.uv_layer = layer_uv.name
453 blender_texture_slot_diffuse.use_map_color_diffuse = True
454 blender_texture_slot_diffuse.use_map_alpha = False
455 if blender_image_diffuse is not None:
456 self.has_textures = True
457 else:
458 blender_image_diffuse = None
460 # alpha texture
461 if ms3d_material.alphamap:
462 dir_name_alpha = self.directory_name
463 file_name_alpha = path.split(ms3d_material.alphamap)[1]
464 blender_image_alpha = load_image(
465 file_name_alpha, dir_name_alpha)
466 name_alpha = path.splitext(file_name_alpha)[0]
467 if blender_image_alpha:
468 blender_image_alpha.name = FORMAT_IMAGE.format(name_alpha)
469 blender_texture_alpha = blender_context.blend_data.textures.new(
470 name=FORMAT_TEXTURE.format(file_name_alpha),
471 type='IMAGE')
472 blender_texture_alpha.image = blender_image_alpha
473 blender_texture_slot_alpha \
474 = blender_material.texture_slots.add()
475 blender_texture_slot_alpha.texture = blender_texture_alpha
476 blender_texture_slot_alpha.texture_coords = 'UV'
477 blender_texture_slot_alpha.uv_layer = layer_uv.name
478 blender_texture_slot_alpha.use_map_color_diffuse = False
479 blender_texture_slot_alpha.use_map_alpha = True
480 blender_texture_slot_alpha.use_rgb_to_intensity = True
481 blender_material.alpha = 0
482 blender_material.specular_alpha = 0
484 # append blender material to blender mesh, to be linked to
485 blender_mesh.materials.append(blender_material)
487 # translation dictionary
488 ms3d_to_blender_material[ms3d_material_index] \
489 = blender_image_diffuse
491 ##########################
492 # BMesh stuff:
493 # create all triangles
494 length_verts = len(bm.verts)
495 vertex_extra_index = length_verts
496 blender_invalide_normal = Vector()
497 smoothing_group_blender_faces = {}
498 for ms3d_triangle_index, ms3d_triangle in enumerate(
499 ms3d_model.triangles):
500 bmv_list = []
501 bmf_normal = Vector()
503 for index, vert_index in enumerate(ms3d_triangle.vertex_indices):
504 if vert_index < 0 or vert_index >= length_verts:
505 continue
506 bmv = bm.verts[vert_index]
508 blender_normal = self.geometry_correction(
509 ms3d_triangle.vertex_normals[index])
510 if bmv.normal == blender_invalide_normal:
511 bmv.normal = blender_normal
512 elif bmv.normal != blender_normal \
513 and self.options_use_extended_normal_handling:
514 ## search for an already created extra vertex
515 bmv_new = None
516 for vert_index_candidat in range(
517 vertex_extra_index, length_verts):
518 bmv_candidat = bm.verts[vert_index_candidat]
519 if bmv_candidat.co == bmv.co \
520 and bmv_candidat.normal == blender_normal:
521 bmv_new = bmv_candidat
522 vert_index = vert_index_candidat
523 break
525 ## if not exists, create one in blender and ms3d as well
526 if bmv_new is None:
527 ms3d_model.vertices.append(
528 ms3d_model.vertices[vert_index])
529 bmv_new = bm.verts.new(bmv.co)
530 bmv_new.index = -vert_index
531 bmv_new.normal = blender_normal
532 bmv_new[layer_extra] = bmv[layer_extra]
533 vert_index = length_verts
534 length_verts += 1
535 if self.report and self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
536 self.report({'WARNING', 'INFO'},
537 ms3d_str['WARNING_IMPORT_EXTRA_VERTEX_NORMAL'].format(
538 bmv.normal, blender_normal))
539 bmv = bmv_new
541 if [[x] for x in bmv_list if x == bmv]:
542 if self.report and self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
543 self.report(
544 {'WARNING', 'INFO'},
545 ms3d_str['WARNING_IMPORT_SKIP_VERTEX_DOUBLE'].format(
546 ms3d_triangle_index))
547 continue
548 bmv_list.append(bmv)
549 bmf_normal += bmv.normal
551 if len(bmv_list) < 3:
552 if self.report and self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
553 self.report(
554 {'WARNING', 'INFO'},
555 ms3d_str['WARNING_IMPORT_SKIP_LESS_VERTICES'].format(
556 ms3d_triangle_index))
557 continue
559 # create edges for the face
560 # (not really needed, because bm.faces.new() will create its edges,
561 # if not exist, but good if we have already in case we need full control
562 # of bmesh stuff maybe in the future.
563 bme = bm.edges.get((bmv_list[0], bmv_list[1]))
564 if bme is None:
565 bme = bm.edges.new((bmv_list[0], bmv_list[1]))
566 bme.index = len(bm.edges) - 1
567 bme = bm.edges.get((bmv_list[1], bmv_list[2]))
568 if bme is None:
569 bme = bm.edges.new((bmv_list[1], bmv_list[2]))
570 bme.index = len(bm.edges) - 1
571 bme = bm.edges.get((bmv_list[2], bmv_list[0]))
572 if bme is None:
573 bme = bm.edges.new((bmv_list[2], bmv_list[0]))
574 bme.index = len(bm.edges) - 1
576 bmf = bm.faces.get(bmv_list)
577 if bmf is not None:
578 if self.report and self.options_verbose in Ms3dUi.VERBOSE_NORMAL:
579 self.report(
580 {'WARNING', 'INFO'},
581 ms3d_str['WARNING_IMPORT_SKIP_FACE_DOUBLE'].format(
582 ms3d_triangle_index))
583 continue
585 bmf = bm.faces.new(bmv_list)
586 bmf.index = ms3d_triangle_index
587 bmf_normal.normalize()
588 bmf.normal = bmf_normal
590 # blender uv custom data per "face vertex"
591 bmf.loops[0][layer_uv].uv = Vector(
592 (ms3d_triangle.s[0], 1.0 - ms3d_triangle.t[0]))
593 bmf.loops[1][layer_uv].uv = Vector(
594 (ms3d_triangle.s[1], 1.0 - ms3d_triangle.t[1]))
595 bmf.loops[2][layer_uv].uv = Vector(
596 (ms3d_triangle.s[2], 1.0 - ms3d_triangle.t[2]))
598 # ms3d custom data per "mesh face"
599 bmf[layer_smoothing_group] = ms3d_triangle.smoothing_group
601 blender_group_id = ms3d_to_blender_group_index.get(
602 ms3d_triangle.group_index)
603 if blender_group_id is not None:
604 bmf[layer_group] = blender_group_id
606 if ms3d_triangle.group_index >= 0 \
607 and ms3d_triangle.group_index < len(ms3d_model.groups):
608 ms3d_material_index \
609 = ms3d_model.groups[ms3d_triangle.group_index].material_index
610 if ms3d_material_index != Ms3dSpec.NONE_GROUP_MATERIAL_INDEX:
611 bmf.material_index = ms3d_material_index
612 # apply diffuse texture image to face, to be visible in 3d view
613 bmf[layer_texture].image = ms3d_to_blender_material.get(
614 ms3d_material_index)
616 # helper dictionary for post-processing smoothing_groups
617 smoothing_group_blender_face = smoothing_group_blender_faces.get(
618 ms3d_triangle.smoothing_group)
619 if smoothing_group_blender_face is None:
620 smoothing_group_blender_face = []
621 smoothing_group_blender_faces[ms3d_triangle.smoothing_group] \
622 = smoothing_group_blender_face
623 smoothing_group_blender_face.append(bmf)
625 ##########################
626 # BMesh stuff:
627 # create all sharp edges for blender to make smoothing_groups visible
628 for ms3d_smoothing_group_index, blender_face_list \
629 in smoothing_group_blender_faces.items():
630 edge_dict = {}
631 for bmf in blender_face_list:
632 bmf.smooth = True
633 for bme in bmf.edges:
634 if edge_dict.get(bme) is None:
635 edge_dict[bme] = 0
636 else:
637 edge_dict[bme] += 1
638 bme.seam = (edge_dict[bme] == 0)
639 bme.smooth = (edge_dict[bme] != 0)
641 ##########################
642 # BMesh stuff:
643 # finally transfer BMesh to Mesh
644 bm.to_mesh(blender_mesh)
645 bm.free()
649 # end BMesh stuff
650 ####################################################
652 blender_mesh.validate(self.options_verbose in Ms3dUi.VERBOSE_MAXIMAL)
654 return blender_mesh_object
657 ###########################################################################
658 def create_animation(self, blender_context, ms3d_model, blender_mesh_object):
659 ##########################
660 # setup scene
661 blender_scene = blender_context.scene
662 blender_scene.render.fps = ms3d_model.animation_fps
663 if ms3d_model.animation_fps:
664 blender_scene.render.fps_base = (blender_scene.render.fps /
665 ms3d_model.animation_fps)
667 blender_scene.frame_start = 1
668 blender_scene.frame_end = (ms3d_model.number_total_frames
669 + blender_scene.frame_start) - 1
670 blender_scene.frame_current = (ms3d_model.current_time
671 * ms3d_model.animation_fps)
673 ##########################
674 if not ms3d_model.joints:
675 return
677 ##########################
678 ms3d_armature_name = FORMAT_ARMATURE.format(ms3d_model.name)
679 ms3d_armature_object_name = FORMAT_ARMATURE_OBJECT.format(ms3d_model.name)
680 ms3d_action_name = FORMAT_ACTION.format(ms3d_model.name)
682 ##########################
683 # create new blender_armature_object
684 blender_armature = blender_context.blend_data.armatures.new(
685 ms3d_armature_name)
686 blender_armature.ms3d.name = ms3d_model.name
687 blender_armature.draw_type = 'STICK'
688 blender_armature.show_axes = True
689 blender_armature.use_auto_ik = True
690 blender_armature_object = blender_context.blend_data.objects.new(
691 ms3d_armature_object_name, blender_armature)
692 blender_scene.objects.link(blender_armature_object)
693 #blender_armature_object.location = blender_scene.cursor_location
694 blender_armature_object.show_x_ray = True
696 ##########################
697 # create new modifier
698 blender_modifier = blender_mesh_object.modifiers.new(
699 blender_armature.name, type='ARMATURE')
700 blender_modifier.show_expanded = False
701 blender_modifier.use_vertex_groups = True
702 blender_modifier.use_bone_envelopes = False
703 blender_modifier.object = blender_armature_object
705 ##########################
706 # prepare for vertex groups
707 ms3d_to_blender_vertex_groups = {}
708 for ms3d_vertex_index, ms3d_vertex in enumerate(ms3d_model.vertices):
709 # prepare for later use for blender vertex group
710 if ms3d_vertex.bone_id != Ms3dSpec.NONE_VERTEX_BONE_ID:
711 if ms3d_vertex.vertex_ex_object \
712 and ( \
713 ms3d_vertex.vertex_ex_object.bone_ids[0] != \
714 Ms3dSpec.NONE_VERTEX_BONE_ID \
715 or ms3d_vertex.vertex_ex_object.bone_ids[1] != \
716 Ms3dSpec.NONE_VERTEX_BONE_ID \
717 or ms3d_vertex.vertex_ex_object.bone_ids[2] != \
718 Ms3dSpec.NONE_VERTEX_BONE_ID \
720 ms3d_vertex_group_ids_weights = []
721 ms3d_vertex_group_ids_weights.append(
722 (ms3d_vertex.bone_id,
723 float(ms3d_vertex.vertex_ex_object.weights[0] % 101) / 100.0,
725 if ms3d_vertex.vertex_ex_object.bone_ids[0] != \
726 Ms3dSpec.NONE_VERTEX_BONE_ID:
727 ms3d_vertex_group_ids_weights.append(
728 (ms3d_vertex.vertex_ex_object.bone_ids[0],
729 float(ms3d_vertex.vertex_ex_object.weights[1] % 101) / 100.0
731 if ms3d_vertex.vertex_ex_object.bone_ids[1] != \
732 Ms3dSpec.NONE_VERTEX_BONE_ID:
733 ms3d_vertex_group_ids_weights.append(
734 (ms3d_vertex.vertex_ex_object.bone_ids[1],
735 float(ms3d_vertex.vertex_ex_object.weights[2] % 101) / 100.0
737 if ms3d_vertex.vertex_ex_object.bone_ids[2] != \
738 Ms3dSpec.NONE_VERTEX_BONE_ID:
739 ms3d_vertex_group_ids_weights.append(
740 (ms3d_vertex.vertex_ex_object.bone_ids[2],
741 1.0 -
742 float((ms3d_vertex.vertex_ex_object.weights[0] % 101)
743 + (ms3d_vertex.vertex_ex_object.weights[1] % 101)
744 + (ms3d_vertex.vertex_ex_object.weights[2] % 101)) / 100.0
747 else:
748 ms3d_vertex_group_ids_weights = [(ms3d_vertex.bone_id, 1.0), ]
750 for ms3d_vertex_group_id_weight in ms3d_vertex_group_ids_weights:
751 ms3d_vertex_group_id = ms3d_vertex_group_id_weight[0]
752 blender_vertex_weight = ms3d_vertex_group_id_weight[1]
753 blender_vertex_group = ms3d_to_blender_vertex_groups.get(
754 ms3d_vertex_group_id)
755 if blender_vertex_group is None:
756 ms3d_to_blender_vertex_groups[ms3d_vertex_group_id] \
757 = blender_vertex_group = []
758 blender_vertex_group.append((ms3d_vertex_index,
759 blender_vertex_weight))
761 ##########################
762 # blender stuff:
763 # create all vertex groups to be used for bones
764 for ms3d_bone_id, blender_vertex_index_weight_list \
765 in ms3d_to_blender_vertex_groups.items():
766 ms3d_name = ms3d_model.joints[ms3d_bone_id].name
767 blender_vertex_group = blender_mesh_object.vertex_groups.new(
768 ms3d_name)
769 for blender_vertex_id_weight in blender_vertex_index_weight_list:
770 blender_vertex_index = blender_vertex_id_weight[0]
771 blender_vertex_weight = blender_vertex_id_weight[1]
772 blender_vertex_group.add((blender_vertex_index, ),
773 blender_vertex_weight, 'ADD')
775 ##########################
776 # bring joints in the correct order
777 ms3d_joints_ordered = []
778 self.build_ms3d_joint_dependency_order(ms3d_model.joints,
779 ms3d_joints_ordered)
781 ##########################
782 # prepare joint data for later use
783 ms3d_joint_by_name = {}
784 for ms3d_joint in ms3d_joints_ordered:
785 item = ms3d_joint_by_name.get(ms3d_joint.name)
786 if item is None:
787 ms3d_joint.__children = []
788 ms3d_joint_by_name[ms3d_joint.name] = ms3d_joint
790 matrix_local_rot = (Matrix.Rotation(ms3d_joint.rotation[2], 4, 'Z')
791 * Matrix.Rotation(ms3d_joint.rotation[1], 4, 'Y')
792 ) * Matrix.Rotation(ms3d_joint.rotation[0], 4, 'X')
793 matrix_local = Matrix.Translation(Vector(ms3d_joint.position)
794 ) * matrix_local_rot
796 ms3d_joint.__matrix_local_rot = matrix_local_rot
797 ms3d_joint.__matrix_global_rot = matrix_local_rot
798 ms3d_joint.__matrix_local = matrix_local
799 ms3d_joint.__matrix_global = matrix_local
801 if ms3d_joint.parent_name:
802 ms3d_joint_parent = ms3d_joint_by_name.get(
803 ms3d_joint.parent_name)
804 if ms3d_joint_parent is not None:
805 ms3d_joint_parent.__children.append(ms3d_joint)
807 matrix_global = ms3d_joint_parent.__matrix_global \
808 * matrix_local
809 ms3d_joint.__matrix_global = matrix_global
811 matrix_global_rot = ms3d_joint_parent.__matrix_global_rot \
812 * matrix_local_rot
813 ms3d_joint.__matrix_global_rot = matrix_global_rot
815 ##########################
816 # ms3d_joint to blender_edit_bone
817 if ms3d_model.model_ex_object and not self.options_use_joint_size:
818 joint_length = ms3d_model.model_ex_object.joint_size
819 else:
820 joint_length = self.options_joint_size
821 if joint_length < 0.01:
822 joint_length = 0.01
824 blender_scene.objects.active = blender_armature_object
825 enable_edit_mode(True, blender_context)
826 for ms3d_joint in ms3d_joints_ordered:
827 blender_edit_bone = blender_armature.edit_bones.new(ms3d_joint.name)
828 blender_edit_bone.use_connect = False
829 blender_edit_bone.use_inherit_rotation = True
830 blender_edit_bone.use_inherit_scale = True
831 blender_edit_bone.use_local_location = True
832 blender_armature.edit_bones.active = blender_edit_bone
834 ms3d_joint = ms3d_joint_by_name[ms3d_joint.name]
835 ms3d_joint_vector = ms3d_joint.__matrix_global * Vector()
837 blender_edit_bone.head \
838 = self.geometry_correction(ms3d_joint_vector)
840 vector_tail_end_up = ms3d_joint.__matrix_global_rot * Vector((0,1,0))
841 vector_tail_end_dir = ms3d_joint.__matrix_global_rot * Vector((0,0,1))
842 vector_tail_end_up.normalize()
843 vector_tail_end_dir.normalize()
844 blender_edit_bone.tail = blender_edit_bone.head \
845 + self.geometry_correction(
846 vector_tail_end_dir * joint_length)
847 blender_edit_bone.align_roll(self.geometry_correction(
848 vector_tail_end_up))
850 if ms3d_joint.parent_name:
851 ms3d_joint_parent = ms3d_joint_by_name[ms3d_joint.parent_name]
852 blender_edit_bone_parent = ms3d_joint_parent.blender_edit_bone
853 blender_edit_bone.parent = blender_edit_bone_parent
855 ms3d_joint.blender_bone_name = blender_edit_bone.name
856 ms3d_joint.blender_edit_bone = blender_edit_bone
857 enable_edit_mode(False, blender_context)
859 if self.options_use_joint_to_bones:
860 enable_edit_mode(True, blender_context)
861 for ms3d_joint in ms3d_joints_ordered:
862 blender_edit_bone = blender_armature.edit_bones[ms3d_joint.name]
863 if blender_edit_bone.children:
864 new_length = 0.0
865 for child_bone in blender_edit_bone.children:
866 length = (child_bone.head - blender_edit_bone.head).length
867 if new_length <= 0 or length < new_length:
868 new_length = length
869 if new_length >= 0.01:
870 direction = blender_edit_bone.tail - blender_edit_bone.head
871 direction.normalize()
872 blender_edit_bone.tail = blender_edit_bone.head + (direction * new_length)
873 enable_edit_mode(False, blender_context)
875 ##########################
876 # post process bones
877 enable_edit_mode(False, blender_context)
878 for ms3d_joint_name, ms3d_joint in ms3d_joint_by_name.items():
879 blender_bone = blender_armature.bones.get(
880 ms3d_joint.blender_bone_name)
881 if blender_bone is None:
882 continue
884 blender_bone.ms3d.name = ms3d_joint.name
885 blender_bone.ms3d.flags = Ms3dUi.flags_from_ms3d(ms3d_joint.flags)
887 ms3d_joint_ex = ms3d_joint.joint_ex_object
888 if ms3d_joint_ex is not None:
889 blender_bone.ms3d.color = ms3d_joint_ex.color
891 ms3d_comment = ms3d_joint.comment_object
892 if ms3d_comment is not None:
893 blender_bone.ms3d.comment = ms3d_comment.comment
895 ##########################
896 if not self.options_use_animation:
897 return blender_armature_object
900 ##########################
901 # process pose bones
902 enable_pose_mode(True, blender_context)
904 blender_action = blender_context.blend_data.actions.new(ms3d_action_name)
905 if blender_armature_object.animation_data is None:
906 blender_armature_object.animation_data_create()
907 blender_armature_object.animation_data.action = blender_action
909 ##########################
910 # transition between keys may be incorrect
911 # because of the gimbal-lock problem!
912 # http://www.youtube.com/watch?v=zc8b2Jo7mno
913 # http://www.youtube.com/watch?v=rrUCBOlJdt4
914 # you can fix it manually by selecting the affected keyframes
915 # and apply the following option to it:
916 # "Graph Editor -> Key -> Discontinuity (Euler) Filter"
917 # ==> "bpy.ops.graph.euler_filter()"
918 # but this option is only available for Euler rotation f-curves!
920 for ms3d_joint_name, ms3d_joint in ms3d_joint_by_name.items():
921 blender_pose_bone = blender_armature_object.pose.bones.get(
922 ms3d_joint.blender_bone_name)
923 if blender_pose_bone is None:
924 continue
926 data_path = blender_pose_bone.path_from_id('location')
927 fcurve_location_x = blender_action.fcurves.new(data_path, index=0)
928 fcurve_location_y = blender_action.fcurves.new(data_path, index=1)
929 fcurve_location_z = blender_action.fcurves.new(data_path, index=2)
930 for translation_key_frames in ms3d_joint.translation_key_frames:
931 frame = (translation_key_frames.time * ms3d_model.animation_fps)
932 matrix_local = Matrix.Translation(
933 Vector(translation_key_frames.position))
934 v = (matrix_local) * Vector()
935 fcurve_location_x.keyframe_points.insert(frame, -v[0])
936 fcurve_location_y.keyframe_points.insert(frame, v[2])
937 fcurve_location_z.keyframe_points.insert(frame, v[1])
939 if self.options_use_quaternion_rotation:
940 blender_pose_bone.rotation_mode = 'QUATERNION'
941 data_path = blender_pose_bone.path_from_id("rotation_quaternion")
942 fcurve_rotation_w = blender_action.fcurves.new(data_path, index=0)
943 fcurve_rotation_x = blender_action.fcurves.new(data_path, index=1)
944 fcurve_rotation_y = blender_action.fcurves.new(data_path, index=2)
945 fcurve_rotation_z = blender_action.fcurves.new(data_path, index=3)
946 for rotation_key_frames in ms3d_joint.rotation_key_frames:
947 frame = (rotation_key_frames.time * ms3d_model.animation_fps)
948 matrix_local_rot = (
949 Matrix.Rotation(
950 rotation_key_frames.rotation[2], 4, 'Y')
951 * Matrix.Rotation(
952 rotation_key_frames.rotation[1], 4, 'Z')
953 ) * Matrix.Rotation(
954 -rotation_key_frames.rotation[0], 4, 'X')
955 q = (matrix_local_rot).to_quaternion()
956 fcurve_rotation_w.keyframe_points.insert(frame, q.w)
957 fcurve_rotation_x.keyframe_points.insert(frame, q.x)
958 fcurve_rotation_y.keyframe_points.insert(frame, q.y)
959 fcurve_rotation_z.keyframe_points.insert(frame, q.z)
960 else:
961 blender_pose_bone.rotation_mode = 'XZY'
962 data_path = blender_pose_bone.path_from_id("rotation_euler")
963 fcurve_rotation_x = blender_action.fcurves.new(data_path, index=0)
964 fcurve_rotation_y = blender_action.fcurves.new(data_path, index=1)
965 fcurve_rotation_z = blender_action.fcurves.new(data_path, index=2)
966 for rotation_key_frames in ms3d_joint.rotation_key_frames:
967 frame = (rotation_key_frames.time * ms3d_model.animation_fps)
968 fcurve_rotation_x.keyframe_points.insert(
969 frame, -rotation_key_frames.rotation[0])
970 fcurve_rotation_y.keyframe_points.insert(
971 frame, rotation_key_frames.rotation[2])
972 fcurve_rotation_z.keyframe_points.insert(
973 frame, rotation_key_frames.rotation[1])
975 enable_pose_mode(False, blender_context)
977 return blender_armature_object
980 ###########################################################################
981 def geometry_correction(self, value):
982 return Vector((value[2], value[0], value[1]))
985 ###########################################################################
986 def build_ms3d_joint_dependency_order(self, ms3d_joints, ms3d_joints_ordered):
987 ms3d_joints_children = {"": {}}
988 for ms3d_joint in ms3d_joints:
989 if ms3d_joint.parent_name:
990 ms3d_joint_children = ms3d_joints_children.get(
991 ms3d_joint.parent_name)
992 if ms3d_joint_children is None:
993 ms3d_joint_children = ms3d_joints_children[
994 ms3d_joint.parent_name] = {}
995 else:
996 ms3d_joint_children = ms3d_joints_children[""]
998 ms3d_joint_children[ms3d_joint.name] = ms3d_joint
1000 self.traverse_dependencies(
1001 ms3d_joints_ordered,
1002 ms3d_joints_children,
1006 return ms3d_joints_ordered
1009 ###########################################################################
1010 def traverse_dependencies(self, ms3d_joints_ordered, ms3d_joints_children,
1011 key):
1012 ms3d_joint_children = ms3d_joints_children.get(key)
1013 if ms3d_joint_children:
1014 for item in ms3d_joint_children.items():
1015 ms3d_joint_name = item[0]
1016 ms3d_joint = item[1]
1017 ms3d_joints_ordered.append(ms3d_joint)
1018 self.traverse_dependencies(
1019 ms3d_joints_ordered,
1020 ms3d_joints_children,
1021 ms3d_joint_name)
1024 ###############################################################################
1025 #234567890123456789012345678901234567890123456789012345678901234567890123456789
1026 #--------1---------2---------3---------4---------5---------6---------7---------
1027 # ##### END OF FILE #####