diff options
Diffstat (limited to 'io_scene_ms3d/ms3d_import.py')
-rw-r--r-- | io_scene_ms3d/ms3d_import.py | 1005 |
1 files changed, 1005 insertions, 0 deletions
diff --git a/io_scene_ms3d/ms3d_import.py b/io_scene_ms3d/ms3d_import.py new file mode 100644 index 00000000..f4767f11 --- /dev/null +++ b/io_scene_ms3d/ms3d_import.py @@ -0,0 +1,1005 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- + + +# ##### BEGIN COPYRIGHT BLOCK ##### +# +# initial script copyright (c)2011,2012 Alexander Nussbaumer +# +# ##### END COPYRIGHT BLOCK ##### + + +#import python stuff +import io +from mathutils import ( + Vector, + Matrix, + ) +from os import ( + path, + ) +from sys import ( + exc_info, + ) +from time import ( + time, + ) + + +# import io_scene_ms3d stuff +from io_scene_ms3d.ms3d_strings import ( + ms3d_str, + ) +from io_scene_ms3d.ms3d_spec import ( + Ms3dSpec, + Ms3dModel, + Ms3dVertexEx2, + Ms3dVertexEx3, + ) +from io_scene_ms3d.ms3d_utils import ( + select_all, + enable_pose_mode, + enable_edit_mode, + pre_setup_environment, + post_setup_environment, + get_edge_split_modifier_add_if, + ) +from io_scene_ms3d.ms3d_ui import ( + Ms3dUi, + ) + + +#import blender stuff +from bpy import ( + ops, + ) +import bmesh +from bpy_extras.image_utils import ( + load_image, + ) + + +############################################################################### +class Ms3dImporter(): + """ + Load a MilkShape3D MS3D File + """ + def __init__(self, + report, + verbose=False, + use_extended_normal_handling=False, + use_animation=True, + use_quaternion_rotation=False, + use_joint_size=False, + joint_size=1.0, + use_joint_to_bones=False, + ): + self.report = report + self.options_verbose = verbose + self.options_use_extended_normal_handling = use_extended_normal_handling + self.options_use_animation = use_animation + self.options_use_quaternion_rotation = use_quaternion_rotation + self.options_use_joint_size = use_joint_size + self.options_joint_size = joint_size + self.options_use_joint_to_bones = use_joint_to_bones + pass + + ########################################################################### + # create empty blender ms3d_model + # read ms3d file + # fill blender with ms3d_model content + """ read ms3d file and convert ms3d content to bender content """ + def read(self, blender_context, filepath): + + t1 = time() + t2 = None + self.has_textures = False + + try: + # setup environment + pre_setup_environment(self, blender_context) + + # inject splitted filepath + self.directory_name, self.file_name = path.split(filepath) + + # create an empty ms3d template + ms3d_model = Ms3dModel(self.file_name) + + try: + # open ms3d file + with io.FileIO(filepath, 'rb') as raw_io: + # read and inject ms3d data from disk to internal structure + ms3d_model.read(raw_io) + raw_io.close() + finally: + pass + + # if option is set, this time will enlargs the io time + if self.options_verbose: + ms3d_model.print_internal() + + t2 = time() + + is_valid, statistics = ms3d_model.is_valid() + + if is_valid: + # inject ms3d data to blender + self.to_blender(blender_context, ms3d_model) + + blender_scene = blender_context.scene + + # finalize/restore environment + blender_scene.update() + + post_setup_environment(self, blender_context) + + print() + print("##########################################################") + print("Import from MS3D to Blender") + print(statistics) + print("##########################################################") + + except Exception: + type, value, traceback = exc_info() + print("read - exception in try block\n type: '{0}'\n" + " value: '{1}'".format(type, value, traceback)) + + if t2 is None: + t2 = time() + + raise + + else: + pass + + t3 = time() + print(ms3d_str['SUMMARY_IMPORT'].format( + (t3 - t1), (t2 - t1), (t3 - t2))) + + return {"FINISHED"} + + + def internal_read(self, blender_context, raw_io): + try: + # setup environment + pre_setup_environment(self, blender_context) + + try: + ms3d_model.read(raw_io) + finally: + pass + + # if option is set, this time will enlargs the io time + if self.options_verbose: + ms3d_model.print_internal() + + is_valid, statistics = ms3d_model.is_valid() + + if is_valid: + # inject ms3d data to blender + blender_empty_object, blender_mesh_object = self.to_blender(blender_context, ms3d_model) + + blender_scene = blender_context.scene + + # finalize/restore environment + blender_scene.update() + + post_setup_environment(self, blender_context) + + except Exception: + type, value, traceback = exc_info() + print("read - exception in try block\n type: '{0}'\n" + " value: '{1}'".format(type, value, traceback)) + + raise + + else: + pass + + return blender_empty_object, blender_mesh_object + + + ########################################################################### + def to_blender(self, blender_context, ms3d_model): + blender_mesh_object = self.create_geometry(blender_context, ms3d_model) + blender_armature_object = self.create_animation( + blender_context, ms3d_model, blender_mesh_object) + + blender_empty_object = self.organize_objects( + blender_context, ms3d_model, + [blender_mesh_object, blender_armature_object]) + + return blender_empty_object, blender_mesh_object + + + ########################################################################### + def organize_objects(self, blender_context, ms3d_model, blender_objects): + ########################## + # blender_armature_object to blender_mesh_object + # that has bad side effects to the armature + # and causes cyclic dependecies + ###blender_armature_object.parent = blender_mesh_object + ###blender_mesh_object.parent = blender_armature_object + + blender_scene = blender_context.scene + + blender_group = blender_context.blend_data.groups.new( + "{}.g".format(ms3d_model.name)) + blender_empty_object = blender_context.blend_data.objects.new( + "{}.e".format(ms3d_model.name), None) + blender_empty_object.location = blender_scene.cursor_location + blender_scene.objects.link(blender_empty_object) + blender_group.objects.link(blender_empty_object) + + for blender_object in blender_objects: + if blender_object is not None: + blender_group.objects.link(blender_object) + blender_object.parent = blender_empty_object + + return blender_empty_object + + + ########################################################################### + def create_geometry(self, blender_context, ms3d_model): + ########################## + # blender stuff: + # create a blender Mesh + blender_mesh = blender_context.blend_data.meshes.new( + "{}.m".format(ms3d_model.name)) + blender_mesh.ms3d.name = ms3d_model.name + + ms3d_comment = ms3d_model.comment_object + if ms3d_comment is not None: + blender_mesh.ms3d.comment = ms3d_comment.comment + ms3d_model_ex = ms3d_model.model_ex_object + if ms3d_model_ex is not None: + blender_mesh.ms3d.joint_size = ms3d_model_ex.joint_size + blender_mesh.ms3d.alpha_ref = ms3d_model_ex.alpha_ref + blender_mesh.ms3d.transparency_mode \ + = Ms3dUi.transparency_mode_from_ms3d( + ms3d_model_ex.transparency_mode) + + ########################## + # blender stuff: + # link to blender object + blender_mesh_object = blender_context.blend_data.objects.new( + "{}.m".format(ms3d_model.name), blender_mesh) + + ########################## + # blender stuff: + # create edge split modifire, to make sharp edges visible + blender_modifier = get_edge_split_modifier_add_if(blender_mesh_object) + + ########################## + # blender stuff: + # link to blender scene + blender_scene = blender_context.scene + blender_scene.objects.link(blender_mesh_object) + #blender_mesh_object.location = blender_scene.cursor_location + enable_edit_mode(False, blender_context) + select_all(False) + blender_mesh_object.select = True + blender_scene.objects.active = blender_mesh_object + + ########################## + # take this as active object after import + self.active_object = blender_mesh_object + + ########################## + # blender stuff: + # create all (ms3d) groups + ms3d_to_blender_group_index = {} + blender_group_manager = blender_mesh.ms3d + for ms3d_group_index, ms3d_group in enumerate(ms3d_model.groups): + blender_group = blender_group_manager.create_group() + blender_group.name = ms3d_group.name + blender_group.flags = Ms3dUi.flags_from_ms3d(ms3d_group.flags) + blender_group.material_index = ms3d_group.material_index + + ms3d_comment = ms3d_group.comment_object + if ms3d_comment is not None: + blender_group.comment = ms3d_comment.comment + + # translation dictionary + ms3d_to_blender_group_index[ms3d_group_index] = blender_group.id + + #################################################### + # begin BMesh stuff + # + + ########################## + # BMesh stuff: + # create an empty BMesh + bm = bmesh.new() + + ########################## + # BMesh stuff: + # create new Layers for custom data per "mesh face" + layer_texture = bm.faces.layers.tex.get( + ms3d_str['OBJECT_LAYER_TEXTURE']) + if layer_texture is None: + layer_texture = bm.faces.layers.tex.new( + ms3d_str['OBJECT_LAYER_TEXTURE']) + + layer_smoothing_group = bm.faces.layers.int.get( + ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP']) + if layer_smoothing_group is None: + layer_smoothing_group = bm.faces.layers.int.new( + ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP']) + + layer_group = bm.faces.layers.int.get( + ms3d_str['OBJECT_LAYER_GROUP']) + if layer_group is None: + layer_group = bm.faces.layers.int.new( + ms3d_str['OBJECT_LAYER_GROUP']) + + ########################## + # BMesh stuff: + # create new Layers for custom data per "face vertex" + layer_uv = bm.loops.layers.uv.get(ms3d_str['OBJECT_LAYER_UV']) + if layer_uv is None: + layer_uv = bm.loops.layers.uv.new(ms3d_str['OBJECT_LAYER_UV']) + + ########################## + # BMesh stuff: + # create new Layers for custom data per "vertex" + layer_extra = bm.verts.layers.int.get(ms3d_str['OBJECT_LAYER_EXTRA']) + if layer_extra is None: + layer_extra = bm.verts.layers.int.new(ms3d_str['OBJECT_LAYER_EXTRA']) + + ########################## + # BMesh stuff: + # create all vertices + for ms3d_vertex_index, ms3d_vertex in enumerate(ms3d_model.vertices): + bmv = bm.verts.new(self.geometry_correction(ms3d_vertex.vertex)) + + if layer_extra and ms3d_vertex.vertex_ex_object and \ + (isinstance(ms3d_vertex.vertex_ex_object, Ms3dVertexEx2) \ + or isinstance(ms3d_vertex.vertex_ex_object, Ms3dVertexEx3)): + + #bmv[layer_extra] = ms3d_vertex.vertex_ex_object.extra + # bm.verts.layers.int does only support signed int32 + # convert unsigned int32 to signed int32 (little-endian) + unsigned_int32 = ms3d_vertex.vertex_ex_object.extra + bytes_int32 = unsigned_int32.to_bytes( + 4, byteorder='little', signed=False) + signed_int32 = int.from_bytes( + bytes_int32, byteorder='little', signed=True) + bmv[layer_extra] = signed_int32 + + ########################## + # blender stuff (uses BMesh stuff): + # create all materials / image textures + ms3d_to_blender_material = {} + for ms3d_material_index, ms3d_material in enumerate( + ms3d_model.materials): + blender_material = blender_context.blend_data.materials.new( + ms3d_material.name) + + # custom datas + blender_material.ms3d.name = ms3d_material.name + blender_material.ms3d.ambient = ms3d_material.ambient + blender_material.ms3d.diffuse = ms3d_material.diffuse + blender_material.ms3d.specular = ms3d_material.specular + blender_material.ms3d.emissive = ms3d_material.emissive + blender_material.ms3d.shininess = ms3d_material.shininess + blender_material.ms3d.transparency = ms3d_material.transparency + blender_material.ms3d.mode = Ms3dUi.texture_mode_from_ms3d( + ms3d_material.mode) + + if ms3d_material.texture: + blender_material.ms3d.texture = ms3d_material.texture + + if ms3d_material.alphamap: + blender_material.ms3d.alphamap = ms3d_material.alphamap + + ms3d_comment = ms3d_material.comment_object + if ms3d_comment is not None: + blender_material.ms3d.comment = ms3d_comment.comment + + # blender datas + blender_material.ambient = ( + (ms3d_material.ambient[0] + + ms3d_material.ambient[1] + + ms3d_material.ambient[2]) / 3.0) + + blender_material.diffuse_color[0] = ms3d_material.diffuse[0] + blender_material.diffuse_color[1] = ms3d_material.diffuse[1] + blender_material.diffuse_color[2] = ms3d_material.diffuse[2] + + blender_material.specular_color[0] = ms3d_material.specular[0] + blender_material.specular_color[1] = ms3d_material.specular[1] + blender_material.specular_color[2] = ms3d_material.specular[2] + + blender_material.emit = ( + (ms3d_material.emissive[0] + + ms3d_material.emissive[1] + + ms3d_material.emissive[2]) / 3.0) + + blender_material.specular_hardness = ms3d_material.shininess * 4.0 + blender_material.alpha = 1.0 - ms3d_material.transparency + + # diffuse texture + if ms3d_material.texture: + dir_name_diffuse = self.directory_name + file_name_diffuse = path.split(ms3d_material.texture)[1] + blender_image_diffuse = load_image( + file_name_diffuse, dir_name_diffuse) + blender_texture_diffuse = \ + blender_context.blend_data.textures.new( + name=file_name_diffuse, type='IMAGE') + blender_texture_diffuse.image = blender_image_diffuse + blender_texture_slot_diffuse \ + = blender_material.texture_slots.add() + blender_texture_slot_diffuse.texture = blender_texture_diffuse + blender_texture_slot_diffuse.texture_coords = 'UV' + blender_texture_slot_diffuse.uv_layer = layer_uv.name + blender_texture_slot_diffuse.use_map_color_diffuse = True + blender_texture_slot_diffuse.use_map_alpha = False + if blender_image_diffuse is not None: + self.has_textures = True + else: + blender_image_diffuse = None + + # alpha texture + if ms3d_material.alphamap: + dir_name_alpha = self.directory_name + file_name_alpha = path.split(ms3d_material.alphamap)[1] + blender_image_alpha = load_image( + file_name_alpha, dir_name_alpha) + blender_texture_alpha = blender_context.blend_data.textures.new( + name=file_name_alpha, type='IMAGE') + blender_texture_alpha.image = blender_image_alpha + blender_texture_slot_alpha \ + = blender_material.texture_slots.add() + blender_texture_slot_alpha.texture = blender_texture_alpha + blender_texture_slot_alpha.texture_coords = 'UV' + blender_texture_slot_alpha.uv_layer = layer_uv.name + blender_texture_slot_alpha.use_map_color_diffuse = False + blender_texture_slot_alpha.use_map_alpha = True + blender_texture_slot_alpha.use_rgb_to_intensity = True + blender_material.alpha = 0 + blender_material.specular_alpha = 0 + + # append blender material to blender mesh, to be linked to + blender_mesh.materials.append(blender_material) + + # translation dictionary + ms3d_to_blender_material[ms3d_material_index] \ + = blender_image_diffuse + + ########################## + # BMesh stuff: + # create all triangles + length_verts = len(bm.verts) + vertex_extra_index = length_verts + blender_invalide_normal = Vector() + smoothing_group_blender_faces = {} + for ms3d_triangle_index, ms3d_triangle in enumerate( + ms3d_model.triangles): + bmv_list = [] + bmf_normal = Vector() + + for index, vert_index in enumerate(ms3d_triangle.vertex_indices): + if vert_index < 0 or vert_index >= length_verts: + continue + bmv = bm.verts[vert_index] + + blender_normal = self.geometry_correction( + ms3d_triangle.vertex_normals[index]) + if bmv.normal == blender_invalide_normal: + bmv.normal = blender_normal + elif bmv.normal != blender_normal \ + and self.options_use_extended_normal_handling: + ## search for an already created extra vertex + bmv_new = None + for vert_index_candidat in range( + vertex_extra_index, length_verts): + bmv_candidat = bm.verts[vert_index_candidat] + if bmv_candidat.co == bmv.co \ + and bmv_candidat.normal == blender_normal: + bmv_new = bmv_candidat + vert_index = vert_index_candidat + break + + ## if not exists, create one in blender and ms3d as well + if bmv_new is None: + ms3d_model.vertices.append( + ms3d_model.vertices[vert_index]) + bmv_new = bm.verts.new(bmv.co) + bmv_new.normal = blender_normal + bmv_new[layer_extra] = bmv[layer_extra] + vert_index = length_verts + length_verts += 1 + self.report({'WARNING', 'INFO'}, + ms3d_str['WARNING_IMPORT_EXTRA_VERTEX_NORMAL'].format( + bmv.normal, blender_normal)) + bmv = bmv_new + + if [[x] for x in bmv_list if x == bmv]: + self.report( + {'WARNING', 'INFO'}, + ms3d_str['WARNING_IMPORT_SKIP_VERTEX_DOUBLE'].format( + ms3d_triangle_index)) + continue + bmv_list.append(bmv) + bmf_normal += bmv.normal + + if len(bmv_list) < 3: + self.report( + {'WARNING', 'INFO'}, + ms3d_str['WARNING_IMPORT_SKIP_LESS_VERTICES'].format( + ms3d_triangle_index)) + continue + + bmf_normal.normalize() + + bmf = bm.faces.get(bmv_list) + if bmf is not None: + self.report( + {'WARNING', 'INFO'}, + ms3d_str['WARNING_IMPORT_SKIP_FACE_DOUBLE'].format( + ms3d_triangle_index)) + continue + + bmf = bm.faces.new(bmv_list) + bmf.normal = bmf_normal + + # blender uv custom data per "face vertex" + bmf.loops[0][layer_uv].uv = Vector( + (ms3d_triangle.s[0], 1.0 - ms3d_triangle.t[0])) + bmf.loops[1][layer_uv].uv = Vector( + (ms3d_triangle.s[1], 1.0 - ms3d_triangle.t[1])) + bmf.loops[2][layer_uv].uv = Vector( + (ms3d_triangle.s[2], 1.0 - ms3d_triangle.t[2])) + + # ms3d custom data per "mesh face" + bmf[layer_smoothing_group] = ms3d_triangle.smoothing_group + + blender_group_id = ms3d_to_blender_group_index.get( + ms3d_triangle.group_index) + if blender_group_id is not None: + bmf[layer_group] = blender_group_id + + if ms3d_triangle.group_index >= 0 \ + and ms3d_triangle.group_index < len(ms3d_model.groups): + ms3d_material_index \ + = ms3d_model.groups[ms3d_triangle.group_index].material_index + if ms3d_material_index != Ms3dSpec.NONE_GROUP_MATERIAL_INDEX: + bmf.material_index = ms3d_material_index + # apply diffuse texture image to face, to be visible in 3d view + bmf[layer_texture].image = ms3d_to_blender_material.get( + ms3d_material_index) + + # helper dictionary for post-processing smoothing_groups + smoothing_group_blender_face = smoothing_group_blender_faces.get( + ms3d_triangle.smoothing_group) + if smoothing_group_blender_face is None: + smoothing_group_blender_face = [] + smoothing_group_blender_faces[ms3d_triangle.smoothing_group] \ + = smoothing_group_blender_face + smoothing_group_blender_face.append(bmf) + + ########################## + # BMesh stuff: + # create all sharp edges for blender to make smoothing_groups visible + for ms3d_smoothing_group_index, blender_face_list \ + in smoothing_group_blender_faces.items(): + edge_dict = {} + for bmf in blender_face_list: + bmf.smooth = True + for bme in bmf.edges: + if edge_dict.get(bme) is None: + edge_dict[bme] = 0 + else: + edge_dict[bme] += 1 + bme.seam = (edge_dict[bme] == 0) + bme.smooth = (edge_dict[bme] != 0) + + ########################## + # BMesh stuff: + # finally tranfer BMesh to Mesh + bm.to_mesh(blender_mesh) + bm.free() + + + # + # end BMesh stuff + #################################################### + + blender_mesh.validate(self.options_verbose) + + return blender_mesh_object + + + ########################################################################### + def create_animation(self, blender_context, ms3d_model, blender_mesh_object): + ########################## + # setup scene + blender_scene = blender_context.scene + blender_scene.render.fps = ms3d_model.animation_fps + if ms3d_model.animation_fps: + blender_scene.render.fps_base = (blender_scene.render.fps / + ms3d_model.animation_fps) + + blender_scene.frame_start = 1 + blender_scene.frame_end = (ms3d_model.number_total_frames + + blender_scene.frame_start) - 1 + blender_scene.frame_current = (ms3d_model.current_time + * ms3d_model.animation_fps) + + ########################## + if not ms3d_model.joints: + return + + ########################## + ms3d_armature_name = "{}.a".format(ms3d_model.name) + ms3d_action_name = "{}.act".format(ms3d_model.name) + + ########################## + # create new blender_armature_object + blender_armature = blender_context.blend_data.armatures.new( + ms3d_armature_name) + blender_armature.ms3d.name = ms3d_model.name + blender_armature.draw_type = 'STICK' + blender_armature.show_axes = True + blender_armature.use_auto_ik = True + blender_armature_object = blender_context.blend_data.objects.new( + ms3d_armature_name, blender_armature) + blender_scene.objects.link(blender_armature_object) + #blender_armature_object.location = blender_scene.cursor_location + blender_armature_object.show_x_ray = True + + ########################## + # create new modifier + blender_modifier = blender_mesh_object.modifiers.new( + ms3d_armature_name, type='ARMATURE') + blender_modifier.show_expanded = False + blender_modifier.use_vertex_groups = True + blender_modifier.use_bone_envelopes = False + blender_modifier.object = blender_armature_object + + ########################## + # prepare for vertex groups + ms3d_to_blender_vertex_groups = {} + for ms3d_vertex_index, ms3d_vertex in enumerate(ms3d_model.vertices): + # prepare for later use for blender vertex group + if ms3d_vertex.bone_id != Ms3dSpec.NONE_VERTEX_BONE_ID: + if ms3d_vertex.vertex_ex_object \ + and ( \ + ms3d_vertex.vertex_ex_object.bone_ids[0] != \ + Ms3dSpec.NONE_VERTEX_BONE_ID \ + or ms3d_vertex.vertex_ex_object.bone_ids[1] != \ + Ms3dSpec.NONE_VERTEX_BONE_ID \ + or ms3d_vertex.vertex_ex_object.bone_ids[2] != \ + Ms3dSpec.NONE_VERTEX_BONE_ID \ + ): + ms3d_vertex_group_ids_weights = [] + ms3d_vertex_group_ids_weights.append( + (ms3d_vertex.bone_id, + float(ms3d_vertex.vertex_ex_object.weights[0] % 101) / 100.0, + )) + if ms3d_vertex.vertex_ex_object.bone_ids[0] != \ + Ms3dSpec.NONE_VERTEX_BONE_ID: + ms3d_vertex_group_ids_weights.append( + (ms3d_vertex.vertex_ex_object.bone_ids[0], + float(ms3d_vertex.vertex_ex_object.weights[1] % 101) / 100.0 + )) + if ms3d_vertex.vertex_ex_object.bone_ids[1] != \ + Ms3dSpec.NONE_VERTEX_BONE_ID: + ms3d_vertex_group_ids_weights.append( + (ms3d_vertex.vertex_ex_object.bone_ids[1], + float(ms3d_vertex.vertex_ex_object.weights[2] % 101) / 100.0 + )) + if ms3d_vertex.vertex_ex_object.bone_ids[2] != \ + Ms3dSpec.NONE_VERTEX_BONE_ID: + ms3d_vertex_group_ids_weights.append( + (ms3d_vertex.vertex_ex_object.bone_ids[2], + 1.0 - + float((ms3d_vertex.vertex_ex_object.weights[0] % 101) + + (ms3d_vertex.vertex_ex_object.weights[1] % 101) + + (ms3d_vertex.vertex_ex_object.weights[2] % 101)) / 100.0 + )) + + else: + ms3d_vertex_group_ids_weights = [(ms3d_vertex.bone_id, 1.0), ] + + for ms3d_vertex_group_id_weight in ms3d_vertex_group_ids_weights: + ms3d_vertex_group_id = ms3d_vertex_group_id_weight[0] + blender_vertex_weight = ms3d_vertex_group_id_weight[1] + blender_vertex_group = ms3d_to_blender_vertex_groups.get( + ms3d_vertex_group_id) + if blender_vertex_group is None: + ms3d_to_blender_vertex_groups[ms3d_vertex_group_id] \ + = blender_vertex_group = [] + blender_vertex_group.append((ms3d_vertex_index, + blender_vertex_weight)) + + ########################## + # blender stuff: + # create all vertex groups to be used for bones + for ms3d_bone_id, blender_vertex_index_weight_list \ + in ms3d_to_blender_vertex_groups.items(): + ms3d_name = ms3d_model.joints[ms3d_bone_id].name + blender_vertex_group = blender_mesh_object.vertex_groups.new( + ms3d_name) + for blender_vertex_id_weight in blender_vertex_index_weight_list: + blender_vertex_index = blender_vertex_id_weight[0] + blender_vertex_weight = blender_vertex_id_weight[1] + blender_vertex_group.add((blender_vertex_index, ), + blender_vertex_weight, 'ADD') + + ########################## + # bring joints in the correct order + ms3d_joints_ordered = [] + self.build_ms3d_joint_dependency_order(ms3d_model.joints, + ms3d_joints_ordered) + + ########################## + # prepare joint data for later use + ms3d_joint_by_name = {} + for ms3d_joint in ms3d_joints_ordered: + item = ms3d_joint_by_name.get(ms3d_joint.name) + if item is None: + ms3d_joint.__children = [] + ms3d_joint_by_name[ms3d_joint.name] = ms3d_joint + + matrix_local_rot = (Matrix.Rotation(ms3d_joint.rotation[2], 4, 'Z') + * Matrix.Rotation(ms3d_joint.rotation[1], 4, 'Y') + ) * Matrix.Rotation(ms3d_joint.rotation[0], 4, 'X') + matrix_local = Matrix.Translation(Vector(ms3d_joint.position) + ) * matrix_local_rot + + ms3d_joint.__matrix_local_rot = matrix_local_rot + ms3d_joint.__matrix_global_rot = matrix_local_rot + ms3d_joint.__matrix_local = matrix_local + ms3d_joint.__matrix_global = matrix_local + + if ms3d_joint.parent_name: + ms3d_joint_parent = ms3d_joint_by_name.get( + ms3d_joint.parent_name) + if ms3d_joint_parent is not None: + ms3d_joint_parent.__children.append(ms3d_joint) + + matrix_global = ms3d_joint_parent.__matrix_global \ + * matrix_local + ms3d_joint.__matrix_global = matrix_global + + matrix_global_rot = ms3d_joint_parent.__matrix_global_rot \ + * matrix_local_rot + ms3d_joint.__matrix_global_rot = matrix_global_rot + + ########################## + # ms3d_joint to blender_edit_bone + if ms3d_model.model_ex_object and not self.options_use_joint_size: + joint_length = ms3d_model.model_ex_object.joint_size + else: + joint_length = self.options_joint_size + if joint_length < 0.01: + joint_length = 0.01 + + blender_scene.objects.active = blender_armature_object + enable_edit_mode(True, blender_context) + for ms3d_joint in ms3d_joints_ordered: + blender_edit_bone = blender_armature.edit_bones.new(ms3d_joint.name) + blender_edit_bone.use_connect = False + blender_edit_bone.use_inherit_rotation = True + blender_edit_bone.use_inherit_scale = True + blender_edit_bone.use_local_location = True + blender_armature.edit_bones.active = blender_edit_bone + + ms3d_joint = ms3d_joint_by_name[ms3d_joint.name] + ms3d_joint_vector = ms3d_joint.__matrix_global * Vector() + + blender_edit_bone.head \ + = self.geometry_correction(ms3d_joint_vector) + + vector_tail_end_up = ms3d_joint.__matrix_global_rot * Vector((0,1,0)) + vector_tail_end_dir = ms3d_joint.__matrix_global_rot * Vector((0,0,1)) + vector_tail_end_up.normalize() + vector_tail_end_dir.normalize() + blender_edit_bone.tail = blender_edit_bone.head \ + + self.geometry_correction( + vector_tail_end_dir * joint_length) + blender_edit_bone.align_roll(self.geometry_correction( + vector_tail_end_up)) + + if ms3d_joint.parent_name: + ms3d_joint_parent = ms3d_joint_by_name[ms3d_joint.parent_name] + blender_edit_bone_parent = ms3d_joint_parent.blender_edit_bone + blender_edit_bone.parent = blender_edit_bone_parent + + ms3d_joint.blender_bone_name = blender_edit_bone.name + ms3d_joint.blender_edit_bone = blender_edit_bone + enable_edit_mode(False, blender_context) + + if self.options_use_joint_to_bones: + enable_edit_mode(True, blender_context) + for ms3d_joint in ms3d_joints_ordered: + blender_edit_bone = blender_armature.edit_bones[ms3d_joint.name] + if blender_edit_bone.children: + new_length = 0.0 + for child_bone in blender_edit_bone.children: + length = (child_bone.head - blender_edit_bone.head).length + if new_length <= 0 or length < new_length: + new_length = length + if new_length >= 0.01: + direction = blender_edit_bone.tail - blender_edit_bone.head + direction.normalize() + blender_edit_bone.tail = blender_edit_bone.head + (direction * new_length) + enable_edit_mode(False, blender_context) + + ########################## + # post process bones + enable_edit_mode(False, blender_context) + for ms3d_joint_name, ms3d_joint in ms3d_joint_by_name.items(): + blender_bone = blender_armature.bones.get( + ms3d_joint.blender_bone_name) + if blender_bone is None: + continue + + blender_bone.ms3d.name = ms3d_joint.name + blender_bone.ms3d.flags = Ms3dUi.flags_from_ms3d(ms3d_joint.flags) + + ms3d_joint_ex = ms3d_joint.joint_ex_object + if ms3d_joint_ex is not None: + blender_bone.ms3d.color = ms3d_joint_ex.color + + ms3d_comment = ms3d_joint.comment_object + if ms3d_comment is not None: + blender_bone.ms3d.comment = ms3d_comment.comment + + ########################## + if not self.options_use_animation: + return blender_armature_object + + + ########################## + # process pose bones + enable_pose_mode(True, blender_context) + + blender_action = blender_context.blend_data.actions.new(ms3d_action_name) + if blender_armature_object.animation_data is None: + blender_armature_object.animation_data_create() + blender_armature_object.animation_data.action = blender_action + + ########################## + # transition between keys may be incorrect + # because of the gimbal-lock problem! + # http://www.youtube.com/watch?v=zc8b2Jo7mno + # http://www.youtube.com/watch?v=rrUCBOlJdt4 + # you can fix it manually by selecting the affected keyframes + # and allpy the following option to it: + # "Graph Editor -> Key -> Discontinuity (Euler) Filter" + # ==> "bpy.ops.graph.euler_filter()" + # but this option is only available for Euler rotation f-curves! + # + for ms3d_joint_name, ms3d_joint in ms3d_joint_by_name.items(): + blender_pose_bone = blender_armature_object.pose.bones.get( + ms3d_joint.blender_bone_name) + if blender_pose_bone is None: + continue + + data_path = blender_pose_bone.path_from_id('location') + fcurve_location_x = blender_action.fcurves.new(data_path, index=0) + fcurve_location_y = blender_action.fcurves.new(data_path, index=1) + fcurve_location_z = blender_action.fcurves.new(data_path, index=2) + for translation_key_frames in ms3d_joint.translation_key_frames: + frame = (translation_key_frames.time * ms3d_model.animation_fps) + matrix_local = Matrix.Translation( + Vector(translation_key_frames.position)) + v = (matrix_local) * Vector() + fcurve_location_x.keyframe_points.insert(frame, -v[0]) + fcurve_location_y.keyframe_points.insert(frame, v[2]) + fcurve_location_z.keyframe_points.insert(frame, v[1]) + + if self.options_use_quaternion_rotation: + blender_pose_bone.rotation_mode = 'QUATERNION' + data_path = blender_pose_bone.path_from_id("rotation_quaternion") + fcurve_rotation_w = blender_action.fcurves.new(data_path, index=0) + fcurve_rotation_x = blender_action.fcurves.new(data_path, index=1) + fcurve_rotation_y = blender_action.fcurves.new(data_path, index=2) + fcurve_rotation_z = blender_action.fcurves.new(data_path, index=3) + for rotation_key_frames in ms3d_joint.rotation_key_frames: + frame = (rotation_key_frames.time * ms3d_model.animation_fps) + matrix_local_rot = ( + Matrix.Rotation( + rotation_key_frames.rotation[2], 4, 'Y') + * Matrix.Rotation( + rotation_key_frames.rotation[1], 4, 'Z') + ) * Matrix.Rotation( + -rotation_key_frames.rotation[0], 4, 'X') + q = (matrix_local_rot).to_quaternion() + fcurve_rotation_w.keyframe_points.insert(frame, q.w) + fcurve_rotation_x.keyframe_points.insert(frame, q.x) + fcurve_rotation_y.keyframe_points.insert(frame, q.y) + fcurve_rotation_z.keyframe_points.insert(frame, q.z) + else: + blender_pose_bone.rotation_mode = 'XZY' + data_path = blender_pose_bone.path_from_id("rotation_euler") + fcurve_rotation_x = blender_action.fcurves.new(data_path, index=0) + fcurve_rotation_y = blender_action.fcurves.new(data_path, index=1) + fcurve_rotation_z = blender_action.fcurves.new(data_path, index=2) + for rotation_key_frames in ms3d_joint.rotation_key_frames: + frame = (rotation_key_frames.time * ms3d_model.animation_fps) + fcurve_rotation_x.keyframe_points.insert( + frame, -rotation_key_frames.rotation[0]) + fcurve_rotation_y.keyframe_points.insert( + frame, rotation_key_frames.rotation[2]) + fcurve_rotation_z.keyframe_points.insert( + frame, rotation_key_frames.rotation[1]) + + enable_pose_mode(False, blender_context) + + return blender_armature_object + + + ########################################################################### + def geometry_correction(self, value): + return Vector((value[2], value[0], value[1])) + + + ########################################################################### + def build_ms3d_joint_dependency_order(self, ms3d_joints, ms3d_joints_ordered): + ms3d_joints_children = {"": {}} + for ms3d_joint in ms3d_joints: + if ms3d_joint.parent_name: + ms3d_joint_children = ms3d_joints_children.get( + ms3d_joint.parent_name) + if ms3d_joint_children is None: + ms3d_joint_children = ms3d_joints_children[ + ms3d_joint.parent_name] = {} + else: + ms3d_joint_children = ms3d_joints_children[""] + + ms3d_joint_children[ms3d_joint.name] = ms3d_joint + + self.traverse_dependencies( + ms3d_joints_ordered, + ms3d_joints_children, + "") + + + return ms3d_joints_ordered + + + ########################################################################### + def traverse_dependencies(self, ms3d_joints_ordered, ms3d_joints_children, + key): + ms3d_joint_children = ms3d_joints_children.get(key) + if ms3d_joint_children: + for item in ms3d_joint_children.items(): + ms3d_joint_name = item[0] + ms3d_joint = item[1] + ms3d_joints_ordered.append(ms3d_joint) + self.traverse_dependencies( + ms3d_joints_ordered, + ms3d_joints_children, + ms3d_joint_name) + + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- +# ##### END OF FILE ##### |