Skip to content
Snippets Groups Projects
ms3d_export.py 36.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### 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 math import (
            pi,
            )
    from mathutils import (
            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,
            Ms3dVertex,
            Ms3dTriangle,
            Ms3dGroup,
            Ms3dMaterial,
            Ms3dJoint,
            Ms3dRotationKeyframe,
            Ms3dTranslationKeyframe,
            Ms3dCommentEx,
            )
    from io_scene_ms3d.ms3d_utils import (
            select_all,
            enable_edit_mode,
            pre_setup_environment,
            post_setup_environment,
            matrix_difference,
            )
    from io_scene_ms3d.ms3d_ui import (
            Ms3dUi,
            Ms3dMaterialProperties,
            Ms3dMaterialHelper,
            )
    
    
    #import blender stuff
    from bpy import (
            ops,
            )
    import bmesh
    
    
    ###############################################################################
    class Ms3dExporter():
        """
        Load a MilkShape3D MS3D File
        """
        def __init__(self,
                report,
                verbose=False,
                use_blender_names=True,
                use_blender_materials=False,
                apply_transform=True,
                apply_modifiers=True,
                apply_modifiers_mode='PREVIEW',
                use_animation=True,
                normalize_weights=True,
                shrink_to_keys=False,
                bake_each_frame=True,
                ):
            self.report = report
            self.options_verbose = verbose
            self.options_use_blender_names = use_blender_names
            self.options_use_blender_materials = use_blender_materials
            self.options_apply_transform = apply_transform
            self.options_apply_modifiers = apply_modifiers
            self.options_apply_modifiers_mode = apply_modifiers_mode
            self.options_use_animation = use_animation
            self.options_normalize_weights = normalize_weights
            self.options_shrink_to_keys = shrink_to_keys
            self.options_bake_each_frame = bake_each_frame
            pass
    
        # create a empty ms3d ms3d_model
        # fill ms3d_model with blender content
        # writer ms3d file
        def write(self, blender_context, filepath):
            """convert bender content to ms3d content and write it to file"""
    
            t1 = time()
            t2 = None
    
            try:
                # setup environment
                pre_setup_environment(self, blender_context)
    
                # create an empty ms3d template
                ms3d_model = Ms3dModel()
    
                # inject blender data to ms3d file
                self.from_blender(blender_context, ms3d_model)
    
                t2 = time()
    
                try:
                    # write ms3d file to disk
                    with io.FileIO(filepath, "wb") as raw_io:
                        ms3d_model.write(raw_io)
                        raw_io.flush()
                        raw_io.close()
                finally:
                    pass
    
                # if option is set, this time will enlargs the io time
                if self.options_verbose:
                    ms3d_model.print_internal()
    
                post_setup_environment(self, blender_context)
                # restore active object
                blender_context.scene.objects.active = self.active_object
    
                if ((not blender_context.scene.objects.active)
                        and (blender_context.selected_objects)):
                    blender_context.scene.objects.active \
                            = blender_context.selected_objects[0]
    
                # restore pre operator undo state
                blender_context.user_preferences.edit.use_global_undo = self.undo
    
                is_valid, statistics = ms3d_model.is_valid()
                print()
                print("##########################################################")
                print("Export from Blender to MS3D")
                print(statistics)
                print("##########################################################")
    
            except Exception:
                type, value, traceback = exc_info()
                print("write - 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_EXPORT'].format(
                    (t3 - t1), (t2 - t1), (t3 - t2)))
    
            return {"FINISHED"}
    
    
        ###########################################################################
        def from_blender(self, blender_context, ms3d_model):
            blender_mesh_objects = []
    
            source = (blender_context.active_object, )
    
            for blender_object in source:
                if blender_object and blender_object.type == 'MESH' \
                        and blender_object.is_visible(blender_context.scene):
                    blender_mesh_objects.append(blender_object)
    
            blender_to_ms3d_bones = {}
    
            self.create_animation(blender_context, ms3d_model, blender_mesh_objects, blender_to_ms3d_bones)
            self.create_geometry(blender_context, ms3d_model, blender_mesh_objects,
                    blender_to_ms3d_bones)
    
    
        ###########################################################################
        def create_geometry(self, blender_context, ms3d_model, blender_mesh_objects, blender_to_ms3d_bones):
            blender_scene = blender_context.scene
    
            blender_to_ms3d_vertices = {}
            blender_to_ms3d_triangles = {}
            blender_to_ms3d_groups = {}
            blender_to_ms3d_materials = {}
    
            for blender_mesh_object in blender_mesh_objects:
                blender_mesh = blender_mesh_object.data
    
                ms3d_model._model_ex_object.joint_size = \
                        blender_mesh.ms3d.joint_size
                ms3d_model._model_ex_object.alpha_ref = blender_mesh.ms3d.alpha_ref
                ms3d_model._model_ex_object.transparency_mode = \
                        Ms3dUi.transparency_mode_to_ms3d(
                        blender_mesh.ms3d.transparency_mode)
    
                ##########################
                # prepare ms3d groups if available
                # works only for exporting active object
                ##EXPORT_ACTIVE_ONLY:
                for ms3d_local_group_index, blender_ms3d_group in enumerate(
                        blender_mesh.ms3d.groups):
                    ms3d_group = Ms3dGroup()
                    ms3d_group.__index = len(ms3d_model._groups)
                    ms3d_group.name = blender_ms3d_group.name
                    ms3d_group.flags = Ms3dUi.flags_to_ms3d(blender_ms3d_group.flags)
                    if blender_ms3d_group.comment:
                        ms3d_group._comment_object = Ms3dCommentEx()
                        ms3d_group._comment_object.comment = \
                                blender_ms3d_group.comment
                        ms3d_group._comment_object.index = len(ms3d_model._groups)
                    ms3d_group.material_index = None # to mark as not setted
                    ms3d_model._groups.append(ms3d_group)
                    blender_to_ms3d_groups[blender_ms3d_group.id] = ms3d_group
    
                ##########################
                # i have to use BMesh, because there are several custom data stored.
                # BMesh doesn't support quads_convert_to_tris()
                # so, i use that very ugly way:
                # create a complete copy of mesh and bend object data
                # to be able to apply operations to it.
    
                # temporary, create a full heavy copy of the model
                # (object, mesh, modifiers)
                blender_mesh_temp = blender_mesh_object.data.copy()
                blender_mesh_object_temp = blender_mesh_object.copy()
                blender_mesh_object_temp.data = blender_mesh_temp
                blender_scene.objects.link(blender_mesh_object_temp)
                blender_scene.objects.active = blender_mesh_object_temp
    
                # apply transform
                if self.options_apply_transform:
    
                    matrix_transform = blender_mesh_object_temp.matrix_basis
    
    
                # apply modifiers
                for modifier in blender_mesh_object_temp.modifiers:
                    if self.options_apply_modifiers:
                        # disable only armature modifiers and only,
                        # when use_animation is enabled
                        if  self.options_use_animation \
                                and modifier.type in {'ARMATURE', }:
                            modifier.show_viewport = False
                            modifier.show_render = False
                    else:
                        # disable all modifiers,
                        # to be able to add and apply triangulate modifier later
                        modifier.show_viewport = False
                        modifier.show_render = False
    
                # convert to tris by using the triangulate modifier
                blender_mesh_object_temp.modifiers.new("temp", 'TRIANGULATE')
                blender_mesh_temp = blender_mesh_object_temp.to_mesh(
                        blender_scene,
                        True,
                        self.options_apply_modifiers_mode)
    
                enable_edit_mode(True, blender_context)
                bm = bmesh.new()
                bm.from_mesh(blender_mesh_temp)
    
                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'])
    
                layer_uv = bm.loops.layers.uv.get(ms3d_str['OBJECT_LAYER_UV'])
                if layer_uv is None:
                    if bm.loops.layers.uv:
                        layer_uv = bm.loops.layers.uv[0]
                    else:
                        layer_uv = bm.loops.layers.uv.new(
                                ms3d_str['OBJECT_LAYER_UV'])
    
                layer_deform = bm.verts.layers.deform.active
    
                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'])
    
    
                ##########################
                # handle vertices
                for bmv in bm.verts:
                    item = blender_to_ms3d_vertices.get(bmv)
                    if item is None:
                        index = len(ms3d_model._vertices)
                        ms3d_vertex = Ms3dVertex()
                        ms3d_vertex.__index = index
    
                        ms3d_vertex._vertex = self.geometry_correction(
                                matrix_transform * bmv.co)
    
                        if self.options_use_animation and layer_deform:
                            blender_vertex_group_ids = bmv[layer_deform]
                            if blender_vertex_group_ids:
    
                                for blender_index, blender_weight \
                                        in blender_vertex_group_ids.items():
                                    ms3d_joint = blender_to_ms3d_bones.get(
                                            blender_mesh_object_temp.vertex_groups[\
                                                    blender_index].name)
                                    if ms3d_joint:
    
                                        weight = bone_weights.get(ms3d_joint.__index)
                                        if not weight:
                                            weight = 0
                                        bone_weights[ms3d_joint.__index] = weight + blender_weight
    
                                # sort (bone_id: weight) according its weights
                                # to skip only less important weights in the next pass
                                bone_weights_sorted = sorted(bone_weights.items(), key=lambda item: item[1], reverse=True)
    
                                count = 0
                                bone_ids = []
                                weights = []
                                for ms3d_index, blender_weight \
                                        in bone_weights_sorted:
    
                                    if count == 0:
                                        ms3d_vertex.bone_id = ms3d_index
                                        weights.append(blender_weight)
                                    elif count == 1:
                                        bone_ids.append(ms3d_index)
                                        weights.append(blender_weight)
                                    elif count == 2:
                                        bone_ids.append(ms3d_index)
                                        weights.append(blender_weight)
                                    elif count == 3:
                                        bone_ids.append(ms3d_index)
                                        self.report(
                                                {'WARNING', 'INFO'},
                                                ms3d_str['WARNING_EXPORT_SKIP_WEIGHT'])
                                    else:
                                        # only first three weights will be supported / four bones
                                        self.report(
                                                {'WARNING', 'INFO'},
                                                ms3d_str['WARNING_EXPORT_SKIP_WEIGHT_EX'])
                                        break
                                    count += 1
    
                                # normalize weights to 100%
    
                                if self.options_normalize_weights:
    
                                    for weight in weights:
                                        weight_sum += weight
    
    
                                    if weight_sum > 0.0:
                                        weight_normalize = 1.0 / weight_sum
                                    else:
                                        weight_normalize = 1.0
    
                                    for index, weight in enumerate(weights):
                                        if index >= count-1:
    
                                        normalized_weight = weight * weight_normalize
    
                                        weight_sum -= normalized_weight
                                        weights[index] = normalized_weight
    
    
                                # fill up missing values
                                while len(bone_ids) < 3:
                                    bone_ids.append(Ms3dSpec.DEFAULT_VERTEX_BONE_ID)
                                while len(weights) < 3:
                                    weights.append(0.0)
    
    
                                ms3d_vertex._vertex_ex_object._bone_ids = \
                                        tuple(bone_ids)
                                ms3d_vertex._vertex_ex_object._weights = \
    
                                        tuple([int(value * 100) for value in weights])
    
    
                        if layer_extra:
                            #ms3d_vertex._vertex_ex_object.extra = bmv[layer_extra]
                            # bm.verts.layers.int does only support signed int32
                            # convert signed int32 to unsigned int32 (little-endian)
                            signed_int32 = bmv[layer_extra]
                            bytes_int32 = signed_int32.to_bytes(
                                    4, byteorder='little', signed=True)
                            unsigned_int32 = int.from_bytes(
                                    bytes_int32, byteorder='little', signed=False)
                            ms3d_vertex._vertex_ex_object.extra = unsigned_int32
    
                        ms3d_model._vertices.append(ms3d_vertex)
                        blender_to_ms3d_vertices[bmv] = ms3d_vertex
    
                ##########################
                # handle faces / tris
                for bmf in bm.faces:
                    item = blender_to_ms3d_triangles.get(bmf)
                    if item is None:
                        index = len(ms3d_model._triangles)
                        ms3d_triangle = Ms3dTriangle()
                        ms3d_triangle.__index = index
                        bmv0 = bmf.verts[0]
                        bmv1 = bmf.verts[1]
                        bmv2 = bmf.verts[2]
                        ms3d_vertex0 = blender_to_ms3d_vertices[bmv0]
                        ms3d_vertex1 = blender_to_ms3d_vertices[bmv1]
                        ms3d_vertex2 = blender_to_ms3d_vertices[bmv2]
                        ms3d_vertex0.reference_count += 1
                        ms3d_vertex1.reference_count += 1
                        ms3d_vertex2.reference_count += 1
                        ms3d_triangle._vertex_indices = (
                                ms3d_vertex0.__index,
                                ms3d_vertex1.__index,
                                ms3d_vertex2.__index,
                                )
                        ms3d_triangle._vertex_normals = (
                                self.geometry_correction(bmv0.normal),
                                self.geometry_correction(bmv1.normal),
                                self.geometry_correction(bmv2.normal),
                                )
                        ms3d_triangle._s = (
                                bmf.loops[0][layer_uv].uv.x,
                                bmf.loops[1][layer_uv].uv.x,
                                bmf.loops[2][layer_uv].uv.x,
                                )
                        ms3d_triangle._t = (
                                1.0 - bmf.loops[0][layer_uv].uv.y,
                                1.0 - bmf.loops[1][layer_uv].uv.y,
                                1.0 - bmf.loops[2][layer_uv].uv.y,
                                )
    
                        ms3d_triangle.smoothing_group = bmf[layer_smoothing_group]
                        ms3d_model._triangles.append(ms3d_triangle)
    
                        ms3d_material = self.get_ms3d_material_add_if(
                                blender_mesh, ms3d_model,
                                blender_to_ms3d_materials, bmf.material_index)
                        ms3d_group = blender_to_ms3d_groups.get(bmf[layer_group])
    
                        ##EXPORT_ACTIVE_ONLY:
                        if ms3d_group is not None:
                            if ms3d_material is None:
                                ms3d_group.material_index = \
                                        Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX
                            else:
                                if ms3d_group.material_index is None:
                                    ms3d_group.material_index = \
                                            ms3d_material.__index
                                else:
                                    if ms3d_group.material_index != \
                                            ms3d_material.__index:
                                        ms3d_group = \
                                                self.get_ms3d_group_by_material_add_if(
                                                ms3d_model, ms3d_material)
                        else:
                            if ms3d_material is not None:
                                ms3d_group = self.get_ms3d_group_by_material_add_if(
                                        ms3d_model, ms3d_material)
                            else:
                                ms3d_group = self.get_ms3d_group_default_material_add_if(
                                        ms3d_model)
    
                        if ms3d_group is not None:
                            ms3d_group._triangle_indices.append(
                                    ms3d_triangle.__index)
                            ms3d_triangle.group_index = ms3d_group.__index
    
                        blender_to_ms3d_triangles[bmf] = ms3d_triangle
    
                if bm is not None:
                    bm.free()
    
                enable_edit_mode(False, blender_context)
    
                ##########################
                # remove the temporary data
                blender_scene.objects.unlink(blender_mesh_object_temp)
                if blender_mesh_temp is not None:
                    blender_mesh_temp.user_clear()
                    blender_context.blend_data.meshes.remove(blender_mesh_temp)
                blender_mesh_temp = None
                if blender_mesh_object_temp is not None:
                    blender_mesh_temp = blender_mesh_object_temp.data.user_clear()
                    blender_mesh_object_temp.user_clear()
                    blender_context.blend_data.objects.remove(
                            blender_mesh_object_temp)
                if blender_mesh_temp is not None:
                    blender_mesh_temp.user_clear()
                    blender_context.blend_data.meshes.remove(blender_mesh_temp)
    
    
        ###########################################################################
        def create_animation(self, blender_context, ms3d_model,
                blender_mesh_objects, blender_to_ms3d_bones):
            ##########################
            # setup scene
            blender_scene = blender_context.scene
    
            if not self.options_use_animation:
                ms3d_model.animation_fps = 24
                ms3d_model.number_total_frames = 1
                ms3d_model.current_time = 0
                return
    
            frame_start = blender_scene.frame_start
            frame_end = blender_scene.frame_end
            frame_total = (frame_end - frame_start) + 1
            frame_step = blender_scene.frame_step
            frame_offset = 0
    
            fps = blender_scene.render.fps * blender_scene.render.fps_base
            time_base = 1.0 / fps
    
            base_bone_correction = Matrix.Rotation(pi / 2, 4, 'Z')
    
            for blender_mesh_object in blender_mesh_objects:
                blender_bones = None
                blender_action = None
                blender_nla_tracks = None
                for blender_modifier in blender_mesh_object.modifiers:
                    if blender_modifier.type == 'ARMATURE' \
                            and blender_modifier.object.pose:
                        blender_bones = blender_modifier.object.data.bones
                        blender_pose_bones = blender_modifier.object.pose.bones
                        if blender_modifier.object.animation_data:
                            blender_action = \
                                    blender_modifier.object.animation_data.action
                            blender_nla_tracks = \
                                    blender_modifier.object.animation_data.nla_tracks
    
    
                        # apply transform
                        if self.options_apply_transform:
                            matrix_transform = blender_modifier.object.matrix_basis
                        else:
                            matrix_transform = 1
    
    
                        break
    
                if blender_bones is None \
                        and (blender_action is None and blender_nla_tracks is None):
                    continue
    
                ##########################
                # bones
                blender_bones_ordered = []
                self.build_blender_bone_dependency_order(
                        blender_bones, blender_bones_ordered)
                for blender_bone_name in blender_bones_ordered:
                    blender_bone_oject = blender_bones[blender_bone_name]
                    ms3d_joint = Ms3dJoint()
                    ms3d_joint.__index = len(ms3d_model._joints)
    
                    blender_bone_ms3d = blender_bone_oject.ms3d
                    blender_bone = blender_bone_oject
    
                    ms3d_joint.flags = Ms3dUi.flags_to_ms3d(blender_bone_ms3d.flags)
                    if blender_bone_ms3d.comment:
                        ms3d_joint._comment_object = Ms3dCommentEx()
                        ms3d_joint._comment_object.comment = \
                                blender_bone_ms3d.comment
                        ms3d_joint._comment_object.index = ms3d_joint.__index
    
                    ms3d_joint.joint_ex_object._color = blender_bone_ms3d.color[:]
    
                    ms3d_joint.name = blender_bone.name
    
                    if blender_bone.parent:
                        ms3d_joint.parent_name = blender_bone.parent.name
                        ms3d_joint.__matrix = matrix_difference(
    
                                matrix_transform * blender_bone.matrix_local,
                                matrix_transform * blender_bone.parent.matrix_local)
    
                    else:
                        ms3d_joint.__matrix = base_bone_correction \
    
                                * matrix_transform * blender_bone.matrix_local
    
    
                    mat = ms3d_joint.__matrix
                    loc = mat.to_translation()
                    rot = mat.to_euler('XZY')
                    ms3d_joint._position = self.joint_correction(loc)
                    ms3d_joint._rotation = self.joint_correction(rot)
    
                    ms3d_model._joints.append(ms3d_joint)
                    blender_to_ms3d_bones[blender_bone.name] = ms3d_joint
    
                ##########################
                # animation
                frames = None
                frames_location = set()
                frames_rotation = set()
                frames_scale = set()
    
                if blender_action:
                    self.fill_keyframe_sets(
                            blender_action.fcurves,
                            frames_location, frames_rotation, frames_scale,
                            0)
    
                if blender_nla_tracks:
                    for nla_track in blender_nla_tracks:
                        if nla_track.mute:
                            continue
                        for strip in nla_track.strips:
                            if strip.mute:
                                continue
                            frame_correction = strip.frame_start \
                                    - strip.action_frame_start
                            self.fill_keyframe_sets(
                                    strip.action.fcurves,
                                    frames_location, frames_rotation, frames_scale,
                                    frame_correction)
    
                frames = set(frames_location)
                frames = frames.union(frames_rotation)
                frames = frames.union(frames_scale)
    
                if not self.options_shrink_to_keys:
                    frames = frames.intersection(range(
                            blender_scene.frame_start, blender_scene.frame_end + 1))
    
                frames_sorted = list(frames)
                frames_sorted.sort()
    
                if self.options_shrink_to_keys and len(frames_sorted) >= 2:
                    frame_start = frames_sorted[0]
                    frame_end = frames_sorted[len(frames_sorted)-1]
                    frame_total = (frame_end - frame_start) + 1
                    frame_offset = frame_start - 1
    
                if self.options_bake_each_frame:
                    frames_sorted = range(int(frame_start), int(frame_end + 1),
                            int(frame_step))
    
                frame_temp = blender_scene.frame_current
    
                for current_frame in frames_sorted:
                    blender_scene.frame_set(current_frame)
    
                    current_time = (current_frame - frame_offset) * time_base
                    for blender_bone_name in blender_bones_ordered:
                        blender_bone = blender_bones[blender_bone_name]
                        blender_pose_bone = blender_pose_bones[blender_bone_name]
                        ms3d_joint = blender_to_ms3d_bones[blender_bone_name]
    
                        m1 = blender_bone.matrix_local.inverted()
                        if blender_pose_bone.parent:
                            m2 = blender_pose_bone.parent.matrix_channel.inverted()
                        else:
    
                        m3 = blender_pose_bone.matrix.copy()
                        m = ((m1 * m2) * m3)
                        loc = m.to_translation()
                        rot = m.to_euler('XZY')
    
                        ms3d_joint.translation_key_frames.append(
                                Ms3dTranslationKeyframe(
                                        current_time, self.joint_correction(loc)
                                        )
                                )
                        ms3d_joint.rotation_key_frames.append(
                                Ms3dRotationKeyframe(
                                        current_time, self.joint_correction(rot)
                                        )
                                )
    
                blender_scene.frame_set(frame_temp)
    
            ms3d_model.animation_fps = fps
            if ms3d_model.number_joints > 0:
                ms3d_model.number_total_frames = int(frame_total)
                ms3d_model.current_time = ((blender_scene.frame_current \
                        - blender_scene.frame_start) + 1) * time_base
            else:
                ms3d_model.number_total_frames = 1
                ms3d_model.current_time = 0
    
    
        ###########################################################################
        def get_ms3d_group_default_material_add_if(self, ms3d_model):
            markerName = "MaterialGroupDefault"
            markerComment = "group without material"
    
            for ms3d_group in ms3d_model._groups:
                if ms3d_group.material_index == \
                        Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX \
                        and ms3d_group.name == markerName \
                        and ms3d_group._comment_object \
                        and ms3d_group._comment_object.comment == markerComment:
                    return ms3d_group
    
            ms3d_group = Ms3dGroup()
            ms3d_group.__index = len(ms3d_model._groups)
            ms3d_group.name = markerName
            ms3d_group._comment_object = Ms3dCommentEx()
            ms3d_group._comment_object.comment = markerComment
            ms3d_group._comment_object.index = len(ms3d_model._groups)
            ms3d_group.material_index = Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX
    
            ms3d_model._groups.append(ms3d_group)
    
            return ms3d_group
    
    
        ###########################################################################
        def get_ms3d_group_by_material_add_if(self, ms3d_model, ms3d_material):
            if ms3d_material.__index < 0 \
                    or ms3d_material.__index >= len(ms3d_model.materials):
                return None
    
            markerName = "MaterialGroup.{}".format(ms3d_material.__index)
            markerComment = "MaterialGroup({})".format(ms3d_material.name)
    
            for ms3d_group in ms3d_model._groups:
                if ms3d_group.name == markerName \
                        and ms3d_group._comment_object \
                        and ms3d_group._comment_object.comment == markerComment:
                    return ms3d_group
    
            ms3d_group = Ms3dGroup()
            ms3d_group.__index = len(ms3d_model._groups)
            ms3d_group.name = markerName
            ms3d_group._comment_object = Ms3dCommentEx()
            ms3d_group._comment_object.comment = markerComment
            ms3d_group._comment_object.index = len(ms3d_model._groups)
            ms3d_group.material_index = ms3d_material.__index
    
            ms3d_model._groups.append(ms3d_group)
    
            return ms3d_group
    
    
        ###########################################################################
        def get_ms3d_material_add_if(self, blender_mesh, ms3d_model,
                blender_to_ms3d_materials, blender_index):
            if blender_index < 0 or blender_index >= len(blender_mesh.materials):
                return None
    
            blender_material = blender_mesh.materials[blender_index]
            ms3d_material = blender_to_ms3d_materials.get(blender_material)
            if ms3d_material is None:
                ms3d_material = Ms3dMaterial()
                ms3d_material.__index = len(ms3d_model.materials)
    
                blender_ms3d_material = blender_material.ms3d
    
                if not self.options_use_blender_names \
                        and not self.options_use_blender_materials \
                        and blender_ms3d_material.name:
                    ms3d_material.name = blender_ms3d_material.name
                else:
                    ms3d_material.name = blender_material.name
    
                temp_material = None
                if self.options_use_blender_materials:
                    temp_material = Ms3dMaterial()
                    Ms3dMaterialHelper.copy_from_blender(
                            None, None, temp_material, blender_material)
                else:
                    temp_material = blender_ms3d_material
    
                ms3d_material._ambient = temp_material.ambient[:]
                ms3d_material._diffuse = temp_material.diffuse[:]
                ms3d_material._specular = temp_material.specular[:]
                ms3d_material._emissive = temp_material.emissive[:]
                ms3d_material.shininess = temp_material.shininess
                ms3d_material.transparency = temp_material.transparency
                ms3d_material.texture = temp_material.texture
                ms3d_material.alphamap = temp_material.alphamap
    
                ms3d_material.mode = Ms3dUi.texture_mode_to_ms3d(
                        blender_ms3d_material.mode)
                if blender_ms3d_material.comment:
                    ms3d_material._comment_object = Ms3dCommentEx()
                    ms3d_material._comment_object.comment = \
                            blender_ms3d_material.comment
                    ms3d_material._comment_object.index = ms3d_material.__index
    
                ms3d_model.materials.append(ms3d_material)
    
                blender_to_ms3d_materials[blender_material] = ms3d_material
    
            return ms3d_material
    
    
        ###########################################################################
        def geometry_correction(self, value):
            return (value[1], value[2], value[0])
    
    
        ###########################################################################
        def joint_correction(self, value):
            return (-value[0], value[2], value[1])
    
    
        ###########################################################################
        def build_blender_bone_dependency_order(self, blender_bones,
                blender_bones_ordered):
            if not blender_bones:
                return blender_bones_ordered
    
            blender_bones_children = {None: []}
            for blender_bone in blender_bones:
                if blender_bone.parent:
                    blender_bone_children = blender_bones_children.get(
                            blender_bone.parent.name)
                    if blender_bone_children is None:
                        blender_bone_children = blender_bones_children[
                                blender_bone.parent.name] = []
                else:
                    blender_bone_children = blender_bones_children[None]
    
                blender_bone_children.append(blender_bone.name)
    
            self.traverse_dependencies(
                    blender_bones_ordered,
                    blender_bones_children,
                    None)
    
            return blender_bones_ordered
    
    
        ###########################################################################
        def traverse_dependencies(self, blender_bones_ordered,
                blender_bones_children, key):
            blender_bone_children = blender_bones_children.get(key)
            if blender_bone_children:
                for blender_bone_name in blender_bone_children:
                    blender_bones_ordered.append(blender_bone_name)
                    self.traverse_dependencies(
                            blender_bones_ordered,
                            blender_bones_children,
                            blender_bone_name)
    
        ###########################################################################
        def fill_keyframe_sets(self,
                fcurves,
                frames_location, frames_rotation, frames_scale,
                frame_correction):
            for fcurve in fcurves:
                if fcurve.data_path.endswith(".location"):
                    frames = frames_location
                elif fcurve.data_path.endswith(".rotation_euler"):
                    frames = frames_rotation
                elif fcurve.data_path.endswith(".rotation_quaternion"):
                    frames = frames_rotation
                elif fcurve.data_path.endswith(".scale"):
                    frames = frames_scale
                else:
                    pass
    
                for keyframe_point in fcurve.keyframe_points:
                    frames.add(int(keyframe_point.co[0] + frame_correction))
    
    
    ###############################################################################
    #234567890123456789012345678901234567890123456789012345678901234567890123456789
    #--------1---------2---------3---------4---------5---------6---------7---------
    # ##### END OF FILE #####