From 4a8ab07200b6cebdbd1a5056413d10689cb1ead3 Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Mon, 22 May 2023 11:24:42 +0200 Subject: [PATCH 01/38] Added times of last load --- bseq/importer.py | 7 ++++++- bseq/panels.py | 2 ++ bseq/properties.py | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/bseq/importer.py b/bseq/importer.py index 7a3e5fa..71b8c60 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -7,6 +7,7 @@ from .utils import show_message_box import numpy as np from mathutils import Matrix +import time # this import is not useless import additional_file_formats @@ -233,6 +234,8 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, def update_obj(scene, depsgraph=None): for obj in bpy.data.objects: + start_time = time.perf_counter() + if obj.BSEQ.init == False: continue if obj.BSEQ.enabled == False: @@ -302,4 +305,6 @@ def update_obj(scene, depsgraph=None): update_mesh(meshio_mesh, obj.data) apply_transformation(meshio_mesh, obj, depsgraph) - \ No newline at end of file + + end_time = time.perf_counter() + obj.BSEQ.last_benchmark = (end_time - start_time) * 1000 diff --git a/bseq/panels.py b/bseq/panels.py index d75cefb..007d084 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -116,6 +116,8 @@ def draw(self, context): col2.prop(obj.BSEQ, 'use_relative', text="") col1.label(text='Pattern') col2.prop(obj.BSEQ, 'pattern', text="") + col1.label(text='Last loading time (ms)') + col2.prop(obj.BSEQ, 'last_benchmark', text="") # geometry nodes settings layout.label(text="Geometry Nodes") diff --git a/bseq/properties.py b/bseq/properties.py index 7e31f59..5a5968c 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -84,6 +84,7 @@ class BSEQ_obj_property(bpy.types.PropertyGroup): pattern: bpy.props.StringProperty() frame: bpy.props.IntProperty() start_end_frame: bpy.props.IntVectorProperty(name="Start and End Frames", size=2, default=(0, 0)) + last_benchmark: bpy.props.FloatProperty(name="Last Loading Time") # set this property for mesh, not object (maybe change later?) class BSEQ_mesh_property(bpy.types.PropertyGroup): From b61c1fdb63d3b8bf8320e262553904202e9d15c1 Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Mon, 22 May 2023 16:29:46 +0200 Subject: [PATCH 02/38] obj files now load with meshio loader --- bseq/importer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bseq/importer.py b/bseq/importer.py index 71b8c60..c031451 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -196,6 +196,10 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] + if fileseq.extension() == '.obj': + create_meshio_obj(filepath) + return + meshio_mesh = None enabled = True try: From b17453f380167d2d650c65de016bb235f9a66627 Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Mon, 22 May 2023 16:52:16 +0200 Subject: [PATCH 03/38] create_meshio_obj now uses default blender obj importer --- bseq/importer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bseq/importer.py b/bseq/importer.py index c031451..b05eb8f 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -181,6 +181,11 @@ def create_meshio_obj(filepath): "Meshio Loading Error" + str(e), icon="ERROR") + if ".obj" in filepath: + bpy.ops.import_scene.obj(filepath=filepath) + obj = bpy.context.selected_objects[0] + obj.name = os.path.basename(filepath) + return # create the object name = os.path.basename(filepath) mesh = bpy.data.meshes.new(name) From 3c835375c515e830dd283d809361b86e610e1a1d Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Mon, 22 May 2023 17:03:33 +0200 Subject: [PATCH 04/38] create_meshio_obj now only checks for the ending --- bseq/importer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index b05eb8f..cb047db 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -180,8 +180,8 @@ def create_meshio_obj(filepath): show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), "Meshio Loading Error" + str(e), icon="ERROR") - - if ".obj" in filepath: + + if filepath.endswith(".obj"): bpy.ops.import_scene.obj(filepath=filepath) obj = bpy.context.selected_objects[0] obj.name = os.path.basename(filepath) From 43071ac797e5cc9da9d872df2aee7af27385ea19 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 25 May 2023 14:27:40 +0200 Subject: [PATCH 05/38] Update (not working atm) --- bseq/importer.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index cb047db..845531e 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -181,6 +181,7 @@ def create_meshio_obj(filepath): "Meshio Loading Error" + str(e), icon="ERROR") + # Do I need this for multi-loading? if filepath.endswith(".obj"): bpy.ops.import_scene.obj(filepath=filepath) obj = bpy.context.selected_objects[0] @@ -201,24 +202,27 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] - if fileseq.extension() == '.obj': - create_meshio_obj(filepath) - return - - meshio_mesh = None - enabled = True - try: - meshio_mesh = meshio.read(filepath) - except Exception as e: - show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "Meshio Loading Error" + str(e), - icon="ERROR") + if filepath.endswith(".obj"): + bpy.ops.import_scene.obj(filepath=filepath) + object = bpy.context.selected_objects[-1] + object.name = fileseq.basename() + "@" + fileseq.extension() enabled = False + else: + meshio_mesh = None + enabled = True + try: + meshio_mesh = meshio.read(filepath) + except Exception as e: + show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + "Meshio Loading Error" + str(e), + icon="ERROR") + enabled = False + + name = fileseq.basename() + "@" + fileseq.extension() + mesh = bpy.data.meshes.new(name) + object = bpy.data.objects.new(name, mesh) # create the object - name = fileseq.basename() + "@" + fileseq.extension() - mesh = bpy.data.meshes.new(name) - object = bpy.data.objects.new(name, mesh) object.BSEQ.use_relative = use_relative if use_relative: if root_path != "": From 01dcf7d7c7686c2bd5bd19699778889089d2726c Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Wed, 21 Jun 2023 00:36:06 +0200 Subject: [PATCH 06/38] Current not wokring version (but mzd is now fixed) --- additional_file_formats/mzd.py | 3 ++- bseq/importer.py | 28 ++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/additional_file_formats/mzd.py b/additional_file_formats/mzd.py index 23a0029..af4ff3c 100644 --- a/additional_file_formats/mzd.py +++ b/additional_file_formats/mzd.py @@ -22,6 +22,7 @@ def readMZD_to_meshio(filepath): with open(filepath, 'rb') as file: byte = file.read(24) + # check if mzd file is empty if byte != head: return -4 while 1: @@ -49,7 +50,7 @@ def readMZD_to_meshio(filepath): if out_numVertices < 0: return -127 if out_numVertices == 0: - break + return meshio.Mesh(points=np.array([]), cells={}) byte = file.read(12 * out_numVertices) out_vertPositions = np.frombuffer(byte, dtype=np.float32) diff --git a/bseq/importer.py b/bseq/importer.py index 845531e..9dd92d8 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -94,6 +94,9 @@ def update_mesh(meshio_mesh, mesh): n_loop = 0 n_verts = len(mesh_vertices) if n_verts == 0: + mesh.clear_geometry() + mesh.update() + mesh.validate() return faces_loop_start = np.array([], dtype=np.uint64) faces_loop_total = np.array([], dtype=np.uint64) @@ -202,11 +205,14 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] - if filepath.endswith(".obj"): + #.obj sequences have to be handled differently + isObj = filepath.endswith(".obj") + if isObj: bpy.ops.import_scene.obj(filepath=filepath) object = bpy.context.selected_objects[-1] object.name = fileseq.basename() + "@" + fileseq.extension() - enabled = False + # object.data.name = fileseq.basename() + "@" + fileseq.extension() + enabled = True else: meshio_mesh = None enabled = True @@ -237,6 +243,8 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object.matrix_world = transform_matrix driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' + if isObj: + return if enabled: update_mesh(meshio_mesh, object.data) bpy.context.collection.objects.link(object) @@ -249,6 +257,9 @@ def update_obj(scene, depsgraph=None): for obj in bpy.data.objects: start_time = time.perf_counter() + isObj = obj.BSEQ.pattern.endswith(".obj") + print("isObj: ", isObj) + if obj.BSEQ.init == False: continue if obj.BSEQ.enabled == False: @@ -305,14 +316,23 @@ def update_obj(scene, depsgraph=None): else: filepath = fs[current_frame % len(fs)] try: - meshio_mesh = meshio.read(filepath) + if filepath.endswith(".obj"): + # Reload the object + obj.select_set(True) + bpy.ops.object.delete() + # bpy.ops.import_scene.obj(filepath=filepath) + # object = bpy.context.selected_objects[-1] + # object.name = fileseq.basename() + "@" + fileseq.extension() + # object.data.name = fileseq.basename() + "@" + fileseq.extension() + else: + meshio_mesh = meshio.read(filepath) except Exception as e: show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), "Meshio Loading Error" + str(e), icon="ERROR") continue - if not isinstance(meshio_mesh, meshio.Mesh): + if not isinstance(meshio_mesh, meshio.Mesh) or not isObj: show_message_box('function preprocess does not return meshio object', "ERROR") continue update_mesh(meshio_mesh, obj.data) From c38a8c62bdb12168619fd061b1e5407d76971274 Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Wed, 21 Jun 2023 09:49:50 +0200 Subject: [PATCH 07/38] Better (not working) obj loader --- bseq/importer.py | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index 9dd92d8..fd7a19b 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -80,8 +80,9 @@ def apply_transformation(meshio_mesh, obj, depsgraph): # evaluate the rigid body transformations (only relevant for .bin format) rigid_body_transformation = mathutils.Matrix.Identity(4) - if meshio_mesh.field_data.get("transformation_matrix") is not None: - rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"] + if meshio_mesh is not None: + if meshio_mesh.field_data.get("transformation_matrix") is not None: + rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"] # multiply everything together (with custom transform matrix) obj.matrix_world = rigid_body_transformation @ eval_transform_matrix @@ -256,9 +257,6 @@ def update_obj(scene, depsgraph=None): for obj in bpy.data.objects: start_time = time.perf_counter() - - isObj = obj.BSEQ.pattern.endswith(".obj") - print("isObj: ", isObj) if obj.BSEQ.init == False: continue @@ -284,6 +282,24 @@ def update_obj(scene, depsgraph=None): pattern = bpy.path.native_pathsep(pattern) fs = fileseq.FileSequence(pattern) + if pattern.endswith(".obj"): + filepath = fs[current_frame % len(fs)] + + # Reload the object + tmp_transform = obj.matrix_world + obj.select_set(True) + bpy.ops.object.delete() + bpy.ops.import_scene.obj(filepath=filepath) + obj = bpy.context.selected_objects[-1] + obj.name = fs.basename() + "@" + fs.extension() + obj.data.name = fs.basename() + "@" + fs.extension() + obj.matrix_world = tmp_transform + apply_transformation(meshio_mesh, obj, depsgraph) + + end_time = time.perf_counter() + obj.BSEQ.last_benchmark = (end_time - start_time) * 1000 + return + if obj.BSEQ.use_advance and obj.BSEQ.script_name: script = bpy.data.texts[obj.BSEQ.script_name] try: @@ -316,23 +332,14 @@ def update_obj(scene, depsgraph=None): else: filepath = fs[current_frame % len(fs)] try: - if filepath.endswith(".obj"): - # Reload the object - obj.select_set(True) - bpy.ops.object.delete() - # bpy.ops.import_scene.obj(filepath=filepath) - # object = bpy.context.selected_objects[-1] - # object.name = fileseq.basename() + "@" + fileseq.extension() - # object.data.name = fileseq.basename() + "@" + fileseq.extension() - else: - meshio_mesh = meshio.read(filepath) + meshio_mesh = meshio.read(filepath) except Exception as e: show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), "Meshio Loading Error" + str(e), icon="ERROR") continue - if not isinstance(meshio_mesh, meshio.Mesh) or not isObj: + if not isinstance(meshio_mesh, meshio.Mesh): show_message_box('function preprocess does not return meshio object', "ERROR") continue update_mesh(meshio_mesh, obj.data) From 72a721eefc2ef7ebdacd60588791d2eb92981989 Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Wed, 21 Jun 2023 10:01:00 +0200 Subject: [PATCH 08/38] Some debugging lines --- bseq/importer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bseq/importer.py b/bseq/importer.py index fd7a19b..f44dd8f 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -209,6 +209,7 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, #.obj sequences have to be handled differently isObj = filepath.endswith(".obj") if isObj: + print(str(filepath)) bpy.ops.import_scene.obj(filepath=filepath) object = bpy.context.selected_objects[-1] object.name = fileseq.basename() + "@" + fileseq.extension() @@ -291,6 +292,9 @@ def update_obj(scene, depsgraph=None): bpy.ops.object.delete() bpy.ops.import_scene.obj(filepath=filepath) obj = bpy.context.selected_objects[-1] + print(str(filepath)) + print(current_frame % len(fs)) + print(obj.name) obj.name = fs.basename() + "@" + fs.extension() obj.data.name = fs.basename() + "@" + fs.extension() obj.matrix_world = tmp_transform @@ -298,7 +302,7 @@ def update_obj(scene, depsgraph=None): end_time = time.perf_counter() obj.BSEQ.last_benchmark = (end_time - start_time) * 1000 - return + continue if obj.BSEQ.use_advance and obj.BSEQ.script_name: script = bpy.data.texts[obj.BSEQ.script_name] From 3b64e075f126e2345429ed57c7dac650525758dc Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 21 Jun 2023 13:44:21 +0200 Subject: [PATCH 09/38] Added functionality to use from_pydata and objloader now works --- bseq/importer.py | 43 ++++++++++++++++++++++++++++--------------- bseq/panels.py | 3 +++ bseq/properties.py | 4 ++++ 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index f44dd8f..4ee5079 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -120,6 +120,8 @@ def update_mesh(meshio_mesh, mesh): faces_loop_start = np.roll(faces_loop_start, 1) faces_loop_start[0] = 0 + start_time = time.perf_counter() + if len(mesh.vertices) == n_verts and len(mesh.polygons) == n_poly and len(mesh.loops) == n_loop: pass else: @@ -128,12 +130,20 @@ def update_mesh(meshio_mesh, mesh): mesh.loops.add(n_loop) mesh.polygons.add(n_poly) + mesh.vertices.foreach_set("co", mesh_vertices.ravel()) mesh.loops.foreach_set("vertex_index", loops_vert_idx) mesh.polygons.foreach_set("loop_start", faces_loop_start) mesh.polygons.foreach_set("loop_total", faces_loop_total) mesh.polygons.foreach_set("use_smooth", [shade_scheme] * len(faces_loop_total)) + # mesh.clear_geometry() + # mesh.from_pydata(mesh_vertices, [], data) + + end_time = time.perf_counter() + print("foreach_set time: ", end_time - start_time) + + mesh.update() mesh.validate() @@ -208,13 +218,19 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, #.obj sequences have to be handled differently isObj = filepath.endswith(".obj") - if isObj: + if isObj and bpy.context.scene.BSEQ.use_blender_obj_import: print(str(filepath)) bpy.ops.import_scene.obj(filepath=filepath) - object = bpy.context.selected_objects[-1] - object.name = fileseq.basename() + "@" + fileseq.extension() - # object.data.name = fileseq.basename() + "@" + fileseq.extension() enabled = True + + tmp_obj = bpy.context.selected_objects[-1] + + name = fileseq.basename() + "@" + fileseq.extension() + object = bpy.data.objects.new(name, tmp_obj.data) + + tmp_obj.select_set(True) + bpy.ops.object.delete() + else: meshio_mesh = None enabled = True @@ -245,9 +261,7 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object.matrix_world = transform_matrix driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' - if isObj: - return - if enabled: + if enabled and not isObj: update_mesh(meshio_mesh, object.data) bpy.context.collection.objects.link(object) bpy.ops.object.select_all(action="DESELECT") @@ -283,21 +297,20 @@ def update_obj(scene, depsgraph=None): pattern = bpy.path.native_pathsep(pattern) fs = fileseq.FileSequence(pattern) - if pattern.endswith(".obj"): + if pattern.endswith(".obj") and scene.BSEQ.use_blender_obj_import: filepath = fs[current_frame % len(fs)] # Reload the object - tmp_transform = obj.matrix_world - obj.select_set(True) - bpy.ops.object.delete() bpy.ops.import_scene.obj(filepath=filepath) - obj = bpy.context.selected_objects[-1] + tmp_obj = bpy.context.selected_objects[-1] print(str(filepath)) print(current_frame % len(fs)) print(obj.name) - obj.name = fs.basename() + "@" + fs.extension() - obj.data.name = fs.basename() + "@" + fs.extension() - obj.matrix_world = tmp_transform + + obj.data = tmp_obj.data + tmp_obj.select_set(True) + bpy.ops.object.delete() + apply_transformation(meshio_mesh, obj, depsgraph) end_time = time.perf_counter() diff --git a/bseq/panels.py b/bseq/panels.py index 007d084..5e1ffc3 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -207,6 +207,9 @@ def draw(self, context): col1.label(text="Root Directory") col2.prop(importer_prop, "root_path", text="") + col1.label(text="Use Blender .obj Importer") + col2.prop(importer_prop, "use_blender_obj_import", text="") + layout.operator("sequence.load") layout.operator("wm.seq_import_batch") diff --git a/bseq/properties.py b/bseq/properties.py index 5a5968c..89fad52 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -73,6 +73,10 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): size=3, subtype="COORDINATES", default=[1,1,1]) + + use_blender_obj_import: bpy.props.BoolProperty(name='Use Blender Object Import', + description="Whether or not to use Blender's built-in object import function", + default=True) class BSEQ_obj_property(bpy.types.PropertyGroup): init: bpy.props.BoolProperty(default=False) From 5b36b1c368aa0b1878d180ff948fca23887c8bcd Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Wed, 21 Jun 2023 18:44:14 +0200 Subject: [PATCH 10/38] Minor updates (with old mesh loading) --- bseq/importer.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index 4ee5079..1db6612 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -119,9 +119,7 @@ def update_mesh(meshio_mesh, mesh): # Add a zero as first entry faces_loop_start = np.roll(faces_loop_start, 1) faces_loop_start[0] = 0 - - start_time = time.perf_counter() - + if len(mesh.vertices) == n_verts and len(mesh.polygons) == n_poly and len(mesh.loops) == n_loop: pass else: @@ -130,20 +128,16 @@ def update_mesh(meshio_mesh, mesh): mesh.loops.add(n_loop) mesh.polygons.add(n_poly) - mesh.vertices.foreach_set("co", mesh_vertices.ravel()) mesh.loops.foreach_set("vertex_index", loops_vert_idx) mesh.polygons.foreach_set("loop_start", faces_loop_start) mesh.polygons.foreach_set("loop_total", faces_loop_total) mesh.polygons.foreach_set("use_smooth", [shade_scheme] * len(faces_loop_total)) + # newer function but is about 4 times slower # mesh.clear_geometry() # mesh.from_pydata(mesh_vertices, [], data) - end_time = time.perf_counter() - print("foreach_set time: ", end_time - start_time) - - mesh.update() mesh.validate() @@ -219,7 +213,6 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, #.obj sequences have to be handled differently isObj = filepath.endswith(".obj") if isObj and bpy.context.scene.BSEQ.use_blender_obj_import: - print(str(filepath)) bpy.ops.import_scene.obj(filepath=filepath) enabled = True @@ -303,9 +296,6 @@ def update_obj(scene, depsgraph=None): # Reload the object bpy.ops.import_scene.obj(filepath=filepath) tmp_obj = bpy.context.selected_objects[-1] - print(str(filepath)) - print(current_frame % len(fs)) - print(obj.name) obj.data = tmp_obj.data tmp_obj.select_set(True) From b8521b09a42f63adb0d171925e510f5142a841b0 Mon Sep 17 00:00:00 2001 From: justo46 Date: Sun, 20 Aug 2023 22:43:19 +0200 Subject: [PATCH 11/38] Updated submodule extern/fileseq --- extern/fileseq | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/fileseq b/extern/fileseq index 9ec0493..6d42146 160000 --- a/extern/fileseq +++ b/extern/fileseq @@ -1 +1 @@ -Subproject commit 9ec049373af37ec21a21d2a5564deb344a96f97f +Subproject commit 6d4214677663f80bd6e72cb94c7d9fc12357d3f8 From c8822674627dd123cbbb56d1fdef26234428769c Mon Sep 17 00:00:00 2001 From: justo46 Date: Sun, 20 Aug 2023 22:44:28 +0200 Subject: [PATCH 12/38] Updated submodule extern/meshio --- extern/meshio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/meshio b/extern/meshio index a6175e0..0138cc8 160000 --- a/extern/meshio +++ b/extern/meshio @@ -1 +1 @@ -Subproject commit a6175e0d9dfb2aa274392d1cd396e991f0487cbc +Subproject commit 0138cc8692b806b44b32d344f7961e8370121ff7 From 5f7c828fb27eb05362a7eb49bd68168a7289a6c1 Mon Sep 17 00:00:00 2001 From: justo46 Date: Sun, 20 Aug 2023 22:45:13 +0200 Subject: [PATCH 13/38] Updated submodule extern/python-future --- extern/python-future | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/python-future b/extern/python-future index 80523f3..af1db97 160000 --- a/extern/python-future +++ b/extern/python-future @@ -1 +1 @@ -Subproject commit 80523f383fbba1c6de0551e19d0277e73e69573c +Subproject commit af1db970b0879b59e7aeb798c27a623144561cff From 65d1c12d02351dce2e166ad7fc706f98bc4ba3a8 Mon Sep 17 00:00:00 2001 From: justo46 Date: Sun, 20 Aug 2023 22:45:59 +0200 Subject: [PATCH 14/38] Updated submodule extern/rich --- extern/rich | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/rich b/extern/rich index 3734ff4..720800e 160000 --- a/extern/rich +++ b/extern/rich @@ -1 +1 @@ -Subproject commit 3734ff45d7b30541aabdd656efb80c9896d445b3 +Subproject commit 720800e6930d85ad027b1e9bd0cbb96b5e994ce3 From 193ff0a63ad83a97710012a8a38bdbc70cb33fcc Mon Sep 17 00:00:00 2001 From: justo46 Date: Sun, 20 Aug 2023 23:20:45 +0200 Subject: [PATCH 15/38] Renamed symbol --- bseq/importer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index 1db6612..025adfa 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -211,8 +211,8 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, filepath = fileseq[current_frame % len(fileseq)] #.obj sequences have to be handled differently - isObj = filepath.endswith(".obj") - if isObj and bpy.context.scene.BSEQ.use_blender_obj_import: + is_obj_seq = filepath.endswith(".obj") + if is_obj_seq and bpy.context.scene.BSEQ.use_blender_obj_import: bpy.ops.import_scene.obj(filepath=filepath) enabled = True @@ -254,7 +254,7 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object.matrix_world = transform_matrix driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' - if enabled and not isObj: + if enabled and not is_obj_seq: update_mesh(meshio_mesh, object.data) bpy.context.collection.objects.link(object) bpy.ops.object.select_all(action="DESELECT") From ed0d543842226732b1447caff2ef05bc42498617 Mon Sep 17 00:00:00 2001 From: justo46 Date: Mon, 21 Aug 2023 11:35:11 +0200 Subject: [PATCH 16/38] Added files of submodules --- extern/fileseq | 2 +- extern/meshio | 2 +- extern/python-future | 2 +- extern/rich | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/fileseq b/extern/fileseq index 6d42146..9ec0493 160000 --- a/extern/fileseq +++ b/extern/fileseq @@ -1 +1 @@ -Subproject commit 6d4214677663f80bd6e72cb94c7d9fc12357d3f8 +Subproject commit 9ec049373af37ec21a21d2a5564deb344a96f97f diff --git a/extern/meshio b/extern/meshio index 0138cc8..a6175e0 160000 --- a/extern/meshio +++ b/extern/meshio @@ -1 +1 @@ -Subproject commit 0138cc8692b806b44b32d344f7961e8370121ff7 +Subproject commit a6175e0d9dfb2aa274392d1cd396e991f0487cbc diff --git a/extern/python-future b/extern/python-future index af1db97..80523f3 160000 --- a/extern/python-future +++ b/extern/python-future @@ -1 +1 @@ -Subproject commit af1db970b0879b59e7aeb798c27a623144561cff +Subproject commit 80523f383fbba1c6de0551e19d0277e73e69573c diff --git a/extern/rich b/extern/rich index 720800e..3734ff4 160000 --- a/extern/rich +++ b/extern/rich @@ -1 +1 @@ -Subproject commit 720800e6930d85ad027b1e9bd0cbb96b5e994ce3 +Subproject commit 3734ff45d7b30541aabdd656efb80c9896d445b3 From 65023fd24f2f24a79492558005ec475936b1197b Mon Sep 17 00:00:00 2001 From: justo46 Date: Tue, 22 Aug 2023 15:21:25 +0200 Subject: [PATCH 17/38] A lot of UI changes --- __init__.py | 15 ++- bseq/__init__.py | 12 +- bseq/callback.py | 2 +- bseq/globals.py | 18 ++- bseq/importer.py | 9 +- bseq/operators.py | 163 +++++++++++++++++++++----- bseq/panels.py | 283 +++++++++++++++++++++++++-------------------- bseq/properties.py | 101 ++++++++++------ bseq/utils.py | 6 +- 9 files changed, 398 insertions(+), 211 deletions(-) diff --git a/__init__.py b/__init__.py index 2b0824c..a3ead55 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ "name": "Sequence Loader", "description": "Loader for meshio supported mesh files/ simulation sequences", "author": "Interactive Computer Graphics", - "version": (0, 1, 5), + "version": (0, 1, 6), "blender": (3, 4, 0), "warning": "", "support": "COMMUNITY", @@ -21,7 +21,7 @@ bpy.context.preferences.filepaths.use_relative_paths = False from bseq import * -from bseq.operators import menu_func_import +from bseq.operators import menu_func_import, add_keymap, delete_keymap classes = [ BSEQ_obj_property, @@ -33,9 +33,13 @@ BSEQ_OT_resetins, BSEQ_OT_resetmesh, BSEQ_Import, + BSEQ_Import_Child1, + BSEQ_Import_Child2, + BSEQ_Globals_Panel, BSEQ_List_Panel, BSEQ_UL_Obj_List, BSEQ_Settings, + BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, BSEQ_OT_set_as_split_norm, @@ -48,10 +52,10 @@ BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, WM_OT_batchSequences, + WM_OT_batchSequences_Settings, WM_OT_MeshioObject ] - def register(): bpy.app.handlers.load_post.append(BSEQ_initialize) for cls in classes: @@ -60,8 +64,8 @@ def register(): bpy.types.Scene.BSEQ = bpy.props.PointerProperty(type=BSEQ_scene_property) bpy.types.Object.BSEQ = bpy.props.PointerProperty(type=BSEQ_obj_property) bpy.types.Mesh.BSEQ = bpy.props.PointerProperty(type=BSEQ_mesh_property) - bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + add_keymap() # manually call this function once # so when addon being installed, it can run correctly @@ -76,10 +80,9 @@ def unregister(): del bpy.types.Object.BSEQ bpy.app.handlers.load_post.remove(BSEQ_initialize) bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + delete_keymap() unsubscribe_to_selected() - if __name__ == "__main__": - # unregister() register() diff --git a/bseq/__init__.py b/bseq/__init__.py index d039423..aad51b4 100644 --- a/bseq/__init__.py +++ b/bseq/__init__.py @@ -1,7 +1,7 @@ from bseq.utils import refresh_obj -from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, WM_OT_batchSequences, WM_OT_MeshioObject +from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, WM_OT_batchSequences, WM_OT_batchSequences_Settings, WM_OT_MeshioObject from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property -from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_Import, BSEQ_Templates, BSEQ_UL_Att_List, draw_template +from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_Import, BSEQ_Import_Child1, BSEQ_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template from .messenger import subscribe_to_selected, unsubscribe_to_selected import bpy from bpy.app.handlers import persistent @@ -12,7 +12,8 @@ @persistent def BSEQ_initialize(scene): if update_obj not in bpy.app.handlers.frame_change_post: - bpy.app.handlers.frame_change_post.append(auto_refresh) + bpy.app.handlers.frame_change_post.append(auto_refresh_active) + bpy.app.handlers.frame_change_post.append(auto_refresh_all) bpy.app.handlers.frame_change_post.append(update_obj) subscribe_to_selected() if print_information not in bpy.app.handlers.render_init: @@ -25,11 +26,15 @@ def BSEQ_initialize(scene): "BSEQ_obj_property", "BSEQ_initialize", "BSEQ_Import", + "BSEQ_Import_Child1", + "BSEQ_Import_Child2", + "BSEQ_Globals_Panel", "BSEQ_List_Panel", "BSEQ_UL_Obj_List", "BSEQ_scene_property", "BSEQ_Templates", "BSEQ_Settings", + "BSEQ_Advanced_Panel", "BSEQ_UL_Att_List", "subscribe_to_selected", "BSEQ_OT_resetpt", @@ -48,5 +53,6 @@ def BSEQ_initialize(scene): "BSEQ_OT_refresh_sequences", "BSEQ_OT_set_start_end_frames", "WM_OT_batchSequences", + "WM_OT_batchSequences_Settings", "WM_OT_MeshioObject" ] diff --git a/bseq/callback.py b/bseq/callback.py index e89bb6b..4357b5d 100644 --- a/bseq/callback.py +++ b/bseq/callback.py @@ -25,7 +25,7 @@ def update_path(self, context): return [("None", "No sequence detected", "", 1)] file_sequences.clear() - if len(f) >= 20: + if len(f) >= 100: file_sequences.append(("None", "Too much sequence detected, could be false detection, please use pattern below", "", 1)) else: count = 1 diff --git a/bseq/globals.py b/bseq/globals.py index e875ff0..e9da36a 100644 --- a/bseq/globals.py +++ b/bseq/globals.py @@ -24,12 +24,24 @@ def print_information(scene): file.write("Object name: {}\n".format(obj.name)) file.write("Is it being animated: {}\n".format(bseq_prop.enabled)) file.write("Filepath: {}\n".format(bseq_prop.pattern)) - file.write("Is it relative path: {}\n".format(bseq_prop.use_relative)) + file.write("Is it relative path: {}\n".format(bpy.path.is_subdir(obj.BSEQ.pattern, bpy.path.abspath("//")))) file.write("\n\n") -def auto_refresh(scene, depsgraph=None): - if not bpy.context.scene.BSEQ.auto_refresh: +def auto_refresh_all(scene, depsgraph=None): + if not bpy.context.scene.BSEQ.auto_refresh_all: + return + for obj in bpy.data.objects: + if obj.BSEQ.init == False: + continue + if obj.BSEQ.enabled == False: + continue + if obj.mode != "OBJECT": + continue + refresh_obj(obj, scene) + +def auto_refresh_active(scene, depsgraph=None): + if not bpy.context.scene.BSEQ.auto_refresh_active: return for obj in bpy.data.objects: if obj.BSEQ.init == False: diff --git a/bseq/importer.py b/bseq/importer.py index 8edc5b2..bee33cd 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -205,7 +205,7 @@ def create_meshio_obj(filepath): bpy.context.view_layer.objects.active = object -def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])): +def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])): current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] @@ -240,8 +240,8 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object = bpy.data.objects.new(name, mesh) # create the object - object.BSEQ.use_relative = use_relative - if use_relative: + print("File path: " + filepath) + if bpy.path.is_subdir(filepath, bpy.path.abspath("//")): if root_path != "": object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=root_path) else: @@ -280,7 +280,8 @@ def update_obj(scene, depsgraph=None): current_frame = obj.BSEQ.frame meshio_mesh = None pattern = obj.BSEQ.pattern - if obj.BSEQ.use_relative: + # check if the path is relative + if bpy.path.is_subdir(pattern, bpy.path.abspath("//")): if scene.BSEQ.root_path != "": pattern = bpy.path.abspath(pattern, start=scene.BSEQ.root_path) else: diff --git a/bseq/operators.py b/bseq/operators.py index ca2b636..384edb7 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -7,10 +7,9 @@ from .importer import create_obj, create_meshio_obj import numpy as np - # Here are load and delete operations class BSEQ_OT_load(bpy.types.Operator): - '''This operator loads a sequnce''' + '''This operator loads a sequence''' bl_label = "Load Sequence" bl_idname = "sequence.load" bl_options = {"UNDO"} @@ -48,13 +47,13 @@ def execute(self, context): importer_prop.custom_scale) if importer_prop.use_custom_transform else Matrix.Identity(4)) - create_obj(fs, importer_prop.relative, importer_prop.root_path, transform_matrix=transform_matrix) + create_obj(fs, importer_prop.root_path, transform_matrix=transform_matrix) return {"FINISHED"} class BSEQ_OT_edit(bpy.types.Operator): - '''This operator changes a sequnce''' - bl_label = "Edit Sequences Path" + '''This operator changes a sequence''' + bl_label = "Edit Sequence's Path" bl_idname = "sequence.edit" bl_options = {"UNDO"} @@ -93,15 +92,18 @@ def execute(self, context): if not obj: return {"CANCELLED"} if importer_prop.relative: - obj.BSEQ.pattern = bpy.path.relpath(str(fs)) + if importer_prop.root_path != "": + object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=importer_prop.root_path) + else: + object.BSEQ.pattern = bpy.path.relpath(str(fileseq)) + else: obj.BSEQ.pattern = str(fs) - obj.BSEQ.use_relative = importer_prop.relative return {"FINISHED"} class BSEQ_OT_resetpt(bpy.types.Operator): - '''This operator reset the geometry nodes of the sequence as a point cloud''' + '''This operator resets the geometry nodes of the sequence as a point cloud''' bl_label = "Reset Geometry Nodes as Point Cloud" bl_idname = "bseq.resetpt" bl_options = {"UNDO"} @@ -136,7 +138,7 @@ def execute(self, context): class BSEQ_OT_resetmesh(bpy.types.Operator): - '''This operator reset the geometry nodes of the sequence as a point cloud''' + '''This operator resets the geometry nodes of the sequence as a point cloud''' bl_label = "Reset Geometry Nodes as Mesh" bl_idname = "bseq.resetmesh" bl_options = {"UNDO"} @@ -161,7 +163,7 @@ def execute(self, context): class BSEQ_OT_resetins(bpy.types.Operator): - '''This operator reset the geometry nodes of the sequence as a point cloud''' + '''This operator resets the geometry nodes of the sequence as a point cloud''' bl_label = "Reset Geometry Nodes as Instances" bl_idname = "bseq.resetins" bl_options = {"UNDO"} @@ -209,8 +211,8 @@ def execute(self, context): class BSEQ_OT_set_as_split_norm(bpy.types.Operator): - '''This operator set the vertex attribute as vertex split normals''' - bl_label = "Set as split normal per Vertex" + '''This operator sets the vertex attributes as vertex split normals''' + bl_label = "Set as split normal per vertex" bl_idname = "bseq.setsplitnorm" bl_options = {"UNDO"} @@ -228,8 +230,8 @@ def execute(self, context): class BSEQ_OT_remove_split_norm(bpy.types.Operator): - '''This operator remove the vertex attribute as vertex split normals''' - bl_label = "Remove split normal per Vertex" + '''This operator removes the vertex attributes as vertex split normals''' + bl_label = "Remove split normal per vertex" bl_idname = "bseq.removesplitnorm" bl_options = {"UNDO"} @@ -244,8 +246,8 @@ def execute(self, context): class BSEQ_OT_disable_selected(bpy.types.Operator): - '''This operator disable all selected sequence''' - bl_label = "Disable Selected Sequence" + '''This operator disables all selected sequence''' + bl_label = "Disable selected sequence" bl_idname = "bseq.disableselected" bl_options = {"UNDO"} @@ -257,8 +259,8 @@ def execute(self, context): class BSEQ_OT_enable_selected(bpy.types.Operator): - '''This operator enable all selected sequence''' - bl_label = "Enable Selected Sequence" + '''This operator enables all selected sequence''' + bl_label = "Enable selected sequence" bl_idname = "bseq.enableselected" bl_options = {"UNDO"} @@ -270,8 +272,8 @@ def execute(self, context): class BSEQ_OT_refresh_seq(bpy.types.Operator): - '''This operator refresh the sequence''' - bl_label = "Refresh Sequence" + '''This operator refreshes the sequence''' + bl_label = "Refresh sequence" bl_idname = "bseq.refresh" def execute(self, context): @@ -282,8 +284,8 @@ def execute(self, context): return {"FINISHED"} class BSEQ_OT_disable_all(bpy.types.Operator): - '''This operator disable all selected sequence''' - bl_label = "Disable All Sequences" + '''This operator disables all selected sequence''' + bl_label = "Disable all sequences" bl_idname = "bseq.disableall" bl_options = {"UNDO"} @@ -294,8 +296,8 @@ def execute(self, context): return {"FINISHED"} class BSEQ_OT_enable_all(bpy.types.Operator): - '''This operator enable all selected sequence''' - bl_label = "Enable All Sequences" + '''This operator enables all selected sequence''' + bl_label = "Enable all sequences" bl_idname = "bseq.enableall" bl_options = {"UNDO"} @@ -307,8 +309,8 @@ def execute(self, context): class BSEQ_OT_refresh_sequences(bpy.types.Operator): '''This operator refreshes all found sequences''' - bl_label = "" #"Refresh Found Sequences" - bl_idname = "bseq.refreshseqs" + bl_label = "Refresh all sequences" + bl_idname = "bseq.refreshall" bl_options = {"UNDO"} def execute(self, context): @@ -319,7 +321,7 @@ def execute(self, context): return {"FINISHED"} class BSEQ_OT_set_start_end_frames(bpy.types.Operator): - '''This changes the timeline start and end frames to the length of a specific sequence''' + '''This operator changes the timeline start and end frames to the length of a specific sequence''' bl_label = "Set timeline" bl_idname = "bseq.set_start_end_frames" bl_options = {"UNDO"} @@ -341,15 +343,27 @@ def execute(self, context): class WM_OT_batchSequences(bpy.types.Operator, ImportHelper): """Batch Import Sequences""" bl_idname = "wm.seq_import_batch" - bl_label = "Import multiple sequences" + bl_label = "Import Sequences" bl_options = {'PRESET', 'UNDO'} + # filter_glob: bpy.types.StringProperty( + # default="*.txt", + # options={'HIDDEN'}, + # maxlen=255, # Max internal buffer length, longer would be clamped. + # ) + files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) def execute(self, context): scene = context.scene importer_prop = scene.BSEQ + if importer_prop.relative and not bpy.data.is_saved: + # use relative but file not saved + show_message_box("When using relative path, please save file before using it", icon="ERROR") + return {"CANCELLED"} + + folder = Path(self.filepath) used_seqs = set() @@ -362,14 +376,48 @@ def execute(self, context): if matching_seqs: transform_matrix = (Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) if importer_prop.use_custom_transform else Matrix.Identity(4)) - create_obj(matching_seqs[0], False, importer_prop.root_path, transform_matrix=transform_matrix) + create_obj(matching_seqs[0], importer_prop.root_path, transform_matrix=transform_matrix) used_seqs.add(matching_seqs[0]) return {'FINISHED'} + + def draw(self, context): + pass + +class WM_OT_batchSequences_Settings(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Settings Panel" + bl_options = {'HIDE_HEADER'} + # bl_parent_id = "FILE_PT_operator" # Optional + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + return operator.bl_idname == "WM_OT_seq_import_batch" + + def draw(self, context): + layout = self.layout + importer_prop = context.scene.BSEQ + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = sfile.active_operator + + layout.prop(operator, 'type') + layout.prop(operator, 'use_setting') + + layout.alignment = 'LEFT' + layout.prop(importer_prop, "relative", text="Relative Path") + if importer_prop.relative: + layout.prop(importer_prop, "root_path", text="Root Directory") class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper): """Batch Import Meshio Objects""" bl_idname = "wm.meshio_import_batch" - bl_label = "Import multiple Meshio objects" + bl_label = "Import Multiple Meshio Objects" bl_options = {'PRESET', 'UNDO'} files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) @@ -386,3 +434,58 @@ def menu_func_import(self, context): self.layout.operator( WM_OT_MeshioObject.bl_idname, text="MeshIO Object") + +# Default Keymap Configuration +addon_keymaps = [] + +def add_keymap(): + wm = bpy.context.window_manager + + # Add new keymap section for BSEQ + + kc = wm.keyconfigs.addon + if kc: + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("sequence.load", type='F', value='PRESS', shift=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("bseq.disableselected", type='D', value='PRESS', shift=True, ctrl=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("bseq.enableselected", type='E', value='PRESS', shift=True, ctrl=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("bseq.refresh", type='R', value='PRESS', shift=True, ctrl=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("bseq.disableall", type='D', value='PRESS', shift=True, alt=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("bseq.enableall", type='E', value='PRESS', shift=True, alt=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("bseq.refreshall", type='R', value='PRESS', shift=True, alt=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("bseq.set_start_end_frames", type='F', value='PRESS', shift=True, ctrl=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("wm.seq_import_batch", type='I', value='PRESS', shift=True, ctrl=True) + addon_keymaps.append((km, kmi)) + + km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') + kmi = km.keymap_items.new("wm.meshio_import_batch", type='M', value='PRESS', shift=True, ctrl=True) + addon_keymaps.append((km, kmi)) + +def delete_keymap(): + for km, kmi in addon_keymaps: + km.keymap_items.remove(kmi) + addon_keymaps.clear() diff --git a/bseq/panels.py b/bseq/panels.py index 5e1ffc3..25b3e1a 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -22,19 +22,23 @@ def filter_items(self, context, data, property): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: - row = layout.row() - row.prop(item, "name", text='Name ', emboss=False) + split = layout.split(factor=0.7) + col1 = split.column() + col2 = split.column() + split2 = col2.split(factor=0.5) + col2 = split2.column() + col3 = split2.column() + col1.prop(item, "name", text='', emboss=False) if item.BSEQ.enabled: - row.prop(item.BSEQ, "enabled", text = "ENABLED", icon="PLAY") - row.prop(item.BSEQ, "frame", text = "Current Frame:") + col2.prop(item.BSEQ, "enabled", text="", icon="PLAY") + col3.prop(item.BSEQ, "frame", text="") else: - row.prop(item.BSEQ, "enabled", text = "DISABLED", icon="PAUSE") - row.label(text = "Animation Stopped") + col2.prop(item.BSEQ, "enabled", text ="", icon="PAUSE") + col3.label(text="", icon="BLANK1") else: # actually, I guess this line of code won't be executed? layout.label(text="", translate=False, icon_value=icon) - class BSEQ_UL_Att_List(bpy.types.UIList): ''' This controls the list of attributes available for this sequence @@ -43,84 +47,65 @@ class BSEQ_UL_Att_List(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: layout.enabled = False - layout.prop(item, "name", text='Name ', emboss=False) + layout.prop(item, "name", text='', emboss=False) obj = bpy.data.objects[context.scene.BSEQ.selected_obj_num] mesh = obj.data if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == item.name: - layout.label(text="using as split norm") + layout.label(text="Use as split norm.") else: # actually, I guess this line of code won't be executed? layout.label(text="", translate=False, icon_value=icon) - -class BSEQ_List_Panel(bpy.types.Panel): - ''' - This is the panel of imported sequences, bottom part of images/9.png - ''' - bl_label = "Imported Sequences" - bl_idname = "BSEQ_PT_list" +class BSEQ_Panel: bl_space_type = 'VIEW_3D' bl_region_type = "UI" bl_category = "Sequence Loader" bl_context = "objectmode" +class BSEQ_Globals_Panel(BSEQ_Panel, bpy.types.Panel): + bl_label = "Global Settings" + bl_idname = "BSEQ_PT_global" + bl_options = {'DEFAULT_CLOSED'} + def draw(self, context): layout = self.layout sim_loader = context.scene.BSEQ - row = layout.row() - row.template_list("BSEQ_UL_Obj_List", "", bpy.data, "objects", sim_loader, "selected_obj_num", rows=2) - row = layout.row() - row.operator("bseq.enableselected", text="Enable Selected") - row.operator("bseq.disableselected", text="Disable Selected") - row.operator("bseq.refresh", text="Refresh") - row = layout.row() - row.operator("bseq.enableall", text="Enable All") - row.operator("bseq.disableall", text="Disable All") - row.operator("bseq.set_start_end_frames", text="Set timeline") - + split = layout.split() + col1 = split.column() + col1.alignment = 'RIGHT' + col2 = split.column() + col1.label(text='Global Settings') + col2.prop(sim_loader, "print", text="Print Sequence Information") + col2.prop(sim_loader, "auto_refresh_active", text="Auto Refresh Active") + col2.prop(sim_loader, "auto_refresh_all", text="Auto Refresh All") -class BSEQ_Settings(bpy.types.Panel): - ''' - This is the panel of settings of selected sequence - ''' - bl_label = "Sequence Settings" - bl_idname = "BSEQ_PT_settings" - bl_space_type = 'VIEW_3D' - bl_region_type = "UI" - bl_category = "Sequence Loader" - bl_context = "objectmode" - bl_options = {"DEFAULT_CLOSED"} +class BSEQ_Advanced_Panel(BSEQ_Panel, bpy.types.Panel): + bl_label = "Advanced Settings" + bl_idname = "BSEQ_PT_advanced" + bl_options = {'DEFAULT_CLOSED'} def draw(self, context): layout = self.layout sim_loader = context.scene.BSEQ + + split = layout.split() + col1 = split.column() + col2 = split.column() + if sim_loader.selected_obj_num >= len(bpy.data.objects): return obj = bpy.data.objects[sim_loader.selected_obj_num] if not obj.BSEQ.init: return - # path settings - layout.label(text="Sequence Information (read-only)") - box = layout.box() - - split = box.split() - col1 = split.column() - col1.alignment = 'RIGHT' - col2 = split.column(align=False) - - col2.enabled = False - col1.label(text='Relative') - col2.prop(obj.BSEQ, 'use_relative', text="") - col1.label(text='Pattern') - col2.prop(obj.BSEQ, 'pattern', text="") - col1.label(text='Last loading time (ms)') - col2.prop(obj.BSEQ, 'last_benchmark', text="") + col1.label(text='Script') + col2.prop_search(obj.BSEQ, 'script_name', bpy.data, 'texts', text="") # geometry nodes settings - layout.label(text="Geometry Nodes") + layout.label(text="Geometry Nodes (select sequence first)") + box = layout.box() box.label(text="Point Cloud and Instances Material") split = box.split() @@ -140,6 +125,59 @@ def draw(self, context): col3.operator('bseq.resetins', text="Instances") +class BSEQ_List_Panel(BSEQ_Panel, bpy.types.Panel): + ''' + This is the panel of imported sequences, bottom part of images/9.png + ''' + bl_label = "Sequences" + bl_idname = "BSEQ_PT_list" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + sim_loader = context.scene.BSEQ + row = layout.row() + row.template_list("BSEQ_UL_Obj_List", "", bpy.data, "objects", sim_loader, "selected_obj_num", rows=2) + row = layout.row() + row.operator("bseq.enableselected", text="Activate") + row.operator("bseq.disableselected", text="Deactivate") + row.operator("bseq.refresh", text="Refresh") + row = layout.row() + row.operator("bseq.enableall", text="Activate All") + row.operator("bseq.disableall", text="Deactivate All") + row.operator("bseq.set_start_end_frames", text="Set timeline") + +class BSEQ_Settings(BSEQ_Panel, bpy.types.Panel): + ''' + This is the panel of settings of selected sequence + ''' + bl_label = "Sequence Properties" + bl_idname = "BSEQ_PT_settings" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + layout = self.layout + sim_loader = context.scene.BSEQ + importer_prop = context.scene.BSEQ + + if sim_loader.selected_obj_num >= len(bpy.data.objects): + return + obj = bpy.data.objects[sim_loader.selected_obj_num] + if not obj.BSEQ.init: + return + + split = layout.split() + col1 = split.column() + col1.alignment = 'RIGHT' + col2 = split.column(align=False) + + col1.label(text='Pattern') + col2.prop(obj.BSEQ, 'pattern', text="") + col1.label(text='Last loading time (ms)') + row2 = col2.row() + row2.enabled = False + row2.prop(obj.BSEQ, 'last_benchmark', text="", ) + # attributes settings layout.label(text="Attributes") box = layout.box() @@ -148,39 +186,70 @@ def draw(self, context): box.operator("bseq.setsplitnorm", text="Set selected as normal") box.operator("bseq.removesplitnorm", text="Clear normal") - # advance settings - layout.label(text="Advanced") - box = layout.box() - split = box.split() +class BSEQ_Import(BSEQ_Panel, bpy.types.Panel): + ''' + This is the panel of main addon interface. see images/1.jpg + ''' + bl_label = "Import" + bl_idname = "BSEQ_PT_panel" + + def draw(self, context): + layout = self.layout + scene = context.scene + importer_prop = scene.BSEQ + + layout.operator("wm.seq_import_batch") + + split = layout.split() + col1 = split.column() + col2 = split.column() + + #layout.label(text="Global Settings") + #box = layout.box() + split = layout.split(factor=0.5) col1 = split.column() col1.alignment = 'RIGHT' col2 = split.column(align=False) - col1.label(text="Show Settings") - col2.prop(obj.BSEQ, 'use_advance', text="") - if obj.BSEQ.use_advance: - col1.label(text='Script') - col2.prop_search(obj.BSEQ, 'script_name', bpy.data, 'texts', text="") + col1.label(text="Import Settings") -class BSEQ_Import(bpy.types.Panel): - ''' - This is the panel of main addon interface. see images/1.jpg - ''' - bl_label = "Sequence Loader" - bl_idname = "BSEQ_PT_panel" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "Sequence Loader" - bl_context = "objectmode" + # col2.prop(importer_prop, "use_blender_obj_import", text="Blender .obj Importer") + col2.prop(importer_prop, "relative", text="Relative Path") + + if importer_prop.relative: + col1.label(text="Root Directory") + col2.prop(importer_prop, "root_path", text="") + + col2.prop(importer_prop, "use_imported_normals", text="Use Imported Normals") + + col2.prop(importer_prop, "use_custom_transform", text="Custom Transform") + + if importer_prop.use_custom_transform: + split = layout.split(factor=0.33) + box_col1 = split.column() + box_col2 = split.column() + box_col3 = split.column() + + box_col1.label(text="Location:") + box_col1.prop(importer_prop, "custom_location", text="") + + box_col2.label(text="Rotation:") + box_col2.prop(importer_prop, "custom_rotation", text="") + + box_col3.label(text="Scale:") + box_col3.prop(importer_prop, "custom_scale", text="") + +class BSEQ_Import_Child1(BSEQ_Panel, bpy.types.Panel): + bl_parent_id = "BSEQ_PT_panel" + bl_label = "Import from folder" + bl_options = {'DEFAULT_CLOSED'} def draw(self, context): layout = self.layout scene = context.scene importer_prop = scene.BSEQ - layout.label(text="Basic Import Settings") - box = layout.box() - split = box.split() + split = layout.split() col1 = split.column() col1.alignment = 'RIGHT' col2 = split.column(align=False) @@ -188,7 +257,7 @@ def draw(self, context): col1.label(text="Directory") col2.prop(importer_prop, "path", text="") - col1.label(text="Use Custom Pattern") + col1.label(text="Custom Pattern") col2.prop(importer_prop, "use_pattern", text="") col1.label(text="Sequence Pattern") if importer_prop.use_pattern: @@ -198,62 +267,24 @@ def draw(self, context): col3 = split2.column() col4 = split2.column() col3.prop(importer_prop, "fileseq", text="") - col4.operator("bseq.refreshseqs", icon="FILE_REFRESH") - - col1.label(text="Use Relative Path") - col2.prop(importer_prop, "relative", text="") + col4.operator("bseq.refreshall", text='', icon="FILE_REFRESH") - if importer_prop.relative: - col1.label(text="Root Directory") - col2.prop(importer_prop, "root_path", text="") + layout.operator("sequence.load") - col1.label(text="Use Blender .obj Importer") - col2.prop(importer_prop, "use_blender_obj_import", text="") +class BSEQ_Import_Child2(BSEQ_Panel, bpy.types.Panel): + bl_parent_id = "BSEQ_PT_panel" + bl_label = "Test" + bl_options = {'HIDE_HEADER'} - layout.operator("sequence.load") + def draw(self, context): + layout = self.layout + scene = context.scene + importer_prop = scene.BSEQ - layout.operator("wm.seq_import_batch") - split = layout.split() col1 = split.column() col2 = split.column() - # check if edit_obj exist - # if not exist any more, then delete the object manually - # see here https://blender.stackexchange.com/a/164835 for more details - # I personally think this implementation is not a good design, - # but can't think of any better ways now - if importer_prop.edit_obj and context.scene.objects.get(importer_prop.edit_obj.name) == None: - bpy.data.objects.remove(importer_prop.edit_obj) - - col1.prop_search(importer_prop, 'edit_obj', bpy.data, 'objects', text="") - col2.operator("sequence.edit") - - layout.label(text="Global Settings") - box = layout.box() - split = box.split() - col1 = split.column() - col1.alignment = 'RIGHT' - col2 = split.column(align=False) - - col1.label(text="Print Sequence Information on Render") - col2.prop(importer_prop, "print", text="") - col1.label(text="Auto refresh all the sequence every frame") - col2.prop(importer_prop, "auto_refresh", text="") - col1.label(text="Use custom transformation matrix") - col2.prop(importer_prop, "use_custom_transform", text="") - - if importer_prop.use_custom_transform: - box.label(text="Location:") - box.prop(importer_prop, "custom_location", text="") - - box.label(text="Rotation:") - box.prop(importer_prop, "custom_rotation", text="") - - box.label(text="Scale:") - box.prop(importer_prop, "custom_scale", text="") - - class BSEQ_Templates(bpy.types.Menu): ''' Here is the template panel, shown in the text editor -> templates diff --git a/bseq/properties.py b/bseq/properties.py index 89fad52..6bf1bfb 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -5,34 +5,52 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): path: bpy.props.StringProperty(name="Directory", subtype="DIR_PATH", - description="You need to go to the folder with the sequence, then click \"Accept\". ", - update=update_path) - relative: bpy.props.BoolProperty(name='Use relative path', description="whether or not to use reletive path", default=False) + description="You need to go to the folder with the sequence, then click \"Accept\"", + update=update_path, + ) + + relative: bpy.props.BoolProperty(name='Use relative path', + description="Use relative path", + default=True, + ) + + use_imported_normals: bpy.props.BoolProperty(name='Use Imported Normals', + description="Use normals from imported mesh", + default=False, + ) + root_path: bpy.props.StringProperty(name="Root Directory", subtype="DIR_PATH", - description="Select a root folder for all relative paths. When not set the current filename is used.", - update=update_path) + description="Select a root folder for all relative paths. If not set, the current filename is used", + update=update_path, + default="", + ) + fileseq: bpy.props.EnumProperty( name="File Sequences", - description="Please choose the file sequences you want", + description="Choose file sequences.", items=item_fileseq, ) + use_pattern: bpy.props.BoolProperty(name='Use pattern', - description="whether or not to use manually typed pattern", - default=False) + description="Use manually typed pattern, if the sequence can't be deteced", + default=False, + ) + pattern: bpy.props.StringProperty(name="Pattern", - description="You can specify the pattern here, in case the sequence can't be deteced.") + description="Custom pattern.", + ) - file_paths: bpy.props.StringProperty(name="File", - subtype="FILE_PATH", - description="Select a root folder for all relative paths. When not set the current filename is used.") - selected_obj_deselectall_flag: bpy.props.BoolProperty(default=True, - description="the flag to determine whether call deselect all or not ") + description="Flag that determines whether to deselect all items or not", + ) + selected_obj_num: bpy.props.IntProperty(name='imported count', - description='the number of imported sequence, when selecting from ui list', + description='Number of imported sequences, when selecting from UI list', default=0, - update=update_selected_obj_num) + update=update_selected_obj_num, + ) + selected_attribute_num: bpy.props.IntProperty(default=0) material: bpy.props.PointerProperty( @@ -45,50 +63,61 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): poll=poll_edit_obj, ) - print: bpy.props.BoolProperty(name='print', - description="whether or not to print additional information when rendering", - default=True) + print: bpy.props.BoolProperty(name='Print Sequence Information', + description="Print additional information during rendering to a file in the same folder as the render output", + default=True, + ) - auto_refresh: bpy.props.BoolProperty(name='auto refresh', - description="whether or not to auto refresh all the sequence every frame", - default=False) - - use_custom_transform: bpy.props.BoolProperty(name='Use custom transformation matrix', - description="Whether or not to use a custom transformation matrix", - default=False) + auto_refresh_active: bpy.props.BoolProperty(name='Auto Refresh Active Sequences', + description="Auto refresh all active sequences every frame", + default=False, + ) + + auto_refresh_all: bpy.props.BoolProperty(name='Auto Refresh All Sequences', + description="Auto refresh all sequences every frame", + default=False, + ) + + use_custom_transform: bpy.props.BoolProperty(name='Custom Transform', + description="Use a custom transformation matrix when importing", + default=False, + ) custom_location: bpy.props.FloatVectorProperty(name='Custom Location', description='Set custom location vector', size=3, - subtype="TRANSLATION") + subtype="TRANSLATION", + ) custom_rotation: bpy.props.FloatVectorProperty(name='Custom Rotation', description='Set custom rotation vector', size=3, subtype="EULER", - default=[0,0,0]) + default=[0,0,0], + ) custom_scale: bpy.props.FloatVectorProperty(name='Custom Scale', description='Set custom scaling vector', size=3, subtype="COORDINATES", - default=[1,1,1]) + default=[1,1,1], + ) - use_blender_obj_import: bpy.props.BoolProperty(name='Use Blender Object Import', - description="Whether or not to use Blender's built-in object import function", - default=True) + use_blender_obj_import: bpy.props.BoolProperty(name='Blender .obj import', + description="Use Blender's built-in .obj import function (or meshio's .obj import function)", + default=True, + ) class BSEQ_obj_property(bpy.types.PropertyGroup): init: bpy.props.BoolProperty(default=False) enabled: bpy.props.BoolProperty(default=True, - description="When disbaled, the sequence won't be updated at each frame. Enabled by default") + description="If disabled, the sequence won't be updated each frame") use_advance: bpy.props.BoolProperty(default=False) script_name: bpy.props.StringProperty() - use_relative: bpy.props.BoolProperty(default=False) pattern: bpy.props.StringProperty() frame: bpy.props.IntProperty() - start_end_frame: bpy.props.IntVectorProperty(name="Start and End Frames", size=2, default=(0, 0)) - last_benchmark: bpy.props.FloatProperty(name="Last Loading Time") + start_end_frame: bpy.props.IntVectorProperty(name="Start and end frames", size=2, default=(0, 0)) + last_benchmark: bpy.props.FloatProperty(name="Last loading time") # set this property for mesh, not object (maybe change later?) class BSEQ_mesh_property(bpy.types.PropertyGroup): diff --git a/bseq/utils.py b/bseq/utils.py index 7f6fd35..bcf1e35 100644 --- a/bseq/utils.py +++ b/bseq/utils.py @@ -27,8 +27,9 @@ def stop_animation(): def refresh_obj(obj, scene): + is_relative = bpy.path.is_subdir(obj.BSEQ.pattern, bpy.path.abspath("//")) fs = obj.BSEQ.pattern - if obj.BSEQ.use_relative: + if is_relative: if scene.BSEQ.root_path != "": fs = bpy.path.abspath(fs, start=scene.BSEQ.root_path) else: @@ -38,7 +39,8 @@ def refresh_obj(obj, scene): fs = fileseq.findSequenceOnDisk(fs.dirname() + fs.basename() + "@" + fs.extension()) obj.BSEQ.start_end_frame = (fs.start(), fs.end()) fs = str(fs) - if obj.BSEQ.use_relative: + # obj.BSEQ.pattern is a path and I want to check if it is a relative path + if is_relative: if scene.BSEQ.root_path != "": fs = bpy.path.relpath(fs, start=scene.BSEQ.root_path) else: From f5f6b4f5516ab2692544cc09297bcc68d62d2871 Mon Sep 17 00:00:00 2001 From: justo46 Date: Tue, 22 Aug 2023 15:21:25 +0200 Subject: [PATCH 18/38] Revert "Revert "A lot of UI changes"" This reverts commit 34d179c9f0205f547db932b1fb7d71fd0dbb9b35. From 860298030702cdb3c5b933d02220b9ec28dac166 Mon Sep 17 00:00:00 2001 From: justo46 Date: Tue, 10 Oct 2023 17:36:41 +0200 Subject: [PATCH 19/38] Fixed obj. loading duplicate bug --- bseq/importer.py | 77 ++++++++++++++++++++++++++++++++++++++++------ bseq/operators.py | 40 ++++++++++++++++++------ bseq/panels.py | 15 +++++++-- bseq/properties.py | 5 +++ 4 files changed, 114 insertions(+), 23 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index bee33cd..696464b 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -191,8 +191,23 @@ def create_meshio_obj(filepath): # Do I need this for multi-loading? if filepath.endswith(".obj"): + # Save all current objects + objs = set(bpy.context.scene.objects) + # Reload the object bpy.ops.import_scene.obj(filepath=filepath) - obj = bpy.context.selected_objects[0] + # Substract all previous items from the current items and print their names + imported_objs = set(bpy.context.scene.objects) - objs + print(", ".join(o.name for o in imported_objs)) + + # Check if the imported object worked correctly + if len(imported_objs) == 1: + obj = imported_objs.pop() + else: + show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + "obj. Loading Error", + icon="ERROR") + return + obj.name = os.path.basename(filepath) return # create the object @@ -213,16 +228,34 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, #.obj sequences have to be handled differently is_obj_seq = filepath.endswith(".obj") if is_obj_seq and bpy.context.scene.BSEQ.use_blender_obj_import: + + # Save all current objects + objs = set(bpy.context.scene.objects) + # Reload the object bpy.ops.import_scene.obj(filepath=filepath) - enabled = True + # Substract all previous items from the current items and print their names + imported_objs = set(bpy.context.scene.objects) - objs + print(", ".join(o.name for o in imported_objs)) - tmp_obj = bpy.context.selected_objects[-1] + # Check if the imported object worked correctly + if len(imported_objs) == 1: + tmp_obj = imported_objs.pop() + else: + show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + "obj. Loading Error", + icon="ERROR") + return name = fileseq.basename() + "@" + fileseq.extension() - object = bpy.data.objects.new(name, tmp_obj.data) - tmp_obj.select_set(True) - bpy.ops.object.delete() + # Create object with empty mesh + object = bpy.data.objects.new(name, bpy.data.meshes.new(name)) + object.data = tmp_obj.data + + # Delete tmp_obj with data + bpy.data.objects.remove(tmp_obj, do_unlink=True) + + enabled = True else: meshio_mesh = None @@ -294,13 +327,37 @@ def update_obj(scene, depsgraph=None): if pattern.endswith(".obj") and scene.BSEQ.use_blender_obj_import: filepath = fs[current_frame % len(fs)] + print("File path update: " + filepath) + + # Save all current objects + objs = set(scene.objects) # Reload the object bpy.ops.import_scene.obj(filepath=filepath) - tmp_obj = bpy.context.selected_objects[-1] + # Substract all previous items from the current items and print their names + imported_objs = set(scene.objects) - objs + print(", ".join(o.name for o in imported_objs)) + + # Check if the imported object worked correctly + if len(imported_objs) == 1: + new_tmp_obj = imported_objs.pop() + else: + show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + "obj. Loading Error", + icon="ERROR") + continue + + # Copy the data except for material + if obj.data.materials: + # assign to 1st material slot + new_tmp_obj.data.materials[0] = obj.data.materials[0] + else: + # no slots + new_tmp_obj.data.materials.append(obj.data.materials[0]) + + obj.data = new_tmp_obj.data - obj.data = tmp_obj.data - tmp_obj.select_set(True) - bpy.ops.object.delete() + # Delete the temporary object with the data + bpy.data.objects.remove(new_tmp_obj, do_unlink=True) apply_transformation(meshio_mesh, obj, depsgraph) diff --git a/bseq/operators.py b/bseq/operators.py index 384edb7..a32b92a 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -346,13 +346,33 @@ class WM_OT_batchSequences(bpy.types.Operator, ImportHelper): bl_label = "Import Sequences" bl_options = {'PRESET', 'UNDO'} - # filter_glob: bpy.types.StringProperty( - # default="*.txt", - # options={'HIDDEN'}, - # maxlen=255, # Max internal buffer length, longer would be clamped. - # ) + def update_filter_glob(self, context): + bpy.ops.wm.seq_import_batch('INVOKE_DEFAULT') + + filter_string: bpy.props.StringProperty( + default="*.obj", + options={'HIDDEN'}, + update=update_filter_glob, + ) + + filename_ext='' + filter_glob: bpy.props.StringProperty( + default='*.obj', + options={'HIDDEN', 'LIBRARY_EDITABLE'}, + ) files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) + + def invoke(self, context, event): + scene = context.scene + if scene.BSEQ.filter_string: + self.filter_glob = scene.BSEQ.filter_string + else: + self.filter_glob = "*" + + context.window_manager.fileselect_add(self) + + return {'RUNNING_MODAL'} def execute(self, context): scene = context.scene @@ -363,6 +383,7 @@ def execute(self, context): show_message_box("When using relative path, please save file before using it", icon="ERROR") return {"CANCELLED"} + self.filter_glob = '*' folder = Path(self.filepath) used_seqs = set() @@ -379,7 +400,7 @@ def execute(self, context): create_obj(matching_seqs[0], importer_prop.root_path, transform_matrix=transform_matrix) used_seqs.add(matching_seqs[0]) return {'FINISHED'} - + def draw(self, context): pass @@ -403,11 +424,10 @@ def draw(self, context): layout.use_property_split = True layout.use_property_decorate = False # No animation. - sfile = context.space_data - operator = sfile.active_operator + # sfile = context.space_data + # operator = sfile.active_operator - layout.prop(operator, 'type') - layout.prop(operator, 'use_setting') + layout.prop(importer_prop, 'filter_string') layout.alignment = 'LEFT' layout.prop(importer_prop, "relative", text="Relative Path") diff --git a/bseq/panels.py b/bseq/panels.py index 25b3e1a..3c44e7c 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -22,19 +22,27 @@ def filter_items(self, context, data, property): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: - split = layout.split(factor=0.7) + split = layout.split(factor=0.5) col1 = split.column() col2 = split.column() - split2 = col2.split(factor=0.5) + split2 = col2.split(factor=0.25) col2 = split2.column() col3 = split2.column() + split3 = col3.split(factor=0.33) + col3 = split3.column() + col4 = split3.column() + col4.alignment = 'CENTER' + start_frame = item.BSEQ.start_end_frame[0] + end_frame = item.BSEQ.start_end_frame[1] col1.prop(item, "name", text='', emboss=False) if item.BSEQ.enabled: col2.prop(item.BSEQ, "enabled", text="", icon="PLAY") col3.prop(item.BSEQ, "frame", text="") + col4.label(text=str(start_frame) + '-' + str(end_frame)) else: col2.prop(item.BSEQ, "enabled", text ="", icon="PAUSE") col3.label(text="", icon="BLANK1") + col4.label(text=str(start_frame) + '-' + str(end_frame)) else: # actually, I guess this line of code won't be executed? layout.label(text="", translate=False, icon_value=icon) @@ -213,7 +221,8 @@ def draw(self, context): col1.label(text="Import Settings") - # col2.prop(importer_prop, "use_blender_obj_import", text="Blender .obj Importer") + col2.prop(importer_prop, "filter_string", text="Filter String") + col2.prop(importer_prop, "relative", text="Relative Path") if importer_prop.relative: diff --git a/bseq/properties.py b/bseq/properties.py index 6bf1bfb..e2c9401 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -107,6 +107,11 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): description="Use Blender's built-in .obj import function (or meshio's .obj import function)", default=True, ) + + filter_string: bpy.props.StringProperty(name='Filter String', + description='Filter string for file sequences', + default='', + ) class BSEQ_obj_property(bpy.types.PropertyGroup): init: bpy.props.BoolProperty(default=False) From f9851ab1bb88195255227e844e9982bbe0898b71 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 18 Oct 2023 17:40:38 +0200 Subject: [PATCH 20/38] Purges all old meshes (during .obj sequences) --- bseq/importer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bseq/importer.py b/bseq/importer.py index 696464b..807ac01 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -359,6 +359,9 @@ def update_obj(scene, depsgraph=None): # Delete the temporary object with the data bpy.data.objects.remove(new_tmp_obj, do_unlink=True) + # purge old meshes + bpy.ops.outliner.orphans_purge(do_recursive=True) + apply_transformation(meshio_mesh, obj, depsgraph) end_time = time.perf_counter() From e8432eb09205284d21e8f47423fe8c5e9e0d7692 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 19 Oct 2023 03:15:52 +0200 Subject: [PATCH 21/38] Changed class names to fix warnings --- __init__.py | 12 ++++++------ bseq/__init__.py | 16 ++++++++-------- bseq/operators.py | 8 ++++---- bseq/panels.py | 6 +++--- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/__init__.py b/__init__.py index a3ead55..be60ec6 100644 --- a/__init__.py +++ b/__init__.py @@ -32,9 +32,9 @@ BSEQ_OT_resetpt, BSEQ_OT_resetins, BSEQ_OT_resetmesh, - BSEQ_Import, - BSEQ_Import_Child1, - BSEQ_Import_Child2, + BSEQ_PT_Import, + BSEQ_PT_Import_Child1, + BSEQ_PT_Import_Child2, BSEQ_Globals_Panel, BSEQ_List_Panel, BSEQ_UL_Obj_List, @@ -51,9 +51,9 @@ BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, - WM_OT_batchSequences, - WM_OT_batchSequences_Settings, - WM_OT_MeshioObject + BSEQ_OT_batchSequences, + BSEQ_PT_batchSequences_Settings, + BSEQ_OT_MeshioObject ] def register(): diff --git a/bseq/__init__.py b/bseq/__init__.py index aad51b4..71e5eeb 100644 --- a/bseq/__init__.py +++ b/bseq/__init__.py @@ -1,7 +1,7 @@ from bseq.utils import refresh_obj -from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, WM_OT_batchSequences, WM_OT_batchSequences_Settings, WM_OT_MeshioObject +from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batchSequences, BSEQ_PT_batchSequences_Settings, BSEQ_OT_MeshioObject from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property -from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_Import, BSEQ_Import_Child1, BSEQ_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template +from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_PT_Import, BSEQ_PT_Import_Child1, BSEQ_PT_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template from .messenger import subscribe_to_selected, unsubscribe_to_selected import bpy from bpy.app.handlers import persistent @@ -25,9 +25,9 @@ def BSEQ_initialize(scene): "BSEQ_OT_load", "BSEQ_obj_property", "BSEQ_initialize", - "BSEQ_Import", - "BSEQ_Import_Child1", - "BSEQ_Import_Child2", + "BSEQ_PT_Import", + "BSEQ_PT_Import_Child1", + "BSEQ_PT_Import_Child2", "BSEQ_Globals_Panel", "BSEQ_List_Panel", "BSEQ_UL_Obj_List", @@ -52,7 +52,7 @@ def BSEQ_initialize(scene): "BSEQ_OT_enable_all", "BSEQ_OT_refresh_sequences", "BSEQ_OT_set_start_end_frames", - "WM_OT_batchSequences", - "WM_OT_batchSequences_Settings", - "WM_OT_MeshioObject" + "BSEQ_OT_batchSequences", + "BSEQ_PT_batchSequences_Settings", + "BSEQ_OT_MeshioObject" ] diff --git a/bseq/operators.py b/bseq/operators.py index a32b92a..b305f9d 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -340,7 +340,7 @@ def execute(self, context): import meshio from bpy_extras.io_utils import ImportHelper -class WM_OT_batchSequences(bpy.types.Operator, ImportHelper): +class BSEQ_OT_batchSequences(bpy.types.Operator, ImportHelper): """Batch Import Sequences""" bl_idname = "wm.seq_import_batch" bl_label = "Import Sequences" @@ -404,7 +404,7 @@ def execute(self, context): def draw(self, context): pass -class WM_OT_batchSequences_Settings(bpy.types.Panel): +class BSEQ_PT_batchSequences_Settings(bpy.types.Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOL_PROPS' bl_label = "Settings Panel" @@ -434,7 +434,7 @@ def draw(self, context): if importer_prop.relative: layout.prop(importer_prop, "root_path", text="Root Directory") -class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper): +class BSEQ_OT_MeshioObject(bpy.types.Operator, ImportHelper): """Batch Import Meshio Objects""" bl_idname = "wm.meshio_import_batch" bl_label = "Import Multiple Meshio Objects" @@ -452,7 +452,7 @@ def execute(self, context): def menu_func_import(self, context): self.layout.operator( - WM_OT_MeshioObject.bl_idname, + BSEQ_OT_MeshioObject.bl_idname, text="MeshIO Object") # Default Keymap Configuration diff --git a/bseq/panels.py b/bseq/panels.py index 3c44e7c..4dc083a 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -194,7 +194,7 @@ def draw(self, context): box.operator("bseq.setsplitnorm", text="Set selected as normal") box.operator("bseq.removesplitnorm", text="Clear normal") -class BSEQ_Import(BSEQ_Panel, bpy.types.Panel): +class BSEQ_PT_Import(BSEQ_Panel, bpy.types.Panel): ''' This is the panel of main addon interface. see images/1.jpg ''' @@ -248,7 +248,7 @@ def draw(self, context): box_col3.label(text="Scale:") box_col3.prop(importer_prop, "custom_scale", text="") -class BSEQ_Import_Child1(BSEQ_Panel, bpy.types.Panel): +class BSEQ_PT_Import_Child1(BSEQ_Panel, bpy.types.Panel): bl_parent_id = "BSEQ_PT_panel" bl_label = "Import from folder" bl_options = {'DEFAULT_CLOSED'} @@ -280,7 +280,7 @@ def draw(self, context): layout.operator("sequence.load") -class BSEQ_Import_Child2(BSEQ_Panel, bpy.types.Panel): +class BSEQ_PT_Import_Child2(BSEQ_Panel, bpy.types.Panel): bl_parent_id = "BSEQ_PT_panel" bl_label = "Test" bl_options = {'HIDE_HEADER'} From cc6b8585c3f8f2a6772184d2114aabc3081e0441 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 25 Oct 2023 13:40:18 +0200 Subject: [PATCH 22/38] Uncommented seperate obj import --- bseq/importer.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index 807ac01..e00cf9c 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -197,14 +197,13 @@ def create_meshio_obj(filepath): bpy.ops.import_scene.obj(filepath=filepath) # Substract all previous items from the current items and print their names imported_objs = set(bpy.context.scene.objects) - objs - print(", ".join(o.name for o in imported_objs)) # Check if the imported object worked correctly if len(imported_objs) == 1: obj = imported_objs.pop() else: show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "obj. Loading Error", + "obj. Loading Error in create_meshio_obj", icon="ERROR") return @@ -235,14 +234,13 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, bpy.ops.import_scene.obj(filepath=filepath) # Substract all previous items from the current items and print their names imported_objs = set(bpy.context.scene.objects) - objs - print(", ".join(o.name for o in imported_objs)) # Check if the imported object worked correctly if len(imported_objs) == 1: tmp_obj = imported_objs.pop() else: show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "obj. Loading Error", + "obj. Loading Error in create_obj", icon="ERROR") return @@ -273,7 +271,6 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, object = bpy.data.objects.new(name, mesh) # create the object - print("File path: " + filepath) if bpy.path.is_subdir(filepath, bpy.path.abspath("//")): if root_path != "": object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=root_path) @@ -327,25 +324,24 @@ def update_obj(scene, depsgraph=None): if pattern.endswith(".obj") and scene.BSEQ.use_blender_obj_import: filepath = fs[current_frame % len(fs)] - print("File path update: " + filepath) - # Save all current objects objs = set(scene.objects) + # Reload the object bpy.ops.import_scene.obj(filepath=filepath) + # Substract all previous items from the current items and print their names imported_objs = set(scene.objects) - objs - print(", ".join(o.name for o in imported_objs)) # Check if the imported object worked correctly if len(imported_objs) == 1: new_tmp_obj = imported_objs.pop() else: show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "obj. Loading Error", + "obj. Loading Error in update_obj", icon="ERROR") continue - + # Copy the data except for material if obj.data.materials: # assign to 1st material slot From 4a79952f13629159e6f9052a93955493e92850b5 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 25 Oct 2023 13:42:01 +0200 Subject: [PATCH 23/38] Uncommented seperate obj import --- bseq/importer.py | 184 +++++++++++++++++++++++------------------------ 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index e00cf9c..a2885fd 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -189,26 +189,26 @@ def create_meshio_obj(filepath): "Meshio Loading Error" + str(e), icon="ERROR") - # Do I need this for multi-loading? - if filepath.endswith(".obj"): - # Save all current objects - objs = set(bpy.context.scene.objects) - # Reload the object - bpy.ops.import_scene.obj(filepath=filepath) - # Substract all previous items from the current items and print their names - imported_objs = set(bpy.context.scene.objects) - objs - - # Check if the imported object worked correctly - if len(imported_objs) == 1: - obj = imported_objs.pop() - else: - show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "obj. Loading Error in create_meshio_obj", - icon="ERROR") - return + # if filepath.endswith(".obj"): + # # Save all current objects + # objs = set(bpy.context.scene.objects) + # # Reload the object + # bpy.ops.import_scene.obj(filepath=filepath) + # # Substract all previous items from the current items and print their names + # imported_objs = set(bpy.context.scene.objects) - objs + + # # Check if the imported object worked correctly + # if len(imported_objs) == 1: + # obj = imported_objs.pop() + # else: + # show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + # "obj. Loading Error in create_meshio_obj", + # icon="ERROR") + # return - obj.name = os.path.basename(filepath) - return + # obj.name = os.path.basename(filepath) + # return + # create the object name = os.path.basename(filepath) mesh = bpy.data.meshes.new(name) @@ -225,50 +225,50 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, filepath = fileseq[current_frame % len(fileseq)] #.obj sequences have to be handled differently - is_obj_seq = filepath.endswith(".obj") - if is_obj_seq and bpy.context.scene.BSEQ.use_blender_obj_import: - - # Save all current objects - objs = set(bpy.context.scene.objects) - # Reload the object - bpy.ops.import_scene.obj(filepath=filepath) - # Substract all previous items from the current items and print their names - imported_objs = set(bpy.context.scene.objects) - objs - - # Check if the imported object worked correctly - if len(imported_objs) == 1: - tmp_obj = imported_objs.pop() - else: - show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "obj. Loading Error in create_obj", - icon="ERROR") - return - - name = fileseq.basename() + "@" + fileseq.extension() - - # Create object with empty mesh - object = bpy.data.objects.new(name, bpy.data.meshes.new(name)) - object.data = tmp_obj.data + # is_obj_seq = filepath.endswith(".obj") + # if is_obj_seq and bpy.context.scene.BSEQ.use_blender_obj_import: + + # # Save all current objects + # objs = set(bpy.context.scene.objects) + # # Reload the object + # bpy.ops.import_scene.obj(filepath=filepath) + # # Substract all previous items from the current items and print their names + # imported_objs = set(bpy.context.scene.objects) - objs + + # # Check if the imported object worked correctly + # if len(imported_objs) == 1: + # tmp_obj = imported_objs.pop() + # else: + # show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + # "obj. Loading Error in create_obj", + # icon="ERROR") + # return + + # name = fileseq.basename() + "@" + fileseq.extension() + + # # Create object with empty mesh + # object = bpy.data.objects.new(name, bpy.data.meshes.new(name)) + # object.data = tmp_obj.data - # Delete tmp_obj with data - bpy.data.objects.remove(tmp_obj, do_unlink=True) + # # Delete tmp_obj with data + # bpy.data.objects.remove(tmp_obj, do_unlink=True) - enabled = True + # enabled = True - else: - meshio_mesh = None - enabled = True - try: - meshio_mesh = meshio.read(filepath) - except Exception as e: - show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "Meshio Loading Error" + str(e), - icon="ERROR") - enabled = False - - name = fileseq.basename() + "@" + fileseq.extension() - mesh = bpy.data.meshes.new(name) - object = bpy.data.objects.new(name, mesh) + # else: + meshio_mesh = None + enabled = True + try: + meshio_mesh = meshio.read(filepath) + except Exception as e: + show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + "Meshio Loading Error" + str(e), + icon="ERROR") + enabled = False + + name = fileseq.basename() + "@" + fileseq.extension() + mesh = bpy.data.meshes.new(name) + object = bpy.data.objects.new(name, mesh) # create the object if bpy.path.is_subdir(filepath, bpy.path.abspath("//")): @@ -321,48 +321,48 @@ def update_obj(scene, depsgraph=None): pattern = bpy.path.native_pathsep(pattern) fs = fileseq.FileSequence(pattern) - if pattern.endswith(".obj") and scene.BSEQ.use_blender_obj_import: - filepath = fs[current_frame % len(fs)] + # if pattern.endswith(".obj") and scene.BSEQ.use_blender_obj_import: + # filepath = fs[current_frame % len(fs)] - # Save all current objects - objs = set(scene.objects) + # # Save all current objects + # objs = set(scene.objects) - # Reload the object - bpy.ops.import_scene.obj(filepath=filepath) + # # Reload the object + # bpy.ops.import_scene.obj(filepath=filepath) - # Substract all previous items from the current items and print their names - imported_objs = set(scene.objects) - objs + # # Substract all previous items from the current items and print their names + # imported_objs = set(scene.objects) - objs - # Check if the imported object worked correctly - if len(imported_objs) == 1: - new_tmp_obj = imported_objs.pop() - else: - show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - "obj. Loading Error in update_obj", - icon="ERROR") - continue + # # Check if the imported object worked correctly + # if len(imported_objs) == 1: + # new_tmp_obj = imported_objs.pop() + # else: + # show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), + # "obj. Loading Error in update_obj", + # icon="ERROR") + # continue - # Copy the data except for material - if obj.data.materials: - # assign to 1st material slot - new_tmp_obj.data.materials[0] = obj.data.materials[0] - else: - # no slots - new_tmp_obj.data.materials.append(obj.data.materials[0]) + # # Copy the data except for material + # if obj.data.materials: + # # assign to 1st material slot + # new_tmp_obj.data.materials[0] = obj.data.materials[0] + # else: + # # no slots + # new_tmp_obj.data.materials.append(obj.data.materials[0]) - obj.data = new_tmp_obj.data + # obj.data = new_tmp_obj.data - # Delete the temporary object with the data - bpy.data.objects.remove(new_tmp_obj, do_unlink=True) + # # Delete the temporary object with the data + # bpy.data.objects.remove(new_tmp_obj, do_unlink=True) - # purge old meshes - bpy.ops.outliner.orphans_purge(do_recursive=True) + # # purge old meshes + # bpy.ops.outliner.orphans_purge(do_recursive=True) - apply_transformation(meshio_mesh, obj, depsgraph) + # apply_transformation(meshio_mesh, obj, depsgraph) - end_time = time.perf_counter() - obj.BSEQ.last_benchmark = (end_time - start_time) * 1000 - continue + # end_time = time.perf_counter() + # obj.BSEQ.last_benchmark = (end_time - start_time) * 1000 + # continue if obj.BSEQ.use_advance and obj.BSEQ.script_name: script = bpy.data.texts[obj.BSEQ.script_name] From 0b9e602db8f6e9985b9e91a7244fb102d8c13306 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 25 Oct 2023 13:46:11 +0200 Subject: [PATCH 24/38] Update meshio --- extern/meshio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/meshio b/extern/meshio index a6175e0..0138cc8 160000 --- a/extern/meshio +++ b/extern/meshio @@ -1 +1 @@ -Subproject commit a6175e0d9dfb2aa274392d1cd396e991f0487cbc +Subproject commit 0138cc8692b806b44b32d344f7961e8370121ff7 From e99f151bea9d9c1cd9afe9418fe6306733cc639b Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 25 Oct 2023 13:53:04 +0200 Subject: [PATCH 25/38] Small fix --- bseq/importer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index a2885fd..ebe950f 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -208,7 +208,7 @@ def create_meshio_obj(filepath): # obj.name = os.path.basename(filepath) # return - + # create the object name = os.path.basename(filepath) mesh = bpy.data.meshes.new(name) @@ -284,7 +284,7 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, object.matrix_world = transform_matrix driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' - if enabled and not is_obj_seq: + if enabled: # and not is_obj_seq: update_mesh(meshio_mesh, object.data) bpy.context.collection.objects.link(object) bpy.ops.object.select_all(action="DESELECT") From 42cf4481de40d3e4375f6789ae4f82c839458c4e Mon Sep 17 00:00:00 2001 From: justo46 Date: Tue, 7 Nov 2023 23:39:51 +0100 Subject: [PATCH 26/38] Added compatibility for split vertex normals with objs --- bseq/importer.py | 141 ++++++++++++---------------------------------- bseq/messenger.py | 5 +- bseq/panels.py | 7 ++- 3 files changed, 43 insertions(+), 110 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index ebe950f..0abeeff 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -81,7 +81,7 @@ def apply_transformation(meshio_mesh, obj, depsgraph): # evaluate the rigid body transformations (only relevant for .bin format) rigid_body_transformation = mathutils.Matrix.Identity(4) if meshio_mesh is not None: - if meshio_mesh.field_data.get("transformation_matrix") is not None: + if "transformation_matrix" in meshio_mesh.field_data: rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"] # multiply everything together (with custom transform matrix) @@ -142,11 +142,10 @@ def update_mesh(meshio_mesh, mesh): mesh.validate() # copy attributes - attributes = mesh.attributes for k, v in meshio_mesh.point_data.items(): k = "bseq_" + k attribute = None - if k not in attributes: + if k not in mesh.attributes: if len(v.shape) == 1: # one dimensional attribute attribute = mesh.attributes.new(k, "FLOAT", "POINT") @@ -165,7 +164,7 @@ def update_mesh(meshio_mesh, mesh): show_message_box('more than 2 dimensional tensor, ignored') continue else: - attribute = attributes[k] + attribute = mesh.attributes[k] name_string = None if attribute.data_type == "FLOAT": name_string = "value" @@ -174,10 +173,36 @@ def update_mesh(meshio_mesh, mesh): attribute.data.foreach_set(name_string, v.ravel()) - # set as split norm - if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == k: - mesh.use_auto_smooth = True - mesh.normals_split_custom_set_from_vertices(v) + # # set as split norm + # if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == k: + # mesh.use_auto_smooth = True + # mesh.normals_split_custom_set_from_vertices(v) + + # I want to set normals if the scene property use_imported_normals is true and the normals are either in point_data["obj:vn"] or field_data["obj:vn"] + if bpy.context.scene.BSEQ.use_imported_normals: + print("use_imported_normals") + # print all the keys in point_data, field_data, cell_data + print("point_data", meshio_mesh.point_data.keys()) + print("field_data", meshio_mesh.field_data.keys()) + print("cell_data", meshio_mesh.cell_data.keys()) + + mesh.use_auto_smooth = True + + + if "obj:vn" in meshio_mesh.point_data and len(meshio_mesh.point_data["obj:vn"]) == len(mesh.vertices): + print("obj:vn in point_data", len(mesh.loops)) + # vert_norms = [tuple(x) for x in meshio_mesh.point_data["obj:vn"]] + + mesh.normals_split_custom_set_from_vertices(meshio_mesh.point_data["obj:vn"]) + + for i in range(len(mesh.vertices)): + print(mesh.vertices[i].normal) + elif "obj:vn" in meshio_mesh.field_data and "obj:vn_face_idx" in meshio_mesh.cell_data: + print("obj:vn in field_data") + indices = meshio_mesh.cell_data["obj:vn_face_idx"][0] + indices = [item for sublist in indices for item in sublist] + vert_norms = [meshio_mesh.field_data["obj:vn"][i - 1] for i in indices] + mesh.normals_split_custom_set(vert_norms) # function to create a single meshio object def create_meshio_obj(filepath): @@ -188,27 +213,7 @@ def create_meshio_obj(filepath): show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), "Meshio Loading Error" + str(e), icon="ERROR") - - # if filepath.endswith(".obj"): - # # Save all current objects - # objs = set(bpy.context.scene.objects) - # # Reload the object - # bpy.ops.import_scene.obj(filepath=filepath) - # # Substract all previous items from the current items and print their names - # imported_objs = set(bpy.context.scene.objects) - objs - - # # Check if the imported object worked correctly - # if len(imported_objs) == 1: - # obj = imported_objs.pop() - # else: - # show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - # "obj. Loading Error in create_meshio_obj", - # icon="ERROR") - # return - - # obj.name = os.path.basename(filepath) - # return - + return # create the object name = os.path.basename(filepath) mesh = bpy.data.meshes.new(name) @@ -224,38 +229,6 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] - #.obj sequences have to be handled differently - # is_obj_seq = filepath.endswith(".obj") - # if is_obj_seq and bpy.context.scene.BSEQ.use_blender_obj_import: - - # # Save all current objects - # objs = set(bpy.context.scene.objects) - # # Reload the object - # bpy.ops.import_scene.obj(filepath=filepath) - # # Substract all previous items from the current items and print their names - # imported_objs = set(bpy.context.scene.objects) - objs - - # # Check if the imported object worked correctly - # if len(imported_objs) == 1: - # tmp_obj = imported_objs.pop() - # else: - # show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - # "obj. Loading Error in create_obj", - # icon="ERROR") - # return - - # name = fileseq.basename() + "@" + fileseq.extension() - - # # Create object with empty mesh - # object = bpy.data.objects.new(name, bpy.data.meshes.new(name)) - # object.data = tmp_obj.data - - # # Delete tmp_obj with data - # bpy.data.objects.remove(tmp_obj, do_unlink=True) - - # enabled = True - - # else: meshio_mesh = None enabled = True try: @@ -284,13 +257,12 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, object.matrix_world = transform_matrix driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' - if enabled: # and not is_obj_seq: + if enabled: update_mesh(meshio_mesh, object.data) bpy.context.collection.objects.link(object) bpy.ops.object.select_all(action="DESELECT") bpy.context.view_layer.objects.active = object - def update_obj(scene, depsgraph=None): for obj in bpy.data.objects: @@ -320,49 +292,6 @@ def update_obj(scene, depsgraph=None): # in case the blender file was created on windows system, but opened in linux system pattern = bpy.path.native_pathsep(pattern) fs = fileseq.FileSequence(pattern) - - # if pattern.endswith(".obj") and scene.BSEQ.use_blender_obj_import: - # filepath = fs[current_frame % len(fs)] - - # # Save all current objects - # objs = set(scene.objects) - - # # Reload the object - # bpy.ops.import_scene.obj(filepath=filepath) - - # # Substract all previous items from the current items and print their names - # imported_objs = set(scene.objects) - objs - - # # Check if the imported object worked correctly - # if len(imported_objs) == 1: - # new_tmp_obj = imported_objs.pop() - # else: - # show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), - # "obj. Loading Error in update_obj", - # icon="ERROR") - # continue - - # # Copy the data except for material - # if obj.data.materials: - # # assign to 1st material slot - # new_tmp_obj.data.materials[0] = obj.data.materials[0] - # else: - # # no slots - # new_tmp_obj.data.materials.append(obj.data.materials[0]) - - # obj.data = new_tmp_obj.data - - # # Delete the temporary object with the data - # bpy.data.objects.remove(new_tmp_obj, do_unlink=True) - - # # purge old meshes - # bpy.ops.outliner.orphans_purge(do_recursive=True) - - # apply_transformation(meshio_mesh, obj, depsgraph) - - # end_time = time.perf_counter() - # obj.BSEQ.last_benchmark = (end_time - start_time) * 1000 - # continue if obj.BSEQ.use_advance and obj.BSEQ.script_name: script = bpy.data.texts[obj.BSEQ.script_name] diff --git a/bseq/messenger.py b/bseq/messenger.py index 507bc49..0409779 100644 --- a/bseq/messenger.py +++ b/bseq/messenger.py @@ -6,7 +6,10 @@ def selected_callback(): # seems like that this is not necessary # if not bpy.context.view_layer.objects.active: # return - + + if not bpy.context.active_object: + return + name = bpy.context.active_object.name idx = bpy.data.objects.find(name) if idx >= 0: diff --git a/bseq/panels.py b/bseq/panels.py index 4dc083a..f7702ac 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -22,18 +22,19 @@ def filter_items(self, context, data, property): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: - split = layout.split(factor=0.5) + split = layout.split(factor=0.4) col1 = split.column() col2 = split.column() split2 = col2.split(factor=0.25) col2 = split2.column() col3 = split2.column() - split3 = col3.split(factor=0.33) + split3 = col3.split(factor=0.5) col3 = split3.column() col4 = split3.column() - col4.alignment = 'CENTER' + col4.alignment = 'EXPAND' start_frame = item.BSEQ.start_end_frame[0] end_frame = item.BSEQ.start_end_frame[1] + col1.prop(item, "name", text='', emboss=False) if item.BSEQ.enabled: col2.prop(item.BSEQ, "enabled", text="", icon="PLAY") From 78d78d01db08147744f42fbe638aff35c6cad077 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 8 Nov 2023 15:00:32 +0100 Subject: [PATCH 27/38] Added obj.py --- additional_file_formats/obj.py | 172 +++++++++++++++++++++++++++++++++ bseq/importer.py | 98 +++++++++---------- 2 files changed, 219 insertions(+), 51 deletions(-) create mode 100644 additional_file_formats/obj.py diff --git a/additional_file_formats/obj.py b/additional_file_formats/obj.py new file mode 100644 index 0000000..48b4c07 --- /dev/null +++ b/additional_file_formats/obj.py @@ -0,0 +1,172 @@ +""" +I/O for the Wavefront .obj file format, cf. +. +""" +import datetime + +import numpy as np + +# from ..__about__ import __version__ +# from .._exceptions import WriteError +# from .._files import open_file +# from .._helpers import register_format +# from .._mesh import CellBlock, Mesh + +import meshio + + +def read(filename): + with open_file(filename, "r") as f: + mesh = read_buffer(f) + return mesh + + +def read_buffer(f): + points = [] + vertex_normals = [] + texture_coords = [] + face_groups = [] + face_normals = [] + face_texture_coords = [] + face_group_ids = [] + face_group_id = -1 + while True: + line = f.readline() + + if not line: + # EOF + break + + strip = line.strip() + + if len(strip) == 0 or strip[0] == "#": + continue + + split = strip.split() + + if split[0] == "v": + points.append([float(item) for item in split[1:]]) + elif split[0] == "vn": + vertex_normals.append([float(item) for item in split[1:]]) + elif split[0] == "vt": + texture_coords.append([float(item) for item in split[1:]]) + elif split[0] == "s": + # "s 1" or "s off" controls smooth shading + pass + elif split[0] == "f": + # old: dat = [int(item.split("/")[0]) for item in split[1:]] + # A face in obj has one of the following formats: 1, 1/2, 1//3, 1/2/3 + # We want to support all formats now amd store the texture and normal indices in other arrays + face_indices = [] + face_texture_indices = [] + face_normal_indices = [] + + for item in split[1:]: + indices = item.split("/") + face_indices.append(int(indices[0])) + if len(indices) > 1 and indices[1] != "": + face_texture_indices.append(int(indices[1])) + if len(indices) > 2: + face_normal_indices.append(int(indices[2])) + + if len(face_groups) == 0 or ( + len(face_groups[-1]) > 0 and len(face_groups[-1][-1]) != len(face_indices) + ): + face_groups.append([]) + face_group_ids.append([]) + face_texture_coords.append([]) + face_normals.append([]) + face_groups[-1].append(face_indices) + face_group_ids[-1].append(face_group_id) + if face_texture_indices: + face_texture_coords[-1].append(face_texture_indices) + if face_normal_indices: + face_normals[-1].append(face_normal_indices) + elif split[0] == "g": + # new group + face_groups.append([]) + face_group_ids.append([]) + face_texture_coords.append([]) + face_normals.append([]) + face_group_id += 1 + else: + # who knows + pass + + # There may be empty groups, too. + # Remove them. + face_groups = [f for f in face_groups if len(f) > 0] + face_group_ids = [g for g in face_group_ids if len(g) > 0] + face_normals = [n for n in face_normals if len(n) > 0] + face_texture_coords = [t for t in face_texture_coords if len(t) > 0] + + # convert to numpy arrays and remove + points = np.array(points) + face_groups = [np.array(f) for f in face_groups] + texture_coords = [np.array(t) for t in texture_coords] + vertex_normals = [np.array(n) for n in vertex_normals] + point_data = {} + cell_data = {} + field_data = {} + + if face_texture_coords and len(texture_coords) == max([max(max(face)) for face in face_texture_coords]): + field_data["obj:vt"] = texture_coords + cell_data["obj:vt_face_idx"] = face_texture_coords + elif len(texture_coords) == len(points): + point_data["obj:vt"] = texture_coords + + if face_normals and len(vertex_normals) == max([max(max(face)) for face in face_normals]): + field_data["obj:vn"] = vertex_normals + cell_data["obj:vn_face_idx"] = face_normals + elif len(vertex_normals) == len(points): + point_data["obj:vn"] = vertex_normals + + cell_data["obj:group_ids"] = [] + cells = [] + for f, gid in zip(face_groups, face_group_ids): + if f.shape[1] == 3: + cells.append(CellBlock("triangle", f - 1)) + elif f.shape[1] == 4: + cells.append(CellBlock("quad", f - 1)) + else: + cells.append(CellBlock("polygon", f - 1)) + cell_data["obj:group_ids"].append(gid) + + return Mesh(points, cells, point_data=point_data, cell_data=cell_data, field_data=field_data) + + +def write(filename, mesh): + for c in mesh.cells: + if c.type not in ["triangle", "quad", "polygon"]: + raise WriteError( + "Wavefront .obj files can only contain triangle or quad cells." + ) + + with open_file(filename, "w") as f: + f.write( + "# Created by meshio v{}, {}\n".format( + __version__, datetime.datetime.now().isoformat() + ) + ) + for p in mesh.points: + f.write(f"v {p[0]} {p[1]} {p[2]}\n") + + if "obj:vn" in mesh.point_data: + dat = mesh.point_data["obj:vn"] + fmt = "vn " + " ".join(["{}"] * dat.shape[1]) + "\n" + for vn in dat: + f.write(fmt.format(*vn)) + + if "obj:vt" in mesh.point_data: + dat = mesh.point_data["obj:vt"] + fmt = "vt " + " ".join(["{}"] * dat.shape[1]) + "\n" + for vt in dat: + f.write(fmt.format(*vt)) + + for cell_block in mesh.cells: + fmt = "f " + " ".join(["{}"] * cell_block.data.shape[1]) + "\n" + for c in cell_block.data: + f.write(fmt.format(*(c + 1))) + + +register_format("obj", [".obj"], read, {"obj": write}) diff --git a/bseq/importer.py b/bseq/importer.py index 0abeeff..d907712 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -87,6 +87,31 @@ def apply_transformation(meshio_mesh, obj, depsgraph): # multiply everything together (with custom transform matrix) obj.matrix_world = rigid_body_transformation @ eval_transform_matrix +# function to create a single custom Blender mesh attribute +def create_or_retrieve_attribute(mesh, k, v): + if k not in mesh.attributes: + if len(v) == 0: + return mesh.attributes.new(k, "FLOAT", "POINT") + if len(v.shape) == 1: + # one dimensional attribute + return mesh.attributes.new(k, "FLOAT", "POINT") + if len(v.shape) == 2: + dim = v.shape[1] + if dim > 3: + show_message_box('higher than 3 dimensional attribue, ignored') + return None + if dim == 1: + return mesh.attributes.new(k, "FLOAT", "POINT") + if dim == 2: + return mesh.attributes.new(k, "FLOAT2", "POINT") + if dim == 3: + return mesh.attributes.new(k, "FLOAT_VECTOR", "POINT") + if len(v.shape) > 2: + show_message_box('more than 2 dimensional tensor, ignored') + return None + else: + return mesh.attributes[k] + def update_mesh(meshio_mesh, mesh): # extract information from the meshio mesh mesh_vertices = meshio_mesh.points @@ -141,30 +166,18 @@ def update_mesh(meshio_mesh, mesh): mesh.update() mesh.validate() + if bpy.context.scene.BSEQ.use_imported_normals: + if "obj:vn" in meshio_mesh.point_data: + mesh.BSEQ.split_norm_att_name = "bseq_obj:vn" + elif "normals" in meshio_mesh.point_data and len(meshio_mesh.point_data["normals"]) == len(mesh.vertices): + mesh.BSEQ.split_norm_att_name = "bseq_normals" + elif "obj:vn" in meshio_mesh.field_data and "obj:vn_face_idx" in meshio_mesh.cell_data: + mesh.BSEQ.split_norm_att_name = "obj:vn" + # copy attributes for k, v in meshio_mesh.point_data.items(): k = "bseq_" + k - attribute = None - if k not in mesh.attributes: - if len(v.shape) == 1: - # one dimensional attribute - attribute = mesh.attributes.new(k, "FLOAT", "POINT") - if len(v.shape) == 2: - dim = v.shape[1] - if dim > 3: - show_message_box('higher than 3 dimensional attribue, ignored') - continue - if dim == 1: - attribute = mesh.attributes.new(k, "FLOAT", "POINT") - if dim == 2: - attribute = mesh.attributes.new(k, "FLOAT2", "POINT") - if dim == 3: - attribute = mesh.attributes.new(k, "FLOAT_VECTOR", "POINT") - if len(v.shape) > 2: - show_message_box('more than 2 dimensional tensor, ignored') - continue - else: - attribute = mesh.attributes[k] + attribute = create_or_retrieve_attribute(mesh, k, v) name_string = None if attribute.data_type == "FLOAT": name_string = "value" @@ -173,36 +186,20 @@ def update_mesh(meshio_mesh, mesh): attribute.data.foreach_set(name_string, v.ravel()) - # # set as split norm - # if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == k: - # mesh.use_auto_smooth = True - # mesh.normals_split_custom_set_from_vertices(v) - - # I want to set normals if the scene property use_imported_normals is true and the normals are either in point_data["obj:vn"] or field_data["obj:vn"] - if bpy.context.scene.BSEQ.use_imported_normals: - print("use_imported_normals") - # print all the keys in point_data, field_data, cell_data - print("point_data", meshio_mesh.point_data.keys()) - print("field_data", meshio_mesh.field_data.keys()) - print("cell_data", meshio_mesh.cell_data.keys()) - - mesh.use_auto_smooth = True - - - if "obj:vn" in meshio_mesh.point_data and len(meshio_mesh.point_data["obj:vn"]) == len(mesh.vertices): - print("obj:vn in point_data", len(mesh.loops)) - # vert_norms = [tuple(x) for x in meshio_mesh.point_data["obj:vn"]] - - mesh.normals_split_custom_set_from_vertices(meshio_mesh.point_data["obj:vn"]) + # set as split normal per vertex + if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == k: + mesh.use_auto_smooth = True + mesh.normals_split_custom_set_from_vertices(v) - for i in range(len(mesh.vertices)): - print(mesh.vertices[i].normal) - elif "obj:vn" in meshio_mesh.field_data and "obj:vn_face_idx" in meshio_mesh.cell_data: - print("obj:vn in field_data") - indices = meshio_mesh.cell_data["obj:vn_face_idx"][0] - indices = [item for sublist in indices for item in sublist] - vert_norms = [meshio_mesh.field_data["obj:vn"][i - 1] for i in indices] - mesh.normals_split_custom_set(vert_norms) + for k, v in meshio_mesh.field_data.items(): + if k not in mesh.attributes: + attribute = create_or_retrieve_attribute(mesh, k, []) + + # set split normal per loop per vertex + if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == k: + # Currently hard-coded for .obj files + indices = [item for sublist in meshio_mesh.cell_data["obj:vn_face_idx"][0] for item in sublist] + mesh.normals_split_custom_set([meshio_mesh.field_data["obj:vn"][i - 1] for i in indices]) # function to create a single meshio object def create_meshio_obj(filepath): @@ -223,7 +220,6 @@ def create_meshio_obj(filepath): bpy.ops.object.select_all(action="DESELECT") bpy.context.view_layer.objects.active = object - def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])): current_frame = bpy.context.scene.frame_current From ada1a6265751e7a486df3d051a451cf30ce1bcc6 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 8 Nov 2023 15:23:08 +0100 Subject: [PATCH 28/38] Default normals now work with .obj and .vtk --- additional_file_formats/__init__.py | 3 +- additional_file_formats/obj.py | 49 ++++------------------------- bseq/importer.py | 1 + 3 files changed, 9 insertions(+), 44 deletions(-) diff --git a/additional_file_formats/__init__.py b/additional_file_formats/__init__.py index 4d0b7e7..66f512f 100644 --- a/additional_file_formats/__init__.py +++ b/additional_file_formats/__init__.py @@ -1,2 +1,3 @@ from . import bgeo -from . import mzd \ No newline at end of file +from . import mzd +from . import obj \ No newline at end of file diff --git a/additional_file_formats/obj.py b/additional_file_formats/obj.py index 48b4c07..57437b3 100644 --- a/additional_file_formats/obj.py +++ b/additional_file_formats/obj.py @@ -6,8 +6,6 @@ import numpy as np -# from ..__about__ import __version__ -# from .._exceptions import WriteError # from .._files import open_file # from .._helpers import register_format # from .._mesh import CellBlock, Mesh @@ -16,7 +14,7 @@ def read(filename): - with open_file(filename, "r") as f: + with open(filename, "r") as f: mesh = read_buffer(f) return mesh @@ -125,48 +123,13 @@ def read_buffer(f): cells = [] for f, gid in zip(face_groups, face_group_ids): if f.shape[1] == 3: - cells.append(CellBlock("triangle", f - 1)) + cells.append(meshio.CellBlock("triangle", f - 1)) elif f.shape[1] == 4: - cells.append(CellBlock("quad", f - 1)) + cells.append(meshio.CellBlock("quad", f - 1)) else: - cells.append(CellBlock("polygon", f - 1)) + cells.append(meshio.CellBlock("polygon", f - 1)) cell_data["obj:group_ids"].append(gid) - return Mesh(points, cells, point_data=point_data, cell_data=cell_data, field_data=field_data) + return meshio.Mesh(points, cells, point_data=point_data, cell_data=cell_data, field_data=field_data) - -def write(filename, mesh): - for c in mesh.cells: - if c.type not in ["triangle", "quad", "polygon"]: - raise WriteError( - "Wavefront .obj files can only contain triangle or quad cells." - ) - - with open_file(filename, "w") as f: - f.write( - "# Created by meshio v{}, {}\n".format( - __version__, datetime.datetime.now().isoformat() - ) - ) - for p in mesh.points: - f.write(f"v {p[0]} {p[1]} {p[2]}\n") - - if "obj:vn" in mesh.point_data: - dat = mesh.point_data["obj:vn"] - fmt = "vn " + " ".join(["{}"] * dat.shape[1]) + "\n" - for vn in dat: - f.write(fmt.format(*vn)) - - if "obj:vt" in mesh.point_data: - dat = mesh.point_data["obj:vt"] - fmt = "vt " + " ".join(["{}"] * dat.shape[1]) + "\n" - for vt in dat: - f.write(fmt.format(*vt)) - - for cell_block in mesh.cells: - fmt = "f " + " ".join(["{}"] * cell_block.data.shape[1]) + "\n" - for c in cell_block.data: - f.write(fmt.format(*(c + 1))) - - -register_format("obj", [".obj"], read, {"obj": write}) +meshio.register_format("obj", [".obj"], read, {"obj": None}) diff --git a/bseq/importer.py b/bseq/importer.py index d907712..05e7ced 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -198,6 +198,7 @@ def update_mesh(meshio_mesh, mesh): # set split normal per loop per vertex if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == k: # Currently hard-coded for .obj files + mesh.use_auto_smooth = True indices = [item for sublist in meshio_mesh.cell_data["obj:vn_face_idx"][0] for item in sublist] mesh.normals_split_custom_set([meshio_mesh.field_data["obj:vn"][i - 1] for i in indices]) From 3a22ae9fe756fa4959693c5db8db5eb82e381b13 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 8 Nov 2023 15:39:11 +0100 Subject: [PATCH 29/38] Added import zip operator --- __init__.py | 7 ++++--- bseq/__init__.py | 9 +++++---- bseq/operators.py | 30 +++++++++++++++++++++++++----- bseq/panels.py | 2 ++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/__init__.py b/__init__.py index be60ec6..da808a8 100644 --- a/__init__.py +++ b/__init__.py @@ -51,9 +51,10 @@ BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, - BSEQ_OT_batchSequences, - BSEQ_PT_batchSequences_Settings, - BSEQ_OT_MeshioObject + BSEQ_OT_batch_sequences, + BSEQ_PT_batch_sequences_settings, + BSEQ_OT_meshio_object, + BSEQ_OT_import_zip ] def register(): diff --git a/bseq/__init__.py b/bseq/__init__.py index 71e5eeb..a463c9f 100644 --- a/bseq/__init__.py +++ b/bseq/__init__.py @@ -1,5 +1,5 @@ from bseq.utils import refresh_obj -from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batchSequences, BSEQ_PT_batchSequences_Settings, BSEQ_OT_MeshioObject +from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_PT_Import, BSEQ_PT_Import_Child1, BSEQ_PT_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template from .messenger import subscribe_to_selected, unsubscribe_to_selected @@ -52,7 +52,8 @@ def BSEQ_initialize(scene): "BSEQ_OT_enable_all", "BSEQ_OT_refresh_sequences", "BSEQ_OT_set_start_end_frames", - "BSEQ_OT_batchSequences", - "BSEQ_PT_batchSequences_Settings", - "BSEQ_OT_MeshioObject" + "BSEQ_OT_batch_sequences", + "BSEQ_PT_batch_sequences_settings", + "BSEQ_OT_meshio_object", + "BSEQ_OT_import_zip" ] diff --git a/bseq/operators.py b/bseq/operators.py index b305f9d..114c2c8 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -335,12 +335,11 @@ def execute(self, context): return {"FINISHED"} - from pathlib import Path import meshio from bpy_extras.io_utils import ImportHelper -class BSEQ_OT_batchSequences(bpy.types.Operator, ImportHelper): +class BSEQ_OT_batch_sequences(bpy.types.Operator, ImportHelper): """Batch Import Sequences""" bl_idname = "wm.seq_import_batch" bl_label = "Import Sequences" @@ -404,7 +403,7 @@ def execute(self, context): def draw(self, context): pass -class BSEQ_PT_batchSequences_Settings(bpy.types.Panel): +class BSEQ_PT_batch_sequences_settings(bpy.types.Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOL_PROPS' bl_label = "Settings Panel" @@ -434,7 +433,28 @@ def draw(self, context): if importer_prop.relative: layout.prop(importer_prop, "root_path", text="Root Directory") -class BSEQ_OT_MeshioObject(bpy.types.Operator, ImportHelper): +class BSEQ_OT_import_zip(bpy.types.Operator, ImportHelper): + """Import a zip file""" + bl_idname = "bseq.import_zip" + bl_label = "Import Zip" + bl_options = {'PRESET', 'UNDO'} + + filename_ext = ".zip" + filter_glob: bpy.props.StringProperty( + default="*.zip", + options={'HIDDEN', 'LIBRARY_EDITABLE'}, + ) + + files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) + + def execute(self, context): + import zipfile + zip_file = zipfile.ZipFile(self.filepath) + zip_file.extractall(Path(self.filepath).parent) + + return {'FINISHED'} + +class BSEQ_OT_meshio_object(bpy.types.Operator, ImportHelper): """Batch Import Meshio Objects""" bl_idname = "wm.meshio_import_batch" bl_label = "Import Multiple Meshio Objects" @@ -452,7 +472,7 @@ def execute(self, context): def menu_func_import(self, context): self.layout.operator( - BSEQ_OT_MeshioObject.bl_idname, + BSEQ_OT_meshio_object.bl_idname, text="MeshIO Object") # Default Keymap Configuration diff --git a/bseq/panels.py b/bseq/panels.py index f7702ac..7315c86 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -249,6 +249,8 @@ def draw(self, context): box_col3.label(text="Scale:") box_col3.prop(importer_prop, "custom_scale", text="") + layout.operator("bseq.import_zip", text="Import from zip") + class BSEQ_PT_Import_Child1(BSEQ_Panel, bpy.types.Panel): bl_parent_id = "BSEQ_PT_panel" bl_label = "Import from folder" From d9294c4918dfbc6c5e52176b7bd3c8157b318b0e Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 8 Nov 2023 17:05:33 +0100 Subject: [PATCH 30/38] Import + Delete Zips works now --- __init__.py | 4 +++- bseq/__init__.py | 8 +++++--- bseq/operators.py | 27 ++++++++++++++++++++++++++- bseq/panels.py | 9 +++++++-- bseq/properties.py | 9 +++++++++ 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/__init__.py b/__init__.py index da808a8..11eb63d 100644 --- a/__init__.py +++ b/__init__.py @@ -54,7 +54,8 @@ BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, - BSEQ_OT_import_zip + BSEQ_OT_import_zip, + BSEQ_OT_delete_zips ] def register(): @@ -76,6 +77,7 @@ def register(): def unregister(): for cls in classes: bpy.utils.unregister_class(cls) + bpy.utils.unregister_class(BSEQ_ImportedZip) bpy.types.TEXT_MT_templates.remove(draw_template) del bpy.types.Scene.BSEQ del bpy.types.Object.BSEQ diff --git a/bseq/__init__.py b/bseq/__init__.py index a463c9f..ca931ca 100644 --- a/bseq/__init__.py +++ b/bseq/__init__.py @@ -1,6 +1,6 @@ from bseq.utils import refresh_obj -from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip -from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property +from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip, BSEQ_OT_delete_zips +from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property, BSEQ_ImportedZip from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_PT_Import, BSEQ_PT_Import_Child1, BSEQ_PT_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template from .messenger import subscribe_to_selected, unsubscribe_to_selected import bpy @@ -55,5 +55,7 @@ def BSEQ_initialize(scene): "BSEQ_OT_batch_sequences", "BSEQ_PT_batch_sequences_settings", "BSEQ_OT_meshio_object", - "BSEQ_OT_import_zip" + "BSEQ_OT_import_zip", + "BSEQ_OT_delete_zips", + "BSEQ_ImportedZip" ] diff --git a/bseq/operators.py b/bseq/operators.py index 114c2c8..1b8c0ed 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -451,9 +451,34 @@ def execute(self, context): import zipfile zip_file = zipfile.ZipFile(self.filepath) zip_file.extractall(Path(self.filepath).parent) - + zip_file.close() + + folder = str(Path(self.filepath).parent) + '/' + str(Path(self.filepath).name) + folder = folder[:-4] + + seqs = fileseq.findSequencesOnDisk(str(folder)) + for s in seqs: + create_obj(s, folder, transform_matrix=Matrix.Identity(4)) + + created_folder = context.scene.BSEQ.imported_zips.add() + created_folder.path = folder + return {'FINISHED'} +class BSEQ_OT_delete_zips(bpy.types.Operator): + """Delete a zip file""" + bl_idname = "bseq.delete_zips" + bl_label = "Delete Zip" + bl_options = {'PRESET', 'UNDO'} + + def execute(self, context): + folders = context.scene.BSEQ.imported_zips + for folder in folders: + import shutil + shutil.rmtree(folder.path) + return {'FINISHED'} + + class BSEQ_OT_meshio_object(bpy.types.Operator, ImportHelper): """Batch Import Meshio Objects""" bl_idname = "wm.meshio_import_batch" diff --git a/bseq/panels.py b/bseq/panels.py index 7315c86..3f8225a 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -249,8 +249,13 @@ def draw(self, context): box_col3.label(text="Scale:") box_col3.prop(importer_prop, "custom_scale", text="") - layout.operator("bseq.import_zip", text="Import from zip") - + split = layout.split(factor=0.5) + col1 = split.column() + col2 = split.column() + + col1.operator("bseq.import_zip", text="Import from zip") + col2.operator("bseq.delete_zips", text="Delete created folders") + class BSEQ_PT_Import_Child1(BSEQ_Panel, bpy.types.Panel): bl_parent_id = "BSEQ_PT_panel" bl_label = "Import from folder" diff --git a/bseq/properties.py b/bseq/properties.py index e2c9401..47db833 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -2,6 +2,13 @@ from .callback import * from mathutils import Matrix +class BSEQ_ImportedZip(bpy.types.PropertyGroup): + path: bpy.props.StringProperty(name="Directory", + subtype="DIR_PATH", + ) + +bpy.utils.register_class(BSEQ_ImportedZip) + class BSEQ_scene_property(bpy.types.PropertyGroup): path: bpy.props.StringProperty(name="Directory", subtype="DIR_PATH", @@ -112,6 +119,8 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): description='Filter string for file sequences', default='', ) + + imported_zips: bpy.props.CollectionProperty(type=BSEQ_ImportedZip) class BSEQ_obj_property(bpy.types.PropertyGroup): init: bpy.props.BoolProperty(default=False) From 739f61de5d500a57bd9251a96cfbdad6f2f3ed03 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 9 Nov 2023 00:30:48 +0100 Subject: [PATCH 31/38] Lowered number of shown seqs, because it's not the main function anymore --- bseq/callback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bseq/callback.py b/bseq/callback.py index 4357b5d..6aaff62 100644 --- a/bseq/callback.py +++ b/bseq/callback.py @@ -25,7 +25,7 @@ def update_path(self, context): return [("None", "No sequence detected", "", 1)] file_sequences.clear() - if len(f) >= 100: + if len(f) >= 30: file_sequences.append(("None", "Too much sequence detected, could be false detection, please use pattern below", "", 1)) else: count = 1 From 62fcd6246f13ee8559f3184b43661a3920a9cc35 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 9 Nov 2023 11:45:09 +0100 Subject: [PATCH 32/38] Imported extracted zip folders are stored in tmp_zips in a custom location and this whole folder is then deleted --- __init__.py | 8 ++++---- bseq/__init__.py | 6 +++--- bseq/operators.py | 49 +++++++++++++++++++++++++++++++++++++--------- bseq/panels.py | 20 +++++++++++-------- bseq/properties.py | 9 --------- 5 files changed, 59 insertions(+), 33 deletions(-) diff --git a/__init__.py b/__init__.py index 11eb63d..647d15a 100644 --- a/__init__.py +++ b/__init__.py @@ -2,8 +2,8 @@ "name": "Sequence Loader", "description": "Loader for meshio supported mesh files/ simulation sequences", "author": "Interactive Computer Graphics", - "version": (0, 1, 6), - "blender": (3, 4, 0), + "version": (0, 2, 0), + "blender": (3, 6, 0), "warning": "", "support": "COMMUNITY", "category": "Import-Export", @@ -55,7 +55,8 @@ BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip, - BSEQ_OT_delete_zips + BSEQ_OT_delete_zips, + BSEQ_addon_preferences ] def register(): @@ -77,7 +78,6 @@ def register(): def unregister(): for cls in classes: bpy.utils.unregister_class(cls) - bpy.utils.unregister_class(BSEQ_ImportedZip) bpy.types.TEXT_MT_templates.remove(draw_template) del bpy.types.Scene.BSEQ del bpy.types.Object.BSEQ diff --git a/bseq/__init__.py b/bseq/__init__.py index ca931ca..146672a 100644 --- a/bseq/__init__.py +++ b/bseq/__init__.py @@ -1,6 +1,6 @@ from bseq.utils import refresh_obj -from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip, BSEQ_OT_delete_zips -from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property, BSEQ_ImportedZip +from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip, BSEQ_OT_delete_zips, BSEQ_addon_preferences +from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_PT_Import, BSEQ_PT_Import_Child1, BSEQ_PT_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template from .messenger import subscribe_to_selected, unsubscribe_to_selected import bpy @@ -57,5 +57,5 @@ def BSEQ_initialize(scene): "BSEQ_OT_meshio_object", "BSEQ_OT_import_zip", "BSEQ_OT_delete_zips", - "BSEQ_ImportedZip" + "BSEQ_addon_preferences" ] diff --git a/bseq/operators.py b/bseq/operators.py index 1b8c0ed..4664dbf 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -7,6 +7,8 @@ from .importer import create_obj, create_meshio_obj import numpy as np +addon_name = "blendersequenceloader" + # Here are load and delete operations class BSEQ_OT_load(bpy.types.Operator): '''This operator loads a sequence''' @@ -433,6 +435,21 @@ def draw(self, context): if importer_prop.relative: layout.prop(importer_prop, "root_path", text="Root Directory") +class BSEQ_addon_preferences(bpy.types.AddonPreferences): + bl_idname = addon_name + + zips_folder: bpy.props.StringProperty( + name="Zips Folder", + subtype='DIR_PATH', + ) + + def draw(self, context): + layout = self.layout + layout.label(text="Please set a folder to store the extracted zip files") + layout.prop(self, "zips_folder", text="Zips Folder") + +zip_folder_name = '/tmp_zips' + class BSEQ_OT_import_zip(bpy.types.Operator, ImportHelper): """Import a zip file""" bl_idname = "bseq.import_zip" @@ -450,18 +467,27 @@ class BSEQ_OT_import_zip(bpy.types.Operator, ImportHelper): def execute(self, context): import zipfile zip_file = zipfile.ZipFile(self.filepath) - zip_file.extractall(Path(self.filepath).parent) + + addon_prefs = context.preferences.addons[addon_name].preferences + # Check if a string is empty: + if not addon_prefs.zips_folder: + show_message_box("Please set a folder to store the extracted zip files", icon="ERROR") + return {"CANCELLED"} + zips_folder = addon_prefs.zips_folder + zip_folder_name + + valid_files = [info.filename for info in zip_file.infolist() if not info.filename.startswith('__MACOSX/')] + zip_file.extractall(zips_folder, members=valid_files) zip_file.close() - folder = str(Path(self.filepath).parent) + '/' + str(Path(self.filepath).name) - folder = folder[:-4] + folder = str(zips_folder) + '/' + str(Path(self.filepath).name)[:-4] + print(folder) seqs = fileseq.findSequencesOnDisk(str(folder)) for s in seqs: create_obj(s, folder, transform_matrix=Matrix.Identity(4)) - created_folder = context.scene.BSEQ.imported_zips.add() - created_folder.path = folder + # created_folder = context.scene.BSEQ.imported_zips.add() + # created_folder.path = folder return {'FINISHED'} @@ -472,10 +498,15 @@ class BSEQ_OT_delete_zips(bpy.types.Operator): bl_options = {'PRESET', 'UNDO'} def execute(self, context): - folders = context.scene.BSEQ.imported_zips - for folder in folders: - import shutil - shutil.rmtree(folder.path) + # folders = context.scene.BSEQ.imported_zips + # for folder in folders: + + addon_prefs = context.preferences.addons[addon_name].preferences + zips_folder = addon_prefs.zips_folder + zip_folder_name + + import shutil + shutil.rmtree(zips_folder) + return {'FINISHED'} diff --git a/bseq/panels.py b/bseq/panels.py index 3f8225a..494fe3e 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -207,7 +207,10 @@ def draw(self, context): scene = context.scene importer_prop = scene.BSEQ - layout.operator("wm.seq_import_batch") + row = layout.row() + + row.scale_y = 1.5 + row.operator("wm.seq_import_batch") split = layout.split() col1 = split.column() @@ -249,13 +252,6 @@ def draw(self, context): box_col3.label(text="Scale:") box_col3.prop(importer_prop, "custom_scale", text="") - split = layout.split(factor=0.5) - col1 = split.column() - col2 = split.column() - - col1.operator("bseq.import_zip", text="Import from zip") - col2.operator("bseq.delete_zips", text="Delete created folders") - class BSEQ_PT_Import_Child1(BSEQ_Panel, bpy.types.Panel): bl_parent_id = "BSEQ_PT_panel" bl_label = "Import from folder" @@ -288,6 +284,14 @@ def draw(self, context): layout.operator("sequence.load") + split = layout.split(factor=0.5) + col1 = split.column() + col2 = split.column() + + col1.operator("bseq.import_zip", text="Import from zip") + col2.operator("bseq.delete_zips", text="Delete created folders") + + class BSEQ_PT_Import_Child2(BSEQ_Panel, bpy.types.Panel): bl_parent_id = "BSEQ_PT_panel" bl_label = "Test" diff --git a/bseq/properties.py b/bseq/properties.py index 47db833..a0d231f 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -2,13 +2,6 @@ from .callback import * from mathutils import Matrix -class BSEQ_ImportedZip(bpy.types.PropertyGroup): - path: bpy.props.StringProperty(name="Directory", - subtype="DIR_PATH", - ) - -bpy.utils.register_class(BSEQ_ImportedZip) - class BSEQ_scene_property(bpy.types.PropertyGroup): path: bpy.props.StringProperty(name="Directory", subtype="DIR_PATH", @@ -120,8 +113,6 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): default='', ) - imported_zips: bpy.props.CollectionProperty(type=BSEQ_ImportedZip) - class BSEQ_obj_property(bpy.types.PropertyGroup): init: bpy.props.BoolProperty(default=False) enabled: bpy.props.BoolProperty(default=True, From 468fbe7d3fab2f2df8aad984fa3c45dfa6eeab43 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 23 Nov 2023 00:43:26 +0100 Subject: [PATCH 33/38] Made addon ready for release --- __init__.py | 7 +-- bseq/__init__.py | 5 ++- bseq/globals.py | 5 ++- bseq/importer.py | 36 +++++++-------- bseq/operators.py | 110 +++++++++++++++++++++++++++++---------------- bseq/panels.py | 45 +++++++++++++------ bseq/properties.py | 6 ++- bseq/utils.py | 40 +++++++++++------ extern/meshio | 2 +- 9 files changed, 160 insertions(+), 96 deletions(-) diff --git a/__init__.py b/__init__.py index 647d15a..6fee449 100644 --- a/__init__.py +++ b/__init__.py @@ -54,9 +54,10 @@ BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, - BSEQ_OT_import_zip, - BSEQ_OT_delete_zips, - BSEQ_addon_preferences + # BSEQ_OT_import_zip, + # BSEQ_OT_delete_zips, + # BSEQ_addon_preferences, + BSEQ_OT_load_all ] def register(): diff --git a/bseq/__init__.py b/bseq/__init__.py index 146672a..864fe88 100644 --- a/bseq/__init__.py +++ b/bseq/__init__.py @@ -1,5 +1,5 @@ from bseq.utils import refresh_obj -from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip, BSEQ_OT_delete_zips, BSEQ_addon_preferences +from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip, BSEQ_OT_delete_zips, BSEQ_addon_preferences, BSEQ_OT_load_all from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_PT_Import, BSEQ_PT_Import_Child1, BSEQ_PT_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template from .messenger import subscribe_to_selected, unsubscribe_to_selected @@ -57,5 +57,6 @@ def BSEQ_initialize(scene): "BSEQ_OT_meshio_object", "BSEQ_OT_import_zip", "BSEQ_OT_delete_zips", - "BSEQ_addon_preferences" + "BSEQ_addon_preferences", + "BSEQ_OT_load_all" ] diff --git a/bseq/globals.py b/bseq/globals.py index e9da36a..5536bfe 100644 --- a/bseq/globals.py +++ b/bseq/globals.py @@ -23,8 +23,9 @@ def print_information(scene): if bseq_prop.init: file.write("Object name: {}\n".format(obj.name)) file.write("Is it being animated: {}\n".format(bseq_prop.enabled)) - file.write("Filepath: {}\n".format(bseq_prop.pattern)) - file.write("Is it relative path: {}\n".format(bpy.path.is_subdir(obj.BSEQ.pattern, bpy.path.abspath("//")))) + file.write("Filepath: {}\n".format(bseq_prop.path)) + file.write("Pattern: {}\n".format(bseq_prop.pattern)) + file.write("Current file: {}\n".format(bseq_prop.current_file)) file.write("\n\n") diff --git a/bseq/importer.py b/bseq/importer.py index 05e7ced..6e2e5cd 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -4,7 +4,7 @@ import traceback import fileseq import os -from .utils import show_message_box +from .utils import show_message_box, get_relative_path, get_absolute_path import numpy as np from mathutils import Matrix import time @@ -221,7 +221,7 @@ def create_meshio_obj(filepath): bpy.ops.object.select_all(action="DESELECT") bpy.context.view_layer.objects.active = object -def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])): +def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix.Identity(4)): current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] @@ -241,13 +241,14 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, object = bpy.data.objects.new(name, mesh) # create the object - if bpy.path.is_subdir(filepath, bpy.path.abspath("//")): - if root_path != "": - object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=root_path) - else: - object.BSEQ.pattern = bpy.path.relpath(str(fileseq)) + if use_relative: + full_path = get_relative_path(str(fileseq), root_path) else: - object.BSEQ.pattern = str(fileseq) + full_path = str(fileseq) + # path is only the directory in which the file is located + object.BSEQ.path = os.path.dirname(full_path) + object.BSEQ.pattern = os.path.basename(full_path) + object.BSEQ.current_file = filepath object.BSEQ.init = True object.BSEQ.enabled = enabled object.BSEQ.start_end_frame = (fileseq.start(), fileseq.end()) @@ -261,7 +262,6 @@ def create_obj(fileseq, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, bpy.context.view_layer.objects.active = object def update_obj(scene, depsgraph=None): - for obj in bpy.data.objects: start_time = time.perf_counter() @@ -278,17 +278,11 @@ def update_obj(scene, depsgraph=None): show_message_box("Warning: Might not be able load the correct frame because the dependency graph is not available.", "BSEQ Warning") current_frame = obj.BSEQ.frame meshio_mesh = None - pattern = obj.BSEQ.pattern - # check if the path is relative - if bpy.path.is_subdir(pattern, bpy.path.abspath("//")): - if scene.BSEQ.root_path != "": - pattern = bpy.path.abspath(pattern, start=scene.BSEQ.root_path) - else: - pattern = bpy.path.abspath(pattern) - + # in case the blender file was created on windows system, but opened in linux system - pattern = bpy.path.native_pathsep(pattern) - fs = fileseq.FileSequence(pattern) + full_path = get_absolute_path(obj, scene) + + fs = fileseq.FileSequence(full_path) if obj.BSEQ.use_advance and obj.BSEQ.script_name: script = bpy.data.texts[obj.BSEQ.script_name] @@ -303,6 +297,7 @@ def update_obj(scene, depsgraph=None): user_process = locals()['process'] try: user_process(fs, current_frame, obj.data) + obj.BSEQ.current_file = "Controlled by user process" except Exception as e: show_message_box("Error when calling user process: " + traceback.format_exc(), icon="ERROR") del locals()['process'] @@ -313,6 +308,7 @@ def update_obj(scene, depsgraph=None): user_preprocess = locals()['preprocess'] try: meshio_mesh = user_preprocess(fs, current_frame) + obj.BSEQ.current_file = "Controlled by user preprocess" except Exception as e: show_message_box("Error when calling user preprocess: " + traceback.format_exc(), icon="ERROR") # this continue means only if error occures, then goes to next bpy.object @@ -321,8 +317,10 @@ def update_obj(scene, depsgraph=None): del locals()['preprocess'] else: filepath = fs[current_frame % len(fs)] + filepath = os.path.normpath(filepath) try: meshio_mesh = meshio.read(filepath) + obj.BSEQ.current_file = filepath except Exception as e: show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(), "Meshio Loading Error" + str(e), diff --git a/bseq/operators.py b/bseq/operators.py index 4664dbf..20e308e 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -3,12 +3,25 @@ import fileseq from .messenger import * import traceback -from .utils import refresh_obj, show_message_box +from .utils import refresh_obj, show_message_box, get_relative_path from .importer import create_obj, create_meshio_obj import numpy as np addon_name = "blendersequenceloader" +def relative_path_error(): + show_message_box("When using relative path, please save file before using it", icon="ERROR") + return {"CANCELLED"} + +def get_transform_matrix(importer_prop): + if importer_prop.use_custom_transform: + return Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) + else: + return Matrix.Identity(4) + +def create_obj_wrapper(seq, importer_prop): + create_obj(seq, importer_prop.use_relative, importer_prop.root_path, transform_matrix=get_transform_matrix(importer_prop)) + # Here are load and delete operations class BSEQ_OT_load(bpy.types.Operator): '''This operator loads a sequence''' @@ -20,10 +33,8 @@ def execute(self, context): scene = context.scene importer_prop = scene.BSEQ - if importer_prop.relative and not bpy.data.is_saved: - # use relative but file not saved - show_message_box("When using relative path, please save file before using it", icon="ERROR") - return {"CANCELLED"} + if importer_prop.use_relative and not bpy.data.is_saved: + return relative_path_error() fs = importer_prop.fileseq use_pattern = importer_prop.use_pattern @@ -43,13 +54,7 @@ def execute(self, context): show_message_box(traceback.format_exc(), "Can't find sequence: " + str(fs), "ERROR") return {"CANCELLED"} - transform_matrix = (Matrix.LocRotScale( - importer_prop.custom_location, - importer_prop.custom_rotation, - importer_prop.custom_scale) - if importer_prop.use_custom_transform else Matrix.Identity(4)) - - create_obj(fs, importer_prop.root_path, transform_matrix=transform_matrix) + create_obj_wrapper(fs, importer_prop) return {"FINISHED"} @@ -63,10 +68,9 @@ def execute(self, context): scene = context.scene importer_prop = scene.BSEQ - if importer_prop.relative and not bpy.data.is_saved: + if importer_prop.use_relative and not bpy.data.is_saved: # use relative but file not saved - show_message_box("When using relative path, please save file before using it", icon="ERROR") - return {"CANCELLED"} + return relative_path_error() fs = importer_prop.fileseq use_pattern = importer_prop.use_pattern @@ -93,7 +97,7 @@ def execute(self, context): obj = sim_loader.edit_obj if not obj: return {"CANCELLED"} - if importer_prop.relative: + if importer_prop.use_relative: if importer_prop.root_path != "": object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=importer_prop.root_path) else: @@ -379,10 +383,8 @@ def execute(self, context): scene = context.scene importer_prop = scene.BSEQ - if importer_prop.relative and not bpy.data.is_saved: - # use relative but file not saved - show_message_box("When using relative path, please save file before using it", icon="ERROR") - return {"CANCELLED"} + if importer_prop.use_relative and not bpy.data.is_saved: + return relative_path_error() self.filter_glob = '*' @@ -393,13 +395,13 @@ def execute(self, context): # Check if there exists a matching file sequence for every selection fp = str(Path(folder.parent, selection.name)) seqs = fileseq.findSequencesOnDisk(str(folder.parent)) - matching_seqs = [s for s in seqs if fp in list(s) and s not in used_seqs] - - if matching_seqs: - transform_matrix = (Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) - if importer_prop.use_custom_transform else Matrix.Identity(4)) - create_obj(matching_seqs[0], importer_prop.root_path, transform_matrix=transform_matrix) - used_seqs.add(matching_seqs[0]) + matching_seq = [s for s in seqs if fp in list(s) and str(s) not in used_seqs] + + if matching_seq: + matching_seq = matching_seq[0] + used_seqs.add(str(matching_seq)) + + create_obj_wrapper(matching_seq, importer_prop) return {'FINISHED'} def draw(self, context): @@ -425,15 +427,15 @@ def draw(self, context): layout.use_property_split = True layout.use_property_decorate = False # No animation. - # sfile = context.space_data - # operator = sfile.active_operator + # # sfile = context.space_data + # # operator = sfile.active_operator - layout.prop(importer_prop, 'filter_string') + # layout.prop(importer_prop, 'filter_string') - layout.alignment = 'LEFT' - layout.prop(importer_prop, "relative", text="Relative Path") - if importer_prop.relative: - layout.prop(importer_prop, "root_path", text="Root Directory") + # layout.alignment = 'LEFT' + # layout.prop(importer_prop, "relative", text="Relative Path") + # if importer_prop.use_relative: + # layout.prop(importer_prop, "root_path", text="Root Directory") class BSEQ_addon_preferences(bpy.types.AddonPreferences): bl_idname = addon_name @@ -444,9 +446,10 @@ class BSEQ_addon_preferences(bpy.types.AddonPreferences): ) def draw(self, context): - layout = self.layout - layout.label(text="Please set a folder to store the extracted zip files") - layout.prop(self, "zips_folder", text="Zips Folder") + # layout = self.layout + # layout.label(text="Please set a folder to store the extracted zip files") + # layout.prop(self, "zips_folder", text="Zips Folder") + pass zip_folder_name = '/tmp_zips' @@ -465,6 +468,8 @@ class BSEQ_OT_import_zip(bpy.types.Operator, ImportHelper): files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) def execute(self, context): + importer_prop = context.scene.BSEQ + import zipfile zip_file = zipfile.ZipFile(self.filepath) @@ -477,14 +482,19 @@ def execute(self, context): valid_files = [info.filename for info in zip_file.infolist() if not info.filename.startswith('__MACOSX/')] zip_file.extractall(zips_folder, members=valid_files) - zip_file.close() + zip_file.close() folder = str(zips_folder) + '/' + str(Path(self.filepath).name)[:-4] print(folder) seqs = fileseq.findSequencesOnDisk(str(folder)) + if not seqs: + show_message_box("No sequences found in the zip file", icon="ERROR") + return {"CANCELLED"} + for s in seqs: - create_obj(s, folder, transform_matrix=Matrix.Identity(4)) + # Import it with absolute paths + create_obj(s, False, folder, transform_matrix=get_transform_matrix(importer_prop)) # created_folder = context.scene.BSEQ.imported_zips.add() # created_folder.path = folder @@ -509,6 +519,28 @@ def execute(self, context): return {'FINISHED'} +class BSEQ_OT_load_all(bpy.types.Operator): + """Load all sequences""" + bl_idname = "bseq.load_all" + bl_label = "Load All" + bl_options = {'PRESET', 'UNDO'} + + def execute(self, context): + importer_prop = context.scene.BSEQ + + if importer_prop.use_relative and not bpy.data.is_saved: + return relative_path_error() + + dir = importer_prop.path + seqs = fileseq.findSequencesOnDisk(str(dir)) + + for s in seqs: + print(s) + + for s in seqs: + create_obj_wrapper(s, importer_prop) + return {'FINISHED'} + class BSEQ_OT_meshio_object(bpy.types.Operator, ImportHelper): """Batch Import Meshio Objects""" diff --git a/bseq/panels.py b/bseq/panels.py index 494fe3e..a9c9633 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -85,10 +85,14 @@ def draw(self, context): col1.alignment = 'RIGHT' col2 = split.column() - col1.label(text='Global Settings') - col2.prop(sim_loader, "print", text="Print Sequence Information") - col2.prop(sim_loader, "auto_refresh_active", text="Auto Refresh Active") - col2.prop(sim_loader, "auto_refresh_all", text="Auto Refresh All") + col1.label(text="Root Directory") + col2.prop(sim_loader, "root_path", text="") + col1.label(text="Print Sequence Information") + col2.prop(sim_loader, "print", text="") + col1.label(text="Auto Refresh Active") + col2.prop(sim_loader, "auto_refresh_active", text="") + col1.label(text="Auto Refresh All") + col2.prop(sim_loader, "auto_refresh_all", text="") class BSEQ_Advanced_Panel(BSEQ_Panel, bpy.types.Panel): bl_label = "Advanced Settings" @@ -180,8 +184,16 @@ def draw(self, context): col1.alignment = 'RIGHT' col2 = split.column(align=False) + col1.label(text='Path') + col2.prop(obj.BSEQ, 'path', text="") col1.label(text='Pattern') col2.prop(obj.BSEQ, 'pattern', text="") + # Read-only + col1.label(text='Current File') + # make it read-only + row1 = col2.row() + row1.enabled = False + row1.prop(obj.BSEQ, 'current_file', text="") col1.label(text='Last loading time (ms)') row2 = col2.row() row2.enabled = False @@ -225,13 +237,14 @@ def draw(self, context): col1.label(text="Import Settings") - col2.prop(importer_prop, "filter_string", text="Filter String") + # col2.prop(importer_prop, "filter_string", text="Filter String") - col2.prop(importer_prop, "relative", text="Relative Path") + col2.prop(importer_prop, "use_relative", text="Relative Path") - if importer_prop.relative: - col1.label(text="Root Directory") - col2.prop(importer_prop, "root_path", text="") + split = layout.split(factor=0.5) + col1 = split.column() + col1.alignment = 'RIGHT' + col2 = split.column(align=False) col2.prop(importer_prop, "use_imported_normals", text="Use Imported Normals") @@ -282,14 +295,18 @@ def draw(self, context): col3.prop(importer_prop, "fileseq", text="") col4.operator("bseq.refreshall", text='', icon="FILE_REFRESH") - layout.operator("sequence.load") - - split = layout.split(factor=0.5) + split = layout.split(factor=0.7) col1 = split.column() col2 = split.column() + col1.operator("sequence.load") + col2.operator("bseq.load_all") + + # split = layout.split(factor=0.5) + # col1 = split.column() + # col2 = split.column() - col1.operator("bseq.import_zip", text="Import from zip") - col2.operator("bseq.delete_zips", text="Delete created folders") + # col1.operator("bseq.import_zip", text="Import from zip") + # col2.operator("bseq.delete_zips", text="Delete created folders") class BSEQ_PT_Import_Child2(BSEQ_Panel, bpy.types.Panel): diff --git a/bseq/properties.py b/bseq/properties.py index a0d231f..cbaec85 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -9,9 +9,9 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): update=update_path, ) - relative: bpy.props.BoolProperty(name='Use relative path', + use_relative: bpy.props.BoolProperty(name='Use relative path', description="Use relative path", - default=True, + default=False, ) use_imported_normals: bpy.props.BoolProperty(name='Use Imported Normals', @@ -119,7 +119,9 @@ class BSEQ_obj_property(bpy.types.PropertyGroup): description="If disabled, the sequence won't be updated each frame") use_advance: bpy.props.BoolProperty(default=False) script_name: bpy.props.StringProperty() + path: bpy.props.StringProperty(subtype="DIR_PATH") pattern: bpy.props.StringProperty() + current_file: bpy.props.StringProperty() frame: bpy.props.IntProperty() start_end_frame: bpy.props.IntVectorProperty(name="Start and end frames", size=2, default=(0, 0)) last_benchmark: bpy.props.FloatProperty(name="Last loading time") diff --git a/bseq/utils.py b/bseq/utils.py index bcf1e35..4e7e957 100644 --- a/bseq/utils.py +++ b/bseq/utils.py @@ -1,5 +1,6 @@ import bpy import fileseq +import os def show_message_box(message="", title="Message Box", icon="INFO"): ''' @@ -24,25 +25,36 @@ def stop_animation(): # if playing animation, then stop it, otherwise it will keep showing message box bpy.ops.screen.animation_cancel() +def get_relative_path(path, root_path): + if root_path != "": + path = bpy.path.relpath(path, start=root_path) + else: + path = bpy.path.relpath(path) + return path + +# convert relative path to absolute path +def convert_to_absolute_path(path, root_path): + if root_path != "": + path = bpy.path.abspath(path, start=root_path) + else: + path = bpy.path.abspath(path) + return path + +def get_absolute_path(obj, scene): + full_path = os.path.join(bpy.path.native_pathsep(obj.BSEQ.path), obj.BSEQ.pattern) + full_path = convert_to_absolute_path(full_path, scene.BSEQ.root_path) + return full_path def refresh_obj(obj, scene): - is_relative = bpy.path.is_subdir(obj.BSEQ.pattern, bpy.path.abspath("//")) - fs = obj.BSEQ.pattern - if is_relative: - if scene.BSEQ.root_path != "": - fs = bpy.path.abspath(fs, start=scene.BSEQ.root_path) - else: - fs = bpy.path.abspath(fs) - fs = bpy.path.native_pathsep(fs) + is_relative = obj.BSEQ.path.startswith("//") + print("is_relative: ", is_relative) + fs = get_absolute_path(obj, scene) fs = fileseq.findSequenceOnDisk(fs) fs = fileseq.findSequenceOnDisk(fs.dirname() + fs.basename() + "@" + fs.extension()) obj.BSEQ.start_end_frame = (fs.start(), fs.end()) fs = str(fs) - # obj.BSEQ.pattern is a path and I want to check if it is a relative path if is_relative: - if scene.BSEQ.root_path != "": - fs = bpy.path.relpath(fs, start=scene.BSEQ.root_path) - else: - fs = bpy.path.relpath(fs) - obj.BSEQ.pattern = fs + fs = get_relative_path(fs, scene.BSEQ.root_path) + obj.BSEQ.path = os.path.dirname(fs) + obj.BSEQ.pattern = os.path.basename(fs) diff --git a/extern/meshio b/extern/meshio index 0138cc8..a6175e0 160000 --- a/extern/meshio +++ b/extern/meshio @@ -1 +1 @@ -Subproject commit 0138cc8692b806b44b32d344f7961e8370121ff7 +Subproject commit a6175e0d9dfb2aa274392d1cd396e991f0487cbc From 4f7574846c6624b3df67a27ef1d2e891d89c2244 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 23 Nov 2023 00:54:00 +0100 Subject: [PATCH 34/38] Updated submodule extern/fileseq --- extern/fileseq | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/fileseq b/extern/fileseq index 9ec0493..584ee22 160000 --- a/extern/fileseq +++ b/extern/fileseq @@ -1 +1 @@ -Subproject commit 9ec049373af37ec21a21d2a5564deb344a96f97f +Subproject commit 584ee2218b74a9c7f4127c922cc2c33dc5a706b4 From 28f40e75bb10607cfb8e9339d42b910dcf1d5768 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 23 Nov 2023 00:55:23 +0100 Subject: [PATCH 35/38] Updated submodule extern/meshio --- extern/meshio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/meshio b/extern/meshio index a6175e0..0138cc8 160000 --- a/extern/meshio +++ b/extern/meshio @@ -1 +1 @@ -Subproject commit a6175e0d9dfb2aa274392d1cd396e991f0487cbc +Subproject commit 0138cc8692b806b44b32d344f7961e8370121ff7 From fabef65553b796776a2169b8f0756d209af18be2 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 23 Nov 2023 00:56:03 +0100 Subject: [PATCH 36/38] Updated submodule extern/python-future --- extern/python-future | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/python-future b/extern/python-future index 80523f3..af1db97 160000 --- a/extern/python-future +++ b/extern/python-future @@ -1 +1 @@ -Subproject commit 80523f383fbba1c6de0551e19d0277e73e69573c +Subproject commit af1db970b0879b59e7aeb798c27a623144561cff From 10eb46d79a3bb86603c7cdb40cb42e3404b81543 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 23 Nov 2023 00:57:48 +0100 Subject: [PATCH 37/38] Updated submodule extern/rich --- extern/rich | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/rich b/extern/rich index 3734ff4..fd98182 160000 --- a/extern/rich +++ b/extern/rich @@ -1 +1 @@ -Subproject commit 3734ff45d7b30541aabdd656efb80c9896d445b3 +Subproject commit fd981823644ccf50d685ac9c0cfe8e1e56c9dd35 From 2c0a15dec516d2aad5e3c8836221b23e7626beb9 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 23 Nov 2023 01:08:07 +0100 Subject: [PATCH 38/38] Minor GUI adjustments --- bseq/panels.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/bseq/panels.py b/bseq/panels.py index a9c9633..d7fc270 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -228,27 +228,21 @@ def draw(self, context): col1 = split.column() col2 = split.column() - #layout.label(text="Global Settings") - #box = layout.box() split = layout.split(factor=0.5) col1 = split.column() col1.alignment = 'RIGHT' col2 = split.column(align=False) - col1.label(text="Import Settings") - # col2.prop(importer_prop, "filter_string", text="Filter String") - col2.prop(importer_prop, "use_relative", text="Relative Path") - - split = layout.split(factor=0.5) - col1 = split.column() - col1.alignment = 'RIGHT' - col2 = split.column(align=False) + col1.label(text="Relative Path") + col2.prop(importer_prop, "use_relative", text="") - col2.prop(importer_prop, "use_imported_normals", text="Use Imported Normals") + col1.label(text="Import Default Normals") + col2.prop(importer_prop, "use_imported_normals", text="") - col2.prop(importer_prop, "use_custom_transform", text="Custom Transform") + col1.label(text="Custom Transform") + col2.prop(importer_prop, "use_custom_transform", text="") if importer_prop.use_custom_transform: split = layout.split(factor=0.33)