From 7504cb5e96d4e45e2ddf265b821904c8d0a34410 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 22 Jul 2023 17:40:41 +0200 Subject: [PATCH 01/26] 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 02/26] 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 03/26] 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 From 1928cdf967ef88997adfb112f1dcd12742e5d22c Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sun, 23 Jul 2023 12:59:10 +0200 Subject: [PATCH 04/26] Export_3ds: Fixed position scaling for animations --- 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 79d00463e..83e947b52 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1321,7 +1321,7 @@ def make_object_node(ob, translation, rotation, scale, name_id): if parent is None or (parent.name not in name_id): ob_pos = translation[name] ob_rot = rotation[name] - ob_size = scale[name] + ob_size = ob.scale else: # Calculate child position and rotation of the object center, no scale applied ob_pos = translation[name] - translation[parent.name] -- 2.30.2 From a7b20de484536b46c229e001d04d81a30936a184 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 16:21:08 +0200 Subject: [PATCH 05/26] io_scene_3ds: Take scene units into account for import --- io_scene_3ds/__init__.py | 10 +++++----- io_scene_3ds/import_3ds.py | 30 +++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 0aad653ad..9262f5b0e 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -55,9 +55,9 @@ 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", + convert_unit: BoolProperty( + name="Convert Units", + description="Converts to scene unit length settings", default=False, ) use_image_search: BoolProperty( @@ -149,7 +149,7 @@ class MAX3DS_PT_import_transform(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "constrain_size") - layout.prop(operator, "convert_measure") + layout.prop(operator, "convert_unit") layout.prop(operator, "use_apply_transform") layout.prop(operator, "use_world_matrix") layout.prop(operator, "axis_forward") @@ -295,4 +295,4 @@ def unregister(): if __name__ == "__main__": - register() \ No newline at end of file + register() diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 4fa28e5a3..c7dc5d1d6 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1101,8 +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 MEASURE != 1.0: + child.location = child.location * MEASURE if hierarchy == ROOT_OBJECT: child.location.rotate(CONVERSE) if not contextTrack_flag & 0x100: # Flag 0x100 unlinks X axis @@ -1129,8 +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 MEASURE != 1.0: + child.matrix_world = mathutils.Matrix.Scale(MEASURE,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,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # IMPORT # ########## -def load_3ds(filepath, context, CONSTRAIN=10.0, MEASURE=False, IMAGE_SEARCH=True, +def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None): print("importing 3DS: %r..." % (filepath), end="") @@ -1313,6 +1313,7 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, MEASURE=False, IMAGE_SEARCH=True if bpy.ops.object.select_all.poll(): bpy.ops.object.select_all(action='DESELECT') + MEASURE = 1.0 duration = time.time() current_chunk = Chunk() file = open(filepath, 'rb') @@ -1335,6 +1336,17 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, MEASURE=False, IMAGE_SEARCH=True object_matrix.clear() scn = context.scene + if UNITS: + unit_length = sce.unit_settings.length_unit + if unit_length == 'KILOMETERS': + MEASURE = 1000.0 + elif unit_length == 'CENTIMETERS': + MEASURE = 0.01 + elif unit_length == 'MILLIMETERS': + MEASURE = 0.001 + elif unit_length == 'MICROMETERS': + MEASURE = 0.000001 + imported_objects = [] # Fill this list with objects process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE) @@ -1348,8 +1360,8 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, MEASURE=False, IMAGE_SEARCH=True if ob.type == 'MESH': ob.data.transform(ob.matrix_local.inverted()) - if MEASURE: - unit_mtx = mathutils.Matrix.Scale(0.001,4) + if UNITS: + unit_mtx = mathutils.Matrix.Scale(MEASURE,4) for ob in imported_objects: if ob.type == 'MESH': ob.data.transform(unit_mtx) @@ -1432,13 +1444,13 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, MEASURE=False, IMAGE_SEARCH=True def load(operator, context, filepath="", constrain_size=0.0, - convert_measure=False, use_image_search=True, + convert_unit=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, - MEASURE=convert_measure, IMAGE_SEARCH=use_image_search, + UNITS=convert_unit, IMAGE_SEARCH=use_image_search, WORLD_MATRIX=use_world_matrix, KEYFRAME=read_keyframe, APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, ) -- 2.30.2 From 31c673f60720ed333ed143e45b90d4f539c13373 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 17:03:38 +0200 Subject: [PATCH 06/26] io_scene_3ds: Take scene units into account for export --- io_scene_3ds/__init__.py | 6 ++++++ io_scene_3ds/export_3ds.py | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 9262f5b0e..b50a0cb46 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -176,6 +176,11 @@ class Export3DS(bpy.types.Operator, ExportHelper): soft_min=0.0, soft_max=100000.0, default=1.0, ) + unit_convert: BoolProperty( + name="Convert Units", + description="Converts to scene unit length settings", + default=False, + ) use_selection: BoolProperty( name="Selection Only", description="Export selected objects only", @@ -259,6 +264,7 @@ class MAX3DS_PT_export_transform(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "scale_factor") + layout.prop(operator, "unit_convert") layout.prop(operator, "axis_forward") layout.prop(operator, "axis_up") diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 83e947b52..23fa8d850 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1489,10 +1489,9 @@ def make_ambient_node(world): # EXPORT # ########## -def save(operator, context, filepath="", scale_factor=1.0, use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None): - +def save(operator, context, filepath="", scale_factor=1.0, convert_unit=False, + use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None): """Save the Blender scene to a 3ds file.""" - mtx_scale = mathutils.Matrix.Scale(scale_factor, 4) # Time the export duration = time.time() @@ -1503,6 +1502,20 @@ def save(operator, context, filepath="", scale_factor=1.0, use_selection=False, depsgraph = context.evaluated_depsgraph_get() world = scene.world + unit_measure = 1.0 + if unit_convert: + unit_length = sce.unit_settings.length_unit + if unit_length == 'KILOMETERS': + unit_measure = 0.001 + elif unit_length == 'CENTIMETERS': + unit_measure = 100 + elif unit_length == 'MILLIMETERS': + unit_measure = 1000 + elif unit_length == 'MICROMETERS': + unit_measure = 1000000 + + mtx_scale = mathutils.Matrix.Scale((scale_factor * unit_measure),4) + if global_matrix is None: global_matrix = mathutils.Matrix() -- 2.30.2 From 22803a31540a4c9750f6f2dcd4e227aee8b358c4 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 17:12:25 +0200 Subject: [PATCH 07/26] Export_3ds: Take scene units into account --- 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 23fa8d850..a383fda8e 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1489,7 +1489,7 @@ def make_ambient_node(world): # EXPORT # ########## -def save(operator, context, filepath="", scale_factor=1.0, convert_unit=False, +def save(operator, context, filepath="", scale_factor=1.0, unit_convert=False, use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None): """Save the Blender scene to a 3ds file.""" -- 2.30.2 From 1c8d5ace0accfcab025adf970b7e0fd1beb37f66 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 17:17:29 +0200 Subject: [PATCH 08/26] Export_3ds: Added unit measure to masterscale --- 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 a383fda8e..bbd1c4514 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1538,7 +1538,7 @@ def save(operator, context, filepath="", scale_factor=1.0, unit_convert=False, # Add MASTERSCALE element mscale = _3ds_chunk(MASTERSCALE) - mscale.add_variable("scale", _3ds_float(1.0)) + mscale.add_variable("scale", _3ds_float(unit_measure)) object_info.add_subchunk(mscale) # Init main keyframe data chunk -- 2.30.2 From 70b45a8858c7117d5ad97fb71b0aee183817c209 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 17:25:50 +0200 Subject: [PATCH 09/26] Export_3ds: Added unit measure to masterscale --- 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 bbd1c4514..763391662 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1538,7 +1538,7 @@ def save(operator, context, filepath="", scale_factor=1.0, unit_convert=False, # Add MASTERSCALE element mscale = _3ds_chunk(MASTERSCALE) - mscale.add_variable("scale", _3ds_float(unit_measure)) + mscale.add_variable("scale", _3ds_float(1.0 / unit_measure)) object_info.add_subchunk(mscale) # Init main keyframe data chunk -- 2.30.2 From 3be5db227d933ed4619ac8638ee8293bac35ffc7 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 17:31:17 +0200 Subject: [PATCH 10/26] Export_3ds: Added unit measure to masterscale --- 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 763391662..0a955a939 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1538,7 +1538,7 @@ def save(operator, context, filepath="", scale_factor=1.0, unit_convert=False, # Add MASTERSCALE element mscale = _3ds_chunk(MASTERSCALE) - mscale.add_variable("scale", _3ds_float(1.0 / unit_measure)) + mscale.add_variable("scale", _3ds_float((1.0 / unit_measure))) object_info.add_subchunk(mscale) # Init main keyframe data chunk -- 2.30.2 From 00636818a9aa975276a09c8e4fc22011a56fa97e Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 18:02:48 +0200 Subject: [PATCH 11/26] io_scene_3ds: Changed unit convert to apply units --- io_scene_3ds/__init__.py | 8 ++++---- io_scene_3ds/export_3ds.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index b50a0cb46..e04a071d2 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -176,9 +176,9 @@ class Export3DS(bpy.types.Operator, ExportHelper): soft_min=0.0, soft_max=100000.0, default=1.0, ) - unit_convert: BoolProperty( - name="Convert Units", - description="Converts to scene unit length settings", + apply_unit: BoolProperty( + name="Apply Units", + description="Take the scene unit length settings into account", default=False, ) use_selection: BoolProperty( @@ -264,7 +264,7 @@ class MAX3DS_PT_export_transform(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "scale_factor") - layout.prop(operator, "unit_convert") + layout.prop(operator, "apply_unit") layout.prop(operator, "axis_forward") layout.prop(operator, "axis_up") diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 0a955a939..e7a20030d 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1489,7 +1489,7 @@ def make_ambient_node(world): # EXPORT # ########## -def save(operator, context, filepath="", scale_factor=1.0, unit_convert=False, +def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None): """Save the Blender scene to a 3ds file.""" @@ -1503,7 +1503,7 @@ def save(operator, context, filepath="", scale_factor=1.0, unit_convert=False, world = scene.world unit_measure = 1.0 - if unit_convert: + if apply_unit: unit_length = sce.unit_settings.length_unit if unit_length == 'KILOMETERS': unit_measure = 0.001 @@ -1538,7 +1538,7 @@ def save(operator, context, filepath="", scale_factor=1.0, unit_convert=False, # Add MASTERSCALE element mscale = _3ds_chunk(MASTERSCALE) - mscale.add_variable("scale", _3ds_float((1.0 / unit_measure))) + mscale.add_variable("scale", _3ds_float(1.0)) object_info.add_subchunk(mscale) # Init main keyframe data chunk -- 2.30.2 From 8b1dc3f7b7ce7384e49d0ab32b226268595aba5c Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 25 Jul 2023 18:49:45 +0200 Subject: [PATCH 12/26] Export_3ds: Removed unit measure from masterscale --- 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 0378b1afa..e7a20030d 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1538,7 +1538,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, # Add MASTERSCALE element mscale = _3ds_chunk(MASTERSCALE) - mscale.add_variable("scale", _3ds_float((1.0 / unit_measure))) + mscale.add_variable("scale", _3ds_float(1.0)) object_info.add_subchunk(mscale) # Init main keyframe data chunk -- 2.30.2 From b7a36d80d50f17b1e1d0fd2d33ee722a36b9d95b Mon Sep 17 00:00:00 2001 From: NRGSille Date: Wed, 26 Jul 2023 01:01:56 +0200 Subject: [PATCH 13/26] io_scene_3ds: Added object filter- to export options --- io_scene_3ds/__init__.py | 25 +++++++++++++++++++------ io_scene_3ds/export_3ds.py | 8 ++++---- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index e04a071d2..ddfff583e 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -18,7 +18,7 @@ import bpy bl_info = { "name": "Autodesk 3DS format", "author": "Bob Holcomb, Campbell Barton, Andreas Atteneder, Sebastian Schrand", - "version": (2, 4, 4), + "version": (2, 4, 5), "blender": (3, 6, 0), "location": "File > Import-Export", "description": "3DS Import/Export meshes, UVs, materials, textures, " @@ -170,14 +170,14 @@ class Export3DS(bpy.types.Operator, ExportHelper): ) scale_factor: FloatProperty( - name="Scale", - description="Scale factor for all objects", + name="Scale Factor", + description="Master scale factor for all objects", min=0.0, max=100000.0, soft_min=0.0, soft_max=100000.0, default=1.0, ) apply_unit: BoolProperty( - name="Apply Units", + name="Scene Units", description="Take the scene unit length settings into account", default=False, ) @@ -186,13 +186,23 @@ class Export3DS(bpy.types.Operator, ExportHelper): description="Export selected objects only", default=False, ) + object_filter: bpy.props.EnumProperty( + name="Object Filter", options={'ENUM_FLAG'}, + items=(('MESH',"Mesh".rjust(11),"",'MESH_DATA',0x1), + ('LIGHT',"Light".rjust(12),"",'LIGHT_DATA',0x2), + ('CAMERA',"Camera".rjust(11),"",'CAMERA_DATA',0x4), + ('EMPTY',"Empty".rjust(11),"",'EMPTY_DATA',0x8), + ), + description="Object types to export", + default={'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, + ) use_hierarchy: BoolProperty( name="Export Hierarchy", description="Export hierarchy chunks", default=False, ) write_keyframe: BoolProperty( - name="Write Keyframe", + name="Export Keyframes", description="Write the keyframe data", default=False, ) @@ -238,7 +248,10 @@ class MAX3DS_PT_export_include(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "use_selection") - layout.prop(operator, "use_hierarchy") + laysub = layout.column(align=True) + laysub.enabled = (not operator.use_selection) + laysub.prop(operator, "object_filter") + layout.column().prop(operator, "use_hierarchy") layout.prop(operator, "write_keyframe") diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index e7a20030d..21f6dea20 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1489,8 +1489,8 @@ def make_ambient_node(world): # EXPORT # ########## -def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, - use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None): +def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use_selection=False, + object_filter=None, use_hierarchy=False, write_keyframe=False, global_matrix=None): """Save the Blender scene to a 3ds file.""" # Time the export @@ -1504,7 +1504,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, unit_measure = 1.0 if apply_unit: - unit_length = sce.unit_settings.length_unit + unit_length = scene.unit_settings.length_unit if unit_length == 'KILOMETERS': unit_measure = 0.001 elif unit_length == 'CENTIMETERS': @@ -1566,7 +1566,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, if use_selection: objects = [ob for ob in scene.objects if ob.visible_get(view_layer=layer) and ob.select_get(view_layer=layer)] else: - objects = [ob for ob in scene.objects if ob.visible_get(view_layer=layer)] + objects = [ob for ob in scene.objects if ob.type in object_filter and ob.visible_get(view_layer=layer)] empty_objects = [ob for ob in objects if ob.type == 'EMPTY'] light_objects = [ob for ob in objects if ob.type == 'LIGHT'] -- 2.30.2 From 8c5950824b789824497734c28c1df68c0f7157a2 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Wed, 26 Jul 2023 09:43:18 +0200 Subject: [PATCH 14/26] io_scene_3ds: Added background color, image and gradient chunks --- io_scene_3ds/__init__.py | 21 ++++++++++----------- io_scene_3ds/export_3ds.py | 29 ++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index ddfff583e..6897217f8 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -18,7 +18,7 @@ import bpy bl_info = { "name": "Autodesk 3DS format", "author": "Bob Holcomb, Campbell Barton, Andreas Atteneder, Sebastian Schrand", - "version": (2, 4, 5), + "version": (2, 4, 6), "blender": (3, 6, 0), "location": "File > Import-Export", "description": "3DS Import/Export meshes, UVs, materials, textures, " @@ -188,13 +188,14 @@ class Export3DS(bpy.types.Operator, ExportHelper): ) object_filter: bpy.props.EnumProperty( name="Object Filter", options={'ENUM_FLAG'}, - items=(('MESH',"Mesh".rjust(11),"",'MESH_DATA',0x1), - ('LIGHT',"Light".rjust(12),"",'LIGHT_DATA',0x2), - ('CAMERA',"Camera".rjust(11),"",'CAMERA_DATA',0x4), - ('EMPTY',"Empty".rjust(11),"",'EMPTY_DATA',0x8), - ), + items=(('WORLD', "World".rjust(11), "", 'WORLD_DATA',0x1), + ('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2), + ('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA',0x4), + ('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA',0x8), + ('EMPTY', "Empty".rjust(11), "", 'EMPTY_DATA',0x10), + ), description="Object types to export", - default={'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, + default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, ) use_hierarchy: BoolProperty( name="Export Hierarchy", @@ -248,10 +249,8 @@ class MAX3DS_PT_export_include(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "use_selection") - laysub = layout.column(align=True) - laysub.enabled = (not operator.use_selection) - laysub.prop(operator, "object_filter") - layout.column().prop(operator, "use_hierarchy") + layout.column().prop(operator, "object_filter") + layout.prop(operator, "use_hierarchy") layout.prop(operator, "write_keyframe") diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 21f6dea20..fbd6dc5e5 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -30,6 +30,12 @@ KFDATA = 0xB000 # This is the header for all of the keyframe info # >----- sub defines of OBJECTINFO OBJECTINFO = 0x3D3D # Main mesh object chunk before material and object information MESHVERSION = 0x3D3E # This gives the version of the mesh +BITMAP = 0x1100 # The background image name +USE_BITMAP = 0x1101 # The background image flag +SOLIDBACKGND = 0x1200 # The background color (RGB) +USE_SOLIDBGND = 0x1201 # The background color flag +VGRADIENT = 0x1300 # The background gradient colors +USE_VGRADIENT = 0x1301 # The background gradient flag AMBIENTLIGHT = 0x2100 # The color of the ambient light MATERIAL = 45055 # 0xAFFF // This stored the texture info OBJECT = 16384 # 0x4000 // This stores the faces, vertices, etc... @@ -1549,13 +1555,30 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use curtime = scene.frame_current kfdata = make_kfdata(revision, start, stop, curtime) - # Add AMBIENT color - if world is not None: + # Add AMBIENT and BACKGROUND color + if world is not None and 'WORLD' in object_filter: ambient_chunk = _3ds_chunk(AMBIENTLIGHT) ambient_light = _3ds_chunk(RGB) ambient_light.add_variable("ambient", _3ds_float_color(world.color)) ambient_chunk.add_subchunk(ambient_light) object_info.add_subchunk(ambient_chunk) + if world.use_nodes: + ntree = world.node_tree.links + background_color = _3ds_chunk(RGB) + background_chunk = _3ds_chunk(SOLIDBACKGND) + background_flag = _3ds_chunk(USE_SOLIDBGND) + bgcol, bgtex, nworld = 'BACKGROUND', 'TEX_IMAGE', 'OUTPUT_WORLD' + bg_color = next((lk.from_node.inputs[0].default_value[:3] for lk in ntree if lk.to_node.type == nworld), world.color) + bg_image = next((lk.from_node.image.name for lk in ntree if lk.from_node.type == bgtex and lk.to_node.type in {bgcol, nworld}), False) + background_color.add_variable("color", _3ds_float_color(bg_color)) + background_chunk.add_subchunk(background_color) + if bg_image: + background_image = _3ds_chunk(BITMAP) + background_flag = _3ds_chunk(USE_BITMAP) + background_image.add_variable("image", _3ds_string(sane_name(bg_image))) + object_info.add_subchunk(background_image) + object_info.add_subchunk(background_chunk) + object_info.add_subchunk(background_flag) if write_keyframe and world.animation_data: kfdata.add_subchunk(make_ambient_node(world)) @@ -1564,7 +1587,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use mesh_objects = [] if use_selection: - objects = [ob for ob in scene.objects if ob.visible_get(view_layer=layer) and ob.select_get(view_layer=layer)] + objects = [ob for ob in scene.objects if ob.type in object_filter and ob.visible_get(view_layer=layer) and ob.select_get(view_layer=layer)] else: objects = [ob for ob in scene.objects if ob.type in object_filter and ob.visible_get(view_layer=layer)] -- 2.30.2 From 09f12191459f837463307e45acc22f5a3206ed19 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 27 Jul 2023 16:40:27 +0200 Subject: [PATCH 15/26] io_scene_3ds: Added object filter to import options --- io_scene_3ds/__init__.py | 14 ++- io_scene_3ds/import_3ds.py | 206 +++++++++++++++++++------------------ 2 files changed, 120 insertions(+), 100 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 6897217f8..176722ab4 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -66,6 +66,17 @@ class Import3DS(bpy.types.Operator, ImportHelper): "(Warning, may be slow)", default=True, ) + object_filter: EnumProperty( + name="Object Filter", options={'ENUM_FLAG'}, + items=(('WORLD',"World".rjust(11),"",'WORLD_DATA',0x1), + ('MESH',"Mesh".rjust(11),"",'MESH_DATA',0x2), + ('LIGHT',"Light".rjust(12),"",'LIGHT_DATA',0x4), + ('CAMERA',"Camera".rjust(11),"",'CAMERA_DATA',0x8), + ('EMPTY',"Empty".rjust(11),"",'EMPTY_DATA',0x10), + ), + description="Object types to export", + default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, + ) use_apply_transform: BoolProperty( name="Apply Transform", description="Workaround for object transformations " @@ -124,6 +135,7 @@ class MAX3DS_PT_import_include(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "use_image_search") + layout.column().prop(operator, "object_filter") layout.prop(operator, "read_keyframe") @@ -186,7 +198,7 @@ class Export3DS(bpy.types.Operator, ExportHelper): description="Export selected objects only", default=False, ) - object_filter: bpy.props.EnumProperty( + object_filter: EnumProperty( name="Object Filter", options={'ENUM_FLAG'}, items=(('WORLD', "World".rjust(11), "", 'WORLD_DATA',0x1), ('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2), diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index c7dc5d1d6..89ad73465 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -25,7 +25,6 @@ PRIMARY = 0x4D4D # >----- Main Chunks OBJECTINFO = 0x3D3D # This gives the version of the mesh and is found right before the material and object information VERSION = 0x0002 # This gives the version of the .3ds file -AMBIENTLIGHT = 0x2100 # The color of the ambient light EDITKEYFRAME = 0xB000 # This is the header for all of the key frame info # >----- Data Chunks, used for various attributes @@ -38,6 +37,7 @@ PCT_FLOAT = 0x0031 # percentage float MASTERSCALE = 0x0100 # Master scale factor # >----- sub defines of OBJECTINFO +AMBIENTLIGHT = 0x2100 # The color of the ambient light MATERIAL = 0xAFFF # This stored the texture info OBJECT = 0x4000 # This stores the faces, vertices, etc... @@ -131,13 +131,13 @@ OBJECT_SMOOTH = 0x4150 # The objects face smooth groups OBJECT_TRANS_MATRIX = 0x4160 # The objects Matrix # >------ sub defines of EDITKEYFRAME -KFDATA_AMBIENT = 0xB001 # Keyframe ambient node -KFDATA_OBJECT = 0xB002 # Keyframe object node -KFDATA_CAMERA = 0xB003 # Keyframe camera node -KFDATA_TARGET = 0xB004 # Keyframe target node -KFDATA_LIGHT = 0xB005 # Keyframe light node -KFDATA_LTARGET = 0xB006 # Keyframe light target node -KFDATA_SPOTLIGHT = 0xB007 # Keyframe spotlight node +KF_AMBIENT = 0xB001 # Keyframe ambient node +KF_OBJECT = 0xB002 # Keyframe object node +KF_OBJECT_CAMERA = 0xB003 # Keyframe camera node +KF_TARGET_CAMERA = 0xB004 # Keyframe target node +KF_OBJECT_LIGHT = 0xB005 # Keyframe light node +KF_TARGET_LIGHT = 0xB006 # Keyframe light target node +KF_OBJECT_SPOT_LIGHT = 0xB007 # Keyframe spotlight node KFDATA_KFSEG = 0xB008 # Keyframe start and stop KFDATA_CURTIME = 0xB009 # Keyframe current frame KFDATA_KFHDR = 0xB00A # Keyframe node header @@ -326,7 +326,7 @@ childs_list = [] parent_list = [] def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, - IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE): + FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE): contextObName = None contextLamp = None @@ -470,6 +470,12 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI CreateLightObject = False CreateTrackData = False + CreateWorld = 'WORLD' in FILTER + CreateMesh = 'MESH' in FILTER + CreateLight = 'LIGHT' in FILTER + CreateCamera = 'CAMERA' in FILTER + CreateEmpty = 'EMPTY' in FILTER + def read_short(temp_chunk): temp_data = file.read(SZ_U_SHORT) temp_chunk.bytes_read += SZ_U_SHORT @@ -666,7 +672,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI print("\tNon-Fatal Error: Version greater than 3, may not load correctly: ", version) # is it an ambient light chunk? - elif new_chunk.ID == AMBIENTLIGHT: + elif CreateWorld and new_chunk.ID == AMBIENTLIGHT: path, filename = os.path.split(file.name) realname, ext = os.path.splitext(filename) world = bpy.data.worlds.new("Ambient: " + realname) @@ -683,7 +689,7 @@ 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, MEASURE) + FILTER, 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 @@ -692,15 +698,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI elif new_chunk.ID == OBJECT: if CreateBlenderObject: - putContextMesh( - context, - contextMesh_vertls, - contextMesh_facels, - contextMesh_flag, - contextMeshMaterials, - contextMesh_smooth, - WORLD_MATRIX - ) + putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMesh_flag, + contextMeshMaterials, contextMesh_smooth, WORLD_MATRIX) + contextMesh_vertls = [] contextMesh_facels = [] contextMeshMaterials = [] @@ -709,7 +709,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextMeshUV = None contextMatrix = None - CreateBlenderObject = True + CreateBlenderObject = True if CreateMesh else False contextObName, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len @@ -867,13 +867,13 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI elif new_chunk.ID == OBJECT_MESH: pass - elif new_chunk.ID == OBJECT_VERTICES: + elif CreateMesh and new_chunk.ID == OBJECT_VERTICES: """Worldspace vertex locations""" num_verts = read_short(new_chunk) contextMesh_vertls = struct.unpack('<%df' % (num_verts * 3), file.read(SZ_3FLOAT * num_verts)) new_chunk.bytes_read += SZ_3FLOAT * num_verts - elif new_chunk.ID == OBJECT_FACES: + elif CreateMesh and new_chunk.ID == OBJECT_FACES: num_faces = read_short(new_chunk) temp_data = file.read(SZ_4U_SHORT * num_faces) new_chunk.bytes_read += SZ_4U_SHORT * num_faces # 4 short ints x 2 bytes each @@ -881,7 +881,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextMesh_flag = [contextMesh_facels[i] for i in range(3, (num_faces * 4) + 3, 4)] contextMesh_facels = [contextMesh_facels[i - 3:i] for i in range(3, (num_faces * 4) + 3, 4)] - elif new_chunk.ID == OBJECT_MATERIAL: + elif CreateMesh and new_chunk.ID == OBJECT_MATERIAL: material_name, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len # remove 1 null character. num_faces_using_mat = read_short(new_chunk) @@ -891,19 +891,19 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextMeshMaterials.append((material_name, temp_data)) # look up the material in all the materials - elif new_chunk.ID == OBJECT_SMOOTH: + elif CreateMesh and new_chunk.ID == OBJECT_SMOOTH: temp_data = file.read(SZ_U_INT * num_faces) smoothgroup = struct.unpack('<%dI' % (num_faces), temp_data) new_chunk.bytes_read += SZ_U_INT * num_faces contextMesh_smooth = smoothgroup - elif new_chunk.ID == OBJECT_UV: + elif CreateMesh and new_chunk.ID == OBJECT_UV: num_uv = read_short(new_chunk) temp_data = file.read(SZ_2FLOAT * num_uv) new_chunk.bytes_read += SZ_2FLOAT * num_uv contextMeshUV = struct.unpack('<%df' % (num_uv * 2), temp_data) - elif new_chunk.ID == OBJECT_TRANS_MATRIX: + elif CreateMesh and new_chunk.ID == OBJECT_TRANS_MATRIX: # How do we know the matrix size? 54 == 4x4 48 == 4x3 temp_data = file.read(SZ_4x3MAT) mtx = list(struct.unpack(' Date: Thu, 27 Jul 2023 16:50:58 +0200 Subject: [PATCH 16/26] Import_3ds: Typo / style clean --- io_scene_3ds/import_3ds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 89ad73465..567aa2dbe 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1036,7 +1036,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI elif new_chunk.ID == OBJECT_NODE_HDR: object_name, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len - new_data = file.read(SZ_U_INT) + temp_data = file.read(SZ_U_INT) new_chunk.bytes_read += SZ_U_INT hierarchy = read_short(new_chunk) child = object_dictionary.get(object_name) -- 2.30.2 From 67f0607b6483a79710de27e4f22c1c1b50efdf62 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 27 Jul 2023 16:55:51 +0200 Subject: [PATCH 17/26] Import_3ds: Clear trailing space --- io_scene_3ds/import_3ds.py | 1 - 1 file changed, 1 deletion(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 567aa2dbe..3d5e9f945 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -696,7 +696,6 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # is it an object chunk? elif new_chunk.ID == OBJECT: - if CreateBlenderObject: putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMesh_flag, contextMeshMaterials, contextMesh_smooth, WORLD_MATRIX) -- 2.30.2 From c177741cd4e61a71291e3ea0958088196b39f626 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 27 Jul 2023 17:00:29 +0200 Subject: [PATCH 18/26] Export_3ds: Changed chunk order --- io_scene_3ds/export_3ds.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index fbd6dc5e5..f40659046 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -24,12 +24,12 @@ from bpy_extras import node_shader_utils PRIMARY = 0x4D4D # >----- Main Chunks +OBJECTINFO = 0x3D3D # Main mesh object chunk before material and object information +MESHVERSION = 0x3D3E # This gives the version of the mesh VERSION = 0x0002 # This gives the version of the .3ds file KFDATA = 0xB000 # This is the header for all of the keyframe info # >----- sub defines of OBJECTINFO -OBJECTINFO = 0x3D3D # Main mesh object chunk before material and object information -MESHVERSION = 0x3D3E # This gives the version of the mesh BITMAP = 0x1100 # The background image name USE_BITMAP = 0x1101 # The background image flag SOLIDBACKGND = 0x1200 # The background color (RGB) -- 2.30.2 From 8a4902ea54ed5245fdf7d6f4c30b07ff597960db Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 27 Jul 2023 18:07:55 +0200 Subject: [PATCH 19/26] Import_3ds: Remove None from object list --- io_scene_3ds/import_3ds.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 3d5e9f945..cd6c10f6a 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1267,6 +1267,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextMeshMaterials, contextMesh_smooth, WORLD_MATRIX) # Assign parents to objects + while None in object_list: + object_list.remove(None) + # check _if_ we need to assign first because doing so recalcs the depsgraph for ind, ob in enumerate(object_list): parent = object_parent[ind] -- 2.30.2 From f18b8463b6533bb474f43c451bb6f10c126f262b Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 27 Jul 2023 18:15:54 +0200 Subject: [PATCH 20/26] io_scene_3ds: Fixed object filter description --- io_scene_3ds/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 176722ab4..0430aa68f 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -74,7 +74,7 @@ class Import3DS(bpy.types.Operator, ImportHelper): ('CAMERA',"Camera".rjust(11),"",'CAMERA_DATA',0x8), ('EMPTY',"Empty".rjust(11),"",'EMPTY_DATA',0x10), ), - description="Object types to export", + description="Object types to import", default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, ) use_apply_transform: BoolProperty( -- 2.30.2 From 7116a3fa65be44173118b9827dcfc427ace945b0 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 27 Jul 2023 18:43:50 +0200 Subject: [PATCH 21/26] Import_3ds: Avoid any None in lists --- io_scene_3ds/import_3ds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index cd6c10f6a..e16db52f2 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1049,7 +1049,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI imported_objects.append(child) else: tracking = tracktype = None - if tracktype != 'TARGET' and tracking != 'AMBIENT': + if child is not None and tracktype != 'TARGET' and tracking != 'AMBIENT': object_dict[object_id] = child object_list.append(child) object_parent.append(hierarchy) -- 2.30.2 From 4d4e74437dbed48130835e0693e784fc1987d8ab Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 28 Jul 2023 04:56:19 +0200 Subject: [PATCH 22/26] io_scene_3ds: Some beautify to the UI in file browser --- io_scene_3ds/__init__.py | 57 ++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 0430aa68f..eeddb37cd 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -56,7 +56,7 @@ class Import3DS(bpy.types.Operator, ImportHelper): default=10.0, ) convert_unit: BoolProperty( - name="Convert Units", + name="Scene Units", description="Converts to scene unit length settings", default=False, ) @@ -68,11 +68,11 @@ class Import3DS(bpy.types.Operator, ImportHelper): ) object_filter: EnumProperty( name="Object Filter", options={'ENUM_FLAG'}, - items=(('WORLD',"World".rjust(11),"",'WORLD_DATA',0x1), - ('MESH',"Mesh".rjust(11),"",'MESH_DATA',0x2), - ('LIGHT',"Light".rjust(12),"",'LIGHT_DATA',0x4), - ('CAMERA',"Camera".rjust(11),"",'CAMERA_DATA',0x8), - ('EMPTY',"Empty".rjust(11),"",'EMPTY_DATA',0x10), + items=(('WORLD', "World".rjust(11), "", 'WORLD_DATA', 0x1), + ('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2), + ('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA', 0x4), + ('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA', 0x8), + ('EMPTY', "Empty".rjust(11), "", 'EMPTY_DATA', 0x10), ), description="Object types to import", default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, @@ -84,7 +84,7 @@ class Import3DS(bpy.types.Operator, ImportHelper): default=True, ) read_keyframe: BoolProperty( - name="Read Keyframe", + name="Animation", description="Read the keyframe data", default=True, ) @@ -134,9 +134,13 @@ class MAX3DS_PT_import_include(bpy.types.Panel): sfile = context.space_data operator = sfile.active_operator - layout.prop(operator, "use_image_search") + layrow = layout.row(align=True) + layrow.prop(operator, "use_image_search") + layrow.label(text="", icon='OUTLINER_OB_IMAGE' if operator.use_image_search else 'IMAGE_DATA') layout.column().prop(operator, "object_filter") - layout.prop(operator, "read_keyframe") + layrow = layout.row(align=True) + layrow.prop(operator, "read_keyframe") + layrow.label(text="", icon='ANIM' if operator.read_keyframe else 'DECORATE_DRIVER') class MAX3DS_PT_import_transform(bpy.types.Panel): @@ -161,9 +165,15 @@ class MAX3DS_PT_import_transform(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "constrain_size") - layout.prop(operator, "convert_unit") - layout.prop(operator, "use_apply_transform") - layout.prop(operator, "use_world_matrix") + layrow = layout.row(align=True) + layrow.prop(operator, "convert_unit") + layrow.label(text="", icon='EMPTY_ARROWS' if operator.convert_unit else 'EMPTY_DATA') + layrow = layout.row(align=True) + layrow.prop(operator, "use_apply_transform") + layrow.label(text="", icon='MESH_CUBE' if operator.use_apply_transform else 'MOD_SOLIDIFY') + layrow = layout.row(align=True) + layrow.prop(operator, "use_world_matrix") + layrow.label(text="", icon='WORLD' if operator.use_world_matrix else 'META_BALL') layout.prop(operator, "axis_forward") layout.prop(operator, "axis_up") @@ -194,7 +204,7 @@ class Export3DS(bpy.types.Operator, ExportHelper): default=False, ) use_selection: BoolProperty( - name="Selection Only", + name="Selection", description="Export selected objects only", default=False, ) @@ -210,12 +220,12 @@ class Export3DS(bpy.types.Operator, ExportHelper): default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, ) use_hierarchy: BoolProperty( - name="Export Hierarchy", + name="Hierarchy", description="Export hierarchy chunks", default=False, ) write_keyframe: BoolProperty( - name="Export Keyframes", + name="Animation", description="Write the keyframe data", default=False, ) @@ -260,10 +270,17 @@ class MAX3DS_PT_export_include(bpy.types.Panel): sfile = context.space_data operator = sfile.active_operator - layout.prop(operator, "use_selection") + layrow = layout.row(align=True) + layrow.prop(operator, "use_selection") + layrow.label(text="", icon='RESTRICT_SELECT_OFF' if operator.use_selection else 'RESTRICT_SELECT_ON') layout.column().prop(operator, "object_filter") - layout.prop(operator, "use_hierarchy") - layout.prop(operator, "write_keyframe") + layrow = layout.row(align=True) + layrow.prop(operator, "use_hierarchy") + layrow.label(text="", icon='OUTLINER' if operator.use_hierarchy else 'CON_CHILDOF') + layrow = layout.row(align=True) + layrow.prop(operator, "write_keyframe") + layrow.label(text="", icon='ANIM' if operator.write_keyframe else 'DECORATE_DRIVER') + layout.use_property_split = True class MAX3DS_PT_export_transform(bpy.types.Panel): @@ -288,7 +305,9 @@ class MAX3DS_PT_export_transform(bpy.types.Panel): operator = sfile.active_operator layout.prop(operator, "scale_factor") - layout.prop(operator, "apply_unit") + layrow = layout.row(align=True) + layrow.prop(operator, "apply_unit") + layrow.label(text="", icon='EMPTY_ARROWS' if operator.apply_unit else 'EMPTY_DATA') layout.prop(operator, "axis_forward") layout.prop(operator, "axis_up") -- 2.30.2 From 7fc7bf97bfea0e729881a79f6fa144f921fab26f Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 28 Jul 2023 09:07:46 +0200 Subject: [PATCH 23/26] Import_3ds: Keep None objects in list to preserve hierarchy --- io_scene_3ds/import_3ds.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index abe933a19..c68088f8a 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1054,7 +1054,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI imported_objects.append(child) else: tracking = tracktype = None - if child is not None and tracktype != 'TARGET' and tracking != 'AMBIENT': + if tracktype != 'TARGET' and tracking != 'AMBIENT': object_dict[object_id] = child object_list.append(child) object_parent.append(hierarchy) @@ -1272,14 +1272,12 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextMeshMaterials, contextMesh_smooth, WORLD_MATRIX) # Assign parents to objects - while None in object_list: - object_list.remove(None) - # check _if_ we need to assign first because doing so recalcs the depsgraph for ind, ob in enumerate(object_list): parent = object_parent[ind] if parent == ROOT_OBJECT: - ob.parent = None + if ob is not None: + ob.parent = None elif parent not in object_dict: try: ob.parent = object_list[parent] @@ -1289,7 +1287,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI try: ob.parent = object_dict.get(parent) except: # self to parent exception - ob.parent = None + object_list.remove(ob) #pivot_list[ind] += pivot_list[parent] # Not sure this is correct, should parent space matrix be applied before combining? @@ -1312,7 +1310,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # fix pivots for ind, ob in enumerate(object_list): - if ob.type == 'MESH': + if ob is None: # remove None + object_list.pop(ind) + elif ob.type == 'MESH': pivot = pivot_list[ind] pivot_matrix = object_matrix.get(ob, mathutils.Matrix()) # unlikely to fail pivot_matrix = mathutils.Matrix.Translation(-1 * pivot) -- 2.30.2 From 92bfdea648df16650161ac6c4cbc58a5dd7cabc6 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 28 Jul 2023 09:33:00 +0200 Subject: [PATCH 24/26] Import_3ds: Keep None in object list to preserve hierarchy --- io_scene_3ds/import_3ds.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index c68088f8a..af09d9316 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1281,13 +1281,13 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI elif parent not in object_dict: try: ob.parent = object_list[parent] - except: # seems one object is missing, so take previous one - ob.parent = object_list[parent - 1] + except: # seems object is None or not in list + object_list.pop(ind) else: # get parent from node_id number try: ob.parent = object_dict.get(parent) - except: # self to parent exception - object_list.remove(ob) + except: # object is None or self to parent exception + object_list.pop(ind) #pivot_list[ind] += pivot_list[parent] # Not sure this is correct, should parent space matrix be applied before combining? @@ -1311,7 +1311,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # fix pivots for ind, ob in enumerate(object_list): if ob is None: # remove None - object_list.pop(ind) + object_list.remove(ob) elif ob.type == 'MESH': pivot = pivot_list[ind] pivot_matrix = object_matrix.get(ob, mathutils.Matrix()) # unlikely to fail -- 2.30.2 From c55c3248d3b2e94a5e31f1acdd18d4b42950e42f Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 28 Jul 2023 18:23:14 +0200 Subject: [PATCH 25/26] Import_3ds: Added background and bitmap import --- io_scene_3ds/import_3ds.py | 61 ++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index af09d9316..fc2f73ae5 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -676,7 +676,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI if version > 3: print("\tNon-Fatal Error: Version greater than 3, may not load correctly: ", version) - # is it an ambient light chunk? + # If ambient light chunk elif CreateWorld and new_chunk.ID == AMBIENTLIGHT: path, filename = os.path.split(file.name) realname, ext = os.path.splitext(filename) @@ -691,6 +691,42 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read + # If background chunk + elif CreateWorld and new_chunk.ID == SOLIDBACKGND: + if context.scene.world is None: + path, filename = os.path.split(file.name) + realname, ext = os.path.splitext(filename) + world = bpy.data.worlds.new("Background: " + realname) + context.scene.world = world + world = context.scene.world + world.use_nodes = True + read_chunk(file, temp_chunk) + if temp_chunk.ID == RGB: + world.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == RGBF: + world.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) + else: skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + + # If bitmap chunk + elif CreateWorld and new_chunk.ID == BITMAP: + bitmap_name, read_str_len = read_string(file) + bitmap = load_image(bitmap_name, dirname, place_holder=False, recursive=image_search, check_existing=True) + if context.scene.world is None: + path, filename = os.path.split(file.name) + realname, ext = os.path.splitext(filename) + world = bpy.data.worlds.new("Bitmap: " + realname) + context.scene.world = world + world = context.scene.world + world.use_nodes = True + links = world.node_tree.links + nodes = world.node_tree.nodes + bitmapnode = nodes.new(type='ShaderNodeTexImage') + bitmapnode.label = bitmap_name + bitmapnode.location = (-300, 300) + links.new(bitmapnode.outputs['Color'], nodes['Background'].inputs[0]) + new_chunk.bytes_read += read_str_len + # is it an object info chunk? elif new_chunk.ID == OBJECTINFO: process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, @@ -1045,10 +1081,23 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI hierarchy = read_short(new_chunk) child = object_dictionary.get(object_name) if child is None: - if CreateWorld and object_name == '$AMBIENT$': + if CreateWorld and tracking == 'AMBIENT': child = context.scene.world child.use_nodes = True - elif CreateEmpty and object_name == '$$$DUMMY': + nodetree = child.node_tree + links = nodetree.links + nodes = nodetree.nodes + worldout = nodes['World Output'] + mixshade = nodes.new(type='ShaderNodeMixShader') + ambinode = nodes.new(type='ShaderNodeEmission') + ambinode.inputs[0].default_value[:3] = child.color + worldout.location = (600, 250) + mixshade.location = (300, 250) + links.new(mixshade.outputs[0], worldout.inputs['Surface']) + links.new(nodes['Background'].outputs[0], mixshade.inputs[1]) + links.new(ambinode.outputs[0], mixshade.inputs[2]) + ambinode.label = object_name if object_name != '$AMBIENT$' else "Ambient" + elif CreateEmpty and tracking == 'OBJECT' and object_name == '$$$DUMMY': child = bpy.data.objects.new(object_name, None) # Create an empty object context.view_layer.active_layer_collection.collection.objects.link(child) imported_objects.append(child) @@ -1083,9 +1132,10 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI pivot_list[len(pivot_list) - 1] = mathutils.Vector(pivot) elif new_chunk.ID == MORPH_SMOOTH and tracking == 'OBJECT': # Smooth angle - child.data.use_auto_smooth = True smooth_angle = read_float(new_chunk) - child.data.auto_smooth_angle = smooth_angle + if child.data is not None: # Check if child is a dummy + child.data.use_auto_smooth = True + child.data.auto_smooth_angle = smooth_angle elif KEYFRAME and new_chunk.ID == COL_TRACK_TAG and tracking == 'AMBIENT': # Ambient keyframe_data = {} @@ -1292,6 +1342,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI #pivot_list[ind] += pivot_list[parent] # Not sure this is correct, should parent space matrix be applied before combining? # if parent name + parent_dictionary.pop(None, ...) for par, objs in parent_dictionary.items(): parent = object_dictionary.get(par) for ob in objs: -- 2.30.2 From da86b608721d81e70a667a8ea6007daf7a0d1fec Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 28 Jul 2023 19:42:46 +0200 Subject: [PATCH 26/26] Import_3ds: Fixed mismatched letter --- io_scene_3ds/import_3ds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index fc2f73ae5..9b7fe551d 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1407,7 +1407,7 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, scn = context.scene if UNITS: - unit_length = sce.unit_settings.length_unit + unit_length = scn.unit_settings.length_unit if unit_length == 'KILOMETERS': MEASURE = 1000.0 elif unit_length == 'CENTIMETERS': -- 2.30.2