From 7504cb5e96d4e45e2ddf265b821904c8d0a34410 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 22 Jul 2023 17:40:41 +0200 Subject: [PATCH 01/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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/34] 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 From 820c7a6ec0aca258ce5712e99f3255a3d250f9d6 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 29 Jul 2023 00:45:53 +0200 Subject: [PATCH 27/34] io_scene_3ds: Fixed background and bitmap import --- io_scene_3ds/export_3ds.py | 10 +++--- io_scene_3ds/import_3ds.py | 72 ++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index f40659046..156c1dcba 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -37,6 +37,8 @@ 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 +LAYER_FOG = 0x2302 # The fog atmosphere settings +USE_LAYER_FOG = 0x2303 # The fog atmosphere flag MATERIAL = 45055 # 0xAFFF // This stored the texture info OBJECT = 16384 # 0x4000 // This stores the faces, vertices, etc... @@ -1567,17 +1569,17 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use 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) + amcol, bgcol, bgtex, nworld = 'EMISSION', 'BACKGROUND', 'TEX_ENVIRONMENT', 'OUTPUT_WORLD' + bg_color = next((lk.from_node.inputs[0].default_value[:3] for lk in ntree if lk.to_node.type == bgcol), 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 {amcol, bgcol}), False) background_color.add_variable("color", _3ds_float_color(bg_color)) background_chunk.add_subchunk(background_color) + object_info.add_subchunk(background_chunk) 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)) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 9b7fe551d..363722387 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -44,6 +44,8 @@ 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 +LAYER_FOG = 0x2302 # The fog atmosphere settings +USE_LAYER_FOG = 0x2303 # The fog atmosphere flag MATERIAL = 0xAFFF # This stored the texture info OBJECT = 0x4000 # This stores the faces, vertices, etc... @@ -335,6 +337,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE): contextObName = None + contextWorld = None contextLamp = None contextCamera = None contextMaterial = None @@ -680,52 +683,52 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI 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) - context.scene.world = world + contextWorld = bpy.data.worlds.new("Ambient: " + realname) + context.scene.world = contextWorld read_chunk(file, temp_chunk) if temp_chunk.ID == COLOR_F: - context.scene.world.color[:] = read_float_array(temp_chunk) + contextWorld.color[:] = read_float_array(temp_chunk) elif temp_chunk.ID == LIN_COLOR_F: - context.scene.world.color[:] = read_float_array(temp_chunk) + contextWorld.color[:] = read_float_array(temp_chunk) else: 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: + if contextWorld 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 + contextWorld = bpy.data.worlds.new("Background: " + realname) + context.scene.world = contextWorld + else: + contextWorld.use_nodes = True + read_chunk(file, temp_chunk) + if temp_chunk.ID == RGB: + contextWorld.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == RGBF: + contextWorld.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: + if contextWorld 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 + contextWorld = bpy.data.worlds.new("Bitmap: " + realname) + context.scene.world = contextWorld + else: + contextWorld.use_nodes = True + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes + bitmapnode = nodes.new(type='ShaderNodeTexEnvironment') + bitmapnode.label = bitmap_name + bitmapnode.location = (-300, 300) + bitmapnode.image = load_image(bitmap_name, dirname, place_holder=False, recursive=image_search, check_existing=True) + 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: @@ -1091,8 +1094,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI 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) + ambinode.location = (10, 150) + worldout.location = (600, 200) + mixshade.location = (300, 300) 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]) @@ -1141,12 +1145,12 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI keyframe_data = {} default_data = child.color[:] child.color = read_track_data(new_chunk)[0] - child.node_tree.nodes['Background'].inputs[0].default_value[:3] = child.color + ambinode.inputs[0].default_value[:3] = child.color for keydata in keyframe_data.items(): child.color = keydata[1] child.keyframe_insert(data_path="color", frame=keydata[0]) - child.node_tree.nodes['Background'].inputs[0].default_value[:3] = keydata[1] - child.node_tree.keyframe_insert(data_path="nodes[\"Background\"].inputs[0].default_value", frame=keydata[0]) + ambinode.inputs[0].default_value[:3] = keydata[1] + nodetree.keyframe_insert(data_path="nodes[\"Emission\"].inputs[0].default_value", frame=keydata[0]) contextTrack_flag = False elif KEYFRAME and new_chunk.ID == COL_TRACK_TAG and tracking == 'LIGHT': # Color -- 2.30.2 From 90c5f4aead6f659af4c981a61f26ec3bf3cadb55 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 29 Jul 2023 05:33:58 +0200 Subject: [PATCH 28/34] Import_3ds: Added fog atmosphere settings import --- io_scene_3ds/import_3ds.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 363722387..a3c3a01c3 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -704,9 +704,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI else: contextWorld.use_nodes = True read_chunk(file, temp_chunk) - if temp_chunk.ID == RGB: + if temp_chunk.ID == COLOR_F: contextWorld.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) - elif temp_chunk.ID == RGBF: + elif temp_chunk.ID == LIN_COLOR_F: contextWorld.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 @@ -730,6 +730,37 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI links.new(bitmapnode.outputs['Color'], nodes['Background'].inputs[0]) new_chunk.bytes_read += read_str_len + # If fog chunk + elif new_chunk.ID == LAYER_FOG: + if contextWorld is None: + path, filename = os.path.split(file.name) + realname, ext = os.path.splitext(filename) + contextWorld = bpy.data.worlds.new("LayerFog: " + realname) + context.scene.world = contextWorld + else: + contextWorld.use_nodes = True + links = newWorld.node_tree.links + nodes = newWorld.node_tree.nodes + context.view_layer.use_pass_mist = False + layerfog = nodes.new(type='ShaderNodeVolumeScatter') + layerfog.label = "Layer Fog" + layerfog.location = (300, 100) + links.new(layerfog.outputs['Volume'], nodes['World Output'].inputs['Volume']) + world.mist_settings.start = read_float(new_chunk) + world.mist_settings.depth = read_float(new_chunk) + layerfog.inputs[1].default_value = read_float(new_chunk) + layerfogflag = read_long(new_chunk) + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + layerfog.inputs[0].default_value[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + layerfog.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 + elif new_chunk.ID == USE_LAYER_FOG: + context.view_layer.use_pass_mist = True + # is it an object info chunk? elif new_chunk.ID == OBJECTINFO: process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, -- 2.30.2 From 7d0c49648749eb8bc75f2a6eeb1e864723606636 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 29 Jul 2023 05:38:33 +0200 Subject: [PATCH 29/34] Import_3ds: Added world boolean to fog import --- io_scene_3ds/import_3ds.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index a3c3a01c3..3a997e0c7 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -731,7 +731,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI new_chunk.bytes_read += read_str_len # If fog chunk - elif new_chunk.ID == LAYER_FOG: + elif CreateWorld and new_chunk.ID == LAYER_FOG: if contextWorld is None: path, filename = os.path.split(file.name) realname, ext = os.path.splitext(filename) @@ -758,7 +758,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI else: skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read - elif new_chunk.ID == USE_LAYER_FOG: + elif CreateWorld and new_chunk.ID == USE_LAYER_FOG: context.view_layer.use_pass_mist = True # is it an object info chunk? -- 2.30.2 From 291b2517a9e2559faebecc9316caafd1b58cb69e Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 29 Jul 2023 05:47:17 +0200 Subject: [PATCH 30/34] Import_3ds: Fixed world boolean --- io_scene_3ds/import_3ds.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 3a997e0c7..9b431bcdb 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -739,15 +739,15 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI context.scene.world = contextWorld else: contextWorld.use_nodes = True - links = newWorld.node_tree.links - nodes = newWorld.node_tree.nodes + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes context.view_layer.use_pass_mist = False layerfog = nodes.new(type='ShaderNodeVolumeScatter') layerfog.label = "Layer Fog" layerfog.location = (300, 100) links.new(layerfog.outputs['Volume'], nodes['World Output'].inputs['Volume']) - world.mist_settings.start = read_float(new_chunk) - world.mist_settings.depth = read_float(new_chunk) + contextWorld.mist_settings.start = read_float(new_chunk) + contextWorld.mist_settings.depth = read_float(new_chunk) layerfog.inputs[1].default_value = read_float(new_chunk) layerfogflag = read_long(new_chunk) read_chunk(file, temp_chunk) -- 2.30.2 From 2c7c68ee3b786028db4bfaf0605213b19a6418bc Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 29 Jul 2023 15:46:31 +0200 Subject: [PATCH 31/34] io_scene_3ds: Added fog atmosphere settings --- io_scene_3ds/export_3ds.py | 35 ++++++++++++---- io_scene_3ds/import_3ds.py | 81 ++++++++++++++++++++------------------ 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 156c1dcba..34243e9dd 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1566,21 +1566,42 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use object_info.add_subchunk(ambient_chunk) if world.use_nodes: ntree = world.node_tree.links - background_color = _3ds_chunk(RGB) + background_color_chunk = _3ds_chunk(RGB) background_chunk = _3ds_chunk(SOLIDBACKGND) background_flag = _3ds_chunk(USE_SOLIDBGND) - amcol, bgcol, bgtex, nworld = 'EMISSION', 'BACKGROUND', 'TEX_ENVIRONMENT', 'OUTPUT_WORLD' - bg_color = next((lk.from_node.inputs[0].default_value[:3] for lk in ntree if lk.to_node.type == bgcol), 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 {amcol, bgcol}), False) - background_color.add_variable("color", _3ds_float_color(bg_color)) - background_chunk.add_subchunk(background_color) - object_info.add_subchunk(background_chunk) + bgshader = 'ADD_SHADER', 'MIX_SHADER', 'OUTPUT_WORLD' + bgtexture = 'TEX_IMAGE', 'TEX_ENVIRONMENT' + acol, bcol = 'EMISSION', 'BACKGROUND' + bg_color = next((lk.from_node.inputs[0].default_value[:3] for lk in ntree if lk.from_node.type == bcol and lk.to_node.type in bgshader), world.color) + bg_image = next((lk.from_node.image.name for lk in ntree if lk.from_node.type in bgtexture and lk.to_node.type in {acol, bcol}), False) + background_color_chunk.add_variable("color", _3ds_float_color(bg_color)) + background_chunk.add_subchunk(background_color_chunk) 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) + fogshader = next((lk.from_socket.node for lk in ntree if lk.from_socket.identifier and lk.to_socket.identifier == 'Volume'), False) + if fogshader: + fogflag = 0 + if world.mist_settings.falloff == 'QUADRATIC': + fogflag |= 0x1 + if world.mist_settings.falloff == 'INVERSE_QUADRATIC': + fogflag |= 0x2 + fog_chunk = _3ds_chunk(LAYER_FOG) + fog_color_chunk = _3ds_chunk(RGB) + use_fog_flag = _3ds_chunk(USE_LAYER_FOG) + fog_color_chunk.add_variable("color", _3ds_float_color(fogshader.inputs[0].default_value[:3])) + fog_chunk.add_variable("lowZ", _3ds_float(world.mist_settings.start)) + fog_chunk.add_variable("highZ", _3ds_float(world.mist_settings.depth)) + fog_chunk.add_variable("density", _3ds_float(fogshader.inputs[1].default_value)) + fog_chunk.add_variable("flags", _3ds_uint(fogflag)) + fog_chunk.add_subchunk(fog_color_chunk) + object_info.add_subchunk(fog_chunk) + if layer.use_pass_mist: + object_info.add_subchunk(use_fog_flag) if write_keyframe and world.animation_data: kfdata.add_subchunk(make_ambient_node(world)) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 9b431bcdb..bba5e4703 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -701,15 +701,15 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI realname, ext = os.path.splitext(filename) contextWorld = bpy.data.worlds.new("Background: " + realname) context.scene.world = contextWorld + contextWorld.use_nodes = True + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + contextWorld.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + contextWorld.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) else: - contextWorld.use_nodes = True - read_chunk(file, temp_chunk) - if temp_chunk.ID == COLOR_F: - contextWorld.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) - elif temp_chunk.ID == LIN_COLOR_F: - contextWorld.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 + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read # If bitmap chunk elif CreateWorld and new_chunk.ID == BITMAP: @@ -719,45 +719,50 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI realname, ext = os.path.splitext(filename) contextWorld = bpy.data.worlds.new("Bitmap: " + realname) context.scene.world = contextWorld - else: - contextWorld.use_nodes = True - links = contextWorld.node_tree.links - nodes = contextWorld.node_tree.nodes - bitmapnode = nodes.new(type='ShaderNodeTexEnvironment') - bitmapnode.label = bitmap_name - bitmapnode.location = (-300, 300) - bitmapnode.image = load_image(bitmap_name, dirname, place_holder=False, recursive=image_search, check_existing=True) - links.new(bitmapnode.outputs['Color'], nodes['Background'].inputs[0]) - new_chunk.bytes_read += read_str_len + contextWorld.use_nodes = True + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes + bitmapnode = nodes.new(type='ShaderNodeTexEnvironment') + bitmapnode.label = bitmap_name + bitmapnode.location = (-300, 300) + bitmapnode.image = load_image(bitmap_name, dirname, place_holder=False, recursive=image_search, check_existing=True) + links.new(bitmapnode.outputs['Color'], nodes['Background'].inputs[0]) + new_chunk.bytes_read += read_str_len # If fog chunk elif CreateWorld and new_chunk.ID == LAYER_FOG: + """Fog options flags are bit 20 (0x100000) for background fogging, + bit 0 (0x1) for bottom falloff, and bit 1 (0x2) for top falloff.""" if contextWorld is None: path, filename = os.path.split(file.name) realname, ext = os.path.splitext(filename) contextWorld = bpy.data.worlds.new("LayerFog: " + realname) context.scene.world = contextWorld + contextWorld.use_nodes = True + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes + layerfog = nodes.new(type='ShaderNodeVolumeScatter') + layerfog.label = "Layer Fog" + layerfog.location = (300, 100) + links.new(layerfog.outputs['Volume'], nodes['World Output'].inputs['Volume']) + context.view_layer.use_pass_mist = False + contextWorld.mist_settings.use_mist = True + contextWorld.mist_settings.start = read_float(new_chunk) + contextWorld.mist_settings.depth = read_float(new_chunk) + layerfog.inputs[1].default_value = read_float(new_chunk) + layerfog_flag = read_long(new_chunk) + if layerfog_flag & 0x1: + contextWorld.mist_settings.falloff = 'QUADRATIC' + if layerfog_flag & 0x2: + contextWorld.mist_settings.falloff = 'INVERSE_QUADRATIC' + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + layerfog.inputs[0].default_value[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + layerfog.inputs[0].default_value[:3] = read_float_array(temp_chunk) else: - contextWorld.use_nodes = True - links = contextWorld.node_tree.links - nodes = contextWorld.node_tree.nodes - context.view_layer.use_pass_mist = False - layerfog = nodes.new(type='ShaderNodeVolumeScatter') - layerfog.label = "Layer Fog" - layerfog.location = (300, 100) - links.new(layerfog.outputs['Volume'], nodes['World Output'].inputs['Volume']) - contextWorld.mist_settings.start = read_float(new_chunk) - contextWorld.mist_settings.depth = read_float(new_chunk) - layerfog.inputs[1].default_value = read_float(new_chunk) - layerfogflag = read_long(new_chunk) - read_chunk(file, temp_chunk) - if temp_chunk.ID == COLOR_F: - layerfog.inputs[0].default_value[:3] = read_float_array(temp_chunk) - elif temp_chunk.ID == LIN_COLOR_F: - layerfog.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 + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read elif CreateWorld and new_chunk.ID == USE_LAYER_FOG: context.view_layer.use_pass_mist = True -- 2.30.2 From bd9a39823a73e6bfbeb8b2c63034d599e8496054 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sun, 30 Jul 2023 02:26:35 +0200 Subject: [PATCH 32/34] io_scene_3ds: Added cursor location --- io_scene_3ds/__init__.py | 45 +++++++++++++++++++++++++------------- io_scene_3ds/export_3ds.py | 31 ++++++++++++++++---------- io_scene_3ds/import_3ds.py | 27 +++++++++++++---------- 3 files changed, 66 insertions(+), 37 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index eeddb37cd..c0c13aa1b 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -55,7 +55,7 @@ class Import3DS(bpy.types.Operator, ImportHelper): soft_min=0.0, soft_max=1000.0, default=10.0, ) - convert_unit: BoolProperty( + use_scene_unit: BoolProperty( name="Scene Units", description="Converts to scene unit length settings", default=False, @@ -72,7 +72,7 @@ class Import3DS(bpy.types.Operator, ImportHelper): ('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), + ('EMPTY', "Empty".rjust(11), "", 'EMPTY_AXIS', 0x10), ), description="Object types to import", default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, @@ -83,7 +83,7 @@ class Import3DS(bpy.types.Operator, ImportHelper): "importing incorrectly", default=True, ) - read_keyframe: BoolProperty( + use_keyframes: BoolProperty( name="Animation", description="Read the keyframe data", default=True, @@ -93,6 +93,11 @@ class Import3DS(bpy.types.Operator, ImportHelper): description="Transform to matrix world", default=False, ) + use_cursor: BoolProperty( + name="Cursor Origin", + description="Read the 3D cursor location", + default=False, + ) def execute(self, context): from . import import_3ds @@ -139,8 +144,11 @@ class MAX3DS_PT_import_include(bpy.types.Panel): layrow.label(text="", icon='OUTLINER_OB_IMAGE' if operator.use_image_search else 'IMAGE_DATA') layout.column().prop(operator, "object_filter") layrow = layout.row(align=True) - layrow.prop(operator, "read_keyframe") - layrow.label(text="", icon='ANIM' if operator.read_keyframe else 'DECORATE_DRIVER') + layrow.prop(operator, "use_keyframes") + layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER') + layrow = layout.row(align=True) + layrow.prop(operator, "use_cursor") + layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR') class MAX3DS_PT_import_transform(bpy.types.Panel): @@ -166,8 +174,8 @@ class MAX3DS_PT_import_transform(bpy.types.Panel): layout.prop(operator, "constrain_size") layrow = layout.row(align=True) - layrow.prop(operator, "convert_unit") - layrow.label(text="", icon='EMPTY_ARROWS' if operator.convert_unit else 'EMPTY_DATA') + layrow.prop(operator, "use_scene_unit") + layrow.label(text="", icon='EMPTY_ARROWS' if operator.use_scene_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') @@ -198,7 +206,7 @@ class Export3DS(bpy.types.Operator, ExportHelper): soft_min=0.0, soft_max=100000.0, default=1.0, ) - apply_unit: BoolProperty( + use_scene_unit: BoolProperty( name="Scene Units", description="Take the scene unit length settings into account", default=False, @@ -214,7 +222,7 @@ class Export3DS(bpy.types.Operator, ExportHelper): ('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), + ('EMPTY', "Empty".rjust(11), "", 'EMPTY_AXIS',0x10), ), description="Object types to export", default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, @@ -224,11 +232,16 @@ class Export3DS(bpy.types.Operator, ExportHelper): description="Export hierarchy chunks", default=False, ) - write_keyframe: BoolProperty( + use_keyframes: BoolProperty( name="Animation", description="Write the keyframe data", default=False, ) + use_cursor: BoolProperty( + name="Cursor Origin", + description="Save the 3D cursor location", + default=False, + ) def execute(self, context): from . import export_3ds @@ -278,9 +291,11 @@ class MAX3DS_PT_export_include(bpy.types.Panel): 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 + layrow.prop(operator, "use_keyframes") + layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER') + layrow = layout.row(align=True) + layrow.prop(operator, "use_cursor") + layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR') class MAX3DS_PT_export_transform(bpy.types.Panel): @@ -306,8 +321,8 @@ class MAX3DS_PT_export_transform(bpy.types.Panel): layout.prop(operator, "scale_factor") layrow = layout.row(align=True) - layrow.prop(operator, "apply_unit") - layrow.label(text="", icon='EMPTY_ARROWS' if operator.apply_unit else 'EMPTY_DATA') + layrow.prop(operator, "use_scene_unit") + layrow.label(text="", icon='EMPTY_ARROWS' if operator.use_scene_unit else 'EMPTY_DATA') 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 d4d53b834..0ffedc299 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -36,6 +36,7 @@ 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 +O_CONSTS = 0x1500 # The origin of the 3D cursor AMBIENTLIGHT = 0x2100 # The color of the ambient light LAYER_FOG = 0x2302 # The fog atmosphere settings USE_LAYER_FOG = 0x2303 # The fog atmosphere flag @@ -1497,8 +1498,8 @@ def make_ambient_node(world): # EXPORT # ########## -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): +def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, use_selection=False, + object_filter=None, use_hierarchy=False, use_keyframes=False, global_matrix=None, use_cursor=False): """Save the Blender scene to a 3ds file.""" # Time the export @@ -1511,7 +1512,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use world = scene.world unit_measure = 1.0 - if apply_unit: + if use_scene_unit: unit_length = scene.unit_settings.length_unit if unit_length == 'KILOMETERS': unit_measure = 0.001 @@ -1549,21 +1550,29 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use mscale.add_variable("scale", _3ds_float(1.0)) object_info.add_subchunk(mscale) + # Add 3D cursor location + if use_cursor: + cursor_chunk = _3ds_chunk(O_CONSTS) + cursor_chunk.add_variable("cursor", _3ds_point_3d(scene.cursor.location)) + object_info.add_subchunk(cursor_chunk) + # Init main keyframe data chunk - if write_keyframe: + if use_keyframes: revision = 0x0005 stop = scene.frame_end start = scene.frame_start curtime = scene.frame_current kfdata = make_kfdata(revision, start, stop, curtime) - # Add AMBIENT, BACKGROUND and BITMAP + # Add AMBIENT 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) + + # Add BACKGROUND and BITMAP if world.use_nodes: ntree = world.node_tree.links background_color_chunk = _3ds_chunk(RGB) @@ -1604,7 +1613,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use object_info.add_subchunk(fog_chunk) if layer.use_pass_mist: object_info.add_subchunk(use_fog_flag) - if write_keyframe and world.animation_data: + if use_keyframes and world.animation_data: kfdata.add_subchunk(make_ambient_node(world)) # Make a list of all materials used in the selected meshes (use dictionary, each material is added once) @@ -1746,13 +1755,13 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use operator.report({'WARNING'}, "Object %r can't be written into a 3DS file") # Export object node - if write_keyframe: + if use_keyframes: kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) i += i # Create chunks for all empties - only requires a object node - if write_keyframe: + if use_keyframes: for ob in empty_objects: kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) @@ -1816,7 +1825,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use object_info.add_subchunk(object_chunk) # Export light and spotlight target node - if write_keyframe: + if use_keyframes: kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) if ob.data.type == 'SPOT': kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale, name_id)) @@ -1850,7 +1859,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use object_info.add_subchunk(object_chunk) # Export camera and target node - if write_keyframe: + if use_keyframes: kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale, name_id)) @@ -1858,7 +1867,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use primary.add_subchunk(object_info) # Add main keyframe data chunk to primary chunk - if write_keyframe: + if use_keyframes: primary.add_subchunk(kfdata) # The chunk hierarchy is completely built, now check the size diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 12e606c48..4df55c7af 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -43,6 +43,7 @@ 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 +O_CONSTS = 0x1500 # The origin of the 3D cursor AMBIENTLIGHT = 0x2100 # The color of the ambient light LAYER_FOG = 0x2302 # The fog atmosphere settings USE_LAYER_FOG = 0x2303 # The fog atmosphere flag @@ -333,8 +334,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, - FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE): +def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, FILTER, + IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE, CURSOR): contextObName = None contextWorld = None @@ -679,6 +680,10 @@ 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) + # If cursor location + elif CURSOR and new_chunk.ID == O_CONSTS: + context.scene.cursor.location = read_float_array(new_chunk) + # If ambient light chunk elif CreateWorld and new_chunk.ID == AMBIENTLIGHT: path, filename = os.path.split(file.name) @@ -768,8 +773,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, - FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE) + process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, FILTER, + IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE, CURSOR) # keep track of how much we read in the main chunk new_chunk.bytes_read += temp_chunk.bytes_read @@ -1416,7 +1421,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI ########## def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, FILTER=None, - WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None): + WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None, CURSOR=False): print("importing 3DS: %r..." % (filepath), end="") @@ -1458,8 +1463,8 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, MEASURE = 0.000001 imported_objects = [] # Fill this list with objects - process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, - FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE) + process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, FILTER, + IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE, CURSOR) # fixme, make unglobal object_dictionary.clear() @@ -1553,12 +1558,12 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, file.close() -def load(operator, context, filepath="", constrain_size=0.0, convert_unit=False, +def load(operator, context, filepath="", constrain_size=0.0, use_scene_unit=False, use_image_search=True, object_filter=None, use_world_matrix=False, - read_keyframe=True, use_apply_transform=True, global_matrix=None,): + use_keyframes=True, use_apply_transform=True, global_matrix=None, use_cursor=False): - load_3ds(filepath, context, CONSTRAIN=constrain_size, UNITS=convert_unit, + load_3ds(filepath, context, CONSTRAIN=constrain_size, UNITS=use_scene_unit, IMAGE_SEARCH=use_image_search, FILTER=object_filter, WORLD_MATRIX=use_world_matrix, - KEYFRAME=read_keyframe, APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix,) + KEYFRAME=use_keyframes, APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, CURSOR=use_cursor,) return {'FINISHED'} -- 2.30.2 From 197ae2fe1883144c0afcef3d2e5217e221a9eb88 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sun, 30 Jul 2023 16:29:39 +0200 Subject: [PATCH 33/34] io_scene_3ds: Improved import and export of background color and images --- io_scene_3ds/export_3ds.py | 11 ++++++----- io_scene_3ds/import_3ds.py | 8 +++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 0ffedc299..c9a67702c 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1578,11 +1578,12 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, background_color_chunk = _3ds_chunk(RGB) background_chunk = _3ds_chunk(SOLIDBACKGND) background_flag = _3ds_chunk(USE_SOLIDBGND) - bgshader = 'ADD_SHADER', 'MIX_SHADER', 'OUTPUT_WORLD' - bgtexture = 'TEX_IMAGE', 'TEX_ENVIRONMENT' - acol, bcol = 'EMISSION', 'BACKGROUND' - bg_color = next((lk.from_node.inputs[0].default_value[:3] for lk in ntree if lk.from_node.type == bcol and lk.to_node.type in bgshader), world.color) - bg_image = next((lk.from_node.image.name for lk in ntree if lk.from_node.type in bgtexture and lk.to_node.type in {acol, bcol}), False) + bgtype = 'BACKGROUND' + bgshade = 'ADD_SHADER', 'MIX_SHADER', 'OUTPUT_WORLD' + bg_tex = 'TEX_IMAGE', 'TEX_ENVIRONMENT' + bg_color = next((lk.from_node.inputs[0].default_value[:3] for lk in ntree if lk.from_node.type == bgtype and lk.to_node.type in bgshade), world.color) + bg_mixer = next((lk.from_node.type for lk in ntree if lk.from_node.type in {'MIX', 'MIX_RGB'} and lk.to_node.type == bgtype), bgtype) + bg_image = next((lk.from_node.image.name for lk in ntree if lk.from_node.type in bg_tex and lk.to_node.type == bg_mixer), False) background_color_chunk.add_variable("color", _3ds_float_color(bg_color)) background_chunk.add_subchunk(background_color_chunk) if bg_image: diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 8c3b4d54f..5f0971d18 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -735,11 +735,17 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextWorld.use_nodes = True links = contextWorld.node_tree.links nodes = contextWorld.node_tree.nodes + bitmap_mix = nodes.new(type='ShaderNodeMixRGB') bitmapnode = nodes.new(type='ShaderNodeTexEnvironment') + bitmap_mix.label = "Solid Color" bitmapnode.label = bitmap_name + bitmap_mix.location = (-250, 300) bitmapnode.location = (-300, 300) + bitmap_mix.inputs[2].default_value = nodes['Background'].inputs[0].default_value bitmapnode.image = load_image(bitmap_name, dirname, place_holder=False, recursive=IMAGE_SEARCH, check_existing=True) - links.new(bitmapnode.outputs['Color'], nodes['Background'].inputs[0]) + bitmap_mix.inputs[0].default_value = 0.0 if bitmapnode.image is not None else 1.0 + links.new(bitmap_mix.outputs['Color'], nodes['Background'].inputs[0]) + links.new(bitmapnode.outputs['Color'], bitmap_mix.inputs[1]) new_chunk.bytes_read += read_str_len # If fog chunk -- 2.30.2 From f9c66d2c0d2fcbf8d3b3ee063041c842ee8d273f Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sun, 30 Jul 2023 22:58:23 +0200 Subject: [PATCH 34/34] io_scene_3ds: Advanced world bitmap and color keyframe import and export --- io_scene_3ds/export_3ds.py | 29 ++++++++++++++++++++++++++++- io_scene_3ds/import_3ds.py | 29 +++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index c9a67702c..3cced15fa 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1455,7 +1455,34 @@ def make_ambient_node(world): amb_node_header_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT)) amb_node.add_subchunk(amb_node_header_chunk) - if world.animation_data.action: + if world.use_nodes and world.node_tree.animation_data.action: + action = world.node_tree.animation_data.action + ambinode = next((nd for nd in world.node_tree.nodes if nd.type in {'RGB', 'EMISSION'}), False) + if ambinode and action.fcurves: + fcurves = action.fcurves + fcurves.update() + kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points] + ambipath = ('nodes[\"RGB\"].outputs[0].default_value' if ambinode.type == 'RGB' else + 'nodes[\"Emission\"].inputs[0].default_value') + nkeys = len(kframes) + if not 0 in kframes: + kframes.append(0) + nkeys = nkeys + 1 + kframes = sorted(set(kframes)) + track_chunk.add_variable("track_flags", _3ds_ushort(0x40)) + track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start))) + track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end))) + track_chunk.add_variable("nkeys", _3ds_uint(nkeys)) + + for i, frame in enumerate(kframes): + ambient = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == ambipath] + if not ambient: + ambient.append(world.color) + track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame))) + track_chunk.add_variable("tcb_flags", _3ds_ushort()) + track_chunk.add_variable("color", _3ds_float_color(ambient[:3])) + + elif world.animation_data.action: action = world.animation_data.action if action.fcurves: fcurves = action.fcurves diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 5f0971d18..2cce3b0c7 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -709,19 +709,26 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # If background chunk elif CreateWorld and new_chunk.ID == SOLIDBACKGND: + backgroundcolor = mathutils.Color((0.1, 0.1, 0.1)) if contextWorld is None: path, filename = os.path.split(file.name) realname, ext = os.path.splitext(filename) contextWorld = bpy.data.worlds.new("Background: " + realname) context.scene.world = contextWorld contextWorld.use_nodes = True + worldnodes = contextWorld.node_tree.nodes + backgroundnode = worldnodes['Background'] read_chunk(file, temp_chunk) if temp_chunk.ID == COLOR_F: - contextWorld.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) + backgroundcolor = read_float_array(temp_chunk) elif temp_chunk.ID == LIN_COLOR_F: - contextWorld.node_tree.nodes['Background'].inputs[0].default_value[:3] = read_float_array(temp_chunk) + backgroundcolor = read_float_array(temp_chunk) else: skip_to_end(file, temp_chunk) + backgroundmix = next((wn for wn in worldnodes if wn.type in {'MIX', 'MIX_RGB'}), False) + backgroundnode.inputs[0].default_value[:3] = backgroundcolor + if backgroundmix: + backgroundmix.inputs[2].default_value[:3] = backgroundcolor new_chunk.bytes_read += temp_chunk.bytes_read # If bitmap chunk @@ -738,9 +745,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI bitmap_mix = nodes.new(type='ShaderNodeMixRGB') bitmapnode = nodes.new(type='ShaderNodeTexEnvironment') bitmap_mix.label = "Solid Color" - bitmapnode.label = bitmap_name - bitmap_mix.location = (-250, 300) - bitmapnode.location = (-300, 300) + bitmapnode.label = "Bitmap: " + bitmap_name + bitmap_mix.location = (-250, 360) + bitmapnode.location = (-600, 300) bitmap_mix.inputs[2].default_value = nodes['Background'].inputs[0].default_value bitmapnode.image = load_image(bitmap_name, dirname, place_holder=False, recursive=IMAGE_SEARCH, check_existing=True) bitmap_mix.inputs[0].default_value = 0.0 if bitmapnode.image is not None else 1.0 @@ -1143,13 +1150,17 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI worldout = nodes['World Output'] mixshade = nodes.new(type='ShaderNodeMixShader') ambinode = nodes.new(type='ShaderNodeEmission') + ambilite = nodes.new(type='ShaderNodeRGB') + ambilite.label = "Ambient Color" ambinode.inputs[0].default_value[:3] = child.color ambinode.location = (10, 150) worldout.location = (600, 200) mixshade.location = (300, 300) + ambilite.location = (-250, 150) 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]) + links.new(ambilite.outputs[0], ambinode.inputs[0]) 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 @@ -1195,11 +1206,13 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI keyframe_data = {} default_data = child.color[:] child.color = read_track_data(new_chunk)[0] - ambinode.inputs[0].default_value[:3] = child.color + ambilite.color = child.color + ambinode.inputs[0].default_value[:3] = ambilite.color for keydata in keyframe_data.items(): - child.color = keydata[1] - child.keyframe_insert(data_path="color", frame=keydata[0]) ambinode.inputs[0].default_value[:3] = keydata[1] + child.color = ambilite.outputs[0].default_value[:3] = keydata[1] + child.keyframe_insert(data_path="color", frame=keydata[0]) + nodetree.keyframe_insert(data_path="nodes[\"RGB\"].outputs[0].default_value", frame=keydata[0]) nodetree.keyframe_insert(data_path="nodes[\"Emission\"].inputs[0].default_value", frame=keydata[0]) contextTrack_flag = False -- 2.30.2