From 7504cb5e96d4e45e2ddf265b821904c8d0a34410 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 22 Jul 2023 17:40:41 +0200 Subject: [PATCH 01/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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 From a70ce67693e08853124e5e0b2f80aa67f2fcb37a Mon Sep 17 00:00:00 2001 From: NRGSille Date: Mon, 31 Jul 2023 00:36:28 +0200 Subject: [PATCH 35/42] io_scene_3ds: Added imperial unit measure --- io_scene_3ds/export_3ds.py | 8 +++++++- io_scene_3ds/import_3ds.py | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 3cced15fa..73b5339ed 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1541,12 +1541,18 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, unit_measure = 1.0 if use_scene_unit: unit_length = scene.unit_settings.length_unit - if unit_length == 'KILOMETERS': + if unit_length == 'MILES': + unit_measure = 0.000621371 + elif unit_length == 'KILOMETERS': unit_measure = 0.001 + elif unit_length == 'INCHES': + unit_measure = 39.37007874 elif unit_length == 'CENTIMETERS': unit_measure = 100 elif unit_length == 'MILLIMETERS': unit_measure = 1000 + elif unit_length == 'THOU': + unit_measure = 39370.07874 elif unit_length == 'MICROMETERS': unit_measure = 1000000 diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 2cce3b0c7..e9b640ed4 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1475,12 +1475,18 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, if UNITS: unit_length = scn.unit_settings.length_unit - if unit_length == 'KILOMETERS': + if unit_length == 'MILES': + MEASURE = 1609.344 + elif unit_length == 'KILOMETERS': MEASURE = 1000.0 + elif unit_length == 'INCHES': + MEASURE = 0.3048 elif unit_length == 'CENTIMETERS': MEASURE = 0.01 elif unit_length == 'MILLIMETERS': MEASURE = 0.001 + elif unit_length == 'THOU': + MEASURE = 0.0000254 elif unit_length == 'MICROMETERS': MEASURE = 0.000001 -- 2.30.2 From ed8255c7a6f7d87c352660691fe7e94d19ea31ae Mon Sep 17 00:00:00 2001 From: NRGSille Date: Mon, 31 Jul 2023 12:45:10 +0200 Subject: [PATCH 36/42] io_scene_3ds: Added feet to imperial unit measure --- io_scene_3ds/export_3ds.py | 2 ++ io_scene_3ds/import_3ds.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 73b5339ed..005d5932c 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1545,6 +1545,8 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, unit_measure = 0.000621371 elif unit_length == 'KILOMETERS': unit_measure = 0.001 + elif unit_length == 'FEET': + unit_measure = 3.280839895 elif unit_length == 'INCHES': unit_measure = 39.37007874 elif unit_length == 'CENTIMETERS': diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index ad657f493..850396254 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1479,8 +1479,10 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, MEASURE = 1609.344 elif unit_length == 'KILOMETERS': MEASURE = 1000.0 - elif unit_length == 'INCHES': + elif unit_length == 'FEET': MEASURE = 0.3048 + elif unit_length == 'INCHES': + MEASURE = 0.0254 elif unit_length == 'CENTIMETERS': MEASURE = 0.01 elif unit_length == 'MILLIMETERS': -- 2.30.2 From 5ee88e7de55cdc803b1f28a190144ef4d8e830db Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 1 Aug 2023 16:06:32 +0200 Subject: [PATCH 37/42] io_scene_3ds: Added fog and gradient import and export --- io_scene_3ds/export_3ds.py | 79 +++++++++++++++++------- io_scene_3ds/import_3ds.py | 120 ++++++++++++++++++++++++++++++++----- 2 files changed, 162 insertions(+), 37 deletions(-) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 6f8e2e589..2715d5b66 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -38,8 +38,10 @@ 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 +FOG = 0x2200 # The fog atmosphere settings +USE_FOG = 0x2201 # The fog atmosphere flag +LAYER_FOG = 0x2302 # The fog layer atmosphere settings +USE_LAYER_FOG = 0x2303 # The fog layer atmosphere flag MATERIAL = 45055 # 0xAFFF // This stored the texture info OBJECT = 16384 # 0x4000 // This stores the faces, vertices, etc... @@ -1468,7 +1470,7 @@ def make_ambient_node(world): emission = next((lk.from_socket.node for lk in ambilinks if lk.to_node.type in ambioutput), False) ambinode = next((lk.from_socket.node for lk in ambilinks if lk.to_node.type == 'EMISSION'), emission) 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 + ambipath = ('nodes[\"RGB\"].outputs[0].default_value' if ambinode and ambinode.type == 'RGB' else 'nodes[\"Emission\"].inputs[0].default_value') nkeys = len(kframes) if not 0 in kframes: @@ -1615,16 +1617,18 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, # Add BACKGROUND and BITMAP if world.use_nodes: + bgtype = 'BACKGROUND' ntree = world.node_tree.links background_color_chunk = _3ds_chunk(RGB) background_chunk = _3ds_chunk(SOLIDBACKGND) background_flag = _3ds_chunk(USE_SOLIDBGND) - bgtype = 'BACKGROUND' + bgmixer = 'BACKGROUND', 'MIX', 'MIX_RGB' 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_mixer = next((lk.from_node.type for lk in ntree if lk.from_node.type in bgmixer 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) + gradient = next((lk.from_node.color_ramp.elements for lk in ntree if lk.from_node.type == 'VALTORGB' and lk.to_node.type in bgmixer), False) background_color_chunk.add_variable("color", _3ds_float_color(bg_color)) background_chunk.add_subchunk(background_color_chunk) if bg_image: @@ -1633,28 +1637,59 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, background_image.add_variable("image", _3ds_string(sane_name(bg_image))) object_info.add_subchunk(background_image) object_info.add_subchunk(background_chunk) + + # Add VGRADIENT chunk + if gradient and len(gradient) >= 3: + gradient_chunk = _3ds_chunk(VGRADIENT) + background_flag = _3ds_chunk(USE_VGRADIENT) + gradient_chunk.add_variable("midpoint", _3ds_float(gradient[1].position)) + gradient_topcolor_chunk = _3ds_chunk(RGB) + gradient_topcolor_chunk.add_variable("color", _3ds_float_color(gradient[2].color[:3])) + gradient_chunk.add_subchunk(gradient_topcolor_chunk) + gradient_midcolor_chunk = _3ds_chunk(RGB) + gradient_midcolor_chunk.add_variable("color", _3ds_float_color(gradient[1].color[:3])) + gradient_chunk.add_subchunk(gradient_midcolor_chunk) + gradient_lowcolor_chunk = _3ds_chunk(RGB) + gradient_lowcolor_chunk.add_variable("color", _3ds_float_color(gradient[0].color[:3])) + gradient_chunk.add_subchunk(gradient_lowcolor_chunk) + object_info.add_subchunk(gradient_chunk) object_info.add_subchunk(background_flag) - # Add LAYER_FOG settings - 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) + # Add FOG + fognode = next((lk.from_socket.node for lk in ntree if lk.from_socket.node.type == 'VOLUME_ABSORPTION' and lk.to_socket.node.type in bgshade), False) + if fognode: + fog_chunk = _3ds_chunk(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['Color'].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['Density'].default_value)) - fog_chunk.add_variable("flags", _3ds_uint(fogflag)) + use_fog_flag = _3ds_chunk(USE_FOG) + fog_density = fognode.inputs['Density'].default_value * 100 + fog_color_chunk.add_variable("color", _3ds_float_color(fognode.inputs[0].default_value[:3])) + fog_chunk.add_variable("nearplane", _3ds_float(world.mist_settings.start)) + fog_chunk.add_variable("nearfog", _3ds_float(fog_density * 0.5)) + fog_chunk.add_variable("farplane", _3ds_float(world.mist_settings.depth)) + fog_chunk.add_variable("farfog", _3ds_float(fog_density + fog_density * 0.5)) 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) + + # Add LAYER FOG + foglayer = next((lk.from_socket.node for lk in ntree if lk.from_socket.node.type == 'VOLUME_SCATTER' and lk.to_socket.node.type in bgshade), False) + if foglayer: + layerfog_flag = 0 + if world.mist_settings.falloff == 'QUADRATIC': + layerfog_flag |= 0x1 + if world.mist_settings.falloff == 'INVERSE_QUADRATIC': + layerfog_flag |= 0x2 + layerfog_chunk = _3ds_chunk(LAYER_FOG) + layerfog_color_chunk = _3ds_chunk(RGB) + use_fog_flag = _3ds_chunk(USE_LAYER_FOG) + layerfog_color_chunk.add_variable("color", _3ds_float_color(foglayer.inputs[0].default_value[:3])) + layerfog_chunk.add_variable("lowZ", _3ds_float(world.mist_settings.start)) + layerfog_chunk.add_variable("highZ", _3ds_float(world.mist_settings.height)) + layerfog_chunk.add_variable("density", _3ds_float(foglayer.inputs[1].default_value)) + layerfog_chunk.add_variable("flags", _3ds_uint(layerfog_flag)) + layerfog_chunk.add_subchunk(layerfog_color_chunk) + object_info.add_subchunk(layerfog_chunk) + if fognode or foglayer and layer.use_pass_mist: + object_info.add_subchunk(use_fog_flag) if use_keyframes 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 850396254..42a49bc7f 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -45,8 +45,11 @@ 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 +FOG = 0x2200 # The fog atmosphere settings +USE_FOG = 0x2201 # The fog atmosphere flag +FOG_BGND = 0x2210 # The fog atmosphere background flag +LAYER_FOG = 0x2302 # The fog layer atmosphere settings +USE_LAYER_FOG = 0x2303 # The fog layer atmosphere flag MATERIAL = 0xAFFF # This stored the texture info OBJECT = 0x4000 # This stores the faces, vertices, etc... @@ -746,16 +749,95 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI bitmapnode = nodes.new(type='ShaderNodeTexEnvironment') bitmap_mix.label = "Solid Color" 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 + bitmap_mix.inputs[0].default_value = 0.5 if bitmapnode.image is not None else 1.0 + bitmapnode.location = (-600, 360) if bitmapnode.image is not None else (-600, 300) + bitmap_mix.location = (-250, 300) 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 + # If gradient chunk: + elif CreateWorld and new_chunk.ID == VGRADIENT: + if contextWorld is None: + path, filename = os.path.split(file.name) + realname, ext = os.path.splitext(filename) + contextWorld = bpy.data.worlds.new("Gradient: " + realname) + context.scene.world = contextWorld + contextWorld.use_nodes = True + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes + gradientnode = nodes.new(type='ShaderNodeValToRGB') + gradientnode.location = (-600, 100) + gradientnode.label = "Gradient" + backgroundmix = next((wn for wn in worldnodes if wn.type in {'MIX', 'MIX_RGB'}), False) + if backgroundmix: + links.new(gradientnode.outputs['Color'], backgroundmix.inputs[2]) + else: + links.new(gradientnode.outputs['Color'], nodes['Background'].inputs[0]) + gradientnode.color_ramp.elements.new(read_float(new_chunk)) + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + gradientnode.color_ramp.elements[2].color[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + gradientnode.color_ramp.elements[2].color[:3] = read_float_array(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + gradientnode.color_ramp.elements[1].color[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + gradientnode.color_ramp.elements[1].color[:3] = read_float_array(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + gradientnode.color_ramp.elements[0].color[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + gradientnode.color_ramp.elements[0].color[:3] = read_float_array(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + + # If fog chunk: + elif CreateWorld and new_chunk.ID == FOG: + if contextWorld is None: + path, filename = os.path.split(file.name) + realname, ext = os.path.splitext(filename) + newWorld = bpy.data.worlds.new("LayerFog: " + realname) + context.scene.world = contextWorld + contextWorld.use_nodes = True + links = contextWorld.node_tree.links + nodes = contextWorld.node_tree.nodes + fognode = nodes.new(type='ShaderNodeVolumeAbsorption') + fognode.label = "Fog" + fognode.location = (300, 60) + volumemix = next((wn for wn in worldnodes if wn.label == 'Volume' and wn.type in {'ADD_SHADER', 'MIX_SHADER'}), False) + if volumemix: + links.new(fognode.outputs['Volume'], volumemix.inputs[1]) + else: + links.new(fognode.outputs[0], nodes['World Output'].inputs[1]) + contextWorld.mist_settings.use_mist = True + contextWorld.mist_settings.start = read_float(new_chunk) + nearfog = read_float(new_chunk) * 0.01 + contextWorld.mist_settings.depth = read_float(new_chunk) + farfog = read_float(new_chunk) * 0.01 + fognode.inputs[1].default_value = (nearfog + farfog) * 0.5 + read_chunk(file, temp_chunk) + if temp_chunk.ID == COLOR_F: + fognode.inputs[0].default_value[:3] = read_float_array(temp_chunk) + elif temp_chunk.ID == LIN_COLOR_F: + fognode.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 CreateWorld and new_chunk.ID == FOG_BGND: + pass + + # If layer 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.""" @@ -767,16 +849,23 @@ 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 + mxvolume = nodes.new(type='ShaderNodeMixShader') layerfog = nodes.new(type='ShaderNodeVolumeScatter') layerfog.label = "Layer Fog" - layerfog.location = (300, 100) - links.new(layerfog.outputs['Volume'], nodes['World Output'].inputs['Volume']) + mxvolume.label = "Volume" + layerfog.location = (10, -60) + mxvolume.location = (300, 50) + links.new(layerfog.outputs['Volume'], mxvolume.inputs[2]) + links.new(mxvolume.outputs[0], nodes['World Output'].inputs[1]) + fognode = next((wn for wn in worldnodes if wn.type == 'VOLUME_ABSORPTION'), False) + if fognode: + links.new(fognode.outputs['Volume'], mxvolume.inputs[1]) + fognode.location = (10, 60) 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) - contextWorld.mist_settings.height = contextWorld.mist_settings.depth * 0.5 - layerfog.inputs['Density'].default_value = read_float(new_chunk) + contextWorld.mist_settings.height = read_float(new_chunk) + layerfog.inputs[1].default_value = read_float(new_chunk) layerfog_flag = read_long(new_chunk) if layerfog_flag == 0: contextWorld.mist_settings.falloff = 'LINEAR' @@ -1152,11 +1241,12 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI ambinode = nodes.new(type='ShaderNodeEmission') ambilite = nodes.new(type='ShaderNodeRGB') ambilite.label = "Ambient Color" + mixshade.label = "Surface" ambinode.inputs[0].default_value[:3] = child.color - ambinode.location = (10, 150) - worldout.location = (600, 200) - mixshade.location = (300, 300) - ambilite.location = (-250, 150) + ambinode.location = (10, 180) + worldout.location = (600, 180) + mixshade.location = (300, 280) + ambilite.location = (-250, 100) 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]) -- 2.30.2 From 4655d96488fa6e8417d29d675e8d8e030e9e9599 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 3 Aug 2023 00:34:17 +0200 Subject: [PATCH 38/42] io_scene_3ds: Added spot aspect and projector --- io_scene_3ds/export_3ds.py | 20 ++++++++++++++++++++ io_scene_3ds/import_3ds.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 2715d5b66..ab7c12ca1 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -110,6 +110,9 @@ LIGHT_SPOT_SHADOWED = 0x4630 # Light spot shadow flag LIGHT_SPOT_LSHADOW = 0x4641 # Light spot shadow parameters LIGHT_SPOT_SEE_CONE = 0x4650 # Light spot show cone flag LIGHT_SPOT_RECTANGLE = 0x4651 # Light spot rectangle flag +LIGHT_SPOT_OVERSHOOT = 0x4652 # Light spot overshoot flag +LIGHT_SPOT_PROJECTOR = 0x4653 # Light spot projection bitmap +LIGHT_SPOT_ASPECT = 0x4657 # Light spot aspect ratio # >------ sub defines of CAMERA OBJECT_CAM_RANGES = 0x4720 # The camera range values @@ -1881,6 +1884,23 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, if ob.data.use_square: spot_square_chunk = _3ds_chunk(LIGHT_SPOT_RECTANGLE) spotlight_chunk.add_subchunk(spot_square_chunk) + if ob.scale.x and ob.scale.y != 0.0: + spot_aspect_chunk = _3ds_chunk(LIGHT_SPOT_ASPECT) + spot_aspect_chunk.add_variable("aspect", _3ds_float(round((ob.scale.x / ob.scale.y),4))) + spotlight_chunk.add_subchunk(spot_aspect_chunk) + if ob.data.use_nodes: + links = ob.data.node_tree.links + bptype = 'EMISSION' + bpmix = 'MIX', 'MIX_RGB', 'EMISSION' + bptex = 'TEX_IMAGE', 'TEX_ENVIRONMENT' + bpout = 'ADD_SHADER', 'MIX_SHADER', 'OUTPUT_LIGHT' + bshade = next((lk.from_node.type for lk in links if lk.from_node.type == bptype and lk.to_node.type in bpout), None) + bpnode = next((lk.from_node.type for lk in links if lk.from_node.type in bpmix and lk.to_node.type == bshade), bshade) + bitmap = next((lk.from_node.image for lk in links if lk.from_node.type in bptex and lk.to_node.type == bpnode), False) + if bitmap and bitmap is not None: + spot_projector_chunk = _3ds_chunk(LIGHT_SPOT_PROJECTOR) + spot_projector_chunk.add_variable("image", _3ds_string(sane_name(bitmap.name))) + spotlight_chunk.add_subchunk(spot_projector_chunk) obj_light_chunk.add_subchunk(spotlight_chunk) # Add light to object chunk diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 732a70eee..1d51491d6 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -1156,6 +1156,24 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextLamp.data.show_cone = True elif CreateLightObject and new_chunk.ID == LIGHT_SPOT_RECTANGLE: # Square flag contextLamp.data.use_square = True + elif CreateLightObject and new_chunk.ID == LIGHT_SPOT_ASPECT: # Aspect + contextLamp.empty_display_size = read_float(new_chunk) + elif CreateLightObject and new_chunk.ID == LIGHT_SPOT_PROJECTOR: # Projection + contextLamp.data.use_nodes = True + nodes = contextLamp.data.node_tree.nodes + links = contextLamp.data.node_tree.links + gobo_name, read_str_len = read_string(file) + new_chunk.bytes_read += read_str_len + projection = nodes.new(type='ShaderNodeTexImage') + projection.label = gobo_name + projection.location = (-340, 360) + projection.image = load_image(gobo_name, dirname, place_holder=False, recursive=IMAGE_SEARCH, check_existing=True) + emitnode = next((node for node in nodes if node.type == 'EMISSION'), False) + emission = emitnode if emitnode else nodes.new(type='ShaderNodeEmission') + emission.label = "Projector" + emission.location = (0, 300) + links.new(emission.outputs['Emission'], nodes['Light Output'].inputs[0]) + links.new(projection.outputs['Color'], emission.inputs[0]) elif CreateLightObject and new_chunk.ID == OBJECT_HIERARCHY: # Hierarchy child_id = get_hierarchy(new_chunk) elif CreateLightObject and new_chunk.ID == OBJECT_PARENT: @@ -1613,6 +1631,18 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, # Select all new objects for ob in imported_objects: + if ob.type == 'LIGHT' and ob.data.type == 'SPOT': + aspect = ob.empty_display_size + fac = 1.0 + ratio = (fac / aspect) + align = fac - (ratio - aspect if ratio > fac else aspect - ratio) + shift = align + (align / 2.0) + if aspect > 1.0: + ob.scale.x = fac + align + ob.scale.y = fac - align + elif aspect < 1.0: + ob.scale.x = fac - align + ob.scale.y = fac + align ob.select_set(True) if not APPLY_MATRIX: # Reset transform bpy.ops.object.rotation_clear() -- 2.30.2 From 9cef03ab7f01584472577943d304662758c31a35 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Thu, 3 Aug 2023 23:28:16 +0200 Subject: [PATCH 39/42] io_scene_3ds: Added light radius, distance and attenuation --- io_scene_3ds/export_3ds.py | 12 ++++++++++++ io_scene_3ds/import_3ds.py | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index ab7c12ca1..fe090399e 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -104,6 +104,9 @@ OBJECT_PARENT = 0x4F10 # Parent id of the object # >------ Sub defines of LIGHT LIGHT_MULTIPLIER = 0x465B # The light energy factor +LIGHT_INNER_RANGE = 0x4659 # Light inner range value +LIGHT_OUTER_RANGE = 0x465A # Light outer range value +LIGHT_ATTENUATE = 0x4625 # Light attenuation flag LIGHT_SPOTLIGHT = 0x4610 # The target of a spotlight LIGHT_SPOT_ROLL = 0x4656 # Light spot roll angle LIGHT_SPOT_SHADOWED = 0x4630 # Light spot shadow flag @@ -1851,13 +1854,22 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, obj_light_chunk = _3ds_chunk(OBJECT_LIGHT) color_float_chunk = _3ds_chunk(RGB) light_distance = translation[ob.name] + light_attenuate = _3ds_chunk(LIGHT_ATTENUATE) + light_inner_range = _3ds_chunk(LIGHT_INNER_RANGE) + light_outer_range = _3ds_chunk(LIGHT_OUTER_RANGE) light_energy_factor = _3ds_chunk(LIGHT_MULTIPLIER) object_chunk.add_variable("light", _3ds_string(sane_name(ob.name))) obj_light_chunk.add_variable("location", _3ds_point_3d(light_distance)) color_float_chunk.add_variable("color", _3ds_float_color(ob.data.color)) + light_outer_range.add_variable("distance", _3ds_float(ob.data.cutoff_distance)) + light_inner_range.add_variable("radius", _3ds_float(ob.data.shadow_soft_size)) light_energy_factor.add_variable("energy", _3ds_float(ob.data.energy * 0.001)) obj_light_chunk.add_subchunk(color_float_chunk) + obj_light_chunk.add_subchunk(light_outer_range) + obj_light_chunk.add_subchunk(light_inner_range) obj_light_chunk.add_subchunk(light_energy_factor) + if ob.data.use_custom_distance: + obj_light_chunk.add_subchunk(light_attenuate) if ob.data.type == 'SPOT': cone_angle = math.degrees(ob.data.spot_size) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 3f493375c..16fe88548 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -128,6 +128,7 @@ LIGHT_RAY_BIAS = 0x4658 # Light ray bias value LIGHT_INNER_RANGE = 0x4659 # The light inner range LIGHT_OUTER_RANGE = 0x465A # The light outer range LIGHT_MULTIPLIER = 0x465B # The light energy factor +LIGHT_ATTENUATE = 0x4625 # Light attenuation flag LIGHT_AMBIENT_LIGHT = 0x4680 # Light ambient flag # >------ sub defines of CAMERA @@ -1128,8 +1129,14 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextMatrix = None # Reset matrix elif CreateLightObject and new_chunk.ID == COLOR_F: # Color contextLamp.data.color = read_float_array(new_chunk) + elif CreateLightObject and new_chunk.ID == LIGHT_OUTER_RANGE: # Distance + contextLamp.data.cutoff_distance = read_float(new_chunk) + elif CreateLightObject and new_chunk.ID == LIGHT_INNER_RANGE: # Radius + contextLamp.data.shadow_soft_size = read_float(new_chunk) elif CreateLightObject and new_chunk.ID == LIGHT_MULTIPLIER: # Intensity contextLamp.data.energy = (read_float(new_chunk) * 1000) + elif CreateLightObject and new_chunk.ID == LIGHT_ATTENUATE: # Attenuation + contextLamp.data.use_custom_distance = True # If spotlight chunk elif CreateLightObject and new_chunk.ID == LIGHT_SPOTLIGHT: # Spotlight -- 2.30.2 From 27c7ab156e59ca5e71e0bc7ceabccf9b9f231f12 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 4 Aug 2023 23:44:18 +0200 Subject: [PATCH 40/42] io_scene_3ds: Added pivot origin option and fixed camera and light ranges --- io_scene_3ds/__init__.py | 10 +++++- io_scene_3ds/export_3ds.py | 9 +++-- io_scene_3ds/import_3ds.py | 67 ++++++++++++++++++++++---------------- 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index bbf2f1fe2..a9f7f56b8 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -57,7 +57,12 @@ class Import3DS(bpy.types.Operator, ImportHelper): ) use_scene_unit: BoolProperty( name="Scene Units", - description="Converts to scene unit length settings", + description="Convert to scene unit length settings", + default=False, + ) + use_center_pivot: BoolProperty( + name="Pivot Origin", + description="Move all geometry to pivot origin", default=False, ) use_image_search: BoolProperty( @@ -177,6 +182,9 @@ class MAX3DS_PT_import_transform(bpy.types.Panel): 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_center_pivot") + layrow.label(text="", icon='OVERLAY' if operator.use_center_pivot else 'PIVOT_ACTIVE') + 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) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index fe090399e..503b20ddf 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1858,12 +1858,13 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, light_inner_range = _3ds_chunk(LIGHT_INNER_RANGE) light_outer_range = _3ds_chunk(LIGHT_OUTER_RANGE) light_energy_factor = _3ds_chunk(LIGHT_MULTIPLIER) + light_ratio = ob.data.energy if ob.data.type == 'SUN' else ob.data.energy * 0.001 object_chunk.add_variable("light", _3ds_string(sane_name(ob.name))) obj_light_chunk.add_variable("location", _3ds_point_3d(light_distance)) color_float_chunk.add_variable("color", _3ds_float_color(ob.data.color)) light_outer_range.add_variable("distance", _3ds_float(ob.data.cutoff_distance)) - light_inner_range.add_variable("radius", _3ds_float(ob.data.shadow_soft_size)) - light_energy_factor.add_variable("energy", _3ds_float(ob.data.energy * 0.001)) + light_inner_range.add_variable("radius", _3ds_float(ob.data.shadow_soft_size * 100)) + light_energy_factor.add_variable("energy", _3ds_float(light_ratio)) obj_light_chunk.add_subchunk(color_float_chunk) obj_light_chunk.add_subchunk(light_outer_range) obj_light_chunk.add_subchunk(light_inner_range) @@ -1943,6 +1944,7 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, for ob in camera_objects: object_chunk = _3ds_chunk(OBJECT) camera_chunk = _3ds_chunk(OBJECT_CAMERA) + crange_chunk = _3ds_chunk(OBJECT_CAM_RANGES) camera_distance = translation[ob.name] camera_target = calc_target(camera_distance, rotation[ob.name].x, rotation[ob.name].z) object_chunk.add_variable("camera", _3ds_string(sane_name(ob.name))) @@ -1950,6 +1952,9 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, camera_chunk.add_variable("target", _3ds_point_3d(camera_target)) camera_chunk.add_variable("roll", _3ds_float(round(rotation[ob.name].y, 6))) camera_chunk.add_variable("lens", _3ds_float(ob.data.lens)) + crange_chunk.add_variable("clipstart", _3ds_float(ob.data.clip_start * 0.1)) + crange_chunk.add_variable("clipend", _3ds_float(ob.data.clip_end * 0.1)) + camera_chunk.add_subchunk(crange_chunk) object_chunk.add_subchunk(camera_chunk) # Add hierachy chunks with ID from object_id dictionary diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 16fe88548..4e21198a1 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -890,24 +890,6 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI elif CreateWorld and new_chunk.ID in {USE_FOG, USE_LAYER_FOG}: context.view_layer.use_pass_mist = True - # If object chunk - can be material and mesh, light and spot or camera - elif new_chunk.ID == OBJECT: - if CreateBlenderObject: - putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMesh_flag, - contextMeshMaterials, contextMesh_smooth, WORLD_MATRIX) - - contextMesh_vertls = [] - contextMesh_facels = [] - contextMeshMaterials = [] - contextMesh_flag = None - contextMesh_smooth = None - contextMeshUV = None - contextMatrix = None - - CreateBlenderObject = True if CreateMesh else False - contextObName, read_str_len = read_string(file) - new_chunk.bytes_read += read_str_len - # If material chunk elif new_chunk.ID == MATERIAL: contextAlpha = True @@ -1058,6 +1040,25 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI elif new_chunk.ID == MAT_TEX2_MAP: read_texture(new_chunk, temp_chunk, "Tex", 'TEXTURE') + # If object chunk - can be mesh, light and spot or camera + elif new_chunk.ID == OBJECT: + if CreateBlenderObject: + putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMesh_flag, + contextMeshMaterials, contextMesh_smooth, WORLD_MATRIX) + + contextMesh_vertls = [] + contextMesh_facels = [] + contextMeshMaterials = [] + contextMesh_flag = None + contextMesh_smooth = None + contextMeshUV = None + contextMatrix = None + + CreateBlenderObject = True if CreateMesh else False + CreateLightObject = CreateCameraObject = False + contextObName, read_str_len = read_string(file) + new_chunk.bytes_read += read_str_len + # If mesh chunk elif new_chunk.ID == OBJECT_MESH: pass @@ -1132,7 +1133,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI elif CreateLightObject and new_chunk.ID == LIGHT_OUTER_RANGE: # Distance contextLamp.data.cutoff_distance = read_float(new_chunk) elif CreateLightObject and new_chunk.ID == LIGHT_INNER_RANGE: # Radius - contextLamp.data.shadow_soft_size = read_float(new_chunk) + contextLamp.data.shadow_soft_size = (read_float(new_chunk) * 0.01) elif CreateLightObject and new_chunk.ID == LIGHT_MULTIPLIER: # Intensity contextLamp.data.energy = (read_float(new_chunk) * 1000) elif CreateLightObject and new_chunk.ID == LIGHT_ATTENUATE: # Attenuation @@ -1207,6 +1208,11 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextCamera.rotation_euler.z = direction[1] contextCamera.data.lens = read_float(new_chunk) # Focal length contextMatrix = None # Reset matrix + elif CreateCameraObject and new_chunk.ID == OBJECT_CAM_RANGES: # Range + camrange = read_float(new_chunk) + startrange = camrange if camrange >= 0.01 else 0.1 + contextCamera.data.clip_start = startrange * CONSTRAIN + contextCamera.data.clip_end = read_float(new_chunk) * CONSTRAIN elif CreateCameraObject and new_chunk.ID == OBJECT_HIERARCHY: # Hierarchy child_id = get_hierarchy(new_chunk) elif CreateCameraObject and new_chunk.ID == OBJECT_PARENT: @@ -1560,8 +1566,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI # IMPORT # ########## -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, CURSOR=False): +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, CURSOR=False, PIVOT=False): print("importing 3DS: %r..." % (filepath), end="") @@ -1643,9 +1650,13 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, ob.scale.y = (square / (math.sqrt(pow(aspect,2) + 1.0))) ob.scale.z = 1.0 ob.select_set(True) - if not APPLY_MATRIX: # Reset transform - bpy.ops.object.rotation_clear() - bpy.ops.object.location_clear() + if ob.type == 'MESH': + if PIVOT: + bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN') + if not APPLY_MATRIX: # Reset transform + bpy.ops.object.rotation_clear() + bpy.ops.object.location_clear() + bpy.ops.object.scale_clear() """ if IMPORT_AS_INSTANCE: @@ -1712,11 +1723,11 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, def load(operator, context, filepath="", constrain_size=0.0, use_scene_unit=False, - use_image_search=True, object_filter=None, use_world_matrix=False, - use_keyframes=True, use_apply_transform=True, global_matrix=None, use_cursor=False): + use_image_search=True, object_filter=None, use_world_matrix=False, use_keyframes=True, + use_apply_transform=True, global_matrix=None, use_cursor=False, use_center_pivot=False): 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=use_keyframes, APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, CURSOR=use_cursor,) + IMAGE_SEARCH=use_image_search, FILTER=object_filter, WORLD_MATRIX=use_world_matrix, KEYFRAME=use_keyframes, + APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, CURSOR=use_cursor, PIVOT=use_center_pivot,) return {'FINISHED'} -- 2.30.2 From 50c8062fca8a7e8ae40b5fa316069e30f0940618 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Tue, 26 Sep 2023 19:33:27 +0200 Subject: [PATCH 41/42] io_scene_3ds: Update for Principled BSDF specularity --- io_scene_3ds/__init__.py | 6 +++--- io_scene_3ds/export_3ds.py | 12 ++++++------ io_scene_3ds/import_3ds.py | 2 ++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index a9f7f56b8..3f4e65421 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -17,9 +17,9 @@ from bpy.props import ( import bpy bl_info = { "name": "Autodesk 3DS format", - "author": "Bob Holcomb, Campbell Barton, Andreas Atteneder, Sebastian Schrand", - "version": (2, 4, 6), - "blender": (3, 6, 0), + "author": "Bob Holcomb, Campbell Barton, Sebastian Schrand", + "version": (2, 4, 7), + "blender": (4, 0, 0), "location": "File > Import-Export", "description": "3DS Import/Export meshes, UVs, materials, textures, " "cameras, lamps & animation", diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index 9d77718ea..bc543216c 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -698,7 +698,7 @@ def make_material_chunk(material, image): shading.add_variable("shading", _3ds_ushort(3)) # Phong shading material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, wrap.emission_color[:3])) material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, wrap.base_color[:3])) - material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specular_color[:])) + material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, wrap.specular_tint[:3])) material_chunk.add_subchunk(make_percent_subchunk(MATSHINESS, 1 - wrap.roughness)) material_chunk.add_subchunk(make_percent_subchunk(MATSHIN2, wrap.specular)) material_chunk.add_subchunk(make_percent_subchunk(MATSHIN3, wrap.metallic)) @@ -1211,10 +1211,10 @@ def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size): track_chunk.add_variable("fov", _3ds_float(round(math.degrees(fov), 4))) elif ID == HOTSPOT_TRACK_TAG: # Hotspot - beam_angle = math.degrees(ob.data.spot_size) for i, frame in enumerate(kframes): + beamsize = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_size'), ob.data.spot_size) blend = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_blend'), ob.data.spot_blend) - hot_spot = beam_angle - (blend * math.floor(beam_angle)) + hot_spot = math.degrees(beamsize) - (blend * math.floor(math.degrees(beamsize))) track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame))) track_chunk.add_variable("tcb_flags", _3ds_ushort()) track_chunk.add_variable("hotspot", _3ds_float(round(hot_spot, 4))) @@ -1647,14 +1647,14 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, 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 bgmixer 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) + bg_image = next((lk.from_node.image for lk in ntree if lk.from_node.type in bg_tex and lk.to_node.type == bg_mixer), False) gradient = next((lk.from_node.color_ramp.elements for lk in ntree if lk.from_node.type == 'VALTORGB' and lk.to_node.type in bgmixer), 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))) + background_image.add_variable("image", _3ds_string(sane_name(bg_image.name))) object_info.add_subchunk(background_image) object_info.add_subchunk(background_chunk) @@ -1710,7 +1710,7 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, object_info.add_subchunk(layerfog_chunk) if fognode or foglayer and layer.use_pass_mist: object_info.add_subchunk(use_fog_flag) - if use_keyframes and world.animation_data: + if use_keyframes and world.animation_data or world.node_tree.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) diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 5dc8ca5aa..16964fb00 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -527,6 +527,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextWrapper.metallic = contextMaterial.metallic contextWrapper.roughness = contextMaterial.roughness contextWrapper.specular = contextMaterial.specular_intensity + contextWrapper.specular_tint = contextMaterial.specular_color[:] contextWrapper.emission_color = contextMaterial.line_color[:3] contextWrapper.emission_strength = contextMaterial.line_priority / 100 contextWrapper.alpha = contextMaterial.diffuse_color[3] = contextAlpha @@ -1000,6 +1001,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextWrapper.metallic = contextMaterial.metallic contextWrapper.roughness = contextMaterial.roughness contextWrapper.specular = contextMaterial.specular_intensity + contextWrapper.specular_tint = contextMaterial.specular_color[:] contextWrapper.emission_color = contextMaterial.line_color[:3] contextWrapper.emission_strength = contextMaterial.line_priority / 100 contextWrapper.alpha = contextMaterial.diffuse_color[3] = contextAlpha -- 2.30.2 From 242092e13795de7c70fbc32ebe50e1543c9c295a Mon Sep 17 00:00:00 2001 From: NRGSille Date: Wed, 27 Sep 2023 00:38:44 +0200 Subject: [PATCH 42/42] io_scene_3ds: Move specular color texture to specular tint --- io_scene_3ds/__init__.py | 2 +- io_scene_3ds/export_3ds.py | 10 +++++----- io_scene_3ds/import_3ds.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 3f4e65421..c9d20b13f 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, Sebastian Schrand", - "version": (2, 4, 7), + "version": (2, 4, 8), "blender": (4, 0, 0), "location": "File > Import-Export", "description": "3DS Import/Export meshes, UVs, materials, textures, " diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index bc543216c..a19d57b30 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -620,7 +620,7 @@ def make_material_texture_chunk(chunk_id, texslots, pct): if socket == 'Alpha': mapflags |= 0x40 - if texslot.socket_dst.identifier in {'Base Color', 'Specular IOR Level'}: + if texslot.socket_dst.identifier in {'Base Color', 'Specular Tint'}: mapflags |= 0x80 if image.colorspace_settings.name == 'Non-Color' else 0x200 mat_sub_mapflags.add_variable("mapflags", _3ds_ushort(mapflags)) @@ -650,11 +650,11 @@ def make_material_texture_chunk(chunk_id, texslots, pct): mat_sub_angle.add_variable("mapangle", _3ds_float(round(texslot.rotation[2], 6))) mat_sub.add_subchunk(mat_sub_angle) - if texslot.socket_dst.identifier in {'Base Color', 'Specular IOR Level'}: + if texslot.socket_dst.identifier in {'Base Color', 'Specular Tint'}: rgb = _3ds_chunk(MAP_COL1) # Add tint color base = texslot.owner_shader.material.diffuse_color[:3] spec = texslot.owner_shader.material.specular_color[:] - rgb.add_variable("mapcolor", _3ds_rgb_color(spec if texslot.socket_dst.identifier == 'Specular IOR Level' else base)) + rgb.add_variable("mapcolor", _3ds_rgb_color(spec if texslot.socket_dst.identifier == 'Specular Tint' else base)) mat_sub.add_subchunk(rgb) # Store all textures for this mapto in order. This at least is what the @@ -724,8 +724,8 @@ def make_material_chunk(material, image): material_chunk.add_subchunk(make_texture_chunk(MAT_DIFFUSEMAP, mxtex, mxpct)) primary_tex = True - if wrap.specular_texture: - spec = [wrap.specular_texture] + if wrap.specular_tint_texture: + spec = [wrap.specular_tint_texture] s_pct = material.specular_intensity matmap = make_material_texture_chunk(MAT_SPECMAP, spec, s_pct) if matmap: diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index 16964fb00..5ad730567 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -264,7 +264,7 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of links.new(img_wrap.node_image.outputs['Color'], mixer.inputs[2]) links.new(mixer.outputs['Color'], shader.inputs['Base Color']) elif mapto == 'SPECULARITY': - img_wrap = contextWrapper.specular_texture + img_wrap = contextWrapper.specular_tint_texture elif mapto == 'ALPHA': shader.location = (0, -300) img_wrap = contextWrapper.alpha_texture -- 2.30.2