From 25216eae38add84d17c502c8178e541e7b6954c7 Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Wed, 7 Dec 2022 15:54:36 +0100 Subject: [PATCH 01/21] Added Enable/Disable All and the option to select a root directory for relative paths --- __init__.py | 2 ++ bseq/__init__.py | 4 +++- bseq/globals.py | 2 +- bseq/importer.py | 10 +++++----- bseq/operators.py | 28 ++++++++++++++++++++++++++-- bseq/panels.py | 8 ++++++++ bseq/properties.py | 4 ++++ bseq/utils.py | 6 +++--- 8 files changed, 52 insertions(+), 12 deletions(-) diff --git a/__init__.py b/__init__.py index 2f08102..9e0eaa8 100644 --- a/__init__.py +++ b/__init__.py @@ -42,6 +42,8 @@ BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, + BSEQ_OT_disable_all, + BSEQ_OT_enable_all, ] diff --git a/bseq/__init__.py b/bseq/__init__.py index edaf58c..7740e59 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 +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 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 .messenger import subscribe_to_selected, unsubscribe_to_selected @@ -43,4 +43,6 @@ def BSEQ_initialize(scene): "BSEQ_OT_disable_selected", "BSEQ_OT_enable_selected", "BSEQ_OT_refresh_seq", + "BSEQ_OT_disable_all", + "BSEQ_OT_enable_all", ] diff --git a/bseq/globals.py b/bseq/globals.py index 5577a71..e875ff0 100644 --- a/bseq/globals.py +++ b/bseq/globals.py @@ -38,4 +38,4 @@ def auto_refresh(scene, depsgraph=None): continue if obj.mode != "OBJECT": continue - refresh_obj(obj) \ No newline at end of file + refresh_obj(obj, scene) \ No newline at end of file diff --git a/bseq/importer.py b/bseq/importer.py index 640971b..d13955a 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -137,7 +137,7 @@ def update_mesh(meshio_mesh, mesh): mesh.normals_split_custom_set_from_vertices(v) -def create_obj(fileseq, use_relaitve, transform_matrix=Matrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])): +def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])): current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] @@ -156,9 +156,9 @@ def create_obj(fileseq, use_relaitve, transform_matrix=Matrix([[1, 0, 0, 0], [0, name = fileseq.basename() + "@" + fileseq.extension() mesh = bpy.data.meshes.new(name) object = bpy.data.objects.new(name, mesh) - object.BSEQ.use_relative = use_relaitve - if use_relaitve: - object.BSEQ.pattern = bpy.path.relpath(str(fileseq)) + object.BSEQ.use_relative = use_relative + if use_relative: + object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=root_path) else: object.BSEQ.pattern = str(fileseq) object.BSEQ.init = True @@ -191,7 +191,7 @@ def update_obj(scene, depsgraph=None): meshio_mesh = None pattern = obj.BSEQ.pattern if obj.BSEQ.use_relative: - pattern = bpy.path.abspath(pattern) + pattern = bpy.path.abspath(pattern, start=scene.BSEQ.root_path) # 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) diff --git a/bseq/operators.py b/bseq/operators.py index 05592d1..a469aba 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -41,7 +41,7 @@ def execute(self, context): show_message_box(traceback.format_exc(), "Can't find sequence: " + str(fs), "ERROR") return {"CANCELLED"} - create_obj(fs, importer_prop.relative) + create_obj(fs, importer_prop.relative, importer_prop.root_path) return {"FINISHED"} @@ -270,6 +270,30 @@ class BSEQ_OT_refresh_seq(bpy.types.Operator): def execute(self, context): scene = context.scene obj = bpy.data.objects[scene.BSEQ.selected_obj_num] - refresh_obj(obj) + refresh_obj(obj, scene) return {"FINISHED"} + +class BSEQ_OT_disable_all(bpy.types.Operator): + '''This operator disable all selected sequence''' + bl_label = "Disable All Sequences" + bl_idname = "bseq.disableall" + bl_options = {"UNDO"} + + def execute(self, context): + for obj in bpy.context.scene.collection.all_objects: + if obj.BSEQ.init and obj.BSEQ.enabled: + obj.BSEQ.enabled = False + return {"FINISHED"} + +class BSEQ_OT_enable_all(bpy.types.Operator): + '''This operator enable all selected sequence''' + bl_label = "Enable All Sequences" + bl_idname = "bseq.enableall" + bl_options = {"UNDO"} + + def execute(self, context): + for obj in bpy.context.scene.collection.all_objects: + if obj.BSEQ.init and not obj.BSEQ.enabled: + obj.BSEQ.enabled = True + return {"FINISHED"} diff --git a/bseq/panels.py b/bseq/panels.py index 4ef50e5..43d4f82 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -74,6 +74,10 @@ def draw(self, context): 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") + class BSEQ_Settings(bpy.types.Panel): @@ -192,6 +196,10 @@ def draw(self, context): col1.label(text="Use Relative Path") col2.prop(importer_prop, "relative", text="") + if importer_prop.relative is True: + col1.label(text="Root Directory") + col2.prop(importer_prop, "root_path", text="") + layout.operator("sequence.load") split = layout.split() col1 = split.column() diff --git a/bseq/properties.py b/bseq/properties.py index 73e1ded..ed79cdb 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -8,6 +8,10 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): 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) + 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) fileseq: bpy.props.EnumProperty( name="File Sequences", description="Please choose the file sequences you want", diff --git a/bseq/utils.py b/bseq/utils.py index 775ee6b..af686b6 100644 --- a/bseq/utils.py +++ b/bseq/utils.py @@ -26,13 +26,13 @@ def stop_animation(): -def refresh_obj(obj): +def refresh_obj(obj, scene): fs = obj.BSEQ.pattern if obj.BSEQ.use_relative: - fs = bpy.path.abspath(fs) + fs = bpy.path.abspath(fs, start=scene.BSEQ.root_path) fs = fileseq.findSequenceOnDisk(fs) fs = fileseq.findSequenceOnDisk(fs.dirname() + fs.basename() + "@" + fs.extension()) fs = str(fs) if obj.BSEQ.use_relative: - fs = bpy.path.relpath(fs) + fs = bpy.path.relpath(fs, start=scene.BSEQ.root_path) obj.BSEQ.pattern = fs \ No newline at end of file From 2344d8ea73d0a1c893682048f3a77b2fd6ec1dad Mon Sep 17 00:00:00 2001 From: Julius Stotz Date: Tue, 7 Mar 2023 22:50:21 +0100 Subject: [PATCH 02/21] Added first version of bin file format --- __init__.py | 4 ++-- additional_file_formats/__init__.py | 3 ++- bseq/importer.py | 9 +++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index 9e0eaa8..4411d8e 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, 3), - "blender": (3, 1, 0), + "version": (0, 1, 4), + "blender": (3, 4, 0), "warning": "", "support": "COMMUNITY", "category": "Import-Export", diff --git a/additional_file_formats/__init__.py b/additional_file_formats/__init__.py index 4d0b7e7..ebea389 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 bin \ No newline at end of file diff --git a/bseq/importer.py b/bseq/importer.py index d13955a..bf1ecaf 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -51,6 +51,13 @@ def extract_faces(cell: meshio.CellBlock): show_message_box(cell.type + " is unsupported mesh format yet") return np.array([]) +def apply_transformation(meshio_mesh, obj): + print("Apply Transformations!") + if not meshio_mesh.field_data["transformation_matrix"] is None: + print("Tried to apply 4x4 transformation matrix!") + # apply transformation + obj.matrix_world = meshio_mesh.field_data["transformation_matrix"] + def update_mesh(meshio_mesh, mesh): # extract information from the meshio mesh @@ -81,6 +88,7 @@ def update_mesh(meshio_mesh, mesh): faces_loop_start = np.roll(faces_loop_start, 1) faces_loop_start[0] = 0 + if len(mesh.vertices) == n_verts and len(mesh.polygons) == n_poly and len(mesh.loops) == n_loop: pass else: @@ -239,3 +247,4 @@ def update_obj(scene, depsgraph=None): show_message_box('function preprocess does not return meshio object', "ERROR") continue update_mesh(meshio_mesh, obj.data) + apply_transformation(meshio_mesh, obj) From 0f575deee67c9989b62e5062e3e74a45e34916d3 Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Thu, 9 Mar 2023 12:34:32 +0100 Subject: [PATCH 03/21] Added bin.py --- additional_file_formats/bin.py | 108 +++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 additional_file_formats/bin.py diff --git a/additional_file_formats/bin.py b/additional_file_formats/bin.py new file mode 100644 index 0000000..2a1a1c6 --- /dev/null +++ b/additional_file_formats/bin.py @@ -0,0 +1,108 @@ +import bpy +import mathutils +import re +import os +import struct +import meshio +from math import radians + +def readBIN_to_meshio(filepath): + firstFile = open(filepath, 'rb') + + # read number of bodies + bytes = firstFile.read() + firstFile.close() + + # currently assume that numBodies is always 1 + (numBodies,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] + + # determine length of file name string + (strLength,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] + + # read file name + objFile, bytes = bytes[:strLength], bytes[strLength:] + objFileString = objFile.decode('ascii') + print(objFileString) + + field_data = {} + field_data["translation"] = None + field_data["scaling"] = None + field_data["rotation"] = None + field_data["transformation_matrix"] = None + + # Read scaling factors in first file + (sx,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (sy,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (sz,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + + field_data["scaling"] = (sx, sy, sz) + + (isWall,), bytes = struct.unpack('?', bytes[:1]), bytes[1:] + (colr,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (colg,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (colb,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (cola,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + + # if the object name is empty, then we know that it is not a "first file" + # and we just simply reopen it and start from the beginning + if len(objFileString) == 0: + otherFile = open(filepath, 'rb') + + # reopen same file + bytes = otherFile.read() + otherFile.close() + + # since there is no object referenced, create empty mesh + mesh = meshio.Mesh([], []) + else: + # create mesh from referenced object + dirPath = os.path.dirname(filepath) + objPath = os.path.join(dirPath, objFileString) + + mesh = meshio.read(objPath) + + + # Read translation in first file + (x,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (y,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (z,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + + field_data["translation"] = (x, y, z) + + # Read rotation in first file + r = [] + for _ in range(0,9): + (value,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + r.append(value) + + rotationMatrix = mathutils.Matrix() + rotationMatrix[0][0:3] = r[0], r[3], r[6] + rotationMatrix[1][0:3] = r[1], r[4], r[7] + rotationMatrix[2][0:3] = r[2], r[5], r[8] + + field_data["rotation"] = rotationMatrix.to_quaternion() + + field_data["transformation_matrix"] = mathutils.Matrix.LocRotScale(field_data["translation"],> + + print(field_data["translation"]) + print(field_data["rotation"]) + print(field_data["scaling"]) + print(field_data["transformation_matrix"]) + + #print(filepath) + #print(dirPath) + #print(objFileString) + #print(objPath) + + mesh.field_data = field_data + return mesh + + #print("-----------------------") + #print(objPath) + #print("-----------------------") + bpy.ops.import_scene.obj(filepath=objPath) + + return meshio.Mesh([], [], field_data=field_data) + +# no need for write function +meshio.register_format("bin", [".bin"], readBIN_to_meshio, {".bin": None}) \ No newline at end of file From cf201a88c9f6f2c347670d4143a47f1f24e33b52 Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Mon, 13 Mar 2023 23:56:28 +0100 Subject: [PATCH 04/21] Fixed byte errors and store the rest of the bytes in an (unused) list --- additional_file_formats/bin.py | 146 +++++++++++++++++++++------------ bseq/importer.py | 3 + 2 files changed, 95 insertions(+), 54 deletions(-) diff --git a/additional_file_formats/bin.py b/additional_file_formats/bin.py index 2a1a1c6..14aeda1 100644 --- a/additional_file_formats/bin.py +++ b/additional_file_formats/bin.py @@ -13,39 +13,59 @@ def readBIN_to_meshio(filepath): bytes = firstFile.read() firstFile.close() + isFirstFile = ((len(bytes) % 48) != 0) + # currently assume that numBodies is always 1 (numBodies,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] - - # determine length of file name string - (strLength,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] - - # read file name - objFile, bytes = bytes[:strLength], bytes[strLength:] - objFileString = objFile.decode('ascii') - print(objFileString) - - field_data = {} - field_data["translation"] = None - field_data["scaling"] = None - field_data["rotation"] = None - field_data["transformation_matrix"] = None - - # Read scaling factors in first file - (sx,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - (sy,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - (sz,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - - field_data["scaling"] = (sx, sy, sz) - - (isWall,), bytes = struct.unpack('?', bytes[:1]), bytes[1:] - (colr,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - (colg,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - (colb,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - (cola,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + numBodies *= isFirstFile + print("numBodies: ", numBodies) + print() + + field_datas = [] + for i in range(0, numBodies): + print(i) + # determine length of file name string + (strLength,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] + + # read file name + objFile, bytes = bytes[:strLength], bytes[strLength:] + if i == 0: + objFileString = objFile.decode('ascii') + + print("objFileString: ", objFileString) + + cur_field_data = {} + cur_field_data["translation"] = None + cur_field_data["scaling"] = None + cur_field_data["rotation"] = None + cur_field_data["transformation_matrix"] = None + + # Read scaling factors in first file + (sx,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (sy,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (sz,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + + cur_field_data["scaling"] = (sx, sy, sz) + + (isWall,), bytes = struct.unpack('?', bytes[:1]), bytes[1:] + (colr,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (colg,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (colb,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (cola,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + + field_datas.append(cur_field_data) # if the object name is empty, then we know that it is not a "first file" # and we just simply reopen it and start from the beginning - if len(objFileString) == 0: + if isFirstFile: + # create mesh from referenced object + dirPath = os.path.dirname(filepath) + objPath = os.path.join(dirPath, objFileString) + + print("Tried to load object at path: ", objPath) + + mesh = meshio.read(objPath) + else: otherFile = open(filepath, 'rb') # reopen same file @@ -54,47 +74,65 @@ def readBIN_to_meshio(filepath): # since there is no object referenced, create empty mesh mesh = meshio.Mesh([], []) - else: - # create mesh from referenced object - dirPath = os.path.dirname(filepath) - objPath = os.path.join(dirPath, objFileString) - mesh = meshio.read(objPath) + print("Field Data List:", field_datas) + print() + print("Bytes left:", len(bytes)) + print() + + print(mesh.points) + + i = 0 + while len(bytes) != 0: + cur_field_data = {} + cur_field_data["translation"] = None + cur_field_data["scaling"] = (1, 1, 1) + cur_field_data["rotation"] = None + cur_field_data["transformation_matrix"] = None + + # Read translation in first file + (x,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (y,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + (z,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + print("Old translation vector: ", (x, y, z)) + cur_field_data["translation"] = (x, y, z) - # Read translation in first file - (x,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - (y,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - (z,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + # Read rotation in first file + r = [] + for _ in range(0,9): + (value,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] + r.append(value) - field_data["translation"] = (x, y, z) + rotationMatrix = mathutils.Matrix() + rotationMatrix[0][0:3] = r[0], r[3], r[6] + rotationMatrix[1][0:3] = r[1], r[4], r[7] + rotationMatrix[2][0:3] = r[2], r[5], r[8] - # Read rotation in first file - r = [] - for _ in range(0,9): - (value,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - r.append(value) + cur_field_data["rotation"] = rotationMatrix.to_quaternion() - rotationMatrix = mathutils.Matrix() - rotationMatrix[0][0:3] = r[0], r[3], r[6] - rotationMatrix[1][0:3] = r[1], r[4], r[7] - rotationMatrix[2][0:3] = r[2], r[5], r[8] + cur_field_data["transformation_matrix"] = mathutils.Matrix.LocRotScale(cur_field_data["translation"], cur_field_data["rotation"], cur_field_data["scaling"]) - field_data["rotation"] = rotationMatrix.to_quaternion() + print(cur_field_data["translation"]) + print(cur_field_data["rotation"]) + print(cur_field_data["scaling"]) + print(cur_field_data["transformation_matrix"]) - field_data["transformation_matrix"] = mathutils.Matrix.LocRotScale(field_data["translation"],> + if isFirstFile: + field_datas[i]["translation"] = cur_field_data["translation"] + field_datas[i]["rotation"] = cur_field_data["rotation"] + field_datas[i]["transformation_matrix"] = cur_field_data["transformation_matrix"] + else: + field_datas.append(cur_field_data) - print(field_data["translation"]) - print(field_data["rotation"]) - print(field_data["scaling"]) - print(field_data["transformation_matrix"]) + i += 1 #print(filepath) #print(dirPath) #print(objFileString) #print(objPath) - mesh.field_data = field_data + mesh.field_data = field_datas[0] return mesh #print("-----------------------") diff --git a/bseq/importer.py b/bseq/importer.py index bf1ecaf..9c37be2 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -67,6 +67,9 @@ def update_mesh(meshio_mesh, mesh): n_loop = 0 n_verts = len(mesh_vertices) + if n_verts == 0: + return + faces_loop_start = np.array([], dtype=np.uint64) faces_loop_total = np.array([], dtype=np.uint64) loops_vert_idx = np.array([], dtype=np.uint64) From 66a21806c2f3d826dc98a81bd13af8e7aa787a46 Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Tue, 14 Mar 2023 00:00:58 +0100 Subject: [PATCH 05/21] Added 1 main commit --- bseq/importer.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bseq/importer.py b/bseq/importer.py index 9c37be2..3cd5d45 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -251,3 +251,15 @@ def update_obj(scene, depsgraph=None): continue update_mesh(meshio_mesh, obj.data) apply_transformation(meshio_mesh, obj) + + # force to evaluate the keyframe animation system + obj.location = obj.evaluated_get(depsgraph).location + match obj.rotation_mode: + case "QUATERNION": + obj.rotation_quaternion = obj.evaluated_get(depsgraph).rotation_quaternion + case "AXIS_ANGLE": + obj.rotation_axis_angle = obj.evaluated_get(depsgraph).rotation_axis_angle + case _: + obj.rotation_euler = obj.evaluated_get(depsgraph).rotation_euler + + obj.scale = obj.evaluated_get(depsgraph).scale \ No newline at end of file From 7cb6fe1ae5255f4460dd7dae78713868d22ce588 Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Tue, 14 Mar 2023 00:12:40 +0100 Subject: [PATCH 06/21] Added main commit --- bseq/importer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index 3cd5d45..1d8216e 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -251,7 +251,7 @@ def update_obj(scene, depsgraph=None): continue update_mesh(meshio_mesh, obj.data) apply_transformation(meshio_mesh, obj) - + # force to evaluate the keyframe animation system obj.location = obj.evaluated_get(depsgraph).location match obj.rotation_mode: @@ -262,4 +262,5 @@ def update_obj(scene, depsgraph=None): case _: obj.rotation_euler = obj.evaluated_get(depsgraph).rotation_euler - obj.scale = obj.evaluated_get(depsgraph).scale \ No newline at end of file + obj.scale = obj.evaluated_get(depsgraph).scale + \ No newline at end of file From 5f8be5a93cc12dad710e2be82cbf713168ee10a6 Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Wed, 15 Mar 2023 19:49:04 +0100 Subject: [PATCH 07/21] Added a button to select multiple sequences --- __init__.py | 1 + additional_file_formats/bin.py | 39 ++++++----- bseq/__init__.py | 3 +- bseq/importer.py | 51 +++++++++----- bseq/operators.py | 122 ++++++++++++++++++++++++++++++++- bseq/panels.py | 17 ++++- bseq/properties.py | 9 +++ 7 files changed, 202 insertions(+), 40 deletions(-) diff --git a/__init__.py b/__init__.py index 4411d8e..2a0c691 100644 --- a/__init__.py +++ b/__init__.py @@ -44,6 +44,7 @@ BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, + WM_OT_batchWavefront ] diff --git a/additional_file_formats/bin.py b/additional_file_formats/bin.py index 14aeda1..3991bee 100644 --- a/additional_file_formats/bin.py +++ b/additional_file_formats/bin.py @@ -18,12 +18,10 @@ def readBIN_to_meshio(filepath): # currently assume that numBodies is always 1 (numBodies,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] numBodies *= isFirstFile - print("numBodies: ", numBodies) - print() + #print("numBodies: ", numBodies) field_datas = [] for i in range(0, numBodies): - print(i) # determine length of file name string (strLength,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] @@ -32,7 +30,7 @@ def readBIN_to_meshio(filepath): if i == 0: objFileString = objFile.decode('ascii') - print("objFileString: ", objFileString) + #print("objFileString: ", objFileString) cur_field_data = {} cur_field_data["translation"] = None @@ -62,7 +60,7 @@ def readBIN_to_meshio(filepath): dirPath = os.path.dirname(filepath) objPath = os.path.join(dirPath, objFileString) - print("Tried to load object at path: ", objPath) + #print("Tried to load object at path: ", objPath) mesh = meshio.read(objPath) else: @@ -75,12 +73,12 @@ def readBIN_to_meshio(filepath): # since there is no object referenced, create empty mesh mesh = meshio.Mesh([], []) - print("Field Data List:", field_datas) - print() - print("Bytes left:", len(bytes)) - print() + #print("Field Data List:", field_datas) + #print() + #print("Bytes left:", len(bytes)) + #print() - print(mesh.points) + #print(mesh.points) i = 0 while len(bytes) != 0: @@ -95,7 +93,6 @@ def readBIN_to_meshio(filepath): (y,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] (z,), bytes = struct.unpack('f', bytes[:4]), bytes[4:] - print("Old translation vector: ", (x, y, z)) cur_field_data["translation"] = (x, y, z) # Read rotation in first file @@ -113,10 +110,14 @@ def readBIN_to_meshio(filepath): cur_field_data["transformation_matrix"] = mathutils.Matrix.LocRotScale(cur_field_data["translation"], cur_field_data["rotation"], cur_field_data["scaling"]) - print(cur_field_data["translation"]) - print(cur_field_data["rotation"]) - print(cur_field_data["scaling"]) - print(cur_field_data["transformation_matrix"]) + #print("Translation:") + #print(cur_field_data["translation"]) + #print("Rotation:") + #print(cur_field_data["rotation"]) + #print("Scaling:") + #print(cur_field_data["scaling"]) + #print("Transformation matrix:") + #print(cur_field_data["transformation_matrix"]) if isFirstFile: field_datas[i]["translation"] = cur_field_data["translation"] @@ -127,12 +128,12 @@ def readBIN_to_meshio(filepath): i += 1 - #print(filepath) - #print(dirPath) - #print(objFileString) - #print(objPath) mesh.field_data = field_datas[0] + + #print("Field data:") + #print(mesh.field_data) + return mesh #print("-----------------------") diff --git a/bseq/__init__.py b/bseq/__init__.py index 7740e59..34b1bc8 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 +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, WM_OT_batchWavefront 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 .messenger import subscribe_to_selected, unsubscribe_to_selected @@ -45,4 +45,5 @@ def BSEQ_initialize(scene): "BSEQ_OT_refresh_seq", "BSEQ_OT_disable_all", "BSEQ_OT_enable_all", + "WM_OT_batchWavefront" ] diff --git a/bseq/importer.py b/bseq/importer.py index 1d8216e..aca91ef 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -1,4 +1,5 @@ import bpy +import mathutils import meshio import traceback import fileseq @@ -51,12 +52,37 @@ def extract_faces(cell: meshio.CellBlock): show_message_box(cell.type + " is unsupported mesh format yet") return np.array([]) -def apply_transformation(meshio_mesh, obj): - print("Apply Transformations!") +def apply_transformation(meshio_mesh, obj, depsgraph): + # force to evaluate the keyframe animation system + eval_location = obj.evaluated_get(depsgraph).location + eval_scale = obj.evaluated_get(depsgraph).scale + + match obj.rotation_mode: + case "QUATERNION": + eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion + case "AXIS_ANGLE": + eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle + case _: + eval_rotation = obj.evaluated_get(depsgraph).rotation_euler + + + # get evaluated transformation matrix + eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale) + print("Keyframe Transformations: ", eval_transform_matrix) + + #combined_matrix = mathutils.Matrix.Identity(4) + + rigid_body_transformation = mathutils.Matrix.Identity(4) if not meshio_mesh.field_data["transformation_matrix"] is None: - print("Tried to apply 4x4 transformation matrix!") - # apply transformation - obj.matrix_world = meshio_mesh.field_data["transformation_matrix"] + # first apply rigid-body transformations + rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"] + print("Rigid Body Transformation: ", rigid_body_transformation) + #combined_matrix *= rigid_body_transformation + + #combined_matrix = eval_transform_matrix + #print("Combined_matrix: ", combined_matrix) + + obj.matrix_world = rigid_body_transformation def update_mesh(meshio_mesh, mesh): @@ -250,17 +276,6 @@ def update_obj(scene, depsgraph=None): show_message_box('function preprocess does not return meshio object', "ERROR") continue update_mesh(meshio_mesh, obj.data) - apply_transformation(meshio_mesh, obj) - - # force to evaluate the keyframe animation system - obj.location = obj.evaluated_get(depsgraph).location - match obj.rotation_mode: - case "QUATERNION": - obj.rotation_quaternion = obj.evaluated_get(depsgraph).rotation_quaternion - case "AXIS_ANGLE": - obj.rotation_axis_angle = obj.evaluated_get(depsgraph).rotation_axis_angle - case _: - obj.rotation_euler = obj.evaluated_get(depsgraph).rotation_euler - - obj.scale = obj.evaluated_get(depsgraph).scale + + apply_transformation(meshio_mesh, obj, depsgraph) \ No newline at end of file diff --git a/bseq/operators.py b/bseq/operators.py index a469aba..fa3ed51 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -1,4 +1,5 @@ import bpy +from mathutils import Matrix import fileseq from .messenger import * import traceback @@ -41,7 +42,11 @@ def execute(self, context): show_message_box(traceback.format_exc(), "Can't find sequence: " + str(fs), "ERROR") return {"CANCELLED"} - create_obj(fs, importer_prop.relative, importer_prop.root_path) + transform_matrix = Matrix.Identity(4) + if importer_prop.use_custom_transform: + transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) + + create_obj(fs, importer_prop.relative, importer_prop.root_path, transform_matrix=transform_matrix) return {"FINISHED"} @@ -297,3 +302,118 @@ def execute(self, context): if obj.BSEQ.init and not obj.BSEQ.enabled: obj.BSEQ.enabled = True return {"FINISHED"} + +from pathlib import Path +from bpy_extras.io_utils import ImportHelper + +class WM_OT_batchWavefront(bpy.types.Operator, ImportHelper): + """Batch Import Wavefront""" + bl_idname = "wm.seq_import_batch" + bl_label = "Import multiple sequences" + bl_options = {'PRESET', 'UNDO'} + + filename_ext = ".vtk" + + filter_glob = bpy.props.StringProperty( + default="*.vtk", + options={'HIDDEN'}) + + files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) + + global_scale_setting: bpy.props.FloatProperty( + name="Scale", + description="Value by which to enlarge or shrink" \ + "the objects with respect to the world origin", + min=0.0001, max=10000.0, + soft_min=0.01, soft_max=1000.0, + default=1.0) + + clamp_size_setting: bpy.props.FloatProperty( + name="Clamp Bounding Box", + description="Resize the objects to keep bounding box" \ + "under this value. Value 0 diables clamping", + min=0.0, max=1000.0, + soft_min=0.0, soft_max=1000.0, + default=0.0) + + axis_forward_setting: bpy.props.EnumProperty( + name="Forward Axis", + items=(('X', "X", ""), + ('Y', "Y", ""), + ('Z', "Z", ""), + ('NEGATIVE_X', "-X", ""), + ('NEGATIVE_Y', "-Y", ""), + ('NEGATIVE_Z', "-Z", ""), + ), + default='NEGATIVE_Z') + + axis_up_setting: bpy.props.EnumProperty( + name="Up Axis", + items=(('X', "X", ""), + ('Y', "Y", ""), + ('Z', "Z", ""), + ('NEGATIVE_X', "-X", ""), + ('NEGATIVE_Y', "-Y", ""), + ('NEGATIVE_Z', "-Z", ""), + ), + default='Y') + + validate_setting: bpy.props.BoolProperty( + name="Validate Meshes", + description="Check imported mesh objects for invalid data") + + vgroup_setting: bpy.props.BoolProperty( + name="Vertex Groups", + description="Import OBJ groups as vertex groups") + + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + box = layout.box() + box.label(text="Transform", icon='OBJECT_DATA') + col = box.column() + col.prop(self, "global_scale_setting") + col.prop(self, "clamp_size_setting") + col.separator() + col.row().prop(self, "axis_forward_setting", expand=True) + col.separator(factor=0.5) + col.row().prop(self, "axis_up_setting", expand=True) + col.separator() + + box = layout.box() + box.label(text="Options", icon='EXPORT') + col = box.column() + col.prop(self, "vgroup_setting") + col.prop(self, "validate_setting") + + def execute(self, context): + scene = context.scene + importer_prop = scene.BSEQ + + folder = Path(self.filepath) + used_seqs = [] + for selection in self.files: + fp = Path(folder.parent, selection.name) + # get respective file sequence + print(fp) + seqs = fileseq.findSequencesOnDisk(str(folder.parent)) + print(seqs) + + for s in seqs: + if selection.name.startswith(s.basename()) and selection.name.endswith(s.extension()) and s not in used_seqs: + #TO-DO: modify import_prop.fileseq and then use bpy.ops.sequence.load() + transform_matrix = Matrix.Identity(4) + if importer_prop.use_custom_transform: + transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) + + create_obj(s, False, importer_prop.root_path, transform_matrix=transform_matrix) + + used_seqs.append(s) + print(used_seqs) + break + # load that specific file sequence (only if it is not already in the scene) + #bpy.ops.sequence.load() + return {'FINISHED'} \ No newline at end of file diff --git a/bseq/panels.py b/bseq/panels.py index 43d4f82..504d8da 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -196,11 +196,14 @@ def draw(self, context): col1.label(text="Use Relative Path") col2.prop(importer_prop, "relative", text="") - if importer_prop.relative is True: + if importer_prop.relative: col1.label(text="Root Directory") col2.prop(importer_prop, "root_path", text="") layout.operator("sequence.load") + + layout.operator("wm.seq_import_batch") + split = layout.split() col1 = split.column() col2 = split.column() @@ -227,7 +230,19 @@ def draw(self, context): 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): ''' diff --git a/bseq/properties.py b/bseq/properties.py index ed79cdb..d0dc70b 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -22,6 +22,10 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): default=False) pattern: bpy.props.StringProperty(name="Pattern", description="You can specify the pattern here, in case the sequence can't be deteced.") + + 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 ") @@ -48,7 +52,12 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): 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) + custom_location: bpy.props.FloatVectorProperty(name='Custom Location', description='Set custom location vector', size=3, subtype="TRANSLATION") + custom_rotation: bpy.props.FloatVectorProperty(name='Custom Rotation', description='Set custom rotation vector', size=4, subtype="QUATERNION", default=[1,0,0,0]) + custom_scale: bpy.props.FloatVectorProperty(name='Custom Scale', description='Set custom scaling vector', size=3, subtype="COORDINATES", default=[1,1,1]) class BSEQ_obj_property(bpy.types.PropertyGroup): init: bpy.props.BoolProperty(default=False) From 30cd9b42d93ee6b9d3d54fcfdb0c8db949f275b1 Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Mon, 27 Mar 2023 16:23:01 +0200 Subject: [PATCH 08/21] Added the functionality to import multiple sequences (in top menu and in addon), a custom initial transformation matrix and a list, so the file sequences do not have to be loaded again in every frame --- __init__.py | 7 ++- bseq/__init__.py | 5 ++- bseq/callback.py | 23 +++++----- bseq/importer.py | 16 ++++--- bseq/operators.py | 107 +++++++++++++++++++++++++++++++++++++++++++-- bseq/properties.py | 26 ++++++++--- 6 files changed, 158 insertions(+), 26 deletions(-) diff --git a/__init__.py b/__init__.py index 2a0c691..1b3f4ef 100644 --- a/__init__.py +++ b/__init__.py @@ -22,6 +22,8 @@ from bseq import * +from bseq.operators import menu_func_import + classes = [ BSEQ_obj_property, BSEQ_scene_property, @@ -44,7 +46,8 @@ BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, - WM_OT_batchWavefront + WM_OT_batchSequences, + WM_OT_MeshioObject ] @@ -57,6 +60,7 @@ def register(): 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) # manually call this function once # so when addon being installed, it can run correctly @@ -70,6 +74,7 @@ def unregister(): del bpy.types.Scene.BSEQ del bpy.types.Object.BSEQ bpy.app.handlers.load_post.remove(BSEQ_initialize) + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) unsubscribe_to_selected() diff --git a/bseq/__init__.py b/bseq/__init__.py index 34b1bc8..0aead35 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, WM_OT_batchWavefront +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, WM_OT_batchSequences, 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 .messenger import subscribe_to_selected, unsubscribe_to_selected @@ -45,5 +45,6 @@ def BSEQ_initialize(scene): "BSEQ_OT_refresh_seq", "BSEQ_OT_disable_all", "BSEQ_OT_enable_all", - "WM_OT_batchWavefront" + "WM_OT_batchSequences", + "WM_OT_MeshioObject" ] diff --git a/bseq/callback.py b/bseq/callback.py index a2c6ece..896eabc 100644 --- a/bseq/callback.py +++ b/bseq/callback.py @@ -3,6 +3,7 @@ # Code here are mostly about the callback/update/items functions used in properties.py +file_sequences = [] def update_path(self, context): # When the path has been changed, reset the selected sequence to None @@ -10,12 +11,6 @@ def update_path(self, context): context.scene.BSEQ.use_pattern = False context.scene.BSEQ.pattern = "" - -def item_fileseq(self, context): - ''' - Detects all the file sequences in the directory - ''' - p = context.scene.BSEQ.path try: f = fileseq.findSequencesOnDisk(p) @@ -24,15 +19,23 @@ def item_fileseq(self, context): if not f: return [("None", "No sequence detected", "", 1)] - file_seq = [] + + file_sequences.clear() if len(f) >= 20: - file_seq.append(("None", "Too much sequence detected, could be false detection, please use pattern below", "", 1)) + file_sequences.append(("None", "Too much sequence detected, could be false detection, please use pattern below", "", 1)) else: count = 1 for seq in f: - file_seq.append((str(seq), seq.basename() + "@" + seq.extension(), "", count)) + file_sequences.append((str(seq), seq.basename() + "@" + seq.extension(), "", count)) count += 1 - return file_seq + + +def item_fileseq(self, context): + ''' + Detects all the file sequences in the directory + ''' + return file_sequences + def update_selected_obj_num(self, context): diff --git a/bseq/importer.py b/bseq/importer.py index aca91ef..9ed7f9d 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -73,7 +73,7 @@ def apply_transformation(meshio_mesh, obj, depsgraph): #combined_matrix = mathutils.Matrix.Identity(4) rigid_body_transformation = mathutils.Matrix.Identity(4) - if not meshio_mesh.field_data["transformation_matrix"] is None: + if meshio_mesh.field_data.get("transformation_matrix") is not None: # first apply rigid-body transformations rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"] print("Rigid Body Transformation: ", rigid_body_transformation) @@ -82,7 +82,7 @@ def apply_transformation(meshio_mesh, obj, depsgraph): #combined_matrix = eval_transform_matrix #print("Combined_matrix: ", combined_matrix) - obj.matrix_world = rigid_body_transformation + obj.matrix_world = rigid_body_transformation @ global_transform_matrix def update_mesh(meshio_mesh, mesh): @@ -195,12 +195,15 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object = bpy.data.objects.new(name, mesh) object.BSEQ.use_relative = use_relative if use_relative: - object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=root_path) + if root_path != "": + object.BSEQ.pattern = bpy.path.relpath(str(fileseq), start=root_path) + else: + object.BSEQ.pattern = bpy.path.relpath(str(fileseq)) else: object.BSEQ.pattern = str(fileseq) object.BSEQ.init = True object.BSEQ.enabled = enabled - object.matrix_world = transform_matrix + object.initial_transform_matrix = transform_matrix driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' if enabled: @@ -228,7 +231,10 @@ def update_obj(scene, depsgraph=None): meshio_mesh = None pattern = obj.BSEQ.pattern if obj.BSEQ.use_relative: - pattern = bpy.path.abspath(pattern, start=scene.BSEQ.root_path) + 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) diff --git a/bseq/operators.py b/bseq/operators.py index fa3ed51..438ba6b 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -304,10 +304,11 @@ def execute(self, context): return {"FINISHED"} from pathlib import Path +import meshio from bpy_extras.io_utils import ImportHelper -class WM_OT_batchWavefront(bpy.types.Operator, ImportHelper): - """Batch Import Wavefront""" +class WM_OT_batchSequences(bpy.types.Operator, ImportHelper): + """Batch Import Sequences""" bl_idname = "wm.seq_import_batch" bl_label = "Import multiple sequences" bl_options = {'PRESET', 'UNDO'} @@ -416,4 +417,104 @@ def execute(self, context): break # load that specific file sequence (only if it is not already in the scene) #bpy.ops.sequence.load() - return {'FINISHED'} \ No newline at end of file + return {'FINISHED'} + +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_options = {'PRESET', 'UNDO'} + + files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) + + global_scale_setting: bpy.props.FloatProperty( + name="Scale", + description="Value by which to enlarge or shrink" \ + "the objects with respect to the world origin", + min=0.0001, max=10000.0, + soft_min=0.01, soft_max=1000.0, + default=1.0) + + clamp_size_setting: bpy.props.FloatProperty( + name="Clamp Bounding Box", + description="Resize the objects to keep bounding box" \ + "under this value. Value 0 diables clamping", + min=0.0, max=1000.0, + soft_min=0.0, soft_max=1000.0, + default=0.0) + + axis_forward_setting: bpy.props.EnumProperty( + name="Forward Axis", + items=(('X', "X", ""), + ('Y', "Y", ""), + ('Z', "Z", ""), + ('NEGATIVE_X', "-X", ""), + ('NEGATIVE_Y', "-Y", ""), + ('NEGATIVE_Z', "-Z", ""), + ), + default='NEGATIVE_Z') + + axis_up_setting: bpy.props.EnumProperty( + name="Up Axis", + items=(('X', "X", ""), + ('Y', "Y", ""), + ('Z', "Z", ""), + ('NEGATIVE_X', "-X", ""), + ('NEGATIVE_Y', "-Y", ""), + ('NEGATIVE_Z', "-Z", ""), + ), + default='Y') + + validate_setting: bpy.props.BoolProperty( + name="Validate Meshes", + description="Check imported mesh objects for invalid data") + + vgroup_setting: bpy.props.BoolProperty( + name="Vertex Groups", + description="Import OBJ groups as vertex groups") + + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + box = layout.box() + box.label(text="Transform", icon='OBJECT_DATA') + col = box.column() + col.prop(self, "global_scale_setting") + col.prop(self, "clamp_size_setting") + col.separator() + col.row().prop(self, "axis_forward_setting", expand=True) + col.separator(factor=0.5) + col.row().prop(self, "axis_up_setting", expand=True) + col.separator() + + box = layout.box() + box.label(text="Options", icon='EXPORT') + col = box.column() + col.prop(self, "vgroup_setting") + col.prop(self, "validate_setting") + + def execute(self, context): + scene = context.scene + importer_prop = scene.BSEQ + + folder = Path(self.filepath) + + for selection in self.files: + fp = Path(folder.parent, selection.name) + file = fileseq.FileSequence(str(fp)) + + transform_matrix = Matrix.Identity(4) + if importer_prop.use_custom_transform: + transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) + + create_obj(file, False, importer_prop.root_path, transform_matrix=transform_matrix) + + return {'FINISHED'} + +def menu_func_import(self, context): + self.layout.operator( + WM_OT_MeshioObject.bl_idname, + text="Batch Meshio Object") diff --git a/bseq/properties.py b/bseq/properties.py index d0dc70b..1dcf1fe 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -53,11 +53,26 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): 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) + use_custom_transform: bpy.props.BoolProperty(name='Use custom transformation matrix', + description="Whether or not to use a custom transformation matrix", + default=False) - custom_location: bpy.props.FloatVectorProperty(name='Custom Location', description='Set custom location vector', size=3, subtype="TRANSLATION") - custom_rotation: bpy.props.FloatVectorProperty(name='Custom Rotation', description='Set custom rotation vector', size=4, subtype="QUATERNION", default=[1,0,0,0]) - custom_scale: bpy.props.FloatVectorProperty(name='Custom Scale', description='Set custom scaling vector', size=3, subtype="COORDINATES", default=[1,1,1]) + custom_location: bpy.props.FloatVectorProperty(name='Custom Location', + description='Set custom location vector', + size=3, + subtype="TRANSLATION") + + custom_rotation: bpy.props.FloatVectorProperty(name='Custom Rotation', + description='Set custom rotation vector', + size=4, + subtype="XYZ", + default=[1,0,0,0]) + + custom_scale: bpy.props.FloatVectorProperty(name='Custom Scale', + description='Set custom scaling vector', + size=3, + subtype="COORDINATES", + default=[1,1,1]) class BSEQ_obj_property(bpy.types.PropertyGroup): init: bpy.props.BoolProperty(default=False) @@ -68,7 +83,8 @@ class BSEQ_obj_property(bpy.types.PropertyGroup): use_relative: bpy.props.BoolProperty(default=False) pattern: bpy.props.StringProperty() frame: bpy.props.IntProperty() - + initial_transform_matrix: bpy.props.FloatVectorProperty(name='Custom Transformation Matrix', description='Set custom transformation', size=4, subtype="MATRIX") + #animated_transform_matrix: # set this property for mesh, not object (maybe change later?) class BSEQ_mesh_property(bpy.types.PropertyGroup): From ea852b9b5e3a10f14151b123b53ca7fc7b0a500b Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Mon, 27 Mar 2023 23:30:55 +0200 Subject: [PATCH 09/21] Added object property for initial transformation matrix, made for multiple sequence loading cleaner code and tried to resolve the issue with transformation matrices --- .gitignore | 2 + bseq/importer.py | 52 ++++++++-------- bseq/operators.py | 152 +-------------------------------------------- bseq/properties.py | 14 +++-- 4 files changed, 39 insertions(+), 181 deletions(-) diff --git a/.gitignore b/.gitignore index 59a5874..939e61a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ meshio future fileseq +blender_install.py + docs/_build/* \ No newline at end of file diff --git a/bseq/importer.py b/bseq/importer.py index 9ed7f9d..ca18764 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -52,37 +52,39 @@ def extract_faces(cell: meshio.CellBlock): show_message_box(cell.type + " is unsupported mesh format yet") return np.array([]) +def has_keyframe(obj): + animdata = obj.animation_data + if animdata is not None and animdata.action is not None: + for fcurve in animdata.action.fcurves: + if fcurve.data_path == 'location' or fcurve.data_path == 'scale' or fcurve.data_path == 'rotation': + return len(fcurve.keyframe_points) > 0 + return False + def apply_transformation(meshio_mesh, obj, depsgraph): # force to evaluate the keyframe animation system - eval_location = obj.evaluated_get(depsgraph).location - eval_scale = obj.evaluated_get(depsgraph).scale - - match obj.rotation_mode: - case "QUATERNION": - eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion - case "AXIS_ANGLE": - eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle - case _: - eval_rotation = obj.evaluated_get(depsgraph).rotation_euler - - - # get evaluated transformation matrix - eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale) - print("Keyframe Transformations: ", eval_transform_matrix) - - #combined_matrix = mathutils.Matrix.Identity(4) + eval_transform_matrix = mathutils.Matrix.Identity(4) + if has_keyframe(obj): + eval_location = obj.evaluated_get(depsgraph).location + eval_scale = obj.evaluated_get(depsgraph).scale + + match obj.rotation_mode: + case "QUATERNION": + eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion + case "AXIS_ANGLE": + eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle + case _: + eval_rotation = obj.evaluated_get(depsgraph).rotation_euler + + # get evaluated transformation matrix + eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale) + #print("Keyframe Transformations: ", eval_transform_matrix) + print(has_keyframe(obj)) rigid_body_transformation = mathutils.Matrix.Identity(4) if meshio_mesh.field_data.get("transformation_matrix") is not None: - # first apply rigid-body transformations rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"] - print("Rigid Body Transformation: ", rigid_body_transformation) - #combined_matrix *= rigid_body_transformation - - #combined_matrix = eval_transform_matrix - #print("Combined_matrix: ", combined_matrix) - obj.matrix_world = rigid_body_transformation @ global_transform_matrix + obj.matrix_world = rigid_body_transformation @ obj.BSEQ.initial_transform_matrix @ eval_transform_matrix def update_mesh(meshio_mesh, mesh): @@ -203,7 +205,7 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object.BSEQ.pattern = str(fileseq) object.BSEQ.init = True object.BSEQ.enabled = enabled - object.initial_transform_matrix = transform_matrix + object.BSEQ.initial_transform_matrix = [transform_matrix[j][i] for i in range(4) for j in range(4)] driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' if enabled: diff --git a/bseq/operators.py b/bseq/operators.py index 438ba6b..768b357 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -313,83 +313,8 @@ class WM_OT_batchSequences(bpy.types.Operator, ImportHelper): bl_label = "Import multiple sequences" bl_options = {'PRESET', 'UNDO'} - filename_ext = ".vtk" - - filter_glob = bpy.props.StringProperty( - default="*.vtk", - options={'HIDDEN'}) - files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) - global_scale_setting: bpy.props.FloatProperty( - name="Scale", - description="Value by which to enlarge or shrink" \ - "the objects with respect to the world origin", - min=0.0001, max=10000.0, - soft_min=0.01, soft_max=1000.0, - default=1.0) - - clamp_size_setting: bpy.props.FloatProperty( - name="Clamp Bounding Box", - description="Resize the objects to keep bounding box" \ - "under this value. Value 0 diables clamping", - min=0.0, max=1000.0, - soft_min=0.0, soft_max=1000.0, - default=0.0) - - axis_forward_setting: bpy.props.EnumProperty( - name="Forward Axis", - items=(('X', "X", ""), - ('Y', "Y", ""), - ('Z', "Z", ""), - ('NEGATIVE_X', "-X", ""), - ('NEGATIVE_Y', "-Y", ""), - ('NEGATIVE_Z', "-Z", ""), - ), - default='NEGATIVE_Z') - - axis_up_setting: bpy.props.EnumProperty( - name="Up Axis", - items=(('X', "X", ""), - ('Y', "Y", ""), - ('Z', "Z", ""), - ('NEGATIVE_X', "-X", ""), - ('NEGATIVE_Y', "-Y", ""), - ('NEGATIVE_Z', "-Z", ""), - ), - default='Y') - - validate_setting: bpy.props.BoolProperty( - name="Validate Meshes", - description="Check imported mesh objects for invalid data") - - vgroup_setting: bpy.props.BoolProperty( - name="Vertex Groups", - description="Import OBJ groups as vertex groups") - - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - box = layout.box() - box.label(text="Transform", icon='OBJECT_DATA') - col = box.column() - col.prop(self, "global_scale_setting") - col.prop(self, "clamp_size_setting") - col.separator() - col.row().prop(self, "axis_forward_setting", expand=True) - col.separator(factor=0.5) - col.row().prop(self, "axis_up_setting", expand=True) - col.separator() - - box = layout.box() - box.label(text="Options", icon='EXPORT') - col = box.column() - col.prop(self, "vgroup_setting") - col.prop(self, "validate_setting") - def execute(self, context): scene = context.scene importer_prop = scene.BSEQ @@ -398,14 +323,11 @@ def execute(self, context): used_seqs = [] for selection in self.files: fp = Path(folder.parent, selection.name) - # get respective file sequence - print(fp) + seqs = fileseq.findSequencesOnDisk(str(folder.parent)) - print(seqs) for s in seqs: if selection.name.startswith(s.basename()) and selection.name.endswith(s.extension()) and s not in used_seqs: - #TO-DO: modify import_prop.fileseq and then use bpy.ops.sequence.load() transform_matrix = Matrix.Identity(4) if importer_prop.use_custom_transform: transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) @@ -413,7 +335,6 @@ def execute(self, context): create_obj(s, False, importer_prop.root_path, transform_matrix=transform_matrix) used_seqs.append(s) - print(used_seqs) break # load that specific file sequence (only if it is not already in the scene) #bpy.ops.sequence.load() @@ -426,76 +347,7 @@ class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper): bl_options = {'PRESET', 'UNDO'} files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) - - global_scale_setting: bpy.props.FloatProperty( - name="Scale", - description="Value by which to enlarge or shrink" \ - "the objects with respect to the world origin", - min=0.0001, max=10000.0, - soft_min=0.01, soft_max=1000.0, - default=1.0) - - clamp_size_setting: bpy.props.FloatProperty( - name="Clamp Bounding Box", - description="Resize the objects to keep bounding box" \ - "under this value. Value 0 diables clamping", - min=0.0, max=1000.0, - soft_min=0.0, soft_max=1000.0, - default=0.0) - - axis_forward_setting: bpy.props.EnumProperty( - name="Forward Axis", - items=(('X', "X", ""), - ('Y', "Y", ""), - ('Z', "Z", ""), - ('NEGATIVE_X', "-X", ""), - ('NEGATIVE_Y', "-Y", ""), - ('NEGATIVE_Z', "-Z", ""), - ), - default='NEGATIVE_Z') - - axis_up_setting: bpy.props.EnumProperty( - name="Up Axis", - items=(('X', "X", ""), - ('Y', "Y", ""), - ('Z', "Z", ""), - ('NEGATIVE_X', "-X", ""), - ('NEGATIVE_Y', "-Y", ""), - ('NEGATIVE_Z', "-Z", ""), - ), - default='Y') - validate_setting: bpy.props.BoolProperty( - name="Validate Meshes", - description="Check imported mesh objects for invalid data") - - vgroup_setting: bpy.props.BoolProperty( - name="Vertex Groups", - description="Import OBJ groups as vertex groups") - - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - box = layout.box() - box.label(text="Transform", icon='OBJECT_DATA') - col = box.column() - col.prop(self, "global_scale_setting") - col.prop(self, "clamp_size_setting") - col.separator() - col.row().prop(self, "axis_forward_setting", expand=True) - col.separator(factor=0.5) - col.row().prop(self, "axis_up_setting", expand=True) - col.separator() - - box = layout.box() - box.label(text="Options", icon='EXPORT') - col = box.column() - col.prop(self, "vgroup_setting") - col.prop(self, "validate_setting") - def execute(self, context): scene = context.scene importer_prop = scene.BSEQ @@ -504,7 +356,7 @@ def execute(self, context): for selection in self.files: fp = Path(folder.parent, selection.name) - file = fileseq.FileSequence(str(fp)) + file = fileseq.findSequenceOnDisk(str(fp)) transform_matrix = Matrix.Identity(4) if importer_prop.use_custom_transform: diff --git a/bseq/properties.py b/bseq/properties.py index 1dcf1fe..d839791 100644 --- a/bseq/properties.py +++ b/bseq/properties.py @@ -1,6 +1,6 @@ import bpy from .callback import * - +from mathutils import Matrix class BSEQ_scene_property(bpy.types.PropertyGroup): path: bpy.props.StringProperty(name="Directory", @@ -64,9 +64,9 @@ class BSEQ_scene_property(bpy.types.PropertyGroup): custom_rotation: bpy.props.FloatVectorProperty(name='Custom Rotation', description='Set custom rotation vector', - size=4, - subtype="XYZ", - default=[1,0,0,0]) + size=3, + subtype="EULER", + default=[0,0,0]) custom_scale: bpy.props.FloatVectorProperty(name='Custom Scale', description='Set custom scaling vector', @@ -83,8 +83,10 @@ class BSEQ_obj_property(bpy.types.PropertyGroup): use_relative: bpy.props.BoolProperty(default=False) pattern: bpy.props.StringProperty() frame: bpy.props.IntProperty() - initial_transform_matrix: bpy.props.FloatVectorProperty(name='Custom Transformation Matrix', description='Set custom transformation', size=4, subtype="MATRIX") - #animated_transform_matrix: + initial_transform_matrix: bpy.props.FloatVectorProperty(name='Custom Transformation Matrix', + description='Set custom transformation', + size=16, + subtype="MATRIX") # set this property for mesh, not object (maybe change later?) class BSEQ_mesh_property(bpy.types.PropertyGroup): From 341cbada94efc2fee7cff55f29444b78db5b8cae Mon Sep 17 00:00:00 2001 From: "julius.stotz" Date: Wed, 29 Mar 2023 10:56:33 +0200 Subject: [PATCH 10/21] Added install script --- .gitignore | 2 -- blender_install.py | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 blender_install.py diff --git a/.gitignore b/.gitignore index 939e61a..59a5874 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,4 @@ meshio future fileseq -blender_install.py - docs/_build/* \ No newline at end of file diff --git a/blender_install.py b/blender_install.py new file mode 100644 index 0000000..5fc8c02 --- /dev/null +++ b/blender_install.py @@ -0,0 +1,27 @@ +import bpy +import addon_utils +from datetime import date +import time +import os +import importlib + +bpy.ops.script.reload() + +addon_module = [m for m in addon_utils.modules() if m.bl_info.get('name') == "Sequence Loader"] # get module +# uninstall old version +if addon_module: + #addon_utils.disable("blendersequenceloader") + bpy.ops.preferences.addon_remove(module="blendersequenceloader") + +os.system("python C:\\code\\work\\blender-sequence-loader\\build_addon.py") + +time.sleep(1) +# install new version +today = str(date.today()) +fp = f'C:\\code\\work\\blender-sequence-loader\\blender_sequence_loader_{today}.zip' +bpy.ops.preferences.addon_install(filepath=fp, overwrite=True) +time.sleep(1) +bpy.ops.preferences.addon_enable(module="blendersequenceloader") +addon_utils.enable("blendersequenceloader") +time.sleep(1) +os.system("blender") \ No newline at end of file From fb9f9823ea9d024046b21275393634a78c0f3c6a Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 29 Mar 2023 15:02:51 +0200 Subject: [PATCH 11/21] Fixed keyframe animation system to work with other transformations --- blender_install.py | 20 +++++++++++++------- bseq/importer.py | 31 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/blender_install.py b/blender_install.py index 5fc8c02..cec511f 100644 --- a/blender_install.py +++ b/blender_install.py @@ -4,6 +4,7 @@ import time import os import importlib +from sys import platform bpy.ops.script.reload() @@ -11,17 +12,22 @@ # uninstall old version if addon_module: #addon_utils.disable("blendersequenceloader") + bpy.ops.preferences.addon_disable(module="blendersequenceloader") bpy.ops.preferences.addon_remove(module="blendersequenceloader") -os.system("python C:\\code\\work\\blender-sequence-loader\\build_addon.py") +fp = '' +today = str(date.today()) +if platform == "linux" or platform == "linux2": + fp = f'/servers/karl/ssd-home2/jstotz/blender-sequence-loader/blender_sequence_loader_{today}.zip' + os.system("python /servers/karl/ssd-home2/jstotz/blender-sequence-loader/build_addon.py") +elif platform == "win32" or platform == "cygwin" or platform == "msys": + fp = f'C:\\code\\work\\blender-sequence-loader\\blender_sequence_loader_{today}.zip' + os.system("python C:\\code\\work\\blender-sequence-loader\\build_addon.py") -time.sleep(1) # install new version -today = str(date.today()) -fp = f'C:\\code\\work\\blender-sequence-loader\\blender_sequence_loader_{today}.zip' -bpy.ops.preferences.addon_install(filepath=fp, overwrite=True) -time.sleep(1) +bpy.ops.preferences.addon_install(filepath=fp) +time.sleep(2) bpy.ops.preferences.addon_enable(module="blendersequenceloader") -addon_utils.enable("blendersequenceloader") +#addon_utils.enable("blendersequenceloader") time.sleep(1) os.system("blender") \ No newline at end of file diff --git a/bseq/importer.py b/bseq/importer.py index ca18764..f296df4 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -52,38 +52,37 @@ def extract_faces(cell: meshio.CellBlock): show_message_box(cell.type + " is unsupported mesh format yet") return np.array([]) -def has_keyframe(obj): +def has_keyframe(obj, attr): animdata = obj.animation_data if animdata is not None and animdata.action is not None: for fcurve in animdata.action.fcurves: - if fcurve.data_path == 'location' or fcurve.data_path == 'scale' or fcurve.data_path == 'rotation': + if fcurve.data_path == attr: return len(fcurve.keyframe_points) > 0 return False def apply_transformation(meshio_mesh, obj, depsgraph): - # force to evaluate the keyframe animation system + # evaluate the keyframe animation system eval_transform_matrix = mathutils.Matrix.Identity(4) - if has_keyframe(obj): + + if has_keyframe(obj, "location"): eval_location = obj.evaluated_get(depsgraph).location + if has_keyframe(obj, "scale"): eval_scale = obj.evaluated_get(depsgraph).scale + if has_keyframe(obj, "rotation_quaternion"): + eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion + elif has_keyframe(obj, "rotation_axis_angle"): + eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle + elif has_keyframe(obj, "rotation_euler"): + eval_rotation = obj.evaluated_get(depsgraph).rotation_euler - match obj.rotation_mode: - case "QUATERNION": - eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion - case "AXIS_ANGLE": - eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle - case _: - eval_rotation = obj.evaluated_get(depsgraph).rotation_euler - - # get evaluated transformation matrix - eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale) - #print("Keyframe Transformations: ", eval_transform_matrix) - print(has_keyframe(obj)) + eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale) + # 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"] + # multiply everything together (with custom transform matrix) obj.matrix_world = rigid_body_transformation @ obj.BSEQ.initial_transform_matrix @ eval_transform_matrix From f6c5ac2ca29a027824480ad04fe82a58ec0acae7 Mon Sep 17 00:00:00 2001 From: justo46 Date: Wed, 29 Mar 2023 16:51:26 +0200 Subject: [PATCH 12/21] Optimized adding meshio objects and added a button to refresh the sequences --- __init__.py | 1 + bseq/__init__.py | 3 ++- bseq/importer.py | 20 ++++++++++++++++++++ bseq/operators.py | 28 +++++++++++++++------------- bseq/panels.py | 6 +++++- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/__init__.py b/__init__.py index 1b3f4ef..fca8a90 100644 --- a/__init__.py +++ b/__init__.py @@ -46,6 +46,7 @@ BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, + BSEQ_OT_refresh_sequences, WM_OT_batchSequences, WM_OT_MeshioObject ] diff --git a/bseq/__init__.py b/bseq/__init__.py index 0aead35..361914c 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, 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, WM_OT_batchSequences, 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 .messenger import subscribe_to_selected, unsubscribe_to_selected @@ -45,6 +45,7 @@ def BSEQ_initialize(scene): "BSEQ_OT_refresh_seq", "BSEQ_OT_disable_all", "BSEQ_OT_enable_all", + "BSEQ_OT_refresh_sequences", "WM_OT_batchSequences", "WM_OT_MeshioObject" ] diff --git a/bseq/importer.py b/bseq/importer.py index f296df4..b1b5a54 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -3,6 +3,7 @@ import meshio import traceback import fileseq +import os from .utils import show_message_box import numpy as np from mathutils import Matrix @@ -174,6 +175,25 @@ def update_mesh(meshio_mesh, mesh): mesh.use_auto_smooth = True mesh.normals_split_custom_set_from_vertices(v) +# function to create a single meshio object +def create_meshio_obj(filepath): + meshio_mesh = None + 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") + + # create the object + name = os.path.basename(filepath) + mesh = bpy.data.meshes.new(name) + object = bpy.data.objects.new(name, mesh) + 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 create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])): diff --git a/bseq/operators.py b/bseq/operators.py index 768b357..3235f82 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -4,7 +4,7 @@ from .messenger import * import traceback from .utils import refresh_obj, show_message_box -from .importer import create_obj +from .importer import create_obj, create_meshio_obj import numpy as np @@ -303,6 +303,18 @@ def execute(self, context): obj.BSEQ.enabled = True return {"FINISHED"} +class BSEQ_OT_refresh_sequences(bpy.types.Operator): + '''This operator refreshes all found sequences''' + bl_label = "" #"Refresh Found Sequences" + bl_idname = "bseq.refreshseqs" + bl_options = {"UNDO"} + + def execute(self, context): + scene = context.scene + # call the update function of path by setting it to its own value + scene.BSEQ.path = scene.BSEQ.path + return {"FINISHED"} + from pathlib import Path import meshio from bpy_extras.io_utils import ImportHelper @@ -327,7 +339,7 @@ def execute(self, context): seqs = fileseq.findSequencesOnDisk(str(folder.parent)) for s in seqs: - if selection.name.startswith(s.basename()) and selection.name.endswith(s.extension()) and s not in used_seqs: + if selection in s and s not in used_seqs: transform_matrix = Matrix.Identity(4) if importer_prop.use_custom_transform: transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) @@ -349,21 +361,11 @@ class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper): files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) def execute(self, context): - scene = context.scene - importer_prop = scene.BSEQ - folder = Path(self.filepath) for selection in self.files: fp = Path(folder.parent, selection.name) - file = fileseq.findSequenceOnDisk(str(fp)) - - transform_matrix = Matrix.Identity(4) - if importer_prop.use_custom_transform: - transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) - - create_obj(file, False, importer_prop.root_path, transform_matrix=transform_matrix) - + create_meshio_obj(str(fp)) return {'FINISHED'} def menu_func_import(self, context): diff --git a/bseq/panels.py b/bseq/panels.py index 504d8da..c35ec7a 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -191,7 +191,11 @@ def draw(self, context): if importer_prop.use_pattern: col2.prop(importer_prop, "pattern", text="") else: - col2.prop(importer_prop, "fileseq", text="") + split2 = col2.split(factor=0.75) + 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="") From c8d12e227462977cfb6f8d8aa1a2440e66ea20c6 Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 14:05:36 +0200 Subject: [PATCH 13/21] Fixed keyframe system for multiple files --- bseq/importer.py | 12 ++++++------ bseq/operators.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index b1b5a54..f37d6a1 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -64,17 +64,18 @@ def has_keyframe(obj, attr): def apply_transformation(meshio_mesh, obj, depsgraph): # evaluate the keyframe animation system eval_transform_matrix = mathutils.Matrix.Identity(4) + + eval_location = obj.evaluated_get(depsgraph).location if has_keyframe(obj, "location") else None + eval_scale = obj.evaluated_get(depsgraph).scale if has_keyframe(obj, "scale") else None - if has_keyframe(obj, "location"): - eval_location = obj.evaluated_get(depsgraph).location - if has_keyframe(obj, "scale"): - eval_scale = obj.evaluated_get(depsgraph).scale if has_keyframe(obj, "rotation_quaternion"): eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion elif has_keyframe(obj, "rotation_axis_angle"): eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle elif has_keyframe(obj, "rotation_euler"): eval_rotation = obj.evaluated_get(depsgraph).rotation_euler + else: + eval_rotation = None eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale) @@ -86,7 +87,6 @@ def apply_transformation(meshio_mesh, obj, depsgraph): # multiply everything together (with custom transform matrix) obj.matrix_world = rigid_body_transformation @ obj.BSEQ.initial_transform_matrix @ eval_transform_matrix - def update_mesh(meshio_mesh, mesh): # extract information from the meshio mesh mesh_vertices = meshio_mesh.points @@ -195,7 +195,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, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])): +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]])): current_frame = bpy.context.scene.frame_current filepath = fileseq[current_frame % len(fileseq)] diff --git a/bseq/operators.py b/bseq/operators.py index 3235f82..bd3d850 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -334,12 +334,12 @@ def execute(self, context): folder = Path(self.filepath) used_seqs = [] for selection in self.files: - fp = Path(folder.parent, selection.name) + fp = str(Path(folder.parent, selection.name)) seqs = fileseq.findSequencesOnDisk(str(folder.parent)) for s in seqs: - if selection in s and s not in used_seqs: + if fp in list(s) and s not in used_seqs: transform_matrix = Matrix.Identity(4) if importer_prop.use_custom_transform: transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) From 7c276a0b3c9211eea9310cbb5162db8992dde095 Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 14:52:22 +0200 Subject: [PATCH 14/21] Deleted install file from repo, since this is a local file. --- blender_install.py | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 blender_install.py diff --git a/blender_install.py b/blender_install.py deleted file mode 100644 index cec511f..0000000 --- a/blender_install.py +++ /dev/null @@ -1,33 +0,0 @@ -import bpy -import addon_utils -from datetime import date -import time -import os -import importlib -from sys import platform - -bpy.ops.script.reload() - -addon_module = [m for m in addon_utils.modules() if m.bl_info.get('name') == "Sequence Loader"] # get module -# uninstall old version -if addon_module: - #addon_utils.disable("blendersequenceloader") - bpy.ops.preferences.addon_disable(module="blendersequenceloader") - bpy.ops.preferences.addon_remove(module="blendersequenceloader") - -fp = '' -today = str(date.today()) -if platform == "linux" or platform == "linux2": - fp = f'/servers/karl/ssd-home2/jstotz/blender-sequence-loader/blender_sequence_loader_{today}.zip' - os.system("python /servers/karl/ssd-home2/jstotz/blender-sequence-loader/build_addon.py") -elif platform == "win32" or platform == "cygwin" or platform == "msys": - fp = f'C:\\code\\work\\blender-sequence-loader\\blender_sequence_loader_{today}.zip' - os.system("python C:\\code\\work\\blender-sequence-loader\\build_addon.py") - -# install new version -bpy.ops.preferences.addon_install(filepath=fp) -time.sleep(2) -bpy.ops.preferences.addon_enable(module="blendersequenceloader") -#addon_utils.enable("blendersequenceloader") -time.sleep(1) -os.system("blender") \ No newline at end of file From 53d9cfd703c7e9f94703af389031ee747fce6909 Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 15:00:40 +0200 Subject: [PATCH 15/21] Resolved further changes --- bseq/operators.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/bseq/operators.py b/bseq/operators.py index dec2131..bd3d850 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -42,15 +42,11 @@ def execute(self, context): show_message_box(traceback.format_exc(), "Can't find sequence: " + str(fs), "ERROR") return {"CANCELLED"} -<<<<<<< HEAD transform_matrix = Matrix.Identity(4) if importer_prop.use_custom_transform: transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) create_obj(fs, importer_prop.relative, importer_prop.root_path, transform_matrix=transform_matrix) -======= - create_obj(fs, importer_prop.relative, importer_prop.root_path) ->>>>>>> upstream/main return {"FINISHED"} @@ -280,7 +276,6 @@ def execute(self, context): scene = context.scene obj = bpy.data.objects[scene.BSEQ.selected_obj_num] refresh_obj(obj, scene) -<<<<<<< HEAD return {"FINISHED"} @@ -313,8 +308,6 @@ class BSEQ_OT_refresh_sequences(bpy.types.Operator): bl_label = "" #"Refresh Found Sequences" bl_idname = "bseq.refreshseqs" bl_options = {"UNDO"} -======= ->>>>>>> upstream/main def execute(self, context): scene = context.scene @@ -322,7 +315,6 @@ def execute(self, context): scene.BSEQ.path = scene.BSEQ.path return {"FINISHED"} -<<<<<<< HEAD from pathlib import Path import meshio from bpy_extras.io_utils import ImportHelper @@ -380,28 +372,3 @@ def menu_func_import(self, context): self.layout.operator( WM_OT_MeshioObject.bl_idname, text="Batch Meshio Object") -======= -class BSEQ_OT_disable_all(bpy.types.Operator): - '''This operator disable all selected sequence''' - bl_label = "Disable All Sequences" - bl_idname = "bseq.disableall" - bl_options = {"UNDO"} - - def execute(self, context): - for obj in bpy.context.scene.collection.all_objects: - if obj.BSEQ.init and obj.BSEQ.enabled: - obj.BSEQ.enabled = False - return {"FINISHED"} - -class BSEQ_OT_enable_all(bpy.types.Operator): - '''This operator enable all selected sequence''' - bl_label = "Enable All Sequences" - bl_idname = "bseq.enableall" - bl_options = {"UNDO"} - - def execute(self, context): - for obj in bpy.context.scene.collection.all_objects: - if obj.BSEQ.init and not obj.BSEQ.enabled: - obj.BSEQ.enabled = True - return {"FINISHED"} ->>>>>>> upstream/main From f38ae77d79d24aa981a7e133b44d4b8b5c5e10d5 Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 15:04:01 +0200 Subject: [PATCH 16/21] Updated version + test to import MeshIO Objects --- bseq/operators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bseq/operators.py b/bseq/operators.py index bd3d850..00f20d1 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -371,4 +371,4 @@ def execute(self, context): def menu_func_import(self, context): self.layout.operator( WM_OT_MeshioObject.bl_idname, - text="Batch Meshio Object") + text="MeshIO Object") From 1903ce34f0d34a3efb36a089cd2405b5b5beb45b Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 15:05:16 +0200 Subject: [PATCH 17/21] Updated version --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index fca8a90..41c8404 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, 4), + "version": (0, 1, 5), "blender": (3, 4, 0), "warning": "", "support": "COMMUNITY", From a7d5e99625fcc5a32065e5d83e7320bf631ad9cc Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 15:40:20 +0200 Subject: [PATCH 18/21] Deleted some comments and made the code easier to read --- __init__.py | 1 - additional_file_formats/bin.py | 37 ++++------------------------------ bseq/importer.py | 2 +- bseq/operators.py | 34 +++++++++++++++---------------- 4 files changed, 21 insertions(+), 53 deletions(-) diff --git a/__init__.py b/__init__.py index 41c8404..3620d39 100644 --- a/__init__.py +++ b/__init__.py @@ -21,7 +21,6 @@ bpy.context.preferences.filepaths.use_relative_paths = False from bseq import * - from bseq.operators import menu_func_import classes = [ diff --git a/additional_file_formats/bin.py b/additional_file_formats/bin.py index 3991bee..8bb97ac 100644 --- a/additional_file_formats/bin.py +++ b/additional_file_formats/bin.py @@ -18,7 +18,6 @@ def readBIN_to_meshio(filepath): # currently assume that numBodies is always 1 (numBodies,), bytes = struct.unpack('i', bytes[:4]), bytes[4:] numBodies *= isFirstFile - #print("numBodies: ", numBodies) field_datas = [] for i in range(0, numBodies): @@ -30,8 +29,6 @@ def readBIN_to_meshio(filepath): if i == 0: objFileString = objFile.decode('ascii') - #print("objFileString: ", objFileString) - cur_field_data = {} cur_field_data["translation"] = None cur_field_data["scaling"] = None @@ -60,8 +57,6 @@ def readBIN_to_meshio(filepath): dirPath = os.path.dirname(filepath) objPath = os.path.join(dirPath, objFileString) - #print("Tried to load object at path: ", objPath) - mesh = meshio.read(objPath) else: otherFile = open(filepath, 'rb') @@ -73,13 +68,6 @@ def readBIN_to_meshio(filepath): # since there is no object referenced, create empty mesh mesh = meshio.Mesh([], []) - #print("Field Data List:", field_datas) - #print() - #print("Bytes left:", len(bytes)) - #print() - - #print(mesh.points) - i = 0 while len(bytes) != 0: cur_field_data = {} @@ -107,17 +95,10 @@ def readBIN_to_meshio(filepath): rotationMatrix[2][0:3] = r[2], r[5], r[8] cur_field_data["rotation"] = rotationMatrix.to_quaternion() - - cur_field_data["transformation_matrix"] = mathutils.Matrix.LocRotScale(cur_field_data["translation"], cur_field_data["rotation"], cur_field_data["scaling"]) - - #print("Translation:") - #print(cur_field_data["translation"]) - #print("Rotation:") - #print(cur_field_data["rotation"]) - #print("Scaling:") - #print(cur_field_data["scaling"]) - #print("Transformation matrix:") - #print(cur_field_data["transformation_matrix"]) + cur_field_data["transformation_matrix"] = mathutils.Matrix.LocRotScale( + cur_field_data["translation"], + cur_field_data["rotation"], + cur_field_data["scaling"]) if isFirstFile: field_datas[i]["translation"] = cur_field_data["translation"] @@ -131,17 +112,7 @@ def readBIN_to_meshio(filepath): mesh.field_data = field_datas[0] - #print("Field data:") - #print(mesh.field_data) - return mesh - #print("-----------------------") - #print(objPath) - #print("-----------------------") - bpy.ops.import_scene.obj(filepath=objPath) - - return meshio.Mesh([], [], field_data=field_data) - # no need for write function meshio.register_format("bin", [".bin"], readBIN_to_meshio, {".bin": None}) \ No newline at end of file diff --git a/bseq/importer.py b/bseq/importer.py index a67b7a7..612f315 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -119,7 +119,6 @@ def update_mesh(meshio_mesh, mesh): faces_loop_start = np.roll(faces_loop_start, 1) faces_loop_start[0] = 0 - if len(mesh.vertices) == n_verts and len(mesh.polygons) == n_poly and len(mesh.loops) == n_loop: pass else: @@ -224,6 +223,7 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object.BSEQ.pattern = str(fileseq) object.BSEQ.init = True object.BSEQ.enabled = enabled + # Flatten custom transformation matrix for the property object.BSEQ.initial_transform_matrix = [transform_matrix[j][i] for i in range(4) for j in range(4)] driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' diff --git a/bseq/operators.py b/bseq/operators.py index 00f20d1..e0184d8 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -42,9 +42,11 @@ def execute(self, context): show_message_box(traceback.format_exc(), "Can't find sequence: " + str(fs), "ERROR") return {"CANCELLED"} - transform_matrix = Matrix.Identity(4) - if importer_prop.use_custom_transform: - transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) + 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.relative, importer_prop.root_path, transform_matrix=transform_matrix) return {"FINISHED"} @@ -332,24 +334,20 @@ def execute(self, context): importer_prop = scene.BSEQ folder = Path(self.filepath) - used_seqs = [] + used_seqs = set() + for selection in self.files: + # Check if there exists a matching file sequence for every selection fp = str(Path(folder.parent, selection.name)) - seqs = fileseq.findSequencesOnDisk(str(folder.parent)) - - for s in seqs: - if fp in list(s) and s not in used_seqs: - transform_matrix = Matrix.Identity(4) - if importer_prop.use_custom_transform: - transform_matrix = Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale) - - create_obj(s, False, importer_prop.root_path, transform_matrix=transform_matrix) - - used_seqs.append(s) - break - # load that specific file sequence (only if it is not already in the scene) - #bpy.ops.sequence.load() + matching_seqs = [s for s in seqs if fp in list(s) and s not in used_seqs] + + if matching_seqs: + s = matching_seqs[0] + 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(s, False, importer_prop.root_path, transform_matrix=transform_matrix) + used_seqs.add(s) return {'FINISHED'} class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper): From d9bebfa9df227db26c6f5ec683d5a83718e45c51 Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 15:48:24 +0200 Subject: [PATCH 19/21] Further code improvements --- additional_file_formats/bin.py | 1 - bseq/importer.py | 4 ---- bseq/operators.py | 5 ++--- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/additional_file_formats/bin.py b/additional_file_formats/bin.py index 8bb97ac..c1caf1b 100644 --- a/additional_file_formats/bin.py +++ b/additional_file_formats/bin.py @@ -109,7 +109,6 @@ def readBIN_to_meshio(filepath): i += 1 - mesh.field_data = field_datas[0] return mesh diff --git a/bseq/importer.py b/bseq/importer.py index 612f315..d5dbc93 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -63,8 +63,6 @@ def has_keyframe(obj, attr): def apply_transformation(meshio_mesh, obj, depsgraph): # evaluate the keyframe animation system - eval_transform_matrix = mathutils.Matrix.Identity(4) - eval_location = obj.evaluated_get(depsgraph).location if has_keyframe(obj, "location") else None eval_scale = obj.evaluated_get(depsgraph).scale if has_keyframe(obj, "scale") else None @@ -94,10 +92,8 @@ def update_mesh(meshio_mesh, mesh): n_poly = 0 n_loop = 0 n_verts = len(mesh_vertices) - if n_verts == 0: return - faces_loop_start = np.array([], dtype=np.uint64) faces_loop_total = np.array([], dtype=np.uint64) loops_vert_idx = np.array([], dtype=np.uint64) diff --git a/bseq/operators.py b/bseq/operators.py index e0184d8..0190329 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -343,11 +343,10 @@ def execute(self, context): matching_seqs = [s for s in seqs if fp in list(s) and s not in used_seqs] if matching_seqs: - s = matching_seqs[0] 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(s, False, importer_prop.root_path, transform_matrix=transform_matrix) - used_seqs.add(s) + create_obj(matching_seqs[0], False, importer_prop.root_path, transform_matrix=transform_matrix) + used_seqs.add(matching_seqs[0]) return {'FINISHED'} class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper): From 0a6e25c0d8a9944f5f4270084ff1689e275ae1f6 Mon Sep 17 00:00:00 2001 From: justo46 Date: Fri, 31 Mar 2023 18:08:27 +0200 Subject: [PATCH 20/21] Corrected version --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 3620d39..cb4bed4 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, 4), "blender": (3, 4, 0), "warning": "", "support": "COMMUNITY", From 40c1b52cd3b53f038e78d31ffd42616a4dde8901 Mon Sep 17 00:00:00 2001 From: justo46 Date: Thu, 20 Apr 2023 12:43:22 +0200 Subject: [PATCH 21/21] Added changes in importer.py to this branch --- bseq/importer.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bseq/importer.py b/bseq/importer.py index d5dbc93..8a7b28d 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -63,8 +63,8 @@ def has_keyframe(obj, attr): def apply_transformation(meshio_mesh, obj, depsgraph): # evaluate the keyframe animation system - eval_location = obj.evaluated_get(depsgraph).location if has_keyframe(obj, "location") else None - eval_scale = obj.evaluated_get(depsgraph).scale if has_keyframe(obj, "scale") else None + eval_location = obj.evaluated_get(depsgraph).location if has_keyframe(obj, "location") else obj.location + eval_scale = obj.evaluated_get(depsgraph).scale if has_keyframe(obj, "scale") else obj.scale if has_keyframe(obj, "rotation_quaternion"): eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion @@ -73,7 +73,7 @@ def apply_transformation(meshio_mesh, obj, depsgraph): elif has_keyframe(obj, "rotation_euler"): eval_rotation = obj.evaluated_get(depsgraph).rotation_euler else: - eval_rotation = None + eval_rotation = obj.rotation_euler eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale) @@ -83,7 +83,7 @@ def apply_transformation(meshio_mesh, obj, depsgraph): rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"] # multiply everything together (with custom transform matrix) - obj.matrix_world = rigid_body_transformation @ obj.BSEQ.initial_transform_matrix @ eval_transform_matrix + obj.matrix_world = rigid_body_transformation @ eval_transform_matrix def update_mesh(meshio_mesh, mesh): # extract information from the meshio mesh @@ -220,7 +220,8 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, object.BSEQ.init = True object.BSEQ.enabled = enabled # Flatten custom transformation matrix for the property - object.BSEQ.initial_transform_matrix = [transform_matrix[j][i] for i in range(4) for j in range(4)] + #object.BSEQ.initial_transform_matrix = [transform_matrix[j][i] for i in range(4) for j in range(4)] + object.matrix_world = transform_matrix driver = object.driver_add("BSEQ.frame") driver.driver.expression = 'frame' if enabled: