From 7504cb5e96d4e45e2ddf265b821904c8d0a34410 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 22 Jul 2023 17:40:41 +0200 Subject: [PATCH 1/3] io_scene_3ds: Added unit measure convert option --- io_scene_3ds/__init__.py | 7 +++- io_scene_3ds/export_3ds.py | 2 +- io_scene_3ds/import_3ds.py | 70 ++++++++++++++++++-------------------- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 4369e686f..b67a05eb6 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -55,6 +55,11 @@ class Import3DS(bpy.types.Operator, ImportHelper): soft_min=0.0, soft_max=1000.0, default=10.0, ) + convert_measure: BoolProperty( + name="Convert Measure", + description="Convert from millimeters to meters", + default=False, + ) use_image_search: BoolProperty( name="Image Search", description="Search subdirectories for any associated images " @@ -112,7 +117,7 @@ class Export3DS(bpy.types.Operator, ExportHelper): min=0.0, max=100000.0, soft_min=0.0, soft_max=100000.0, default=1.0, - ) + ) use_selection: BoolProperty( name="Selection Only", description="Export selected objects only", diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index f2f93848a..b1798d251 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1209,7 +1209,7 @@ def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size): elif ID == ROT_TRACK_TAG: # Rotation (angle first [radians], followed by axis) quat = ob_rot.to_quaternion().inverted() - track_chunk.add_variable("rotation", _3ds_point_4d((quat.angle, quat.axis.x, quat.axis.y, quat.axis.z))) + track_chunk.add_variable("rotation", _3ds_point_4d((quat.angle, quat.axis,x, quat.axis.y, quat.axis.z))) elif ID == SCL_TRACK_TAG: # Scale vector track_chunk.add_variable("scale", _3ds_point_3d(ob_size)) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 529166b14..4fa28e5a3 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -325,7 +325,8 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of childs_list = [] parent_list = [] -def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE): +def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, + IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE): contextObName = None contextLamp = None @@ -362,15 +363,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI pivot_list = [] # pivots with hierarchy handling trackposition = {} # keep track to position for target calculation - def putContextMesh( - context, - myContextMesh_vertls, - myContextMesh_facels, - myContextMesh_flag, - myContextMeshMaterials, - myContextMesh_smooth, - WORLD_MATRIX, - ): + def putContextMesh(context, myContextMesh_vertls, myContextMesh_facels, myContextMesh_flag, + myContextMeshMaterials, myContextMesh_smooth, WORLD_MATRIX): + bmesh = bpy.data.meshes.new(contextObName) if myContextMesh_facels is None: @@ -431,8 +426,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI imported_objects.append(ob) if myContextMesh_flag: - """Bit 0 (0x1) sets edge CA visible, Bit 1 (0x2) sets edge BC visible and Bit 2 (0x4) sets edge AB visible - In Blender we use sharp edges for those flags""" + """Bit 0 (0x1) sets edge CA visible, Bit 1 (0x2) sets edge BC visible and + Bit 2 (0x4) sets edge AB visible. In Blender we use sharp edges for those flags.""" for f, pl in enumerate(bmesh.polygons): face = myContextMesh_facels[f] faceflag = myContextMesh_flag[f] @@ -541,7 +536,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI 0x40 activates alpha source, 0x80 activates tinting, 0x100 ignores alpha, 0x200 activates RGB tint. Bits 0x80, 0x100, and 0x200 are only used with TEXMAP, TEX2MAP, and SPECMAP chunks. 0x40, when used with a TEXMAP, TEX2MAP, or SPECMAP chunk must be accompanied with a tint bit, - either 0x100 or 0x200, tintcolor will be processed if colorchunks are present""" + either 0x100 or 0x200, tintcolor will be processed if colorchunks are present.""" tiling = read_short(temp_chunk) if tiling & 0x1: extend = 'decal' @@ -620,7 +615,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI def read_track_data(track_chunk): """Trackflags 0x1, 0x2 and 0x3 are for looping. 0x8, 0x10 and 0x20 - locks the XYZ axes. 0x100, 0x200 and 0x400 unlinks the XYZ axes""" + locks the XYZ axes. 0x100, 0x200 and 0x400 unlinks the XYZ axes.""" tflags = read_short(track_chunk) contextTrack_flag = tflags temp_data = file.read(SZ_U_INT * 2) @@ -687,7 +682,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # is it an object info chunk? elif new_chunk.ID == OBJECTINFO: - process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE) + process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, + IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE) # keep track of how much we read in the main chunk new_chunk.bytes_read += temp_chunk.bytes_read @@ -1105,6 +1101,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI for keydata in keyframe_data.items(): trackposition[keydata[0]] = keydata[1] # Keep track to position for target calculation child.location = apply_constrain(keydata[1]) if hierarchy == ROOT_OBJECT else mathutils.Vector(keydata[1]) + if MEASURE: + child.location = child.location * 0.001 if hierarchy == ROOT_OBJECT: child.location.rotate(CONVERSE) if not contextTrack_flag & 0x100: # Flag 0x100 unlinks X axis @@ -1131,6 +1129,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI scale = mathutils.Vector.Fill(3, (CONSTRAIN * 0.1)) if CONSTRAIN != 0.0 else child.scale transformation = mathutils.Matrix.LocRotScale(locate, rotate, scale) child.matrix_world = transformation + if MEASURE: + child.matrix_world = mathutils.Matrix.Scale(0.001,4) @ child.matrix_world if hierarchy == ROOT_OBJECT: child.matrix_world = CONVERSE @ child.matrix_world child.keyframe_insert(data_path="rotation_euler", index=0, frame=keydata[0]) @@ -1305,7 +1305,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # IMPORT # ########## -def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None): +def load_3ds(filepath, context, CONSTRAIN=10.0, MEASURE=False, IMAGE_SEARCH=True, + WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None): print("importing 3DS: %r..." % (filepath), end="") @@ -1335,7 +1336,8 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX= scn = context.scene imported_objects = [] # Fill this list with objects - process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE) + process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, + IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE) # fixme, make unglobal object_dictionary.clear() @@ -1344,8 +1346,13 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX= if APPLY_MATRIX: for ob in imported_objects: if ob.type == 'MESH': - me = ob.data - me.transform(ob.matrix_local.inverted()) + ob.data.transform(ob.matrix_local.inverted()) + + if MEASURE: + unit_mtx = mathutils.Matrix.Scale(0.001,4) + for ob in imported_objects: + if ob.type == 'MESH': + ob.data.transform(unit_mtx) if CONVERSE and not KEYFRAME: for ob in imported_objects: @@ -1424,25 +1431,16 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX= file.close() -def load(operator, - context, - filepath="", - constrain_size=0.0, - use_image_search=True, - use_world_matrix=False, - read_keyframe=True, - use_apply_transform=True, - global_matrix=None, +def load(operator, context, filepath="", constrain_size=0.0, + convert_measure=False, use_image_search=True, + use_world_matrix=False, read_keyframe=True, + use_apply_transform=True, global_matrix=None, ): - load_3ds(filepath, - context, - CONSTRAIN=constrain_size, - IMAGE_SEARCH=use_image_search, - WORLD_MATRIX=use_world_matrix, - KEYFRAME=read_keyframe, - APPLY_MATRIX=use_apply_transform, - CONVERSE=global_matrix, + load_3ds(filepath, context, CONSTRAIN=constrain_size, + MEASURE=convert_measure, IMAGE_SEARCH=use_image_search, + WORLD_MATRIX=use_world_matrix, KEYFRAME=read_keyframe, + APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, ) return {'FINISHED'} -- 2.30.2 From 0ca031c3173ac83cb3547c480120b54771049bca Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 22 Jul 2023 17:44:39 +0200 Subject: [PATCH 2/3] Fixed mismatched comma --- io_scene_3ds/export_3ds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index b1798d251..f2f93848a 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1209,7 +1209,7 @@ def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size): elif ID == ROT_TRACK_TAG: # Rotation (angle first [radians], followed by axis) quat = ob_rot.to_quaternion().inverted() - track_chunk.add_variable("rotation", _3ds_point_4d((quat.angle, quat.axis,x, quat.axis.y, quat.axis.z))) + track_chunk.add_variable("rotation", _3ds_point_4d((quat.angle, quat.axis.x, quat.axis.y, quat.axis.z))) elif ID == SCL_TRACK_TAG: # Scale vector track_chunk.add_variable("scale", _3ds_point_3d(ob_size)) -- 2.30.2 From 77f7f4dbdf715152564d0bbbe3ae4251ea1725d0 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sun, 23 Jul 2023 12:45:01 +0200 Subject: [PATCH 3/3] Export_3ds: Fixed position scaling --- io_scene_3ds/export_3ds.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index f2f93848a..79d00463e 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1086,7 +1086,6 @@ def make_kfdata(revision, start=0, stop=100, curtime=0): def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size): """Make a chunk for track data. Depending on the ID, this will construct a position, rotation, scale, roll, color, fov, hotspot or falloff track.""" - ob_distance = mathutils.Matrix.Diagonal(ob_size) track_chunk = _3ds_chunk(ID) if ID in {POS_TRACK_TAG, ROT_TRACK_TAG, SCL_TRACK_TAG, ROLL_TRACK_TAG} and ob.animation_data and ob.animation_data.action: @@ -1111,7 +1110,7 @@ def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size): pos_x = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 0), ob_pos.x) pos_y = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 1), ob_pos.y) pos_z = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 2), ob_pos.z) - pos = ob_distance @ mathutils.Vector((pos_x, pos_y, pos_z)) + pos = ob_size @ mathutils.Vector((pos_x, pos_y, pos_z)) track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame))) track_chunk.add_variable("tcb_flags", _3ds_ushort()) track_chunk.add_variable("position", _3ds_point_3d((pos.x, pos.y, pos.z))) @@ -1317,7 +1316,8 @@ def make_object_node(ob, translation, rotation, scale, name_id): obj_morph_smooth.add_variable("angle", _3ds_float(round(ob.data.auto_smooth_angle, 6))) obj_node.add_subchunk(obj_morph_smooth) - # Add track chunks for color, position, rotation and scale + # Add track chunks for position, rotation, size + ob_scale = scale[name] # and collect masterscale if parent is None or (parent.name not in name_id): ob_pos = translation[name] ob_rot = rotation[name] @@ -1328,7 +1328,7 @@ def make_object_node(ob, translation, rotation, scale, name_id): ob_rot = rotation[name].to_quaternion().cross(rotation[parent.name].to_quaternion().copy().inverted()).to_euler() ob_size = mathutils.Vector((1.0, 1.0, 1.0)) - obj_node.add_subchunk(make_track_chunk(POS_TRACK_TAG, ob, ob_pos, ob_rot, ob_size)) + obj_node.add_subchunk(make_track_chunk(POS_TRACK_TAG, ob, ob_pos, ob_rot, ob_scale)) if ob.type in {'MESH', 'EMPTY'}: obj_node.add_subchunk(make_track_chunk(ROT_TRACK_TAG, ob, ob_pos, ob_rot, ob_size)) @@ -1375,7 +1375,7 @@ def make_target_node(ob, translation, rotation, scale, name_id): # Calculate target position ob_pos = translation[name] ob_rot = rotation[name] - ob_size = mathutils.Matrix.Diagonal(scale[name]) + ob_scale = scale[name] target_pos = calc_target(ob_pos, ob_rot.x, ob_rot.z) # Add track chunks for target position @@ -1405,7 +1405,7 @@ def make_target_node(ob, translation, rotation, scale, name_id): rot_target = [fc for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler'] rot_x = next((tc.evaluate(frame) for tc in rot_target if tc.array_index == 0), ob_rot.x) rot_z = next((tc.evaluate(frame) for tc in rot_target if tc.array_index == 2), ob_rot.z) - target_distance = ob_size @ mathutils.Vector((loc_x, loc_y, loc_z)) + target_distance = ob_scale @ mathutils.Vector((loc_x, loc_y, loc_z)) target_pos = calc_target(target_distance, rot_x, rot_z) track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame))) track_chunk.add_variable("tcb_flags", _3ds_ushort()) @@ -1628,29 +1628,29 @@ def save(operator, context, filepath="", scale_factor=1.0, use_selection=False, name_id = {} for ob, data, matrix in mesh_objects: - translation[ob.name] = mtx_scale @ ob.location.copy() - rotation[ob.name] = ob.rotation_euler.copy() - scale[ob.name] = ob.scale.copy() + translation[ob.name] = mtx_scale @ ob.location + rotation[ob.name] = ob.rotation_euler + scale[ob.name] = mtx_scale.copy() name_id[ob.name] = len(name_id) object_id[ob.name] = len(object_id) for ob in empty_objects: - translation[ob.name] = mtx_scale @ ob.location.copy() - rotation[ob.name] = ob.rotation_euler.copy() - scale[ob.name] = ob.scale.copy() + translation[ob.name] = mtx_scale @ ob.location + rotation[ob.name] = ob.rotation_euler + scale[ob.name] = mtx_scale.copy() name_id[ob.name] = len(name_id) for ob in light_objects: - translation[ob.name] = mtx_scale @ ob.location.copy() - rotation[ob.name] = ob.rotation_euler.copy() - scale[ob.name] = mtx_scale.copy().to_scale() + translation[ob.name] = mtx_scale @ ob.location + rotation[ob.name] = ob.rotation_euler + scale[ob.name] = mtx_scale.copy() name_id[ob.name] = len(name_id) object_id[ob.name] = len(object_id) for ob in camera_objects: - translation[ob.name] = mtx_scale @ ob.location.copy() - rotation[ob.name] = ob.rotation_euler.copy() - scale[ob.name] = mtx_scale.copy().to_scale() + translation[ob.name] = mtx_scale @ ob.location + rotation[ob.name] = ob.rotation_euler + scale[ob.name] = mtx_scale.copy() name_id[ob.name] = len(name_id) object_id[ob.name] = len(object_id) -- 2.30.2