diff --git a/io_scene_ms3d/__init__.py b/io_scene_ms3d/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2ae5199d422b1c8818f9795f623317c1b3e05005 --- /dev/null +++ b/io_scene_ms3d/__init__.py @@ -0,0 +1,107 @@ +# ##### 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> + +bl_info = { + 'name': "MilkShape3D MS3D format (.ms3d)", + 'description': "Import / Export MilkShape3D MS3D files"\ + " (conform with MilkShape3D v1.8.4)", + 'author': "Alexander Nussbaumer", + 'version': (0, 93, 1), + 'blender': (2, 65, 3), + 'location': "File > Import & File > Export", + 'warning': "", + 'wiki_url': "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\ + "Scripts/Import-Export/MilkShape3D_MS3D", + 'tracker_url': "http://projects.blender.org/tracker/index.php"\ + "?func=detail&aid=29404", + 'category': "Import-Export", + } + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- + + +# ##### BEGIN COPYRIGHT BLOCK ##### +# +# initial script copyright (c)2011,2012 Alexander Nussbaumer +# +# ##### END COPYRIGHT BLOCK ##### + + +# To support reload properly, try to access a package var, +# if it's there, reload everything +if 'bpy' in locals(): + import imp + if 'io_scene_ms3d.ms3d_ui' in locals(): + imp.reload(io_scene_ms3d.ms3d_ui) +else: + from io_scene_ms3d.ms3d_ui import ( + Ms3dImportOperator, + Ms3dExportOperator, + ) + + +#import blender stuff +from bpy.utils import ( + register_module, + unregister_module, + ) +from bpy.types import ( + INFO_MT_file_export, + INFO_MT_file_import, + ) + + +############################################################################### +# registration +def register(): + #################### + # F8 - key + import imp + imp.reload(ms3d_ui) + # F8 - key + #################### + + ms3d_ui.register() + + register_module(__name__) + INFO_MT_file_export.append(Ms3dExportOperator.menu_func) + INFO_MT_file_import.append(Ms3dImportOperator.menu_func) + + +def unregister(): + ms3d_ui.unregister() + + unregister_module(__name__) + INFO_MT_file_export.remove(Ms3dExportOperator.menu_func) + INFO_MT_file_import.remove(Ms3dImportOperator.menu_func) + + +############################################################################### +# global entry point +if (__name__ == "__main__"): + register() + + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- +# ##### END OF FILE ##### diff --git a/io_scene_ms3d/ms3d_export.py b/io_scene_ms3d/ms3d_export.py new file mode 100644 index 0000000000000000000000000000000000000000..bc6425a19af1034cb5140809ec22f5f8e0146e40 --- /dev/null +++ b/io_scene_ms3d/ms3d_export.py @@ -0,0 +1,880 @@ +# ##### 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_local + else: + matrix_transform = Matrix() + + # 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: + count = 0 + bone_ids = [] + weights = [] + 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: + if count == 0: + ms3d_vertex.bone_id = ms3d_joint.__index + weights.append( + int(blender_weight * 100.0)) + elif count == 1: + bone_ids.append(ms3d_joint.__index) + weights.append( + int(blender_weight * 100.0)) + elif count == 2: + bone_ids.append(ms3d_joint.__index) + weights.append( + int(blender_weight * 100.0)) + elif count == 3: + bone_ids.append(ms3d_joint.__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 + + while len(bone_ids) < 3: + bone_ids.append(Ms3dSpec.DEFAULT_VERTEX_BONE_ID) + while len(weights) < 3: + weights.append(0) + + # normalize weights to 100 + if self.options_normalize_weights: + weight_sum = 0 + for weight in weights: + weight_sum += weight + + if weight_sum > 100: + weight_normalize = 100 / weight_sum + else: + weight_normalize = 1 + + weight_sum = 100 + for index, weight in enumerate(weights): + if index >= count-1: + weights[index] = weight_sum + break + normalized_weight = int( + weight * weight_normalize) + weight_sum -= normalized_weight + weights[index] = normalized_weight + + ms3d_vertex._vertex_ex_object._bone_ids = \ + tuple(bone_ids) + ms3d_vertex._vertex_ex_object._weights = \ + tuple(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 + 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( + blender_bone.matrix_local, + blender_bone.parent.matrix_local) + else: + ms3d_joint.__matrix = base_bone_correction \ + * 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: + m2 = Matrix() + 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 ##### diff --git a/io_scene_ms3d/ms3d_import.py b/io_scene_ms3d/ms3d_import.py new file mode 100644 index 0000000000000000000000000000000000000000..f4767f1193183a7aa9cbf78d070e5dd57a57a103 --- /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 ##### diff --git a/io_scene_ms3d/ms3d_spec.py b/io_scene_ms3d/ms3d_spec.py new file mode 100644 index 0000000000000000000000000000000000000000..1138a01eb0be8cfe6c91d1fdeff8c20f4f0463cc --- /dev/null +++ b/io_scene_ms3d/ms3d_spec.py @@ -0,0 +1,2069 @@ +# ##### 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 ##### + + +from struct import ( + pack, + unpack, + ) +from sys import ( + exc_info, + ) + +############################################################################### +# +# MilkShape 3D 1.8.5 File Format Specification +# +# all specifications were taken from SDK 1.8.5 +# +# some additional specifications were taken from +# MilkShape 3D Viewer v2.0 (Nov 06 2007) - msMesh.h +# + + +############################################################################### +# +# sizes +# + +class Ms3dSpec: + ########################################################################### + # + # max values + # + MAX_VERTICES = 65534 # 0..65533; note: (65534???, 65535???) + MAX_TRIANGLES = 65534 # 0..65533; note: (65534???, 65535???) + MAX_GROUPS = 255 # 1..255; note: (0 default group) + MAX_MATERIALS = 128 # 0..127; note: (-1 no material) + MAX_JOINTS = 128 # 0..127; note: (-1 no joint) + MAX_SMOOTH_GROUP = 32 # 0..32; note: (0 no smoothing group) + MAX_TEXTURE_FILENAME_SIZE = 128 + + ########################################################################### + # + # flags + # + FLAG_NONE = 0 + FLAG_SELECTED = 1 + FLAG_HIDDEN = 2 + FLAG_SELECTED2 = 4 + FLAG_DIRTY = 8 + FLAG_ISKEY = 16 # additional spec from [2] + FLAG_NEWLYCREATED = 32 # additional spec from [2] + FLAG_MARKED = 64 # additional spec from [2] + + FLAG_TEXTURE_NONE = 0x00 + FLAG_TEXTURE_COMBINE_ALPHA = 0x20 + FLAG_TEXTURE_HAS_ALPHA = 0x40 + FLAG_TEXTURE_SPHERE_MAP = 0x80 + + MODE_TRANSPARENCY_SIMPLE = 0 + MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF = 1 + MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES = 2 + + + ########################################################################### + # + # values + # + HEADER = "MS3D000000" + + + ########################################################################### + # + # min, max, default values + # + NONE_VERTEX_BONE_ID = -1 + NONE_GROUP_MATERIAL_INDEX = -1 + + DEFAULT_HEADER = HEADER + DEFAULT_HEADER_VERSION = 4 + DEFAULT_VERTEX_BONE_ID = NONE_VERTEX_BONE_ID + DEFAULT_TRIANGLE_SMOOTHING_GROUP = 0 + DEFAULT_TRIANGLE_GROUP = 0 + DEFAULT_MATERIAL_MODE = FLAG_TEXTURE_NONE + DEFAULT_GROUP_MATERIAL_INDEX = NONE_GROUP_MATERIAL_INDEX + DEFAULT_MODEL_JOINT_SIZE = 1.0 + DEFAULT_MODEL_TRANSPARENCY_MODE = MODE_TRANSPARENCY_SIMPLE + DEFAULT_MODEL_ANIMATION_FPS = 25.0 + DEFAULT_MODEL_SUB_VERSION_COMMENTS = 1 + DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA = 2 + DEFAULT_MODEL_SUB_VERSION_JOINT_EXTRA = 1 + DEFAULT_MODEL_SUB_VERSION_MODEL_EXTRA = 1 + DEFAULT_FLAGS = FLAG_NONE + MAX_MATERIAL_SHININESS = 128 + + # blender default / OpenGL default + DEFAULT_MATERIAL_AMBIENT = (0.2, 0.2, 0.2, 1.0) + DEFAULT_MATERIAL_DIFFUSE = (0.8, 0.8, 0.8, 1.0) + DEFAULT_MATERIAL_SPECULAR = (1.0, 1.0, 1.0, 1.0) + DEFAULT_MATERIAL_EMISSIVE = (0.0, 0.0, 0.0, 1.0) + DEFAULT_MATERIAL_SHININESS = 12.5 + + DEFAULT_JOINT_COLOR = (0.8, 0.8, 0.8) + +############################################################################### +# +# helper class for basic raw io +# +class Ms3dIo: + # sizes for IO + SIZE_BYTE = 1 + SIZE_SBYTE = 1 + SIZE_WORD = 2 + SIZE_DWORD = 4 + SIZE_FLOAT = 4 + LENGTH_ID = 10 + LENGTH_NAME = 32 + LENGTH_FILENAME = 128 + + PRECISION = 4 + + @staticmethod + def read_byte(raw_io): + """ read a single byte from raw_io """ + buffer = raw_io.read(Ms3dIo.SIZE_BYTE) + if not buffer: + raise EOFError() + value = unpack('<B', buffer)[0] + return value + + @staticmethod + def write_byte(raw_io, value): + """ write a single byte to raw_io """ + raw_io.write(pack('<B', value)) + + @staticmethod + def read_sbyte(raw_io): + """ read a single signed byte from raw_io """ + buffer = raw_io.read(Ms3dIo.SIZE_BYTE) + if not buffer: + raise EOFError() + value = unpack('<b', buffer)[0] + return value + + @staticmethod + def write_sbyte(raw_io, value): + """ write a single signed byte to raw_io """ + raw_io.write(pack('<b', value)) + + @staticmethod + def read_word(raw_io): + """ read a word from raw_io """ + buffer = raw_io.read(Ms3dIo.SIZE_WORD) + if not buffer: + raise EOFError() + value = unpack('<H', buffer)[0] + return value + + @staticmethod + def write_word(raw_io, value): + """ write a word to raw_io """ + raw_io.write(pack('<H', value)) + + @staticmethod + def read_dword(raw_io): + """ read a double word from raw_io """ + buffer = raw_io.read(Ms3dIo.SIZE_DWORD) + if not buffer: + raise EOFError() + value = unpack('<I', buffer)[0] + return value + + @staticmethod + def write_dword(raw_io, value): + """ write a double word to raw_io """ + raw_io.write(pack('<I', value)) + + @staticmethod + def read_float(raw_io): + """ read a float from raw_io """ + buffer = raw_io.read(Ms3dIo.SIZE_FLOAT) + if not buffer: + raise EOFError() + value = unpack('<f', buffer)[0] + return value + + @staticmethod + def write_float(raw_io, value): + """ write a float to raw_io """ + raw_io.write(pack('<f', value)) + + @staticmethod + def read_array(raw_io, itemReader, count): + """ read an array[count] of objects from raw_io, by using a itemReader """ + value = [] + for i in range(count): + itemValue = itemReader(raw_io) + value.append(itemValue) + return tuple(value) + + @staticmethod + def write_array(raw_io, itemWriter, count, value): + """ write an array[count] of objects to raw_io, by using a itemWriter """ + for i in range(count): + itemValue = value[i] + itemWriter(raw_io, itemValue) + + @staticmethod + def read_array2(raw_io, itemReader, count, count2): + """ read an array[count][count2] of objects from raw_io, + by using a itemReader """ + value = [] + for i in range(count): + itemValue = Ms3dIo.read_array(raw_io, itemReader, count2) + value.append(tuple(itemValue)) + return value + + @staticmethod + def write_array2(raw_io, itemWriter, count, count2, value): + """ write an array[count][count2] of objects to raw_io, + by using a itemWriter """ + for i in range(count): + itemValue = value[i] + Ms3dIo.write_array(raw_io, itemWriter, count2, itemValue) + + @staticmethod + def read_string(raw_io, length): + """ read a string of a specific length from raw_io """ + value = [] + skip = False + for i in range(length): + buffer = raw_io.read(Ms3dIo.SIZE_SBYTE) + if not buffer: + raise EOFError() + raw = (int)(unpack('<b', buffer)[0]) + if (raw >= 32) & (raw <= 255): + pass + else: + if (raw == 0): + raw = 0 + skip = True + else: + raw = 32 + + c = chr(raw) + + if (not skip): + value.append(c) + + finalValue = "".join(value) + return finalValue + + @staticmethod + def write_string(raw_io, length, value): + """ write a string of a specific length to raw_io """ + l = len(value) + for i in range(length): + if(i < l): + c = value[i] + + if (isinstance(c, str)): + c = c[0] + raw = ord(c) + elif (isinstance(c, int)): + raw = c + else: + pass + else: + raw = 0 + + raw_io.write(pack('<b', (int)(raw % 256))) + + +############################################################################### +# +# multi complex types +# + +############################################################################### +class Ms3dHeader: + """ Ms3dHeader """ + __slots__ = ( + 'id', + 'version', + ) + + def __init__( + self, + default_id=Ms3dSpec.DEFAULT_HEADER, + default_version=Ms3dSpec.DEFAULT_HEADER_VERSION + ): + self.id = default_id + self.version = default_version + + def __repr__(self): + return "\n<id='{}', version={}>".format( + self.id, + self.version + ) + + def __hash__(self): + return hash(self.id) ^ hash(self.version) + + def __eq__(self, other): + return ((self is not None) and (other is not None) + and (self.id == other.id) + and (self.version == other.version)) + + def read(self, raw_io): + self.id = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_ID) + self.version = Ms3dIo.read_dword(raw_io) + return self + + def write(self, raw_io): + Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_ID, self.id) + Ms3dIo.write_dword(raw_io, self.version) + + +############################################################################### +class Ms3dVertex: + """ Ms3dVertex """ + """ + __slots__ was taking out, + to be able to inject additional attributes during runtime + __slots__ = ( + 'flags', + 'bone_id', + 'reference_count', + '_vertex', + '_vertex_ex_object', # Ms3dVertexEx + ) + """ + + def __init__( + self, + default_flags=Ms3dSpec.DEFAULT_FLAGS, + default_vertex=(0.0, 0.0, 0.0), + default_bone_id=Ms3dSpec.DEFAULT_VERTEX_BONE_ID, + default_reference_count=0, + default_vertex_ex_object=None, # Ms3dVertexEx + ): + self.flags = default_flags + self._vertex = default_vertex + self.bone_id = default_bone_id + self.reference_count = default_reference_count + + if default_vertex_ex_object is None: + default_vertex_ex_object = Ms3dVertexEx2() + # Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA = 2 + self._vertex_ex_object = default_vertex_ex_object + # Ms3dVertexEx + + def __repr__(self): + return "\n<flags={}, vertex=({:.{p}f}, {:.{p}f}, {:.{p}f}), bone_id={},"\ + " reference_count={}>".format( + self.flags, + self._vertex[0], + self._vertex[1], + self._vertex[2], + self.bone_id, + self.reference_count, + p=Ms3dIo.PRECISION + ) + + def __hash__(self): + return (hash(self.vertex) + #^ hash(self.flags) + #^ hash(self.bone_id) + #^ hash(self.reference_count) + ) + + def __eq__(self, other): + return ((self.vertex == other.vertex) + #and (self.flags == other.flags) + #and (self.bone_id == other.bone_id) + #and (self.reference_count == other.reference_count) + ) + + + @property + def vertex(self): + return self._vertex + + @property + def vertex_ex_object(self): + return self._vertex_ex_object + + + def read(self, raw_io): + self.flags = Ms3dIo.read_byte(raw_io) + self._vertex = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + self.bone_id = Ms3dIo.read_sbyte(raw_io) + self.reference_count = Ms3dIo.read_byte(raw_io) + return self + + def write(self, raw_io): + Ms3dIo.write_byte(raw_io, self.flags) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.vertex) + Ms3dIo.write_sbyte(raw_io, self.bone_id) + Ms3dIo.write_byte(raw_io, self.reference_count) + + +############################################################################### +class Ms3dTriangle: + """ Ms3dTriangle """ + """ + __slots__ was taking out, + to be able to inject additional attributes during runtime + __slots__ = ( + 'flags', + 'smoothing_group', + 'group_index', + '_vertex_indices', + '_vertex_normals', + '_s', + '_t', + ) + """ + + def __init__( + self, + default_flags=Ms3dSpec.DEFAULT_FLAGS, + default_vertex_indices=(0, 0, 0), + default_vertex_normals=( + (0.0, 0.0, 0.0), + (0.0, 0.0, 0.0), + (0.0, 0.0, 0.0)), + default_s=(0.0, 0.0, 0.0), + default_t=(0.0, 0.0, 0.0), + default_smoothing_group=Ms3dSpec.DEFAULT_TRIANGLE_SMOOTHING_GROUP, + default_group_index=Ms3dSpec.DEFAULT_TRIANGLE_GROUP + ): + self.flags = default_flags + self._vertex_indices = default_vertex_indices + self._vertex_normals = default_vertex_normals + self._s = default_s + self._t = default_t + self.smoothing_group = default_smoothing_group + self.group_index = default_group_index + + def __repr__(self): + return "\n<flags={}, vertex_indices={}, vertex_normals=(({:.{p}f}, "\ + "{:.{p}f}, {:.{p}f}), ({:.{p}f}, {:.{p}f}, {:.{p}f}), ({:.{p}f}, "\ + "{:.{p}f}, {:.{p}f})), s=({:.{p}f}, {:.{p}f}, {:.{p}f}), "\ + "t=({:.{p}f}, {:.{p}f}, {:.{p}f}), smoothing_group={}, "\ + "group_index={}>".format( + self.flags, + self.vertex_indices, + self.vertex_normals[0][0], + self.vertex_normals[0][1], + self.vertex_normals[0][2], + self.vertex_normals[1][0], + self.vertex_normals[1][1], + self.vertex_normals[1][2], + self.vertex_normals[2][0], + self.vertex_normals[2][1], + self.vertex_normals[2][2], + self.s[0], + self.s[1], + self.s[2], + self.t[0], + self.t[1], + self.t[2], + self.smoothing_group, + self.group_index, + p=Ms3dIo.PRECISION + ) + + + @property + def vertex_indices(self): + return self._vertex_indices + + @property + def vertex_normals(self): + return self._vertex_normals + + @property + def s(self): + return self._s + + @property + def t(self): + return self._t + + + def read(self, raw_io): + self.flags = Ms3dIo.read_word(raw_io) + self._vertex_indices = Ms3dIo.read_array(raw_io, Ms3dIo.read_word, 3) + self._vertex_normals = Ms3dIo.read_array2(raw_io, Ms3dIo.read_float, 3, 3) + self._s = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + self._t = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + self.smoothing_group = Ms3dIo.read_byte(raw_io) + self.group_index = Ms3dIo.read_byte(raw_io) + return self + + def write(self, raw_io): + Ms3dIo.write_word(raw_io, self.flags) + Ms3dIo.write_array(raw_io, Ms3dIo.write_word, 3, self.vertex_indices) + Ms3dIo.write_array2(raw_io, Ms3dIo.write_float, 3, 3, self.vertex_normals) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.s) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.t) + Ms3dIo.write_byte(raw_io, self.smoothing_group) + Ms3dIo.write_byte(raw_io, self.group_index) + + +############################################################################### +class Ms3dGroup: + """ Ms3dGroup """ + """ + __slots__ was taking out, + to be able to inject additional attributes during runtime + __slots__ = ( + 'flags', + 'name', + 'material_index', + '_triangle_indices', + '_comment_object', # Ms3dComment + ) + """ + + def __init__( + self, + default_flags=Ms3dSpec.DEFAULT_FLAGS, + default_name="", + default_triangle_indices=None, + default_material_index=Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX, + default_comment_object=None, # Ms3dComment + ): + if (default_name is None): + default_name = "" + + if (default_triangle_indices is None): + default_triangle_indices = [] + + self.flags = default_flags + self.name = default_name + self._triangle_indices = default_triangle_indices + self.material_index = default_material_index + + if default_comment_object is None: + default_comment_object = Ms3dCommentEx() + self._comment_object = default_comment_object # Ms3dComment + + def __repr__(self): + return "\n<flags={}, name='{}', number_triangles={},"\ + " triangle_indices={}, material_index={}>".format( + self.flags, + self.name, + self.number_triangles, + self.triangle_indices, + self.material_index + ) + + + @property + def number_triangles(self): + if self.triangle_indices is None: + return 0 + return len(self.triangle_indices) + + @property + def triangle_indices(self): + return self._triangle_indices + + @property + def comment_object(self): + return self._comment_object + + + def read(self, raw_io): + self.flags = Ms3dIo.read_byte(raw_io) + self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME) + _number_triangles = Ms3dIo.read_word(raw_io) + self._triangle_indices = Ms3dIo.read_array( + raw_io, Ms3dIo.read_word, _number_triangles) + self.material_index = Ms3dIo.read_sbyte(raw_io) + return self + + def write(self, raw_io): + Ms3dIo.write_byte(raw_io, self.flags) + Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name) + Ms3dIo.write_word(raw_io, self.number_triangles) + Ms3dIo.write_array( + raw_io, Ms3dIo.write_word, self.number_triangles, + self.triangle_indices) + Ms3dIo.write_sbyte(raw_io, self.material_index) + + +############################################################################### +class Ms3dMaterial: + """ Ms3dMaterial """ + """ + __slots__ was taking out, + to be able to inject additional attributes during runtime + __slots__ = ( + 'name', + 'shininess', + 'transparency', + 'mode', + 'texture', + 'alphamap', + '_ambient', + '_diffuse', + '_specular', + '_emissive', + '_comment_object', # Ms3dComment + ) + """ + + def __init__( + self, + default_name="", + default_ambient=list(Ms3dSpec.DEFAULT_MATERIAL_AMBIENT), + default_diffuse=list(Ms3dSpec.DEFAULT_MATERIAL_DIFFUSE), + default_specular=list(Ms3dSpec.DEFAULT_MATERIAL_SPECULAR), + default_emissive=list(Ms3dSpec.DEFAULT_MATERIAL_EMISSIVE), + default_shininess=Ms3dSpec.DEFAULT_MATERIAL_SHININESS, + default_transparency=0.0, + default_mode=Ms3dSpec.DEFAULT_MATERIAL_MODE, + default_texture="", + default_alphamap="", + default_comment_object=None, # Ms3dComment + ): + if (default_name is None): + default_name = "" + + if (default_texture is None): + default_texture = "" + + if (default_alphamap is None): + default_alphamap = "" + + self.name = default_name + self._ambient = default_ambient + self._diffuse = default_diffuse + self._specular = default_specular + self._emissive = default_emissive + self.shininess = default_shininess + self.transparency = default_transparency + self.mode = default_mode + self.texture = default_texture + self.alphamap = default_alphamap + + if default_comment_object is None: + default_comment_object = Ms3dCommentEx() + self._comment_object = default_comment_object # Ms3dComment + + def __repr__(self): + return "\n<name='{}', ambient=({:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), "\ + "diffuse=({:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), specular=("\ + "{:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), emissive=({:.{p}f}, "\ + "{:.{p}f}, {:.{p}f}, {:.{p}f}), shininess={:.{p}f}, transparency="\ + "{:.{p}f}, mode={}, texture='{}', alphamap='{}'>".format( + self.name, + self.ambient[0], + self.ambient[1], + self.ambient[2], + self.ambient[3], + self.diffuse[0], + self.diffuse[1], + self.diffuse[2], + self.diffuse[3], + self.specular[0], + self.specular[1], + self.specular[2], + self.specular[3], + self.emissive[0], + self.emissive[1], + self.emissive[2], + self.emissive[3], + self.shininess, + self.transparency, + self.mode, + self.texture, + self.alphamap, + p=Ms3dIo.PRECISION + ) + + def __hash__(self): + return (hash(self.name) + + ^ hash(self.ambient) + ^ hash(self.diffuse) + ^ hash(self.specular) + ^ hash(self.emissive) + + ^ hash(self.shininess) + ^ hash(self.transparency) + ^ hash(self.mode) + + ^ hash(self.texture) + ^ hash(self.alphamap) + ) + + def __eq__(self, other): + return ((self.name == other.name) + + and (self.ambient == other.ambient) + and (self.diffuse == other.diffuse) + and (self.specular == other.specular) + and (self.emissive == other.emissive) + + and (self.shininess == other.shininess) + and (self.transparency == other.transparency) + and (self.mode == other.mode) + + #and (self.texture == other.texture) + #and (self.alphamap == other.alphamap) + ) + + + @property + def ambient(self): + return self._ambient + + @property + def diffuse(self): + return self._diffuse + + @property + def specular(self): + return self._specular + + @property + def emissive(self): + return self._emissive + + @property + def comment_object(self): + return self._comment_object + + + def read(self, raw_io): + self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME) + self._ambient = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4) + self._diffuse = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4) + self._specular = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4) + self._emissive = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4) + self.shininess = Ms3dIo.read_float(raw_io) + self.transparency = Ms3dIo.read_float(raw_io) + self.mode = Ms3dIo.read_byte(raw_io) + self.texture = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_FILENAME) + self.alphamap = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_FILENAME) + return self + + def write(self, raw_io): + Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.ambient) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.diffuse) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.specular) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.emissive) + Ms3dIo.write_float(raw_io, self.shininess) + Ms3dIo.write_float(raw_io, self.transparency) + Ms3dIo.write_byte(raw_io, self.mode) + Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_FILENAME, self.texture) + Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_FILENAME, self.alphamap) + + +############################################################################### +class Ms3dRotationKeyframe: + """ Ms3dRotationKeyframe """ + __slots__ = ( + 'time', + '_rotation', + ) + + def __init__( + self, + default_time=0.0, + default_rotation=(0.0, 0.0, 0.0) + ): + self.time = default_time + self._rotation = default_rotation + + def __repr__(self): + return "\n<time={:.{p}f}, rotation=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format( + self.time, + self.rotation[0], + self.rotation[1], + self.rotation[2], + p=Ms3dIo.PRECISION + ) + + + @property + def rotation(self): + return self._rotation + + + def read(self, raw_io): + self.time = Ms3dIo.read_float(raw_io) + self._rotation = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + return self + + def write(self, raw_io): + Ms3dIo.write_float(raw_io, self.time) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.rotation) + + +############################################################################### +class Ms3dTranslationKeyframe: + """ Ms3dTranslationKeyframe """ + __slots__ = ( + 'time', + '_position', + ) + + def __init__( + self, + default_time=0.0, + default_position=(0.0, 0.0, 0.0) + ): + self.time = default_time + self._position = default_position + + def __repr__(self): + return "\n<time={:.{p}f}, position=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format( + self.time, + self.position[0], + self.position[1], + self.position[2], + p=Ms3dIo.PRECISION + ) + + + @property + def position(self): + return self._position + + + def read(self, raw_io): + self.time = Ms3dIo.read_float(raw_io) + self._position = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + return self + + def write(self, raw_io): + Ms3dIo.write_float(raw_io, self.time) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.position) + + +############################################################################### +class Ms3dJoint: + """ Ms3dJoint """ + """ + __slots__ was taking out, + to be able to inject additional attributes during runtime + __slots__ = ( + 'flags', + 'name', + 'parent_name', + '_rotation', + '_position', + '_rotation_keyframes', + '_translation_keyframes', + '_joint_ex_object', # Ms3dJointEx + '_comment_object', # Ms3dComment + ) + """ + + def __init__( + self, + default_flags=Ms3dSpec.DEFAULT_FLAGS, + default_name="", + default_parent_name="", + default_rotation=(0.0, 0.0, 0.0), + default_position=(0.0, 0.0, 0.0), + default_rotation_keyframes=None, + default_translation_keyframes=None, + default_joint_ex_object=None, # Ms3dJointEx + default_comment_object=None, # Ms3dComment + ): + if (default_name is None): + default_name = "" + + if (default_parent_name is None): + default_parent_name = "" + + if (default_rotation_keyframes is None): + default_rotation_keyframes = [] #Ms3dRotationKeyframe() + + if (default_translation_keyframes is None): + default_translation_keyframes = [] #Ms3dTranslationKeyframe() + + self.flags = default_flags + self.name = default_name + self.parent_name = default_parent_name + self._rotation = default_rotation + self._position = default_position + self._rotation_keyframes = default_rotation_keyframes + self._translation_keyframes = default_translation_keyframes + + if default_comment_object is None: + default_comment_object = Ms3dCommentEx() + self._comment_object = default_comment_object # Ms3dComment + + if default_joint_ex_object is None: + default_joint_ex_object = Ms3dJointEx() + self._joint_ex_object = default_joint_ex_object # Ms3dJointEx + + def __repr__(self): + return "\n<flags={}, name='{}', parent_name='{}', rotation=({:.{p}f}, "\ + "{:.{p}f}, {:.{p}f}), position=({:.{p}f}, {:.{p}f}, {:.{p}f}), "\ + "number_rotation_keyframes={}, number_translation_keyframes={},"\ + " rotation_key_frames={}, translation_key_frames={}>".format( + self.flags, + self.name, + self.parent_name, + self.rotation[0], + self.rotation[1], + self.rotation[2], + self.position[0], + self.position[1], + self.position[2], + self.number_rotation_keyframes, + self.number_translation_keyframes, + self.rotation_key_frames, + self.translation_key_frames, + p=Ms3dIo.PRECISION + ) + + + @property + def rotation(self): + return self._rotation + + @property + def position(self): + return self._position + + @property + def number_rotation_keyframes(self): + if self.rotation_key_frames is None: + return 0 + return len(self.rotation_key_frames) + + @property + def number_translation_keyframes(self): + if self.translation_key_frames is None: + return 0 + return len(self.translation_key_frames) + + @property + def rotation_key_frames(self): + return self._rotation_keyframes + + @property + def translation_key_frames(self): + return self._translation_keyframes + + + @property + def joint_ex_object(self): + return self._joint_ex_object + + + @property + def comment_object(self): + return self._comment_object + + + def read(self, raw_io): + self.flags = Ms3dIo.read_byte(raw_io) + self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME) + self.parent_name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME) + self._rotation = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + self._position = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + _number_rotation_keyframes = Ms3dIo.read_word(raw_io) + _number_translation_keyframes = Ms3dIo.read_word(raw_io) + self._rotation_keyframes = [] + for i in range(_number_rotation_keyframes): + self.rotation_key_frames.append(Ms3dRotationKeyframe().read(raw_io)) + self._translation_keyframes = [] + for i in range(_number_translation_keyframes): + self.translation_key_frames.append( + Ms3dTranslationKeyframe().read(raw_io)) + return self + + def write(self, raw_io): + Ms3dIo.write_byte(raw_io, self.flags) + Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name) + Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.parent_name) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.rotation) + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.position) + Ms3dIo.write_word(raw_io, self.number_rotation_keyframes) + Ms3dIo.write_word(raw_io, self.number_translation_keyframes) + for i in range(self.number_rotation_keyframes): + self.rotation_key_frames[i].write(raw_io) + for i in range(self.number_translation_keyframes): + self.translation_key_frames[i].write(raw_io) + + +############################################################################### +class Ms3dCommentEx: + """ Ms3dCommentEx """ + __slots__ = ( + 'index', + 'comment', + ) + + def __init__( + self, + default_index=0, + default_comment="" + ): + if (default_comment is None): + default_comment = "" + + self.index = default_index + self.comment = default_comment + + def __repr__(self): + return "\n<index={}, comment_length={}, comment='{}'>".format( + self.index, + self.comment_length, + self.comment + ) + + + @property + def comment_length(self): + if self.comment is None: + return 0 + return len(self.comment) + + + def read(self, raw_io): + self.index = Ms3dIo.read_dword(raw_io) + _comment_length = Ms3dIo.read_dword(raw_io) + self.comment = Ms3dIo.read_string(raw_io, _comment_length) + return self + + def write(self, raw_io): + Ms3dIo.write_dword(raw_io, self.index) + Ms3dIo.write_dword(raw_io, self.comment_length) + Ms3dIo.write_string(raw_io, self.comment_length, self.comment) + + +############################################################################### +class Ms3dComment: + """ Ms3dComment """ + __slots__ = ( + 'comment', + ) + + def __init__( + self, + default_comment="" + ): + if (default_comment is None): + default_comment = "" + + self.comment = default_comment + + def __repr__(self): + return "\n<comment_length={}, comment='{}'>".format( + self.comment_length, + self.comment + ) + + + @property + def comment_length(self): + if self.comment is None: + return 0 + return len(self.comment) + + + def read(self, raw_io): + _comment_length = Ms3dIo.read_dword(raw_io) + self.comment = Ms3dIo.read_string(raw_io, _comment_length) + return self + + def write(self, raw_io): + Ms3dIo.write_dword(raw_io, self.comment_length) + Ms3dIo.write_string(raw_io, self.comment_length, self.comment) + + +############################################################################### +class Ms3dVertexEx1: + """ Ms3dVertexEx1 """ + __slots__ = ( + '_bone_ids', + '_weights', + ) + + def __init__( + self, + default_bone_ids=( + Ms3dSpec.DEFAULT_VERTEX_BONE_ID, + Ms3dSpec.DEFAULT_VERTEX_BONE_ID, + Ms3dSpec.DEFAULT_VERTEX_BONE_ID), + default_weights=(100, 0, 0) + ): + self._bone_ids = default_bone_ids + self._weights = default_weights + + def __repr__(self): + return "\n<bone_ids={}, weights={}>".format( + self.bone_ids, + self.weights + ) + + + @property + def bone_ids(self): + return self._bone_ids + + @property + def weights(self): + return self._weights + + + @property + def weight_bone_id(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights + return 100 + + @property + def weight_bone_id0(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights[0] + return 0 + + @property + def weight_bone_id1(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights[1] + return 0 + + @property + def weight_bone_id2(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return 100 - (self._weights[0] + self._weights[1] \ + + self._weights[2]) + return 0 + + + def read(self, raw_io): + self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3) + self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3) + return self + + def write(self, raw_io): + Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids) + Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights) + + +############################################################################### +class Ms3dVertexEx2: + """ Ms3dVertexEx2 """ + __slots__ = ( + 'extra', + '_bone_ids', + '_weights', + ) + + def __init__( + self, + default_bone_ids=( + Ms3dSpec.DEFAULT_VERTEX_BONE_ID, + Ms3dSpec.DEFAULT_VERTEX_BONE_ID, + Ms3dSpec.DEFAULT_VERTEX_BONE_ID), + default_weights=(100, 0, 0), + default_extra=0 + ): + self._bone_ids = default_bone_ids + self._weights = default_weights + self.extra = default_extra + + def __repr__(self): + return "\n<bone_ids={}, weights={}, extra={}>".format( + self.bone_ids, + self.weights, + self.extra + ) + + + @property + def bone_ids(self): + return self._bone_ids + + @property + def weights(self): + return self._weights + + + @property + def weight_bone_id(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights + return 100 + + @property + def weight_bone_id0(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights[0] + return 0 + + @property + def weight_bone_id1(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights[1] + return 0 + + @property + def weight_bone_id2(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return 100 - (self._weights[0] + self._weights[1] \ + + self._weights[2]) + return 0 + + + def read(self, raw_io): + self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3) + self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3) + self.extra = Ms3dIo.read_dword(raw_io) + return self + + def write(self, raw_io): + Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids) + Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights) + Ms3dIo.write_dword(raw_io, self.extra) + + +############################################################################### +class Ms3dVertexEx3: + """ Ms3dVertexEx3 """ + #char bone_ids[3]; // index of joint or -1, if -1, then that weight is + # ignored, since subVersion 1 + #byte weights[3]; // vertex weight ranging from 0 - 100, last weight is + # computed by 1.0 - sum(all weights), since subVersion 1 + #// weight[0] is the weight for bone_id in Ms3dVertex + #// weight[1] is the weight for bone_ids[0] + #// weight[2] is the weight for bone_ids[1] + #// 1.0f - weight[0] - weight[1] - weight[2] is the weight for bone_ids[2] + #unsigned int extra; // vertex extra, which can be used as color or + # anything else, since subVersion 2 + __slots__ = ( + 'extra', + '_bone_ids', + '_weights', + ) + + def __init__( + self, + default_bone_ids=( + Ms3dSpec.DEFAULT_VERTEX_BONE_ID, + Ms3dSpec.DEFAULT_VERTEX_BONE_ID, + Ms3dSpec.DEFAULT_VERTEX_BONE_ID), + default_weights=(100, 0, 0), + default_extra=0 + ): + self._bone_ids = default_bone_ids + self._weights = default_weights + self.extra = default_extra + + def __repr__(self): + return "\n<bone_ids={}, weights={}, extra={}>".format( + self.bone_ids, + self.weights, + self.extra + ) + + + @property + def bone_ids(self): + return self._bone_ids + + @property + def weights(self): + return self._weights + + + @property + def weight_bone_id(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights + return 100 + + @property + def weight_bone_id0(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights[0] + return 0 + + @property + def weight_bone_id1(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return self._weights[1] + return 0 + + @property + def weight_bone_id2(self): + if self._weights[0] or self._weights[1] or self._weights[2]: + return 100 - (self._weights[0] + self._weights[1] \ + + self._weights[2]) + return 0 + + + def read(self, raw_io): + self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3) + self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3) + self.extra = Ms3dIo.read_dword(raw_io) + return self + + def write(self, raw_io): + Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids) + Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights) + Ms3dIo.write_dword(raw_io, self.extra) + + +############################################################################### +class Ms3dJointEx: + """ Ms3dJointEx """ + __slots__ = ( + '_color', + ) + + def __init__( + self, + default_color=Ms3dSpec.DEFAULT_JOINT_COLOR + ): + self._color = default_color + + def __repr__(self): + return "\n<color=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format( + self.color[0], + self.color[1], + self.color[2], + p=Ms3dIo.PRECISION + ) + + + @property + def color(self): + return self._color + + + def read(self, raw_io): + self._color = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3) + return self + + def write(self, raw_io): + Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.color) + + +############################################################################### +class Ms3dModelEx: + """ Ms3dModelEx """ + __slots__ = ( + 'joint_size', + 'transparency_mode', + 'alpha_ref', + ) + + def __init__( + self, + default_joint_size=Ms3dSpec.DEFAULT_MODEL_JOINT_SIZE, + default_transparency_mode\ + =Ms3dSpec.DEFAULT_MODEL_TRANSPARENCY_MODE, + default_alpha_ref=0.0 + ): + self.joint_size = default_joint_size + self.transparency_mode = default_transparency_mode + self.alpha_ref = default_alpha_ref + + def __repr__(self): + return "\n<joint_size={:.{p}f}, transparency_mode={}, alpha_ref={:.{p}f}>".format( + self.joint_size, + self.transparency_mode, + self.alpha_ref, + p=Ms3dIo.PRECISION + ) + + def read(self, raw_io): + self.joint_size = Ms3dIo.read_float(raw_io) + self.transparency_mode = Ms3dIo.read_dword(raw_io) + self.alpha_ref = Ms3dIo.read_float(raw_io) + return self + + def write(self, raw_io): + Ms3dIo.write_float(raw_io, self.joint_size) + Ms3dIo.write_dword(raw_io, self.transparency_mode) + Ms3dIo.write_float(raw_io, self.alpha_ref) + + +############################################################################### +# +# file format +# +############################################################################### +class Ms3dModel: + """ Ms3dModel """ + __slot__ = ( + 'header', + 'animation_fps', + 'current_time', + 'number_total_frames', + 'sub_version_comments', + 'sub_version_vertex_extra', + 'sub_version_joint_extra', + 'sub_version_model_extra', + 'name', + '_vertices', + '_triangles', + '_groups', + '_materials', + '_joints', + '_has_model_comment', + '_comment_object', # Ms3dComment + '_model_ex_object', # Ms3dModelEx + ) + + def __init__( + self, + default_name="" + ): + if (default_name is None): + default_name = "" + + self.name = default_name + + self.animation_fps = Ms3dSpec.DEFAULT_MODEL_ANIMATION_FPS + self.current_time = 0.0 + self.number_total_frames = 0 + self.sub_version_comments \ + = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_COMMENTS + self.sub_version_vertex_extra \ + = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA + self.sub_version_joint_extra \ + = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_JOINT_EXTRA + self.sub_version_model_extra \ + = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_MODEL_EXTRA + + self._vertices = [] #Ms3dVertex() + self._triangles = [] #Ms3dTriangle() + self._groups = [] #Ms3dGroup() + self._materials = [] #Ms3dMaterial() + self._joints = [] #Ms3dJoint() + + self.header = Ms3dHeader() + self._model_ex_object = Ms3dModelEx() + self._comment_object = None #Ms3dComment() + + + @property + def number_vertices(self): + if self.vertices is None: + return 0 + return len(self.vertices) + + @property + def vertices(self): + return self._vertices + + + @property + def number_triangles(self): + if self.triangles is None: + return 0 + return len(self.triangles) + + @property + def triangles(self): + return self._triangles + + + @property + def number_groups(self): + if self.groups is None: + return 0 + return len(self.groups) + + @property + def groups(self): + return self._groups + + + @property + def number_materials(self): + if self.materials is None: + return 0 + return len(self.materials) + + @property + def materials(self): + return self._materials + + + @property + def number_joints(self): + if self.joints is None: + return 0 + return len(self.joints) + + @property + def joints(self): + return self._joints + + + @property + def number_group_comments(self): + if self.groups is None: + return 0 + number = 0 + for item in self.groups: + if item.comment_object is not None and item.comment_object.comment: + number += 1 + return number + + @property + def group_comments(self): + if self.groups is None: + return None + items = [] + for item in self.groups: + if item.comment_object is not None and item.comment_object.comment: + items.append(item) + return items + + + @property + def number_material_comments(self): + if self.materials is None: + return 0 + number = 0 + for item in self.materials: + if item.comment_object is not None and item.comment_object.comment: + number += 1 + return number + + @property + def material_comments(self): + if self.materials is None: + return None + items = [] + for item in self.materials: + if item.comment_object is not None and item.comment_object.comment: + items.append(item) + return items + + + @property + def number_joint_comments(self): + if self.joints is None: + return 0 + number = 0 + for item in self.joints: + if item.comment_object is not None and item.comment_object.comment: + number += 1 + return number + + @property + def joint_comments(self): + if self.joints is None: + return None + items = [] + for item in self.joints: + if item.comment_object is not None and item.comment_object.comment: + items.append(item) + return items + + + @property + def has_model_comment(self): + if self.comment_object is not None and self.comment_object.comment: + return 1 + return 0 + + @property + def comment_object(self): + return self._comment_object + + + @property + def vertex_ex(self): + if not self.sub_version_vertex_extra: + return None + return [item.vertex_ex_object for item in self.vertices] + + @property + def joint_ex(self): + if not self.sub_version_joint_extra: + return None + return [item.joint_ex_object for item in self.joints] + + @property + def model_ex_object(self): + if not self.sub_version_model_extra: + return None + return self._model_ex_object + + + def print_internal(self): + print() + print("##############################################################") + print("## the internal data of Ms3dModel object...") + print("##") + + print("header={}".format(self.header)) + + print("number_vertices={}".format(self.number_vertices)) + print("vertices=[", end="") + if self.vertices: + for obj in self.vertices: + print("{}".format(obj), end="") + print("]") + + print("number_triangles={}".format(self.number_triangles)) + print("triangles=[", end="") + if self.triangles: + for obj in self.triangles: + print("{}".format(obj), end="") + print("]") + + print("number_groups={}".format(self.number_groups)) + print("groups=[", end="") + if self.groups: + for obj in self.groups: + print("{}".format(obj), end="") + print("]") + + print("number_materials={}".format(self.number_materials)) + print("materials=[", end="") + if self.materials: + for obj in self.materials: + print("{}".format(obj), end="") + print("]") + + print("animation_fps={}".format(self.animation_fps)) + print("current_time={}".format(self.current_time)) + print("number_total_frames={}".format(self.number_total_frames)) + + print("number_joints={}".format(self.number_joints)) + print("joints=[", end="") + if self.joints: + for obj in self.joints: + print("{}".format(obj), end="") + print("]") + + print("sub_version_comments={}".format(self.sub_version_comments)) + + print("number_group_comments={}".format(self.number_group_comments)) + print("group_comments=[", end="") + if self.group_comments: + for obj in self.group_comments: + print("{}".format(obj.comment_object), end="") + print("]") + + print("number_material_comments={}".format( + self.number_material_comments)) + print("material_comments=[", end="") + if self.material_comments: + for obj in self.material_comments: + print("{}".format(obj.comment_object), end="") + print("]") + + print("number_joint_comments={}".format(self.number_joint_comments)) + print("joint_comments=[", end="") + if self.joint_comments: + for obj in self.joint_comments: + print("{}".format(obj.comment_object), end="") + print("]") + + print("has_model_comment={}".format(self.has_model_comment)) + print("model_comment={}".format(self.comment_object)) + + print("sub_version_vertex_extra={}".format( + self.sub_version_vertex_extra)) + print("vertex_ex=[", end="") + if self.vertex_ex: + for obj in self.vertex_ex: + print("{}".format(obj), end="") + print("]") + + print("sub_version_joint_extra={}".format( + self.sub_version_joint_extra)) + print("joint_ex=[", end="") + if self.joint_ex: + for obj in self.joint_ex: + print("{}".format(obj), end="") + print("]") + + print("sub_version_model_extra={}".format( + self.sub_version_model_extra)) + print("model_ex={}".format(self.model_ex_object)) + + print("##") + print("## ...end") + print("##############################################################") + print() + + + def read(self, raw_io): + """ + opens, reads and pars MS3D file. + add content to blender scene + """ + + self.header.read(raw_io) + if (self.header != Ms3dHeader()): + print("\nwarning, invalid file header") + + _number_vertices = Ms3dIo.read_word(raw_io) + if (_number_vertices > Ms3dSpec.MAX_VERTICES): + print("\nwarning, invalid count: number_vertices: {}".format( + _number_vertices)) + self._vertices = [] + for i in range(_number_vertices): + self.vertices.append(Ms3dVertex().read(raw_io)) + + _number_triangles = Ms3dIo.read_word(raw_io) + if (_number_triangles > Ms3dSpec.MAX_TRIANGLES): + print("\nwarning, invalid count: number_triangles: {}".format( + _number_triangles)) + self._triangles = [] + for i in range(_number_triangles): + self.triangles.append(Ms3dTriangle().read(raw_io)) + + _number_groups = Ms3dIo.read_word(raw_io) + if (_number_groups > Ms3dSpec.MAX_GROUPS): + print("\nwarning, invalid count: number_groups: {}".format( + _number_groups)) + self._groups = [] + for i in range(_number_groups): + self.groups.append(Ms3dGroup().read(raw_io)) + + _number_materials = Ms3dIo.read_word(raw_io) + if (_number_materials > Ms3dSpec.MAX_MATERIALS): + print("\nwarning, invalid count: number_materials: {}".format( + _number_materials)) + self._materials = [] + for i in range(_number_materials): + self.materials.append(Ms3dMaterial().read(raw_io)) + + self.animation_fps = Ms3dIo.read_float(raw_io) + self.current_time = Ms3dIo.read_float(raw_io) + self.number_total_frames = Ms3dIo.read_dword(raw_io) + + _progress = set() + + try: + # optional data + # doesn't matter if doesn't existing. + + _number_joints = Ms3dIo.read_word(raw_io) + _progress.add('NUMBER_JOINTS') + if (_number_joints > Ms3dSpec.MAX_JOINTS): + print("\nwarning, invalid count: number_joints: {}".format( + _number_joints)) + self._joints = [] + for i in range(_number_joints): + self.joints.append(Ms3dJoint().read(raw_io)) + _progress.add('JOINTS') + + self.sub_version_comments = Ms3dIo.read_dword(raw_io) + _progress.add('SUB_VERSION_COMMENTS') + _number_group_comments = Ms3dIo.read_dword(raw_io) + _progress.add('NUMBER_GROUP_COMMENTS') + if (_number_group_comments > Ms3dSpec.MAX_GROUPS): + print("\nwarning, invalid count:"\ + " number_group_comments: {}".format( + _number_group_comments)) + if _number_group_comments > _number_groups: + print("\nwarning, invalid count:"\ + " number_group_comments: {}, number_groups: {}".format( + _number_group_comments, _number_groups)) + for i in range(_number_group_comments): + item = Ms3dCommentEx().read(raw_io) + if item.index >= 0 and item.index < _number_groups: + self.groups[item.index]._comment_object = item + else: + print("\nwarning, invalid index:"\ + " group_index: {}, number_groups: {}".format( + item.index, _number_groups)) + _progress.add('GROUP_COMMENTS') + + _number_material_comments = Ms3dIo.read_dword(raw_io) + _progress.add('NUMBER_MATERIAL_COMMENTS') + if (_number_material_comments > Ms3dSpec.MAX_MATERIALS): + print("\nwarning, invalid count:"\ + " number_material_comments: {}".format( + _number_material_comments)) + if _number_material_comments > _number_materials: + print("\nwarning, invalid count:"\ + " number_material_comments:"\ + " {}, number_materials: {}".format( + _number_material_comments, _number_materials)) + for i in range(_number_material_comments): + item = Ms3dCommentEx().read(raw_io) + if item.index >= 0 and item.index < _number_materials: + self.materials[item.index]._comment_object = item + else: + print("\nwarning, invalid index:"\ + " material_index: {}, number_materials:"\ + " {}".format(item.index, _number_materials)) + _progress.add('MATERIAL_COMMENTS') + + _number_joint_comments = Ms3dIo.read_dword(raw_io) + _progress.add('NUMBER_JOINT_COMMENTS') + if (_number_joint_comments > Ms3dSpec.MAX_JOINTS): + print("\nwarning, invalid count:"\ + " number_joint_comments: {}".format( + _number_joint_comments)) + if _number_joint_comments > _number_joints: + print("\nwarning, invalid count:"\ + " number_joint_comments: {}, number_joints: {}".format( + _number_joint_comments, _number_joints)) + for i in range(_number_joint_comments): + item = Ms3dCommentEx().read(raw_io) + if item.index >= 0 and item.index < _number_joints: + self.joints[item.index]._comment_object = item + else: + print("\nwarning, invalid index:"\ + " joint_index: {}, number_joints: {}".format( + item.index, _number_joints)) + _progress.add('JOINT_COMMENTS') + + _has_model_comment = Ms3dIo.read_dword(raw_io) + _progress.add('HAS_MODEL_COMMENTS') + if (_has_model_comment != 0): + self._comment_object = Ms3dComment().read(raw_io) + else: + self._comment_object = None + _progress.add('MODEL_COMMENTS') + + self.sub_version_vertex_extra = Ms3dIo.read_dword(raw_io) + _progress.add('SUB_VERSION_VERTEX_EXTRA') + if self.sub_version_vertex_extra > 0: + length = len(self.joints) + for i in range(_number_vertices): + if self.sub_version_vertex_extra == 1: + item = Ms3dVertexEx1() + elif self.sub_version_vertex_extra == 2: + item = Ms3dVertexEx2() + elif self.sub_version_vertex_extra == 3: + item = Ms3dVertexEx3() + else: + print("\nwarning, invalid version:"\ + " sub_version_vertex_extra: {}".format( + sub_version_vertex_extra)) + continue + self.vertices[i]._vertex_ex_object = item.read(raw_io) + _progress.add('VERTEX_EXTRA') + + self.sub_version_joint_extra = Ms3dIo.read_dword(raw_io) + _progress.add('SUB_VERSION_JOINT_EXTRA') + if self.sub_version_joint_extra > 0: + for i in range(_number_joints): + self.joints[i]._joint_ex_object = Ms3dJointEx().read(raw_io) + _progress.add('JOINT_EXTRA') + + self.sub_version_model_extra = Ms3dIo.read_dword(raw_io) + _progress.add('SUB_VERSION_MODEL_EXTRA') + if self.sub_version_model_extra > 0: + self._model_ex_object.read(raw_io) + _progress.add('MODEL_EXTRA') + + except EOFError: + # reached end of optional data. + print("Ms3dModel.read - optional data read: {}".format(_progress)) + pass + + except Exception: + type, value, traceback = exc_info() + print("Ms3dModel.read - exception in optional try block," + " _progress={0}\n type: '{1}'\n value: '{2}'".format( + _progress, type, value, traceback)) + + else: + pass + + # try best to continue far as possible + if not 'JOINTS' in _progress: + _number_joints = 0 + self._joints = [] + + if not 'GROUP_COMMENTS' in _progress: + self.sub_version_comments = 0 + _number_group_comments = 0 + + if not 'MATERIAL_COMMENTS' in _progress: + _number_material_comments = 0 + + if not 'JOINT_COMMENTS' in _progress: + _number_joint_comments = 0 + + if not 'MODEL_COMMENTS' in _progress: + _has_model_comment = 0 + self._comment_object = None # Ms3dComment() + + if not 'VERTEX_EXTRA' in _progress: + self.sub_version_vertex_extra = 0 + + if not 'JOINT_EXTRA' in _progress: + self.sub_version_joint_extra = 0 + + if not 'MODEL_EXTRA' in _progress: + self.sub_version_model_extra = 0 + self._model_ex_object = Ms3dModelEx() + + return + + + def write(self, raw_io): + """ + add blender scene content to MS3D + creates, writes MS3D file. + """ + + self.header.write(raw_io) + + Ms3dIo.write_word(raw_io, self.number_vertices) + for i in range(self.number_vertices): + self.vertices[i].write(raw_io) + + Ms3dIo.write_word(raw_io, self.number_triangles) + for i in range(self.number_triangles): + self.triangles[i].write(raw_io) + + Ms3dIo.write_word(raw_io, self.number_groups) + for i in range(self.number_groups): + self.groups[i].write(raw_io) + + Ms3dIo.write_word(raw_io, self.number_materials) + for i in range(self.number_materials): + self.materials[i].write(raw_io) + + Ms3dIo.write_float(raw_io, self.animation_fps) + Ms3dIo.write_float(raw_io, self.current_time) + Ms3dIo.write_dword(raw_io, self.number_total_frames) + + try: + # optional part + # doesn't matter if it doesn't complete. + Ms3dIo.write_word(raw_io, self.number_joints) + for i in range(self.number_joints): + self.joints[i].write(raw_io) + + Ms3dIo.write_dword(raw_io, self.sub_version_comments) + + Ms3dIo.write_dword(raw_io, self.number_group_comments) + for i in range(self.number_group_comments): + self.group_comments[i].comment_object.write(raw_io) + + Ms3dIo.write_dword(raw_io, self.number_material_comments) + for i in range(self.number_material_comments): + self.material_comments[i].comment_object.write(raw_io) + + Ms3dIo.write_dword(raw_io, self.number_joint_comments) + for i in range(self.number_joint_comments): + self.joint_comments[i].comment_object.write(raw_io) + + Ms3dIo.write_dword(raw_io, self.has_model_comment) + if (self.has_model_comment != 0): + self.comment_object.write(raw_io) + + Ms3dIo.write_dword(raw_io, self.sub_version_vertex_extra) + if (self.sub_version_vertex_extra in {1, 2, 3}): + for i in range(self.number_vertices): + self.vertex_ex[i].write(raw_io) + + Ms3dIo.write_dword(raw_io, self.sub_version_joint_extra) + for i in range(self.number_joints): + self.joint_ex[i].write(raw_io) + + Ms3dIo.write_dword(raw_io, self.sub_version_model_extra) + self.model_ex_object.write(raw_io) + + except Exception: + type, value, traceback = exc_info() + print("Ms3dModel.write - exception in optional try block" + "\n type: '{0}'\n value: '{1}'".format( + type, value, traceback)) + pass + + else: + pass + + return + + + def is_valid(self): + valid = True + result = [] + + format1 = "\n number of {0}: {1}" + format2 = " limit exceeded! (limit is {0})" + + result.append("MS3D statistics:") + result.append(format1.format("vertices ........", + self.number_vertices)) + if (self.number_vertices > Ms3dSpec.MAX_VERTICES): + result.append(format2.format(Ms3dSpec.MAX_VERTICES)) + valid &= False + + result.append(format1.format("triangles .......", + self.number_triangles)) + if (self.number_triangles > Ms3dSpec.MAX_TRIANGLES): + result.append(format2.format(Ms3dSpec.MAX_TRIANGLES)) + valid &= False + + result.append(format1.format("groups ..........", + self.number_groups)) + if (self.number_groups > Ms3dSpec.MAX_GROUPS): + result.append(format2.format(Ms3dSpec.MAX_GROUPS)) + valid &= False + + result.append(format1.format("materials .......", + self.number_materials)) + if (self.number_materials > Ms3dSpec.MAX_MATERIALS): + result.append(format2.format(Ms3dSpec.MAX_MATERIALS)) + valid &= False + + result.append(format1.format("joints ..........", + self.number_joints)) + if (self.number_joints > Ms3dSpec.MAX_JOINTS): + result.append(format2.format(Ms3dSpec.MAX_JOINTS)) + valid &= False + + result.append(format1.format("model comments ..", + self.has_model_comment)) + result.append(format1.format("group comments ..", + self.number_group_comments)) + result.append(format1.format("material comments", + self.number_material_comments)) + result.append(format1.format("joint comments ..", + self.number_joint_comments)) + + #if (not valid): + # result.append("\n\nthe data may be corrupted.") + + return (valid, ("".join(result))) + + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- +# ##### END OF FILE ##### diff --git a/io_scene_ms3d/ms3d_strings.py b/io_scene_ms3d/ms3d_strings.py new file mode 100644 index 0000000000000000000000000000000000000000..84a5a5681e75f0da8179805eef8aa45f45952396 --- /dev/null +++ b/io_scene_ms3d/ms3d_strings.py @@ -0,0 +1,263 @@ +# ##### 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 ##### +SEE_MS3D_DOC = "see MilkShape 3D documentation" + +ms3d_str = { + 'lang': "en-US", + 'RUNTIME_KEY': "Human friendly presentation", + + ############################### + # blender key names + 'OBJECT_LAYER_EXTRA': "ms3d_extra_layer", + 'OBJECT_LAYER_GROUP': "ms3d_group_layer", + 'OBJECT_LAYER_SMOOTHING_GROUP': "ms3d_smoothing_group_layer", + 'OBJECT_MODIFIER_SMOOTHING_GROUP': "ms3d_smoothing_groups", + # for some reason after bm.to_mesh(..) + # the names of 'bm.loops.layers.uv' becomes to 'bm.faces.layers.tex' + # to bypass this issue, i give both the same name. + # 'OBJECT_LAYER_TEXTURE': "ms3d_texture_layer", + 'OBJECT_LAYER_TEXTURE': "ms3d_uv_layer", + 'OBJECT_LAYER_UV': "ms3d_uv_layer", + + ############################### + # strings to be used with 'str().format()' + 'STRING_FORMAT_GROUP': "Group.{:03d}", + 'WARNING_IMPORT_SKIP_FACE_DOUBLE': "skipped face #{}:"\ + " contains double faces with same vertices!", + 'WARNING_IMPORT_SKIP_LESS_VERTICES': "skipped face #{}:"\ + " contains faces too less vertices!", + 'WARNING_IMPORT_SKIP_VERTEX_DOUBLE': "skipped face #{}:"\ + " contains faces with double vertices!", + 'WARNING_IMPORT_EXTRA_VERTEX_NORMAL': "created extra vertex"\ + " because of different normals #{} -> {}.", + 'SUMMARY_IMPORT': "elapsed time: {0:.4}s (media io:"\ + " ~{1:.4}s, converter: ~{2:.4}s)", + 'SUMMARY_EXPORT': "elapsed time: {0:.4}s (converter:"\ + " ~{1:.4}s, media io: ~{2:.4}s)", + 'WARNING_EXPORT_SKIP_WEIGHT' : "skipped weight", + 'WARNING_EXPORT_SKIP_WEIGHT_EX' : "skipped weight:"\ + " limit exceeded", + + ############################### + 'TEXT_OPERATOR': "MilkShape 3D (.ms3d)", + 'FILE_EXT': ".ms3d", + 'FILE_FILTER': "*.ms3d", + 'BL_DESCRIPTION_EXPORTER': "Export to a MilkShape 3D file format (.ms3d)", + 'BL_DESCRIPTION_IMPORTER': "Import from a MilkShape 3D file format (.ms3d)", + 'BL_LABEL_EXPORTER': "Export MS3D", + 'BL_LABEL_GROUP_OPERATOR': "MS3D - Group Collection Operator", + 'BL_LABEL_IMPORTER': "Import MS3D", + 'BL_LABEL_PANEL_SMOOTHING_GROUP': "MS3D - Smoothing Group", + 'BL_LABEL_SMOOTHING_GROUP_OPERATOR': "MS3D Set Smoothing Group"\ + " Operator", + 'BL_LABEL_MATERIAL_OPERATOR' : "MS3D - Copy Material Operator", + 'ENUM_ADD_GROUP_1': "Add", + 'ENUM_ADD_GROUP_2': "adds an item", + 'ENUM_ASSIGN_1': "Assign", + 'ENUM_ASSIGN_2_GROUP': "assign selected faces to selected group", + 'ENUM_ASSIGN_2_SMOOTHING_GROUP': "assign all selected faces to"\ + " selected smoothing group", + 'ENUM_DESELECT_1': "Deselect", + 'ENUM_DESELECT_2_GROUP': "deselects faces of selected group", + 'ENUM_DESELECT_2_SMOOTHING_GROUP': "deselects all faces of selected"\ + " smoothing group", + 'ENUM_FLAG_DIRTY_1': "Dirty", + 'ENUM_FLAG_DIRTY_2': SEE_MS3D_DOC, + 'ENUM_FLAG_HIDDEN_1': "Hidden", + 'ENUM_FLAG_HIDDEN_2': SEE_MS3D_DOC, + 'ENUM_FLAG_ISKEY_1': "Is Key", + 'ENUM_FLAG_ISKEY_2': SEE_MS3D_DOC, + 'ENUM_FLAG_MARKED_1': "Marked", + 'ENUM_FLAG_MARKED_2': SEE_MS3D_DOC, + 'ENUM_FLAG_NEWLYCREATED_1': "Newly Created", + 'ENUM_FLAG_NEWLYCREATED_2': SEE_MS3D_DOC, + 'ENUM_FLAG_NONE_1': "None", + 'ENUM_FLAG_NONE_2': SEE_MS3D_DOC, + 'ENUM_FLAG_SELECTED_1': "Selected", + 'ENUM_FLAG_SELECTED_2': SEE_MS3D_DOC, + 'ENUM_FLAG_SELECTED2_1': "Selected Ex.", + 'ENUM_FLAG_SELECTED2_2': SEE_MS3D_DOC, + 'ENUM_REMOVE_1': "Remove", + 'ENUM_REMOVE_2_GROUP': "remove selected faces from selected group", + 'ENUM_REMOVE_GROUP_1': "Remove", + 'ENUM_REMOVE_GROUP_2': "removes an item", + 'ENUM_SELECT_1': "Select", + 'ENUM_SELECT_2_GROUP': "selects faces of selected group", + 'ENUM_SELECT_2_SMOOTHING_GROUP': "selects all faces of selected"\ + " smoothing group", + 'LABEL_NAME_ANIMATION': "Animation Processing:", + 'LABEL_NAME_OBJECT': "World Processing:", + 'LABEL_NAME_OPTIONS': "Advanced Options:", + 'LABEL_NAME_PROCESSING': "Object Processing:", + 'LABEL_NAME_MODIFIER': "Modifier Processing:", + 'LABEL_PANEL_BUTTON_NONE': "None", + 'LABEL_PANEL_GROUPS': "MS3D - Groups", + 'LABEL_PANEL_JOINTS': "MS3D - Joint", + 'LABEL_PANEL_MATERIALS': "MS3D - Material", + 'LABEL_PANEL_MODEL': "MS3D - Model", + 'PROP_DESC_ALPHA_REF': "ms3d internal raw 'alpha_ref' of Model", + 'PROP_DESC_ALPHAMAP': "ms3d internal raw 'alphamap' file name of"\ + " Material", + 'PROP_DESC_AMBIENT': "ms3d internal raw 'ambient' of Material", + 'PROP_DESC_USE_ANIMATION': "keyframes (rotations, positions)", + 'PROP_DESC_COLOR_JOINT': "ms3d internal raw 'color' of Joint", + 'PROP_DESC_COMMENT_GROUP': "ms3d internal raw 'comment' of Group", + 'PROP_DESC_COMMENT_JOINT': "ms3d internal raw 'comment' of Joint", + 'PROP_DESC_COMMENT_MATERIAL': "ms3d internal raw 'comment' of Material", + 'PROP_DESC_COMMENT_MODEL': "ms3d internal raw 'comment' of Model", + 'PROP_DESC_DIFFUSE': "ms3d internal raw 'diffuse' of Material", + 'PROP_DESC_EMISSIVE': "ms3d internal raw 'emissive' of Material", + 'PROP_DESC_FLAGS_GROUP': "ms3d internal raw 'flags' of Group", + 'PROP_DESC_FLAGS_JOINT': "ms3d internal raw 'flags' of Joint", + 'PROP_DESC_GROUP_NAME': "ms3d internal raw 'name' of Group", + 'PROP_DESC_JOINT_SIZE': "ms3d internal raw 'joint_size' of Model", + 'PROP_DESC_MODE_TEXTURE': "ms3d internal raw 'mode' of Material", + 'PROP_DESC_NAME_ARMATURE': "ms3d internal raw 'name' of Model (not used"\ + " for export)", + 'PROP_DESC_NAME_JOINT': "ms3d internal raw 'name' of Joint", + 'PROP_DESC_NAME_MATERIAL': "ms3d internal raw 'name' of Material", + 'PROP_DESC_NAME_MODEL': "ms3d internal raw 'name' of Model (not used for export)", + 'PROP_DESC_SHININESS': "ms3d internal raw 'shininess' of Material", + 'PROP_DESC_SPECULAR': "ms3d internal raw 'specular' of Material", + 'PROP_DESC_TEXTURE': "ms3d internal raw 'texture' file name of"\ + " Material", + 'PROP_DESC_TRANSPARENCY': "ms3d internal raw 'transparency' of"\ + " Material", + 'PROP_DESC_TRANSPARENCY_MODE': "ms3d internal raw 'transparency_mode'"\ + " of Model", + 'PROP_DESC_VERBOSE': "Run the converter in debug mode."\ + " Check the console for output (Warning, may be very slow)", + 'PROP_FLAG_TEXTURE_COMBINE_ALPHA_1': "Combine Alpha", + 'PROP_FLAG_TEXTURE_COMBINE_ALPHA_2': SEE_MS3D_DOC, + 'PROP_FLAG_TEXTURE_HAS_ALPHA_1': "Has Alpha", + 'PROP_FLAG_TEXTURE_HAS_ALPHA_2': SEE_MS3D_DOC, + 'PROP_FLAG_TEXTURE_SPHERE_MAP_1': "Sphere Map", + 'PROP_FLAG_TEXTURE_SPHERE_MAP_2': SEE_MS3D_DOC, + 'PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_1': "Depth"\ + " Buffered with Alpha Ref", + 'PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_2': SEE_MS3D_DOC, + 'PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_1': "Depth Sorted"\ + " Triangles", + 'PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_2': SEE_MS3D_DOC, + 'PROP_MODE_TRANSPARENCY_SIMPLE_1': "Simple", + 'PROP_MODE_TRANSPARENCY_SIMPLE_2': SEE_MS3D_DOC, + 'PROP_NAME_ALPHA_REF': "Alpha Ref.", + 'PROP_NAME_ALPHAMAP': "Alphamap", + 'PROP_NAME_AMBIENT': "Ambient", + 'PROP_NAME_USE_ANIMATION': "Animation", + 'PROP_NAME_COLOR': "Color", + 'PROP_NAME_COMMENT': "Comment", + 'PROP_NAME_DIFFUSE': "Diffuse", + 'PROP_NAME_EMISSIVE': "Emissive", + 'PROP_NAME_FLAGS': "Flags", + 'PROP_NAME_JOINT_SIZE': "Joint Size", + 'PROP_NAME_MODE': "Mode", + 'PROP_NAME_NAME': "Name", + 'PROP_NAME_ACTIVE': "Active Mesh:", + 'PROP_NAME_SHININESS': "Shininess", + 'PROP_NAME_SPECULAR': "Specular", + 'PROP_NAME_TEXTURE': "Texture", + 'PROP_NAME_TRANSPARENCY': "Transparency", + 'PROP_NAME_TRANSPARENCY_MODE': "Transp. Mode", + 'PROP_NAME_VERBOSE': "Verbose", + 'PROP_SMOOTHING_GROUP_INDEX': "Smoothing group id", + 'PROP_NAME_ROTATION_MODE' : "Bone Rotation Mode", + 'PROP_DESC_ROTATION_MODE' : "set the preferred rotation mode of bones", + 'PROP_ITEM_ROTATION_MODE_EULER_1' : "Euler", + 'PROP_ITEM_ROTATION_MODE_EULER_2' : "use euler bone rotation"\ + " (gimbal-lock can be fixed by using "\ + "'Graph Editor -> Key -> Discontinuity (Euler) Filter')", + 'PROP_ITEM_ROTATION_MODE_QUATERNION_1' : "Quaternion", + 'PROP_ITEM_ROTATION_MODE_QUATERNION_2' : "use quaternion bone rotation"\ + " (no gimbal-lock filter available!)", + 'PROP_NAME_USE_JOINT_SIZE': "Override Joint Size", + 'PROP_DESC_USE_JOINT_SIZE': "use value of 'Joint Size', the value of the"\ + " ms3d file is ignored for representation.", + 'PROP_NAME_IMPORT_JOINT_SIZE': "Joint Size", + 'PROP_DESC_IMPORT_JOINT_SIZE': "size of the joint representation in"\ + " blender", + 'BL_LABEL_SET_SCENE_TO_METRIC' : "Set Scene to 'Metric' [1 mm]", + 'BL_DESC_SET_SCENE_TO_METRIC' : "set Scene | Units to Metric"\ + " (1 Unit = 1 mm),"\ + " Display | Textured Solid,"\ + " View | Clip (0.001 mm ... 1 km)", + 'PROP_NAME_NORMALIZE_WEIGHTS' : "Normalize Weights", + 'PROP_DESC_NORMALIZE_WEIGHTS' : "normalize weights to 100%,"\ + " when its sum of weights is greater than 100%", + 'PROP_NAME_SHRINK_TO_KEYS' : "Shrink To Keys", + 'PROP_DESC_SHRINK_TO_KEYS' : "shrinks the animation to region from"\ + " first keyframe to last keyframe", + 'PROP_NAME_BAKE_EACH_FRAME' : "Bake Each Frame As Key", + 'PROP_DESC_BAKE_EACH_FRAME' : "if enabled, to each frame there will be"\ + " a key baked", + 'LABEL_NAME_JOINT_TO_BONES' : "works only with some models!", + 'PROP_NAME_JOINT_TO_BONES' : "Joints To Bones", + 'PROP_DESC_JOINT_TO_BONES' : "changes the length of the bones", + 'PROP_NAME_USE_BLENDER_NAMES' : "Use Blender Names Only", + 'PROP_DESC_USE_BLENDER_NAMES' : "use only blender names, ignores ms3d"\ + " names (bone names will always be taken from blender)", + 'PROP_NAME_USE_BLENDER_MATERIALS' : "Use Blender Materials", + 'PROP_DESC_USE_BLENDER_MATERIALS' : "ignores ms3d material definition"\ + " (you loose some information by choosing this option)", + 'ENUM_FROM_BLENDER_1' : "Copy From Blender", + 'ENUM_FROM_BLENDER_2' : "takes and copies all available values from"\ + " blender", + 'ENUM_TO_BLENDER_1' : "Copy To Blender", + 'ENUM_TO_BLENDER_2' : "copies and puts all available values to blender", + 'PROP_NAME_EXTENDED_NORMAL_HANDLING': "Extended Normal Handling", + 'PROP_DESC_EXTENDED_NORMAL_HANDLING': "adds extra vertices if normals"\ + " are different", + 'PROP_NAME_APPLY_TRANSFORM': "Apply Transform", + 'PROP_DESC_APPLY_TRANSFORM': "applies location, rotation and scale on"\ + " export", + 'PROP_NAME_APPLY_MODIFIERS': "Apply Modifiers", + 'PROP_DESC_APPLY_MODIFIERS': "applies modifiers on export that are"\ + " enabled (except of armature modifiers)", + 'PROP_NAME_APPLY_MODIFIERS_MODE': "Apply Mode", + 'PROP_DESC_APPLY_MODIFIERS_MODE': "apply modifier, if enabled in its"\ + " mode", + 'PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_1': "View", + 'PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_2': "apply modifiers that are"\ + " enabled in viewport", + 'PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_1': "Render", + 'PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_2': "apply modifiers that are"\ + " enabled in renderer", + + 'PROP_NAME_': "Name", + 'PROP_DESC_': "Description", + # ms3d_str[''] + } + + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- +# ##### END OF FILE ##### diff --git a/io_scene_ms3d/ms3d_ui.py b/io_scene_ms3d/ms3d_ui.py new file mode 100644 index 0000000000000000000000000000000000000000..b3e52b1e8aa07153a198f7acd58ba176b69bbc3a --- /dev/null +++ b/io_scene_ms3d/ms3d_ui.py @@ -0,0 +1,1723 @@ +# ##### 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 +from random import ( + randrange, + ) + + +# import io_scene_ms3d stuff +from io_scene_ms3d.ms3d_strings import ( + ms3d_str, + ) +from io_scene_ms3d.ms3d_spec import ( + Ms3dSpec, + ) +from io_scene_ms3d.ms3d_utils import ( + enable_edit_mode, + get_edge_split_modifier_add_if, + set_sence_to_metric, + ) + + +#import blender stuff +from bmesh import ( + from_edit_mesh, + ) +from bpy.utils import ( + register_class, + unregister_class, + ) +from bpy_extras.io_utils import ( + ExportHelper, + ImportHelper, + ) +from bpy.props import ( + BoolProperty, + CollectionProperty, + EnumProperty, + FloatProperty, + FloatVectorProperty, + IntProperty, + StringProperty, + PointerProperty, + ) +from bpy.types import ( + Operator, + PropertyGroup, + Panel, + Armature, + Bone, + Mesh, + Material, + Action, + Group, + UIList, + ) +from bpy.app import ( + debug, + ) + + +class Ms3dUi: + DEFAULT_VERBOSE = debug + + ########################################################################### + FLAG_TEXTURE_COMBINE_ALPHA = 'COMBINE_ALPHA' + FLAG_TEXTURE_HAS_ALPHA = 'HAS_ALPHA' + FLAG_TEXTURE_SPHERE_MAP = 'SPHERE_MAP' + + @staticmethod + def texture_mode_from_ms3d(ms3d_value): + ui_value = set() + if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA) \ + == Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA: + ui_value.add(Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA) + if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA) \ + == Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA: + ui_value.add(Ms3dUi.FLAG_TEXTURE_HAS_ALPHA) + if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP) \ + == Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP: + ui_value.add(Ms3dUi.FLAG_TEXTURE_SPHERE_MAP) + return ui_value + + @staticmethod + def texture_mode_to_ms3d(ui_value): + ms3d_value = Ms3dSpec.FLAG_TEXTURE_NONE + + if Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA in ui_value: + ms3d_value |= Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA + if Ms3dUi.FLAG_TEXTURE_HAS_ALPHA in ui_value: + ms3d_value |= Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA + if Ms3dUi.FLAG_TEXTURE_SPHERE_MAP in ui_value: + ms3d_value |= Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP + return ms3d_value + + + MODE_TRANSPARENCY_SIMPLE = 'SIMPLE' + MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF \ + = 'DEPTH_BUFFERED_WITH_ALPHA_REF' + MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES = 'DEPTH_SORTED_TRIANGLES' + + @staticmethod + def transparency_mode_from_ms3d(ms3d_value): + if(ms3d_value == Ms3dSpec.MODE_TRANSPARENCY_SIMPLE): + return Ms3dUi.MODE_TRANSPARENCY_SIMPLE + elif(ms3d_value == \ + Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF): + return Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF + elif(ms3d_value == Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES): + return Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES + return None + + @staticmethod + def transparency_mode_to_ms3d(ui_value): + if(ui_value == Ms3dUi.MODE_TRANSPARENCY_SIMPLE): + return Ms3dSpec.MODE_TRANSPARENCY_SIMPLE + elif(ui_value == Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF): + return Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF + elif(ui_value == Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES): + return Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES + return None + + + FLAG_NONE = 'NONE' + FLAG_SELECTED = 'SELECTED' + FLAG_HIDDEN = 'HIDDEN' + FLAG_SELECTED2 = 'SELECTED2' + FLAG_DIRTY = 'DIRTY' + FLAG_ISKEY = 'ISKEY' + FLAG_NEWLYCREATED = 'NEWLYCREATED' + FLAG_MARKED = 'MARKED' + + @staticmethod + def flags_from_ms3d(ms3d_value): + ui_value = set() + if (ms3d_value & Ms3dSpec.FLAG_SELECTED) == Ms3dSpec.FLAG_SELECTED: + ui_value.add(Ms3dUi.FLAG_SELECTED) + if (ms3d_value & Ms3dSpec.FLAG_HIDDEN) == Ms3dSpec.FLAG_HIDDEN: + ui_value.add(Ms3dUi.FLAG_HIDDEN) + if (ms3d_value & Ms3dSpec.FLAG_SELECTED2) == Ms3dSpec.FLAG_SELECTED2: + ui_value.add(Ms3dUi.FLAG_SELECTED2) + if (ms3d_value & Ms3dSpec.FLAG_DIRTY) == Ms3dSpec.FLAG_DIRTY: + ui_value.add(Ms3dUi.FLAG_DIRTY) + if (ms3d_value & Ms3dSpec.FLAG_ISKEY) == Ms3dSpec.FLAG_ISKEY: + ui_value.add(Ms3dUi.FLAG_ISKEY) + if (ms3d_value & Ms3dSpec.FLAG_NEWLYCREATED) == \ + Ms3dSpec.FLAG_NEWLYCREATED: + ui_value.add(Ms3dUi.FLAG_NEWLYCREATED) + if (ms3d_value & Ms3dSpec.FLAG_MARKED) == Ms3dSpec.FLAG_MARKED: + ui_value.add(Ms3dUi.FLAG_MARKED) + return ui_value + + @staticmethod + def flags_to_ms3d(ui_value): + ms3d_value = Ms3dSpec.FLAG_NONE + if Ms3dUi.FLAG_SELECTED in ui_value: + ms3d_value |= Ms3dSpec.FLAG_SELECTED + if Ms3dUi.FLAG_HIDDEN in ui_value: + ms3d_value |= Ms3dSpec.FLAG_HIDDEN + if Ms3dUi.FLAG_SELECTED2 in ui_value: + ms3d_value |= Ms3dSpec.FLAG_SELECTED2 + if Ms3dUi.FLAG_DIRTY in ui_value: + ms3d_value |= Ms3dSpec.FLAG_DIRTY + if Ms3dUi.FLAG_ISKEY in ui_value: + ms3d_value |= Ms3dSpec.FLAG_ISKEY + if Ms3dUi.FLAG_NEWLYCREATED in ui_value: + ms3d_value |= Ms3dSpec.FLAG_NEWLYCREATED + if Ms3dUi.FLAG_MARKED in ui_value: + ms3d_value |= Ms3dSpec.FLAG_MARKED + return ms3d_value + + ########################################################################### + ICON_OPTIONS = 'LAMP' + ICON_OBJECT = 'WORLD' + ICON_PROCESSING = 'OBJECT_DATAMODE' + ICON_MODIFIER = 'MODIFIER' + ICON_ANIMATION = 'RENDER_ANIMATION' + ICON_ROTATION_MODE = 'BONE_DATA' + ICON_ERROR = 'ERROR' + + ########################################################################### + PROP_DEFAULT_VERBOSE = DEFAULT_VERBOSE + + ########################################################################### + PROP_DEFAULT_USE_JOINT_SIZE = False + PROP_DEFAULT_JOINT_SIZE = 0.01 + PROP_JOINT_SIZE_MIN = 0.01 + PROP_JOINT_SIZE_MAX = 10.0 + PROP_JOINT_SIZE_STEP = 0.1 + PROP_JOINT_SIZE_PRECISION = 2 + + ########################################################################### + PROP_DEFAULT_USE_ANIMATION = True + PROP_DEFAULT_NORMALIZE_WEIGHTS = True + PROP_DEFAULT_SHRINK_TO_KEYS = False + PROP_DEFAULT_BAKE_EACH_FRAME = True + PROP_DEFAULT_JOINT_TO_BONES = False + PROP_DEFAULT_USE_BLENDER_NAMES = True + PROP_DEFAULT_USE_BLENDER_MATERIALS = False + PROP_DEFAULT_EXTENDED_NORMAL_HANDLING = False + PROP_DEFAULT_APPLY_TRANSFORM = True + PROP_DEFAULT_APPLY_MODIFIERS = True + + ########################################################################### + PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW = 'PREVIEW' + PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER = 'RENDER' + PROP_DEFAULT_APPLY_MODIFIERS_MODE = PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW + + ########################################################################### + PROP_ITEM_ROTATION_MODE_EULER = 'EULER' + PROP_ITEM_ROTATION_MODE_QUATERNION = 'QUATERNION' + PROP_DEFAULT_ANIMATION_ROTATION = PROP_ITEM_ROTATION_MODE_EULER + + ########################################################################### + OPT_SMOOTHING_GROUP_APPLY = 'io_scene_ms3d.apply_smoothing_group' + OPT_GROUP_APPLY = 'io_scene_ms3d.apply_group' + OPT_MATERIAL_APPLY = 'io_scene_ms3d.apply_material' + + +############################################################################### +class Ms3dImportOperator(Operator, ImportHelper): + """ Load a MilkShape3D MS3D File """ + bl_idname = 'import_scene.ms3d' + bl_label = ms3d_str['BL_LABEL_IMPORTER'] + bl_description = ms3d_str['BL_DESCRIPTION_IMPORTER'] + bl_options = {'PRESET', } + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + + filepath = StringProperty( + subtype='FILE_PATH', + options={'HIDDEN', } + ) + + verbose = BoolProperty( + name=ms3d_str['PROP_NAME_VERBOSE'], + description=ms3d_str['PROP_DESC_VERBOSE'], + default=Ms3dUi.PROP_DEFAULT_VERBOSE, + ) + + use_animation = BoolProperty( + name=ms3d_str['PROP_NAME_USE_ANIMATION'], + description=ms3d_str['PROP_DESC_USE_ANIMATION'], + default=Ms3dUi.PROP_DEFAULT_USE_ANIMATION, + ) + + rotation_mode = EnumProperty( + name=ms3d_str['PROP_NAME_ROTATION_MODE'], + description=ms3d_str['PROP_DESC_ROTATION_MODE'], + items=( (Ms3dUi.PROP_ITEM_ROTATION_MODE_EULER, + ms3d_str['PROP_ITEM_ROTATION_MODE_EULER_1'], + ms3d_str['PROP_ITEM_ROTATION_MODE_EULER_2']), + (Ms3dUi.PROP_ITEM_ROTATION_MODE_QUATERNION, + ms3d_str['PROP_ITEM_ROTATION_MODE_QUATERNION_1'], + ms3d_str['PROP_ITEM_ROTATION_MODE_QUATERNION_2']), + ), + default=Ms3dUi.PROP_DEFAULT_ANIMATION_ROTATION, + ) + + use_joint_size = BoolProperty( + name=ms3d_str['PROP_NAME_USE_JOINT_SIZE'], + description=ms3d_str['PROP_DESC_USE_JOINT_SIZE'], + default=Ms3dUi.PROP_DEFAULT_USE_JOINT_SIZE, + ) + + joint_size = FloatProperty( + name=ms3d_str['PROP_NAME_IMPORT_JOINT_SIZE'], + description=ms3d_str['PROP_DESC_IMPORT_JOINT_SIZE'], + min=Ms3dUi.PROP_JOINT_SIZE_MIN, max=Ms3dUi.PROP_JOINT_SIZE_MAX, + precision=Ms3dUi.PROP_JOINT_SIZE_PRECISION, \ + step=Ms3dUi.PROP_JOINT_SIZE_STEP, + default=Ms3dUi.PROP_DEFAULT_JOINT_SIZE, + subtype='FACTOR', + #options={'HIDDEN', }, + ) + + use_joint_to_bones = BoolProperty( + name=ms3d_str['PROP_NAME_JOINT_TO_BONES'], + description=ms3d_str['PROP_DESC_JOINT_TO_BONES'], + default=Ms3dUi.PROP_DEFAULT_JOINT_TO_BONES, + ) + + use_extended_normal_handling = BoolProperty( + name=ms3d_str['PROP_NAME_EXTENDED_NORMAL_HANDLING'], + description=ms3d_str['PROP_DESC_EXTENDED_NORMAL_HANDLING'], + default=Ms3dUi.PROP_DEFAULT_EXTENDED_NORMAL_HANDLING, + ) + + filename_ext = StringProperty( + default=ms3d_str['FILE_EXT'], + options={'HIDDEN', } + ) + + filter_glob = StringProperty( + default=ms3d_str['FILE_FILTER'], + options={'HIDDEN', } + ) + + + @property + def use_euler_rotation(self): + return (Ms3dUi.PROP_ITEM_ROTATION_MODE_EULER \ + in self.rotation_mode) + + @property + def use_quaternion_rotation(self): + return (Ms3dUi.PROP_ITEM_ROTATION_MODE_QUATERNION \ + in self.rotation_mode) + + + # draw the option panel + def draw(self, blender_context): + layout = self.layout + + box = layout.box() + box.label(ms3d_str['LABEL_NAME_OPTIONS'], icon=Ms3dUi.ICON_OPTIONS) + box.prop(self, 'verbose', icon='SPEAKER') + + box = layout.box() + box.label(ms3d_str['LABEL_NAME_PROCESSING'], + icon=Ms3dUi.ICON_PROCESSING) + box.prop(self, 'use_extended_normal_handling') + + box = layout.box() + box.label(ms3d_str['LABEL_NAME_ANIMATION'], icon=Ms3dUi.ICON_ANIMATION) + box.prop(self, 'use_animation') + if (self.use_animation): + box.prop(self, 'rotation_mode', icon=Ms3dUi.ICON_ROTATION_MODE, + expand=False) + box.prop(self, 'use_joint_size') + if (self.use_joint_size): + col = box.column() + row = col.row() + row.prop(self, 'joint_size') + box.prop(self, 'use_joint_to_bones') + if (self.use_joint_to_bones): + box.box().label(ms3d_str['LABEL_NAME_JOINT_TO_BONES'], + icon=Ms3dUi.ICON_ERROR) + + # entrypoint for MS3D -> blender + def execute(self, blender_context): + """ start executing """ + from io_scene_ms3d.ms3d_import import (Ms3dImporter, ) + return Ms3dImporter( + report=self.report, + verbose=self.verbose, + use_extended_normal_handling=self.use_extended_normal_handling, + use_animation=self.use_animation, + use_quaternion_rotation=self.use_quaternion_rotation, + use_joint_size=self.use_joint_size, + joint_size=self.joint_size, + use_joint_to_bones=self.use_joint_to_bones, + ).read( + blender_context, + self.filepath + ) + + def invoke(self, blender_context, event): + blender_context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL', } + + @staticmethod + def menu_func(cls, blender_context): + cls.layout.operator( + Ms3dImportOperator.bl_idname, + text=ms3d_str['TEXT_OPERATOR'], + ) + + +class Ms3dExportOperator(Operator, ExportHelper): + """Save a MilkShape3D MS3D File""" + bl_idname = 'export_scene.ms3d' + bl_label = ms3d_str['BL_LABEL_EXPORTER'] + bl_description = ms3d_str['BL_DESCRIPTION_EXPORTER'] + bl_options = {'PRESET', } + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + + filepath = StringProperty( + subtype='FILE_PATH', + options={'HIDDEN', } + ) + + verbose = BoolProperty( + name=ms3d_str['PROP_NAME_VERBOSE'], + description=ms3d_str['PROP_DESC_VERBOSE'], + default=Ms3dUi.PROP_DEFAULT_VERBOSE, + ) + + use_blender_names = BoolProperty( + name=ms3d_str['PROP_NAME_USE_BLENDER_NAMES'], + description=ms3d_str['PROP_DESC_USE_BLENDER_NAMES'], + default=Ms3dUi.PROP_DEFAULT_USE_BLENDER_NAMES, + ) + + use_blender_materials = BoolProperty( + name=ms3d_str['PROP_NAME_USE_BLENDER_MATERIALS'], + description=ms3d_str['PROP_DESC_USE_BLENDER_MATERIALS'], + default=Ms3dUi.PROP_DEFAULT_USE_BLENDER_MATERIALS, + ) + + apply_transform = BoolProperty( + name=ms3d_str['PROP_NAME_APPLY_TRANSFORM'], + description=ms3d_str['PROP_DESC_APPLY_TRANSFORM'], + default=Ms3dUi.PROP_DEFAULT_APPLY_TRANSFORM, + ) + + apply_modifiers = BoolProperty( + name=ms3d_str['PROP_NAME_APPLY_MODIFIERS'], + description=ms3d_str['PROP_DESC_APPLY_MODIFIERS'], + default=Ms3dUi.PROP_DEFAULT_APPLY_MODIFIERS, + ) + + apply_modifiers_mode = EnumProperty( + name=ms3d_str['PROP_NAME_APPLY_MODIFIERS_MODE'], + description=ms3d_str['PROP_DESC_APPLY_MODIFIERS_MODE'], + items=( (Ms3dUi.PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW, + ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_1'], + ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_2']), + (Ms3dUi.PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER, + ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_1'], + ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_2']), + ), + default=Ms3dUi.PROP_DEFAULT_APPLY_MODIFIERS_MODE, + ) + + use_animation = BoolProperty( + name=ms3d_str['PROP_NAME_USE_ANIMATION'], + description=ms3d_str['PROP_DESC_USE_ANIMATION'], + default=Ms3dUi.PROP_DEFAULT_USE_ANIMATION, + ) + + normalize_weights = BoolProperty( + name=ms3d_str['PROP_NAME_NORMALIZE_WEIGHTS'], + description=ms3d_str['PROP_DESC_NORMALIZE_WEIGHTS'], + default=Ms3dUi.PROP_DEFAULT_NORMALIZE_WEIGHTS, + ) + + shrink_to_keys = BoolProperty( + name=ms3d_str['PROP_NAME_SHRINK_TO_KEYS'], + description=ms3d_str['PROP_DESC_SHRINK_TO_KEYS'], + default=Ms3dUi.PROP_DEFAULT_SHRINK_TO_KEYS, + ) + + bake_each_frame = BoolProperty( + name=ms3d_str['PROP_NAME_BAKE_EACH_FRAME'], + description=ms3d_str['PROP_DESC_BAKE_EACH_FRAME'], + default=Ms3dUi.PROP_DEFAULT_BAKE_EACH_FRAME, + ) + + check_existing = BoolProperty( + default=False, + options={'HIDDEN', } + ) + + filename_ext = StringProperty( + default=ms3d_str['FILE_EXT'], + options={'HIDDEN', } + ) + + filter_glob = StringProperty( + default=ms3d_str['FILE_FILTER'], + options={'HIDDEN', } + ) + + + ##EXPORT_ACTIVE_ONLY: + ##limit availability to only active mesh object + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.active_object + and blender_context.active_object.type in {'MESH', } + and blender_context.active_object.data + and blender_context.active_object.data.ms3d is not None + ) + + # draw the option panel + def draw(self, blender_context): + layout = self.layout + + box = layout.box() + box.label(ms3d_str['LABEL_NAME_OPTIONS'], icon=Ms3dUi.ICON_OPTIONS) + box.prop(self, 'verbose', icon='SPEAKER') + + box = layout.box() + box.label(ms3d_str['LABEL_NAME_PROCESSING'], + icon=Ms3dUi.ICON_PROCESSING) + row = box.row() + row.label(ms3d_str['PROP_NAME_ACTIVE'], icon='ROTACTIVE') + row.label(blender_context.active_object.name) + #box.prop(self, 'use_blender_names', icon='LINK_BLEND') + box.prop(self, 'use_blender_names') + box.prop(self, 'use_blender_materials') + + box = layout.box() + box.label(ms3d_str['LABEL_NAME_MODIFIER'], + icon=Ms3dUi.ICON_MODIFIER) + box.prop(self, 'apply_transform') + row = box.row() + row.prop(self, 'apply_modifiers') + sub = row.row() + sub.active = self.apply_modifiers + sub.prop(self, 'apply_modifiers_mode', text="") + + box = layout.box() + box.label(ms3d_str['LABEL_NAME_ANIMATION'], + icon=Ms3dUi.ICON_ANIMATION) + box.prop(self, 'use_animation') + if (self.use_animation): + box.prop(self, 'normalize_weights') + box.prop(self, 'shrink_to_keys') + box.prop(self, 'bake_each_frame') + + # entrypoint for blender -> MS3D + def execute(self, blender_context): + """start executing""" + from io_scene_ms3d.ms3d_export import (Ms3dExporter, ) + return Ms3dExporter( + self.report, + verbose=self.verbose, + use_blender_names=self.use_blender_names, + use_blender_materials=self.use_blender_materials, + apply_transform=self.apply_transform, + apply_modifiers=self.apply_modifiers, + apply_modifiers_mode=self.apply_modifiers_mode, + use_animation=self.use_animation, + normalize_weights=self.normalize_weights, + shrink_to_keys=self.shrink_to_keys, + bake_each_frame=self.bake_each_frame, + ).write( + blender_context, + self.filepath + ) + + # + def invoke(self, blender_context, event): + blender_context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL", } + + @staticmethod + def menu_func(cls, blender_context): + cls.layout.operator( + Ms3dExportOperator.bl_idname, + text=ms3d_str['TEXT_OPERATOR'] + ) + + +############################################################################### +## +############################################################################### + + +############################################################################### +class Ms3dSetSmoothingGroupOperator(Operator): + bl_idname = Ms3dUi.OPT_SMOOTHING_GROUP_APPLY + bl_label = ms3d_str['BL_LABEL_SMOOTHING_GROUP_OPERATOR'] + bl_options = {'INTERNAL', } + + smoothing_group_index = IntProperty( + name=ms3d_str['PROP_SMOOTHING_GROUP_INDEX'], + options={'HIDDEN', 'SKIP_SAVE', }, + ) + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object + and blender_context.object.type in {'MESH', } + and blender_context.object.data + and blender_context.object.data.ms3d is not None + and blender_context.mode == 'EDIT_MESH' + and blender_context.tool_settings.mesh_select_mode[2] + ) + + def execute(self, blender_context): + custom_data = blender_context.object.data.ms3d + blender_mesh = blender_context.object.data + bm = from_edit_mesh(blender_mesh) + layer_smoothing_group = bm.faces.layers.int.get( + ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP']) + if custom_data.apply_mode in {'SELECT', 'DESELECT', }: + if layer_smoothing_group is not None: + is_select = (custom_data.apply_mode == 'SELECT') + for bmf in bm.faces: + if (bmf[layer_smoothing_group] \ + == self.smoothing_group_index): + bmf.select_set(is_select) + elif custom_data.apply_mode == 'ASSIGN': + if layer_smoothing_group is None: + layer_smoothing_group = bm.faces.layers.int.new( + ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP']) + blender_mesh_object = blender_context.object + get_edge_split_modifier_add_if(blender_mesh_object) + blender_face_list = [] + for bmf in bm.faces: + if not bmf.smooth: + bmf.smooth = True + if bmf.select: + bmf[layer_smoothing_group] = self.smoothing_group_index + blender_face_list.append(bmf) + 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 + is_border = (edge_dict[bme] == 0) + if is_border: + surround_face_smoothing_group_index \ + = self.smoothing_group_index + for bmf in bme.link_faces: + if bmf[layer_smoothing_group] \ + != surround_face_smoothing_group_index: + surround_face_smoothing_group_index \ + = bmf[layer_smoothing_group] + break; + if surround_face_smoothing_group_index \ + == self.smoothing_group_index: + is_border = False + bme.seam = is_border + bme.smooth = not is_border + bm.free() + enable_edit_mode(False, blender_context) + enable_edit_mode(True, blender_context) + return {'FINISHED', } + + +class Ms3dGroupOperator(Operator): + bl_idname = Ms3dUi.OPT_GROUP_APPLY + bl_label = ms3d_str['BL_LABEL_GROUP_OPERATOR'] + bl_options = {'INTERNAL', } + + mode = EnumProperty( + items=( ('', "", ""), + ('ADD_GROUP', + ms3d_str['ENUM_ADD_GROUP_1'], + ms3d_str['ENUM_ADD_GROUP_2']), + ('REMOVE_GROUP', + ms3d_str['ENUM_REMOVE_GROUP_1'], + ms3d_str['ENUM_REMOVE_GROUP_2']), + ('ASSIGN', + ms3d_str['ENUM_ASSIGN_1'], + ms3d_str['ENUM_ASSIGN_2_GROUP']), + ('REMOVE', + ms3d_str['ENUM_REMOVE_1'], + ms3d_str['ENUM_REMOVE_2_GROUP']), + ('SELECT', + ms3d_str['ENUM_SELECT_1'], + ms3d_str['ENUM_SELECT_2_GROUP']), + ('DESELECT', + ms3d_str['ENUM_DESELECT_1'], + ms3d_str['ENUM_DESELECT_2_GROUP']), + ), + options={'HIDDEN', 'SKIP_SAVE', }, + ) + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object + and blender_context.object.type in {'MESH', } + and blender_context.object.data + and blender_context.object.data.ms3d is not None + and blender_context.mode == 'EDIT_MESH' + #and blender_context.object.data.ms3d.selected_group_index != -1 + ) + + def execute(self, blender_context): + custom_data = blender_context.object.data.ms3d + blender_mesh = blender_context.object.data + bm = None + bm = from_edit_mesh(blender_mesh) + + if self.mode == 'ADD_GROUP': + item = custom_data.create_group() + layer_group = bm.faces.layers.int.get( + ms3d_str['OBJECT_LAYER_GROUP']) + if layer_group is None: + bm.faces.layers.int.new(ms3d_str['OBJECT_LAYER_GROUP']) + + elif self.mode == 'REMOVE_GROUP': + custom_data.remove_group() + + elif (custom_data.selected_group_index >= 0) and ( + custom_data.selected_group_index < len(custom_data.groups)): + if self.mode in {'SELECT', 'DESELECT', }: + layer_group = bm.faces.layers.int.get( + ms3d_str['OBJECT_LAYER_GROUP']) + if layer_group is not None: + is_select = (self.mode == 'SELECT') + id = custom_data.groups[ + custom_data.selected_group_index].id + for bmf in bm.faces: + if bmf[layer_group] == id: + bmf.select_set(is_select) + + elif self.mode in {'ASSIGN', 'REMOVE', }: + 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']) + + is_assign = (self.mode == 'ASSIGN') + id = custom_data.groups[custom_data.selected_group_index].id + for bmf in bm.faces: + if bmf.select: + if is_assign: + bmf[layer_group] = id + else: + bmf[layer_group] = -1 + if bm is not None: + bm.free() + enable_edit_mode(False, blender_context) + enable_edit_mode(True, blender_context) + return {'FINISHED', } + + +class Ms3dMaterialOperator(Operator): + bl_idname = Ms3dUi.OPT_MATERIAL_APPLY + bl_label = ms3d_str['BL_LABEL_MATERIAL_OPERATOR'] + bl_options = {'INTERNAL', } + + mode = EnumProperty( + items=( ('', "", ""), + ('FROM_BLENDER', + ms3d_str['ENUM_FROM_BLENDER_1'], + ms3d_str['ENUM_FROM_BLENDER_2']), + ('TO_BLENDER', + ms3d_str['ENUM_TO_BLENDER_1'], + ms3d_str['ENUM_TO_BLENDER_2']), + ), + options={'HIDDEN', 'SKIP_SAVE', }, + ) + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object + and blender_context.object.type in {'MESH', } + and blender_context.object.data + and blender_context.object.data.ms3d is not None + and blender_context.material + and blender_context.material.ms3d is not None + ) + + def execute(self, blender_context): + blender_material = blender_context.active_object.active_material + ms3d_material = blender_material.ms3d + + if self.mode == 'FROM_BLENDER': + Ms3dMaterialHelper.copy_from_blender(self, blender_context, + ms3d_material, blender_material) + pass + + elif self.mode == 'TO_BLENDER': + # not implemented + pass + + return {'FINISHED', } + + # entrypoint for option via UI + def invoke(self, blender_context, event): + return blender_context.window_manager.invoke_props_dialog(self) + + +############################################################################### +class Ms3dGroupProperties(PropertyGroup): + name = StringProperty( + name=ms3d_str['PROP_NAME_NAME'], + description=ms3d_str['PROP_DESC_GROUP_NAME'], + default="", + #options={'HIDDEN', }, + ) + + flags = EnumProperty( + name=ms3d_str['PROP_NAME_FLAGS'], + description=ms3d_str['PROP_DESC_FLAGS_GROUP'], + items=(#(Ms3dUi.FLAG_NONE, ms3d_str['ENUM_FLAG_NONE_1'], + # ms3d_str['ENUM_FLAG_NONE_2'], + # Ms3dSpec.FLAG_NONE), + (Ms3dUi.FLAG_SELECTED, + ms3d_str['ENUM_FLAG_SELECTED_1'], + ms3d_str['ENUM_FLAG_SELECTED_2'], + Ms3dSpec.FLAG_SELECTED), + (Ms3dUi.FLAG_HIDDEN, + ms3d_str['ENUM_FLAG_HIDDEN_1'], + ms3d_str['ENUM_FLAG_HIDDEN_2'], + Ms3dSpec.FLAG_HIDDEN), + (Ms3dUi.FLAG_SELECTED2, + ms3d_str['ENUM_FLAG_SELECTED2_1'], + ms3d_str['ENUM_FLAG_SELECTED2_2'], + Ms3dSpec.FLAG_SELECTED2), + (Ms3dUi.FLAG_DIRTY, + ms3d_str['ENUM_FLAG_DIRTY_1'], + ms3d_str['ENUM_FLAG_DIRTY_2'], + Ms3dSpec.FLAG_DIRTY), + (Ms3dUi.FLAG_ISKEY, + ms3d_str['ENUM_FLAG_ISKEY_1'], + ms3d_str['ENUM_FLAG_ISKEY_2'], + Ms3dSpec.FLAG_ISKEY), + (Ms3dUi.FLAG_NEWLYCREATED, + ms3d_str['ENUM_FLAG_NEWLYCREATED_1'], + ms3d_str['ENUM_FLAG_NEWLYCREATED_2'], + Ms3dSpec.FLAG_NEWLYCREATED), + (Ms3dUi.FLAG_MARKED, + ms3d_str['ENUM_FLAG_MARKED_1'], + ms3d_str['ENUM_FLAG_MARKED_2'], + Ms3dSpec.FLAG_MARKED), + ), + default=Ms3dUi.flags_from_ms3d(Ms3dSpec.DEFAULT_FLAGS), + options={'ENUM_FLAG', 'ANIMATABLE', }, + ) + + comment = StringProperty( + name=ms3d_str['PROP_NAME_COMMENT'], + description=ms3d_str['PROP_DESC_COMMENT_GROUP'], + default="", + #options={'HIDDEN', }, + ) + + id = IntProperty(options={'HIDDEN', },) + + +class Ms3dModelProperties(PropertyGroup): + name = StringProperty( + name=ms3d_str['PROP_NAME_NAME'], + description=ms3d_str['PROP_DESC_NAME_MODEL'], + default="", + #options={'HIDDEN', }, + ) + + joint_size = FloatProperty( + name=ms3d_str['PROP_NAME_JOINT_SIZE'], + description=ms3d_str['PROP_DESC_JOINT_SIZE'], + min=Ms3dUi.PROP_JOINT_SIZE_MIN, max=Ms3dUi.PROP_JOINT_SIZE_MAX, + precision=Ms3dUi.PROP_JOINT_SIZE_PRECISION, \ + step=Ms3dUi.PROP_JOINT_SIZE_STEP, + default=Ms3dUi.PROP_DEFAULT_JOINT_SIZE, + subtype='FACTOR', + #options={'HIDDEN', }, + ) + + transparency_mode = EnumProperty( + name=ms3d_str['PROP_NAME_TRANSPARENCY_MODE'], + description=ms3d_str['PROP_DESC_TRANSPARENCY_MODE'], + items=( (Ms3dUi.MODE_TRANSPARENCY_SIMPLE, + ms3d_str['PROP_MODE_TRANSPARENCY_SIMPLE_1'], + ms3d_str['PROP_MODE_TRANSPARENCY_SIMPLE_2'], + Ms3dSpec.MODE_TRANSPARENCY_SIMPLE), + (Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES, + ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_1'], + ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_2'], + Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES), + (Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF, + ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_1'], + ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_2'], + Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF), + ), + default=Ms3dUi.transparency_mode_from_ms3d( + Ms3dSpec.DEFAULT_MODEL_TRANSPARENCY_MODE), + #options={'HIDDEN', }, + ) + + alpha_ref = FloatProperty( + name=ms3d_str['PROP_NAME_ALPHA_REF'], + description=ms3d_str['PROP_DESC_ALPHA_REF'], + min=0, max=1, precision=3, step=0.1, + default=0.5, + subtype='FACTOR', + #options={'HIDDEN', }, + ) + + comment = StringProperty( + name=ms3d_str['PROP_NAME_COMMENT'], + description=ms3d_str['PROP_DESC_COMMENT_MODEL'], + default="", + #options={'HIDDEN', }, + ) + + ########################## + # ms3d group handling + # + apply_mode = EnumProperty( + items=( ('ASSIGN', + ms3d_str['ENUM_ASSIGN_1'], + ms3d_str['ENUM_ASSIGN_2_SMOOTHING_GROUP']), + ('SELECT', + ms3d_str['ENUM_SELECT_1'], + ms3d_str['ENUM_SELECT_2_SMOOTHING_GROUP']), + ('DESELECT', + ms3d_str['ENUM_DESELECT_1'], + ms3d_str['ENUM_DESELECT_2_SMOOTHING_GROUP']), + ), + default='SELECT', + options={'HIDDEN', 'SKIP_SAVE', }, + ) + + selected_group_index = IntProperty( + default=-1, + min=-1, + options={'HIDDEN', 'SKIP_SAVE', }, + ) + # + # ms3d group handling + ########################## + + groups = CollectionProperty( + type=Ms3dGroupProperties, + #options={'HIDDEN', }, + ) + + + def generate_unique_id(self): + return randrange(1, 0x7FFFFFFF) # pseudo unique id + + def create_group(self): + item = self.groups.add() + item.id = self.generate_unique_id() + length = len(self.groups) + self.selected_group_index = length - 1 + + item.name = ms3d_str['STRING_FORMAT_GROUP'].format(length) + return item + + def remove_group(self): + index = self.selected_group_index + length = len(self.groups) + if (index >= 0) and (index < length): + if index > 0 or length == 1: + self.selected_group_index = index - 1 + self.groups.remove(index) + + +class Ms3dArmatureProperties(PropertyGroup): + name = StringProperty( + name=ms3d_str['PROP_NAME_NAME'], + description=ms3d_str['PROP_DESC_NAME_ARMATURE'], + default="", + #options={'HIDDEN', }, + ) + + +class Ms3dJointProperties(PropertyGroup): + name = StringProperty( + name=ms3d_str['PROP_NAME_NAME'], + description=ms3d_str['PROP_DESC_NAME_JOINT'], + default="", + #options={'HIDDEN', }, + ) + + flags = EnumProperty( + name=ms3d_str['PROP_NAME_FLAGS'], + description=ms3d_str['PROP_DESC_FLAGS_JOINT'], + items=(#(Ms3dUi.FLAG_NONE, + # ms3d_str['ENUM_FLAG_NONE_1'], + # ms3d_str['ENUM_FLAG_NONE_2'], + # Ms3dSpec.FLAG_NONE), + (Ms3dUi.FLAG_SELECTED, + ms3d_str['ENUM_FLAG_SELECTED_1'], + ms3d_str['ENUM_FLAG_SELECTED_2'], + Ms3dSpec.FLAG_SELECTED), + (Ms3dUi.FLAG_HIDDEN, + ms3d_str['ENUM_FLAG_HIDDEN_1'], + ms3d_str['ENUM_FLAG_HIDDEN_2'], + Ms3dSpec.FLAG_HIDDEN), + (Ms3dUi.FLAG_SELECTED2, + ms3d_str['ENUM_FLAG_SELECTED2_1'], + ms3d_str['ENUM_FLAG_SELECTED2_2'], + Ms3dSpec.FLAG_SELECTED2), + (Ms3dUi.FLAG_DIRTY, + ms3d_str['ENUM_FLAG_DIRTY_1'], + ms3d_str['ENUM_FLAG_DIRTY_2'], + Ms3dSpec.FLAG_DIRTY), + (Ms3dUi.FLAG_ISKEY, + ms3d_str['ENUM_FLAG_ISKEY_1'], + ms3d_str['ENUM_FLAG_ISKEY_2'], + Ms3dSpec.FLAG_ISKEY), + (Ms3dUi.FLAG_NEWLYCREATED, + ms3d_str['ENUM_FLAG_NEWLYCREATED_1'], + ms3d_str['ENUM_FLAG_NEWLYCREATED_2'], + Ms3dSpec.FLAG_NEWLYCREATED), + (Ms3dUi.FLAG_MARKED, + ms3d_str['ENUM_FLAG_MARKED_1'], + ms3d_str['ENUM_FLAG_MARKED_2'], + Ms3dSpec.FLAG_MARKED), + ), + default=Ms3dUi.flags_from_ms3d(Ms3dSpec.DEFAULT_FLAGS), + options={'ENUM_FLAG', 'ANIMATABLE', }, + ) + + color = FloatVectorProperty( + name=ms3d_str['PROP_NAME_COLOR'], + description=ms3d_str['PROP_DESC_COLOR_JOINT'], + subtype='COLOR', size=3, min=0, max=1, precision=3, step=0.1, + default=Ms3dSpec.DEFAULT_JOINT_COLOR, + #options={'HIDDEN', }, + ) + + comment = StringProperty( + name=ms3d_str['PROP_NAME_COMMENT'], + description=ms3d_str['PROP_DESC_COMMENT_JOINT'], + default="", + #options={'HIDDEN', }, + ) + + +class Ms3dMaterialHelper: + @staticmethod + def copy_to_blender_ambient(cls, blender_context): + pass + + @staticmethod + def copy_to_blender_diffuse(cls, blender_context): + cls.id_data.diffuse_color = cls.diffuse[0:3] + #cls.id_data.diffuse_intensity = cls.diffuse[3] + pass + + @staticmethod + def copy_to_blender_specular(cls, blender_context): + cls.id_data.specular_color = cls.specular[0:3] + #cls.id_data.specular_intensity = cls.specular[3] + pass + + @staticmethod + def copy_to_blender_emissive(cls, blender_context): + cls.id_data.emit = (cls.emissive[0] + cls.emissive[1] \ + + cls.emissive[2]) / 3.0 + pass + + @staticmethod + def copy_to_blender_shininess(cls, blender_context): + cls.id_data.specular_hardness = cls.shininess * 4.0 + pass + + @staticmethod + def copy_to_blender_transparency(cls, blender_context): + cls.id_data.alpha = 1.0 - cls.transparency + pass + + + @staticmethod + def copy_from_blender(cls, blender_context, ms3d_material, blender_material): + # copy, bacause of auto update, it would distord original values + blender_material_diffuse_color = blender_material.diffuse_color.copy() + blender_material_diffuse_intensity = blender_material.diffuse_intensity + blender_material_specular_color = blender_material.specular_color.copy() + blender_material_specular_intensity = \ + blender_material.specular_intensity + blender_material_emit = blender_material.emit + blender_material_specular_hardness = \ + blender_material.specular_hardness + blender_material_alpha = blender_material.alpha + + blender_material_texture = None + for slot in blender_material.texture_slots: + if slot and slot.use_map_color_diffuse \ + and slot.texture.type == 'IMAGE': + blender_material_texture = slot.texture.image.filepath + break + + blender_material_alphamap = None + for slot in blender_material.texture_slots: + if slot and not slot.use_map_color_diffuse \ + and slot.use_map_alpha and slot.texture.type == 'IMAGE': + blender_material_alphamap = slot.texture.image.filepath + break + + ms3d_material.diffuse[0] = blender_material_diffuse_color[0] + ms3d_material.diffuse[1] = blender_material_diffuse_color[1] + ms3d_material.diffuse[2] = blender_material_diffuse_color[2] + ms3d_material.diffuse[3] = 1.0 + ms3d_material.specular[0] = blender_material_specular_color[0] + ms3d_material.specular[1] = blender_material_specular_color[1] + ms3d_material.specular[2] = blender_material_specular_color[2] + ms3d_material.specular[3] = 1.0 + ms3d_material.emissive[0] = blender_material_emit + ms3d_material.emissive[1] = blender_material_emit + ms3d_material.emissive[2] = blender_material_emit + ms3d_material.emissive[3] = 1.0 + ms3d_material.shininess = blender_material_specular_hardness / 4.0 + ms3d_material.transparency = 1.0 - blender_material_alpha + + if blender_material_texture: + ms3d_material.texture = blender_material_texture + else: + ms3d_material.texture = "" + + if blender_material_alphamap: + ms3d_material.alphamap = blender_material_alphamap + else: + ms3d_material.alphamap = "" + + +class Ms3dMaterialProperties(PropertyGroup): + name = StringProperty( + name=ms3d_str['PROP_NAME_NAME'], + description=ms3d_str['PROP_DESC_NAME_MATERIAL'], + default="", + #options={'HIDDEN', }, + ) + + ambient = FloatVectorProperty( + name=ms3d_str['PROP_NAME_AMBIENT'], + description=ms3d_str['PROP_DESC_AMBIENT'], + subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1, + default=Ms3dSpec.DEFAULT_MATERIAL_AMBIENT, + update=Ms3dMaterialHelper.copy_to_blender_ambient, + #options={'HIDDEN', }, + ) + + diffuse = FloatVectorProperty( + name=ms3d_str['PROP_NAME_DIFFUSE'], + description=ms3d_str['PROP_DESC_DIFFUSE'], + subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1, + default=Ms3dSpec.DEFAULT_MATERIAL_DIFFUSE, + update=Ms3dMaterialHelper.copy_to_blender_diffuse, + #options={'HIDDEN', }, + ) + + specular = FloatVectorProperty( + name=ms3d_str['PROP_NAME_SPECULAR'], + description=ms3d_str['PROP_DESC_SPECULAR'], + subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1, + default=Ms3dSpec.DEFAULT_MATERIAL_SPECULAR, + update=Ms3dMaterialHelper.copy_to_blender_specular, + #options={'HIDDEN', }, + ) + + emissive = FloatVectorProperty( + name=ms3d_str['PROP_NAME_EMISSIVE'], + description=ms3d_str['PROP_DESC_EMISSIVE'], + subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1, + default=Ms3dSpec.DEFAULT_MATERIAL_EMISSIVE, + update=Ms3dMaterialHelper.copy_to_blender_emissive, + #options={'HIDDEN', }, + ) + + shininess = FloatProperty( + name=ms3d_str['PROP_NAME_SHININESS'], + description=ms3d_str['PROP_DESC_SHININESS'], + min=0, max=Ms3dSpec.MAX_MATERIAL_SHININESS, precision=3, step=0.1, + default=Ms3dSpec.DEFAULT_MATERIAL_SHININESS, + subtype='FACTOR', + update=Ms3dMaterialHelper.copy_to_blender_shininess, + #options={'HIDDEN', }, + ) + + transparency = FloatProperty( + name=ms3d_str['PROP_NAME_TRANSPARENCY'], + description=ms3d_str['PROP_DESC_TRANSPARENCY'], + min=0, max=1, precision=3, step=0.1, + default=0, + subtype='FACTOR', + update=Ms3dMaterialHelper.copy_to_blender_transparency, + #options={'HIDDEN', }, + ) + + mode = EnumProperty( + name=ms3d_str['PROP_NAME_MODE'], + description=ms3d_str['PROP_DESC_MODE_TEXTURE'], + items=( (Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA, + ms3d_str['PROP_FLAG_TEXTURE_COMBINE_ALPHA_1'], + ms3d_str['PROP_FLAG_TEXTURE_COMBINE_ALPHA_2'], + Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA), + (Ms3dUi.FLAG_TEXTURE_HAS_ALPHA, + ms3d_str['PROP_FLAG_TEXTURE_HAS_ALPHA_1'], + ms3d_str['PROP_FLAG_TEXTURE_HAS_ALPHA_2'], + Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA), + (Ms3dUi.FLAG_TEXTURE_SPHERE_MAP, + ms3d_str['PROP_FLAG_TEXTURE_SPHERE_MAP_1'], + ms3d_str['PROP_FLAG_TEXTURE_SPHERE_MAP_2'], + Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP), + ), + default=Ms3dUi.texture_mode_from_ms3d( + Ms3dSpec.DEFAULT_MATERIAL_MODE), + options={'ANIMATABLE', 'ENUM_FLAG', }, + ) + + texture = StringProperty( + name=ms3d_str['PROP_NAME_TEXTURE'], + description=ms3d_str['PROP_DESC_TEXTURE'], + default="", + subtype = 'FILE_PATH' + #options={'HIDDEN', }, + ) + + alphamap = StringProperty( + name=ms3d_str['PROP_NAME_ALPHAMAP'], + description=ms3d_str['PROP_DESC_ALPHAMAP'], + default="", + subtype = 'FILE_PATH' + #options={'HIDDEN', }, + ) + + comment = StringProperty( + name=ms3d_str['PROP_NAME_COMMENT'], + description=ms3d_str['PROP_DESC_COMMENT_MATERIAL'], + default="", + #options={'HIDDEN', }, + ) + + +############################################################################### +# http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=53355 +class Ms3dGroupUILise(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + layout.label(item.name, icon_value=icon) + + +############################################################################### +class Ms3dMeshPanel(Panel): + bl_label = ms3d_str['LABEL_PANEL_MODEL'] + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = 'object' + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object + and blender_context.object.type in {'MESH', } + and blender_context.object.data + and blender_context.object.data.ms3d is not None + ) + + def draw_header(self, blender_context): + layout = self.layout + layout.label(icon='PLUGIN') + + def draw(self, blender_context): + layout = self.layout + custom_data = blender_context.object.data.ms3d + + row = layout.row() + row.prop(custom_data, 'name') + + col = layout.column() + row = col.row() + row.prop(custom_data, 'joint_size') + row = col.row() + row.prop(custom_data, 'transparency_mode') + row = col.row() + row.prop(custom_data, 'alpha_ref', ) + + row = layout.row() + row.prop(custom_data, 'comment') + + +class Ms3dMaterialPanel(Panel): + bl_label = ms3d_str['LABEL_PANEL_MATERIALS'] + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = 'material' + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object + and blender_context.object.type in {'MESH', } + and blender_context.object.data + and blender_context.object.data.ms3d is not None + and blender_context.material + and blender_context.material.ms3d is not None + ) + + def draw_header(self, blender_context): + layout = self.layout + layout.label(icon='PLUGIN') + + def draw(self, blender_context): + layout = self.layout + custom_data = blender_context.material.ms3d + + row = layout.row() + row.prop(custom_data, 'name') + + col = layout.column() + row = col.row() + row.prop(custom_data, 'diffuse') + row.prop(custom_data, 'ambient') + row = col.row() + row.prop(custom_data, 'specular') + row.prop(custom_data, 'emissive') + row = col.row() + row.prop(custom_data, 'shininess') + row.prop(custom_data, 'transparency') + + col = layout.column() + row = col.row() + row.prop(custom_data, 'texture') + row = col.row() + row.prop(custom_data, 'alphamap') + row = col.row() + row.prop(custom_data, 'mode', expand=True) + + row = layout.row() + row.prop(custom_data, 'comment') + + layout.row().operator( + Ms3dUi.OPT_MATERIAL_APPLY, + text=ms3d_str['ENUM_FROM_BLENDER_1'], + icon='APPEND_BLEND').mode = 'FROM_BLENDER' + pass + + +class Ms3dBonePanel(Panel): + bl_label = ms3d_str['LABEL_PANEL_JOINTS'] + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = 'bone' + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object.type in {'ARMATURE', } + and blender_context.active_bone + and isinstance(blender_context.active_bone, Bone) + and blender_context.active_bone.ms3d is not None + ) + + def draw_header(self, blender_context): + layout = self.layout + layout.label(icon='PLUGIN') + + def draw(self, blender_context): + import bpy + layout = self.layout + custom_data = blender_context.active_bone.ms3d + + row = layout.row() + row.prop(custom_data, 'name') + row = layout.row() + row.prop(custom_data, 'flags', expand=True) + row = layout.row() + row.prop(custom_data, 'color') + row = layout.row() + row.prop(custom_data, 'comment') + + +class Ms3dGroupPanel(Panel): + bl_label = ms3d_str['LABEL_PANEL_GROUPS'] + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = 'data' + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object + and blender_context.object.type in {'MESH', } + and blender_context.object.data + and blender_context.object.data.ms3d is not None + ) + + def draw_header(self, blender_context): + layout = self.layout + layout.label(icon='PLUGIN') + + def draw(self, blender_context): + layout = self.layout + custom_data = blender_context.object.data.ms3d + layout.enabled = (blender_context.mode == 'EDIT_MESH') and ( + blender_context.tool_settings.mesh_select_mode[2]) + + row = layout.row() + + # http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=53355 + row.template_list( + listtype_name='Ms3dGroupUILise', + dataptr=custom_data, + propname='groups', + active_dataptr=custom_data, + active_propname='selected_group_index', + rows=2, + type='DEFAULT', + ) + + col = row.column(align=True) + col.operator( + Ms3dUi.OPT_GROUP_APPLY, + text="", icon='ZOOMIN').mode = 'ADD_GROUP' + col.operator( + Ms3dUi.OPT_GROUP_APPLY, + text="", icon='ZOOMOUT').mode = 'REMOVE_GROUP' + + index = custom_data.selected_group_index + collection = custom_data.groups + if (index >= 0 and index < len(collection)): + row = layout.row() + row.prop(collection[index], 'name') + + row = layout.row() + subrow = row.row(align=True) + subrow.operator( + Ms3dUi.OPT_GROUP_APPLY, + text=ms3d_str['ENUM_ASSIGN_1']).mode = 'ASSIGN' + subrow.operator( + Ms3dUi.OPT_GROUP_APPLY, + text=ms3d_str['ENUM_REMOVE_1']).mode = 'REMOVE' + subrow = row.row(align=True) + subrow.operator( + Ms3dUi.OPT_GROUP_APPLY, + text=ms3d_str['ENUM_SELECT_1']).mode = 'SELECT' + subrow.operator( + Ms3dUi.OPT_GROUP_APPLY, + text=ms3d_str['ENUM_DESELECT_1']).mode = 'DESELECT' + + row = layout.row() + row.prop(collection[index], 'flags', expand=True) + + row = layout.row() + row.prop(collection[index], 'comment') + + +class Ms3dSmoothingGroupPanel(Panel): + bl_label = ms3d_str['BL_LABEL_PANEL_SMOOTHING_GROUP'] + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = 'data' + + def preview(self, dict, id, text): + item = dict.get(id) + if item is None: + return "{}".format(text) + elif item: + return "{}:".format(text) + + return "{}.".format(text) + + def build_preview(self, blender_context): + dict = {} + if (blender_context.mode != 'EDIT_MESH') or ( + not blender_context.tool_settings.mesh_select_mode[2]): + return dict + + custom_data = blender_context.object.data.ms3d + blender_mesh = blender_context.object.data + bm = from_edit_mesh(blender_mesh) + layer_smoothing_group = bm.faces.layers.int.get( + ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP']) + if layer_smoothing_group is not None: + for bmf in bm.faces: + item = dict.get(bmf[layer_smoothing_group]) + if item is None: + dict[bmf[layer_smoothing_group]] = bmf.select + else: + if not item: + dict[bmf[layer_smoothing_group]] = bmf.select + return dict + + @classmethod + def poll(cls, blender_context): + return (blender_context + and blender_context.object + and blender_context.object.type in {'MESH', } + and blender_context.object.data + and blender_context.object.data.ms3d is not None + ) + + def draw_header(self, blender_context): + layout = self.layout + layout.label(icon='PLUGIN') + + def draw(self, blender_context): + dict = self.build_preview(blender_context) + + custom_data = blender_context.object.data.ms3d + layout = self.layout + layout.enabled = (blender_context.mode == 'EDIT_MESH') and ( + blender_context.tool_settings.mesh_select_mode[2]) + + row = layout.row() + subrow = row.row() + subrow.prop(custom_data, 'apply_mode', expand=True) + + col = layout.column(align=True) + subrow = col.row(align=True) + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 1, "1") + ).smoothing_group_index = 1 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 2, "2") + ).smoothing_group_index = 2 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 3, "3") + ).smoothing_group_index = 3 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 4, "4") + ).smoothing_group_index = 4 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 5, "5") + ).smoothing_group_index = 5 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 6, "6") + ).smoothing_group_index = 6 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 7, "7") + ).smoothing_group_index = 7 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 8, "8") + ).smoothing_group_index = 8 + subrow = col.row(align=True) + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 9, "9") + ).smoothing_group_index = 9 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 10, "10") + ).smoothing_group_index = 10 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 11, "11") + ).smoothing_group_index = 11 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 12, "12") + ).smoothing_group_index = 12 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 13, "13") + ).smoothing_group_index = 13 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 14, "14") + ).smoothing_group_index = 14 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 15, "15") + ).smoothing_group_index = 15 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 16, "16") + ).smoothing_group_index = 16 + subrow = col.row(align=True) + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 17, "17") + ).smoothing_group_index = 17 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 18, "18") + ).smoothing_group_index = 18 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 19, "19") + ).smoothing_group_index = 19 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 20, "20") + ).smoothing_group_index = 20 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 21, "21") + ).smoothing_group_index = 21 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 22, "22") + ).smoothing_group_index = 22 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 23, "23") + ).smoothing_group_index = 23 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 24, "24") + ).smoothing_group_index = 24 + subrow = col.row(align=True) + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 25, "25") + ).smoothing_group_index = 25 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 26, "26") + ).smoothing_group_index = 26 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 27, "27") + ).smoothing_group_index = 27 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 28, "28") + ).smoothing_group_index = 28 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 29, "29") + ).smoothing_group_index = 29 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 30, "30") + ).smoothing_group_index = 30 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 31, "31") + ).smoothing_group_index = 31 + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 32, "32") + ).smoothing_group_index = 32 + subrow = col.row() + subrow.operator( + Ms3dUi.OPT_SMOOTHING_GROUP_APPLY, + text=self.preview(dict, 0, ms3d_str['LABEL_PANEL_BUTTON_NONE']) + ).smoothing_group_index = 0 + + +############################################################################### +class Ms3dSetSceneToMetricOperator(Operator): + """ . """ + bl_idname = 'io_scene_ms3d.set_sence_to_metric' + bl_label = ms3d_str['BL_LABEL_SET_SCENE_TO_METRIC'] + bl_description = ms3d_str['BL_DESC_SET_SCENE_TO_METRIC'] + + + # + @classmethod + def poll(cls, blender_context): + return True + + # entrypoint for option + def execute(self, blender_context): + return self.set_sence_to_metric(blender_context) + + # entrypoint for option via UI + def invoke(self, blender_context, event): + return blender_context.window_manager.invoke_props_dialog(self) + + + ########################################################################### + def set_sence_to_metric(self, blender_context): + set_sence_to_metric(blender_context) + return {"FINISHED"} + + +############################################################################### +def register(): + register_class(Ms3dSetSceneToMetricOperator) + register_class(Ms3dGroupProperties) + register_class(Ms3dModelProperties) + register_class(Ms3dArmatureProperties) + register_class(Ms3dJointProperties) + register_class(Ms3dMaterialProperties) + inject_properties() + register_class(Ms3dSetSmoothingGroupOperator) + register_class(Ms3dGroupOperator) + +def unregister(): + unregister_class(Ms3dGroupOperator) + unregister_class(Ms3dSetSmoothingGroupOperator) + delete_properties() + unregister_class(Ms3dMaterialProperties) + unregister_class(Ms3dJointProperties) + unregister_class(Ms3dArmatureProperties) + unregister_class(Ms3dModelProperties) + unregister_class(Ms3dGroupProperties) + unregister_class(Ms3dSetSceneToMetricOperator) + +def inject_properties(): + Mesh.ms3d = PointerProperty(type=Ms3dModelProperties) + Armature.ms3d = PointerProperty(type=Ms3dArmatureProperties) + Bone.ms3d = PointerProperty(type=Ms3dJointProperties) + Material.ms3d = PointerProperty(type=Ms3dMaterialProperties) + Action.ms3d = PointerProperty(type=Ms3dArmatureProperties) + Group.ms3d = PointerProperty(type=Ms3dGroupProperties) + +def delete_properties(): + del Mesh.ms3d + del Armature.ms3d + del Bone.ms3d + del Material.ms3d + del Action.ms3d + del Group.ms3d + +############################################################################### + + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- +# ##### END OF FILE ##### diff --git a/io_scene_ms3d/ms3d_utils.py b/io_scene_ms3d/ms3d_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..6617a8da703b6a7c0b1e5b739cd57ec0bb2b70fc --- /dev/null +++ b/io_scene_ms3d/ms3d_utils.py @@ -0,0 +1,222 @@ +# ##### 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 +from os import ( + path + ) + + +# import io_scene_ms3d stuff +from io_scene_ms3d.ms3d_strings import ( + ms3d_str, + ) + + +#import blender stuff +from bpy import ( + ops, + ) + + +############################################################################### +def enable_edit_mode(enable, blender_context): + if blender_context.active_object is None \ + or not blender_context.active_object.type in {'MESH', 'ARMATURE', }: + return + + if enable: + modeString = 'EDIT' + else: + modeString = 'OBJECT' + + if ops.object.mode_set.poll(): + ops.object.mode_set(mode=modeString) + + +############################################################################### +def enable_pose_mode(enable, blender_context): + if blender_context.active_object is None \ + or not blender_context.active_object.type in {'ARMATURE', }: + return + + if enable: + modeString = 'POSE' + else: + modeString = 'OBJECT' + + if ops.object.mode_set.poll(): + ops.object.mode_set(mode=modeString) + + +############################################################################### +def select_all(select): + if select: + actionString = 'SELECT' + else: + actionString = 'DESELECT' + + if ops.object.select_all.poll(): + ops.object.select_all(action=actionString) + + if ops.mesh.select_all.poll(): + ops.mesh.select_all(action=actionString) + + if ops.pose.select_all.poll(): + ops.pose.select_all(action=actionString) + + +############################################################################### +def pre_setup_environment(porter, blender_context): + # inject undo to porter + # and turn off undo + porter.undo = blender_context.user_preferences.edit.use_global_undo + blender_context.user_preferences.edit.use_global_undo = False + + # inject active_object to self + porter.active_object = blender_context.scene.objects.active + + # change to a well defined mode + enable_edit_mode(True, blender_context) + + # enable face-selection-mode + blender_context.tool_settings.mesh_select_mode = (False, False, True) + + # change back to object mode + enable_edit_mode(False, blender_context) + + blender_context.scene.update() + + +############################################################################### +def post_setup_environment(porter, blender_context): + # restore active object + blender_context.scene.objects.active = porter.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 = porter.undo + + +############################################################################### +def get_edge_split_modifier_add_if(blender_mesh_object): + blender_modifier = blender_mesh_object.modifiers.get( + ms3d_str['OBJECT_MODIFIER_SMOOTHING_GROUP']) + + if blender_modifier is None: + blender_modifier = blender_mesh_object.modifiers.new( + ms3d_str['OBJECT_MODIFIER_SMOOTHING_GROUP'], + type='EDGE_SPLIT') + blender_modifier.show_expanded = False + blender_modifier.use_edge_angle = False + blender_modifier.use_edge_sharp = True + + blender_mesh_object.data.show_edge_seams = True + blender_mesh_object.data.show_edge_sharp = True + + return blender_modifier + + +########################################################################### +def rotation_matrix(v_track, v_up): + ## rotation matrix from two vectors + ## http://gamedev.stackexchange.com/questions/20097/how-to-calculate-a-3x3-rotation-matrix-from-2-direction-vectors + ## http://www.fastgraph.com/makegames/3drotation/ + matrix = Matrix().to_3x3() + + c1 = v_track + c1.normalize() + + c0 = c1.cross(v_up) + c0.normalize() + + c2 = c0.cross(c1) + c2.normalize() + + matrix.col[0] = c0 + matrix.col[1] = c1 + matrix.col[2] = c2 + + return matrix + + +############################################################################### +def matrix_difference(mat_src, mat_dst): + mat_dst_inv = mat_dst.copy() + mat_dst_inv.invert() + return mat_dst_inv * mat_src + + +############################################################################### +def set_sence_to_metric(blender_context): + try: + # set metrics + blender_context.scene.unit_settings.system = 'METRIC' + blender_context.scene.unit_settings.system_rotation = 'DEGREES' + blender_context.scene.unit_settings.scale_length = 0.001 # 1.0mm + blender_context.scene.unit_settings.use_separate = False + blender_context.tool_settings.normal_size = 1.0 # 1.0mm + + # set all 3D views to texture shaded + # and set up the clipping + for screen in blender_context.blend_data.screens: + for area in screen.areas: + if (area.type != 'VIEW_3D'): + continue + + for space in area.spaces: + if (space.type != 'VIEW_3D'): + continue + + #space.viewport_shade = 'SOLID' + space.show_textured_solid = True + space.clip_start = 0.1 # 0.1mm + space.clip_end = 1000000.0 # 1km + #screen.scene.game_settings.material_mode = 'MULTITEXTURE' + + except Exception: + raise + + else: + pass + + +############################################################################### + +############################################################################### +#234567890123456789012345678901234567890123456789012345678901234567890123456789 +#--------1---------2---------3---------4---------5---------6---------7--------- +# ##### END OF FILE #####