From 0d26a2a017b1de4bb3688c670103565f0931a520 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 9 Jun 2023 11:20:42 +0200 Subject: [PATCH 1/3] io_scene_3ds: Added hierarchy chunks Added hierarchy chunks to 3ds import and export. Prevents object hierarchy if no keyframer exported #104614 Signed-off-by: NRGSille --- io_scene_3ds/__init__.py | 7 +++- io_scene_3ds/export_3ds.py | 78 +++++++++++++++++++++++++++++++------- io_scene_3ds/import_3ds.py | 77 ++++++++++++++++++++++++++++++++----- 3 files changed, 137 insertions(+), 25 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 35db8c4b8..9e3438536 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -16,7 +16,7 @@ import bpy bl_info = { "name": "Autodesk 3DS format", "author": "Bob Holcomb, Campbell Barton, Andreas Atteneder, Sebastian Schrand", - "version": (2, 4, 1), + "version": (2, 4, 3), "blender": (3, 6, 0), "location": "File > Import-Export", "description": "3DS Import/Export meshes, UVs, materials, textures, " @@ -109,6 +109,11 @@ class Export3DS(bpy.types.Operator, ExportHelper): description="Export selected objects only", default=False, ) + use_hierarchy: bpy.props.BoolProperty( + name="Export Hierarchy", + description="Export hierarchy chunks", + default=False, + ) write_keyframe: BoolProperty( name="Write Keyframe", description="Write the keyframe data", diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index c9bff967f..4d781f75c 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -87,6 +87,8 @@ MASTERSCALE = 0x0100 # Master scale factor OBJECT_MESH = 0x4100 # This lets us know that we are reading a new object OBJECT_LIGHT = 0x4600 # This lets us know we are reading a light object OBJECT_CAMERA = 0x4700 # This lets us know we are reading a camera object +OBJECT_HIERARCHY = 0x4F00 # Hierarchy id of the object +OBJECT_PARENT = 0x4F10 # Parent id of the object # >------ Sub defines of LIGHT LIGHT_MULTIPLIER = 0x465B # The light energy factor @@ -1477,7 +1479,7 @@ def make_ambient_node(world): # EXPORT # ########## -def save(operator, context, filepath="", use_selection=False, write_keyframe=False, global_matrix=None): +def save(operator, context, filepath="", use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None): """Save the Blender scene to a 3ds file.""" # Time the export @@ -1608,6 +1610,7 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal scale = {} # Give all objects a unique ID and build a dictionary from object name to object id + object_id = {} name_id = {} for ob, data, matrix in mesh_objects: @@ -1615,6 +1618,7 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal rotation[ob.name] = ob.rotation_euler.to_quaternion().inverted() scale[ob.name] = ob.scale name_id[ob.name] = len(name_id) + object_id[ob.name] = len(object_id) for ob in empty_objects: translation[ob.name] = ob.location @@ -1622,6 +1626,12 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal scale[ob.name] = ob.scale name_id[ob.name] = len(name_id) + for ob in light_objects: + object_id[ob.name] = len(object_id) + + for ob in camera_objects: + object_id[ob.name] = len(object_id) + # Create object chunks for all meshes i = 0 for ob, mesh, matrix in mesh_objects: @@ -1633,20 +1643,32 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal # Make a mesh chunk out of the mesh object_chunk.add_subchunk(make_mesh_chunk(ob, mesh, matrix, materialDict, translation)) - # Ensure the mesh has no over sized arrays, skip ones that do! + # Add hierachy chunk with ID from object_id dictionary + if use_hierarchy: + obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY) + obj_hierarchy_chunk.add_variable("hierarchy", _3ds_ushort(object_id[ob.name])) + + # Add parent chunk if object has a parent + if ob.parent is not None and (ob.parent.name in object_id): + obj_parent_chunk = _3ds_chunk(OBJECT_PARENT) + obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name])) + obj_hierarchy_chunk.add_subchunk(obj_parent_chunk) + object_chunk.add_subchunk(obj_hierarchy_chunk) + + # ensure the mesh has no over sized arrays - skip ones that do! # Otherwise we cant write since the array size wont fit into USHORT if object_chunk.validate(): object_info.add_subchunk(object_chunk) else: operator.report({'WARNING'}, "Object %r can't be written into a 3DS file") - # Export kf object node + # Export object node if write_keyframe: kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) i += i - # Create chunks for all empties, only requires a kf object node + # Create chunks for all empties - only requires a object node if write_keyframe: for ob in empty_objects: kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) @@ -1654,15 +1676,15 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal # Create light object chunks for ob in light_objects: object_chunk = _3ds_chunk(OBJECT) - light_chunk = _3ds_chunk(OBJECT_LIGHT) + obj_light_chunk = _3ds_chunk(OBJECT_LIGHT) color_float_chunk = _3ds_chunk(RGB) - energy_factor = _3ds_chunk(LIGHT_MULTIPLIER) + light_energy_factor = _3ds_chunk(LIGHT_MULTIPLIER) object_chunk.add_variable("light", _3ds_string(sane_name(ob.name))) - light_chunk.add_variable("location", _3ds_point_3d(ob.location)) + obj_light_chunk.add_variable("location", _3ds_point_3d(ob.location)) color_float_chunk.add_variable("color", _3ds_float_color(ob.data.color)) - energy_factor.add_variable("energy", _3ds_float(ob.data.energy * 0.001)) - light_chunk.add_subchunk(color_float_chunk) - light_chunk.add_subchunk(energy_factor) + 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_energy_factor) if ob.data.type == 'SPOT': cone_angle = math.degrees(ob.data.spot_size) @@ -1684,10 +1706,24 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal if ob.data.use_square: spot_square_chunk = _3ds_chunk(LIGHT_SPOT_RECTANGLE) spotlight_chunk.add_subchunk(spot_square_chunk) - light_chunk.add_subchunk(spotlight_chunk) + obj_light_chunk.add_subchunk(spotlight_chunk) - # Add light to object info - object_chunk.add_subchunk(light_chunk) + # Add light to object chunk + object_chunk.add_subchunk(obj_light_chunk) + + # Add hierachy chunks with ID from object_id dictionary + if use_hierarchy: + obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY) + obj_parent_chunk = _3ds_chunk(OBJECT_PARENT) + obj_hierarchy_chunk.add_variable("hierarchy", _3ds_ushort(object_id[ob.name])) + if ob.parent is None or (ob.parent.name not in object_id): + obj_parent_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT)) + else: # Get the parent ID from the object_id dict + obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name])) + obj_hierarchy_chunk.add_subchunk(obj_parent_chunk) + object_chunk.add_subchunk(obj_hierarchy_chunk) + + # Add light object and hierarchy chunks to object info object_info.add_subchunk(object_chunk) # Export light and spotlight target node @@ -1714,6 +1750,20 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal camera_chunk.add_variable("roll", _3ds_float(round(ob.rotation_euler[1], 6))) camera_chunk.add_variable("lens", _3ds_float(ob.data.lens)) object_chunk.add_subchunk(camera_chunk) + + # Add hierachy chunks with ID from object_id dictionary + if use_hierarchy: + obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY) + obj_parent_chunk = _3ds_chunk(OBJECT_PARENT) + obj_hierarchy_chunk.add_variable("hierarchy", _3ds_ushort(object_id[ob.name])) + if ob.parent is None or (ob.parent.name not in object_id): + obj_parent_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT)) + else: # Get the parent ID from the object_id dict + obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name])) + obj_hierarchy_chunk.add_subchunk(obj_parent_chunk) + object_chunk.add_subchunk(obj_hierarchy_chunk) + + # Add light object and hierarchy chunks to object info object_info.add_subchunk(object_chunk) # Export camera and target node @@ -1755,4 +1805,4 @@ def save(operator, context, filepath="", use_selection=False, write_keyframe=Fal # Debugging only: dump the chunk hierarchy # primary.dump() - return {'FINISHED'} \ No newline at end of file + return {'FINISHED'} diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index fe6c26549..49ac602ea 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -92,6 +92,8 @@ MAT_MAP_BCOL = 0xA368 # Blue mapping OBJECT_MESH = 0x4100 # This lets us know that we are reading a new object OBJECT_LIGHT = 0x4600 # This lets us know we are reading a light object OBJECT_CAMERA = 0x4700 # This lets us know we are reading a camera object +OBJECT_HIERARCHY = 0x4F00 # This lets us know the hierachy id of the object +OBJECT_PARENT = 0x4F10 # This lets us know the parent id of the object # >------ Sub defines of LIGHT LIGHT_SPOTLIGHT = 0x4610 # The target of a spotlight @@ -322,6 +324,9 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of contextWrapper._grid_to_location(1, 0, dst_node=contextWrapper.node_out, ref_node=shader) +childs_list = [] +parent_list = [] + def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE): contextObName = None @@ -461,6 +466,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI temp_chunk = Chunk() CreateBlenderObject = False + CreateCameraObject = False CreateLightObject = False CreateTrackData = False @@ -924,6 +930,23 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI contextMatrix = mathutils.Matrix( (data[:3] + [0], data[3:6] + [0], data[6:9] + [0], data[9:] + [1])).transposed() + # If hierarchy chunk + elif new_chunk.ID == OBJECT_HIERARCHY: + child_id = read_short(new_chunk) + childs_list.insert(child_id, contextObName) + parent_list.insert(child_id, None) + if child_id in parent_list: + idp = parent_list.index(child_id) + parent_list[idp] = contextObName + elif new_chunk.ID == OBJECT_PARENT: + parent_id = read_short(new_chunk) + if parent_id > len(childs_list): + parent_list[child_id] = parent_id + parent_list.extend([None]*(parent_id-len(parent_list))) + parent_list.insert(parent_id, contextObName) + elif parent_id < len(childs_list): + parent_list[child_id] = childs_list[parent_id] + # If light chunk elif contextObName and new_chunk.ID == OBJECT_LIGHT: # Basic lamp support newLamp = bpy.data.lights.new("Lamp", 'POINT') @@ -934,9 +957,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI temp_data = file.read(SZ_3FLOAT) contextLamp.location = struct.unpack('<3f', temp_data) new_chunk.bytes_read += SZ_3FLOAT - contextMatrix = None # Reset matrix CreateBlenderObject = False CreateLightObject = True + contextMatrix = None # Reset matrix elif CreateLightObject and new_chunk.ID == COLOR_F: # Light color temp_data = file.read(SZ_3FLOAT) contextLamp.data.color = struct.unpack('<3f', temp_data) @@ -973,6 +996,21 @@ 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 contextLamp.data.use_square = True + elif CreateLightObject and new_chunk.ID == OBJECT_HIERARCHY: + child_id = read_short(new_chunk) + childs_list.insert(child_id, contextObName) + parent_list.insert(child_id, None) + if child_id in parent_list: + idp = parent_list.index(child_id) + parent_list[idp] = contextObName + elif CreateLightObject and new_chunk.ID == OBJECT_PARENT: + parent_id = read_short(new_chunk) + if parent_id > len(childs_list): + parent_list[child_id] = parent_id + parent_list.extend([None]*(parent_id-len(parent_list))) + parent_list.insert(parent_id, contextObName) + elif parent_id < len(childs_list): + parent_list[child_id] = childs_list[parent_id] # If camera chunk elif contextObName and new_chunk.ID == OBJECT_CAMERA: # Basic camera support @@ -996,8 +1034,24 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI temp_data = file.read(SZ_FLOAT) contextCamera.data.lens = float(struct.unpack(' len(childs_list): + parent_list[child_id] = parent_id + parent_list.extend([None]*(parent_id-len(parent_list))) + parent_list.insert(parent_id, contextObName) + elif parent_id < len(childs_list): + parent_list[child_id] = childs_list[parent_id] # start keyframe section elif new_chunk.ID == EDITKEYFRAME: @@ -1296,12 +1350,22 @@ 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 for par, objs in parent_dictionary.items(): parent = object_dictionary.get(par) for ob in objs: if parent is not None: ob.parent = parent + # If hierarchy + hierarchy = dict(zip(childs_list, parent_list)) + hierarchy.pop(None, ...) + for idt, (child, parent) in enumerate(hierarchy.items()): + child_obj = object_dictionary.get(child) + parent_obj = object_dictionary.get(parent) + if child_obj and parent_obj is not None: + child_obj.parent = parent_obj + # fix pivots for ind, ob in enumerate(object_list): if ob.type == 'MESH': @@ -1312,14 +1376,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI ob.data.transform(pivot_matrix) -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, IMAGE_SEARCH=True, WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None): print("importing 3DS: %r..." % (filepath), end="") -- 2.30.2 From a18936f9205ffb352732edf983153396ecb2fc89 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Fri, 21 Jul 2023 23:22:52 +0200 Subject: [PATCH 2/3] io_scene_3ds: Added masterscale factor --- io_scene_3ds/__init__.py | 7 +++++++ io_scene_3ds/export_3ds.py | 43 +++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index 193ba2c26..4369e686f 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -106,6 +106,13 @@ class Export3DS(bpy.types.Operator, ExportHelper): options={'HIDDEN'}, ) + scale_factor: FloatProperty( + name="Scale", + description="Scale factor for all objects", + 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 7128feb85..e23278795 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -1086,6 +1086,7 @@ 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: @@ -1110,9 +1111,10 @@ 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)) 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))) + track_chunk.add_variable("position", _3ds_point_3d((pos.x, pos.y, pos.z))) elif ID == ROT_TRACK_TAG: # Rotation for i, frame in enumerate(kframes): @@ -1373,8 +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 = scale[name] - + ob_size = mathutils.Matrix.Diagonal(scale[name]) target_pos = calc_target(ob_pos, ob_rot.x, ob_rot.z) # Add track chunks for target position @@ -1404,7 +1405,8 @@ 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_pos = calc_target(mathutils.Vector((loc_x, loc_y, loc_z)), rot_x, rot_z) + target_distance = ob_size @ 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()) track_chunk.add_variable("position", _3ds_point_3d(target_pos)) @@ -1487,9 +1489,11 @@ def make_ambient_node(world): # EXPORT # ########## -def save(operator, context, filepath="", use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None): +def save(operator, context, filepath="", scale_factor=1.0, 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() context.window.cursor_set('WAIT') @@ -1575,6 +1579,7 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals if data: matrix = global_matrix @ mtx data.transform(matrix) + data.transform(mtx_scale) mesh_objects.append((ob_derived, data, matrix)) ma_ls = data.materials ma_ls_len = len(ma_ls) @@ -1623,38 +1628,32 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals name_id = {} for ob, data, matrix in mesh_objects: - translation[ob.name] = ob.location + translation[ob.name] = mtx_scale @ ob.location.copy() rotation[ob.name] = ob.rotation_euler scale[ob.name] = ob.scale name_id[ob.name] = len(name_id) object_id[ob.name] = len(object_id) for ob in empty_objects: - translation[ob.name] = ob.location + translation[ob.name] = mtx_scale @ ob.location.copy() rotation[ob.name] = ob.rotation_euler scale[ob.name] = ob.scale name_id[ob.name] = len(name_id) for ob in light_objects: - translation[ob.name] = ob.location + translation[ob.name] = mtx_scale @ ob.location.copy() rotation[ob.name] = ob.rotation_euler - scale[ob.name] = ob.scale + scale[ob.name] = mtx_scale.to_scale() name_id[ob.name] = len(name_id) object_id[ob.name] = len(object_id) for ob in camera_objects: - translation[ob.name] = ob.location + translation[ob.name] = mtx_scale @ ob.location.copy() rotation[ob.name] = ob.rotation_euler - scale[ob.name] = ob.scale + scale[ob.name] = mtx_scale.to_scale() name_id[ob.name] = len(name_id) object_id[ob.name] = len(object_id) - for ob in light_objects: - object_id[ob.name] = len(object_id) - - for ob in camera_objects: - object_id[ob.name] = len(object_id) - # Create object chunks for all meshes i = 0 for ob, mesh, matrix in mesh_objects: @@ -1701,9 +1700,10 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals object_chunk = _3ds_chunk(OBJECT) obj_light_chunk = _3ds_chunk(OBJECT_LIGHT) color_float_chunk = _3ds_chunk(RGB) + light_distance = mtx_scale @ ob.location.copy() 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(ob.location)) + obj_light_chunk.add_variable("location", _3ds_point_3d(light_distance)) color_float_chunk.add_variable("color", _3ds_float_color(ob.data.color)) light_energy_factor.add_variable("energy", _3ds_float(ob.data.energy * 0.001)) obj_light_chunk.add_subchunk(color_float_chunk) @@ -1712,7 +1712,7 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals if ob.data.type == 'SPOT': cone_angle = math.degrees(ob.data.spot_size) hot_spot = cone_angle - (ob.data.spot_blend * math.floor(cone_angle)) - spot_pos = calc_target(ob.location, ob.rotation_euler.x, ob.rotation_euler.z) + spot_pos = calc_target(light_distance, ob.rotation_euler.x, ob.rotation_euler.z) spotlight_chunk = _3ds_chunk(LIGHT_SPOTLIGHT) spot_roll_chunk = _3ds_chunk(LIGHT_SPOT_ROLL) spotlight_chunk.add_variable("target", _3ds_point_3d(spot_pos)) @@ -1764,9 +1764,10 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals for ob in camera_objects: object_chunk = _3ds_chunk(OBJECT) camera_chunk = _3ds_chunk(OBJECT_CAMERA) - camera_target = calc_target(ob.location, ob.rotation_euler.x, ob.rotation_euler.z) + camera_distance = mtx_scale @ ob.location.copy() + camera_target = calc_target(camera_distance, ob.rotation_euler.x, ob.rotation_euler.z) object_chunk.add_variable("camera", _3ds_string(sane_name(ob.name))) - camera_chunk.add_variable("location", _3ds_point_3d(ob.location)) + camera_chunk.add_variable("location", _3ds_point_3d(camera_distance)) camera_chunk.add_variable("target", _3ds_point_3d(camera_target)) camera_chunk.add_variable("roll", _3ds_float(round(ob.rotation_euler.y, 6))) camera_chunk.add_variable("lens", _3ds_float(ob.data.lens)) -- 2.30.2 From d05b1bcb9c9e7edfac57436ec28c05142329ee93 Mon Sep 17 00:00:00 2001 From: NRGSille Date: Sat, 22 Jul 2023 16:17:30 +0200 Subject: [PATCH 3/3] io_scene_3ds: Added unit measure convert option --- io_scene_3ds/__init__.py | 7 +++- io_scene_3ds/import_3ds.py | 70 ++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 37 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/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