From 64e43e500b7ba21973f55d402605f79cb7a18e4f Mon Sep 17 00:00:00 2001 From: Hantao Hui Date: Tue, 12 Jul 2022 14:29:18 +0200 Subject: [PATCH 1/5] restructure to support additional file formats --- additional_file_formats/README.md | 16 ++++++++++++++++ additional_file_formats/__init__.py | 10 ++-------- additional_file_formats/bgeo.py | 3 +++ additional_file_formats/mzd.py | 7 ++++++- simloader/importer.py | 13 +++---------- 5 files changed, 30 insertions(+), 19 deletions(-) create mode 100644 additional_file_formats/README.md diff --git a/additional_file_formats/README.md b/additional_file_formats/README.md new file mode 100644 index 0000000..244b649 --- /dev/null +++ b/additional_file_formats/README.md @@ -0,0 +1,16 @@ +Here you can find an example if you want to import customized file formats. + +1. create an `example.py` +2. implement a function as following +```python +def read_func(filepath): + # open the filepath linked file + # construct the meshio object + return meshio.Mesh +``` +3. call `meshio.register_format("name", [".extenstion"], read_func, {".extenstion": None})`in global environment +4. add `from . import example` in `__init__.py` + +You can check `bgeo.py` as an example, which reads the partiles-only [.bgeo](https://github.com/wdas/partio) file. + +For more details how to construct the `meshio.Mesh` object, you can check [here](https://github.com/nschloe/meshio/wiki/meshio-Mesh()-data-structure) for the details about the data strucutre, and [here](https://github.com/nschloe/meshio/wiki/Node-ordering-in-cells) more details about the vertex ordering. diff --git a/additional_file_formats/__init__.py b/additional_file_formats/__init__.py index ef8048a..4d0b7e7 100644 --- a/additional_file_formats/__init__.py +++ b/additional_file_formats/__init__.py @@ -1,8 +1,2 @@ -from .mzd import readMZD_to_bpymesh, readMZD_to_meshio -from .bgeo import readbgeo_to_meshio - -additional_format_loader = {'bgeo': readbgeo_to_meshio, 'mzd': readMZD_to_meshio} - -__all__ = [ - readMZD_to_bpymesh, readMZD_to_meshio, readbgeo_to_meshio, additional_format_loader -] +from . import bgeo +from . import mzd \ No newline at end of file diff --git a/additional_file_formats/bgeo.py b/additional_file_formats/bgeo.py index b2c08fe..feb05c1 100644 --- a/additional_file_formats/bgeo.py +++ b/additional_file_formats/bgeo.py @@ -105,3 +105,6 @@ def readbgeo_to_meshio(filepath): raise Exception("file didn't end") return meshio.Mesh(position, [('vertex', [])], point_data=point_attributes) + +# no need for write function +meshio.register_format("bgeo", [".bgeo"], readbgeo_to_meshio, {".bgeo": None}) diff --git a/additional_file_formats/mzd.py b/additional_file_formats/mzd.py index 5cb9d54..23a0029 100644 --- a/additional_file_formats/mzd.py +++ b/additional_file_formats/mzd.py @@ -159,6 +159,7 @@ def readMZD_to_meshio(filepath): pass return meshio.Mesh(out_vertPositions.reshape((out_numVertices, 3)), cells, point_data) + def readMZD_to_bpymesh(filepath, mesh): shade_scheme = False if mesh.polygons: @@ -308,4 +309,8 @@ def readMZD_to_bpymesh(filepath, mesh): else: # print(name) file.seek(size, 1) - pass \ No newline at end of file + pass + + +# no need for write function +meshio.register_format("mzd", [".mzd"], readMZD_to_meshio, {".mzd": None}) \ No newline at end of file diff --git a/simloader/importer.py b/simloader/importer.py index 54f13cd..64f3c8e 100644 --- a/simloader/importer.py +++ b/simloader/importer.py @@ -5,6 +5,7 @@ from .utils import show_message_box import numpy as np from mathutils import Matrix +# this import is not useless import additional_file_formats @@ -144,11 +145,7 @@ def create_obj(fileseq, use_relaitve, transform_matrix=Matrix([[1, 0, 0, 0], [0, meshio_mesh = None enabled = True try: - ext = fileseq.extension().split('.')[-1] - if ext in additional_file_formats.additional_format_loader: - meshio_mesh = additional_file_formats.additional_format_loader[ext](filepath) - 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), @@ -225,11 +222,7 @@ def update_obj(scene, depsgraph=None): else: filepath = fs[current_frame % len(fs)] try: - ext = fs.extension().split('.')[-1] - if ext in additional_file_formats.additional_format_loader: - meshio_mesh = additional_file_formats.additional_format_loader[ext](filepath) - 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), From 42e05bc67bf784a781a8f2ddc2ceb76264dfe818 Mon Sep 17 00:00:00 2001 From: Hantao Hui Date: Tue, 12 Jul 2022 15:42:03 +0200 Subject: [PATCH 2/5] update for dim3 template --- template/dim3.py | 34 ++++++++++++++++++++++++++++++++++ template/dim4.py | 22 ---------------------- 2 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 template/dim3.py delete mode 100644 template/dim4.py diff --git a/template/dim3.py b/template/dim3.py new file mode 100644 index 0000000..7537943 --- /dev/null +++ b/template/dim3.py @@ -0,0 +1,34 @@ +# Here is an template to load 3-d mesh +# By default, the addon only renders the surface faces +# The template here will render all the faces, including the fase inside the mesh +import fileseq +import meshio +import numpy as np + + +def preprocess(fileseq: fileseq.FileSequence, frame_number: int) -> meshio.Mesh: + frame_number = frame_number % len(fileseq) + mesh = meshio.read(fileseq[frame_number]) + new_cells = [] + for cell in mesh.cells: + if cell.type == "tetra": + faces = [] + for d in cell.data: + faces.append([d[1], d[0], d[2]]) + faces.append([d[0], d[1], d[3]]) + faces.append([d[0], d[3], d[2]]) + faces.append([d[1], d[2], d[3]]) + new_cells.append(('triangle', np.array(faces, dtype=np.uint64))) + elif cell.type == "hexahedron": + faces = [] + for d in cell.data: + faces.append([d[0], d[3], d[2], d[1]]) + faces.append([d[1], d[2], d[6], d[5]]) + faces.append([d[1], d[5], d[4], d[0]]) + faces.append([d[4], d[5], d[6], d[7]]) + faces.append([d[2], d[3], d[7], d[6]]) + faces.append([d[0], d[4], d[7], d[3]]) + new_cells.append(('quad', np.array(faces, dtype=np.uint64))) + else: + new_cells.append((cell.type, cell.data)) + return meshio.Mesh(mesh.points, new_cells, mesh.point_data) diff --git a/template/dim4.py b/template/dim4.py deleted file mode 100644 index ed00217..0000000 --- a/template/dim4.py +++ /dev/null @@ -1,22 +0,0 @@ -import fileseq -import meshio -import numpy as np - -def preprocess(fileseq: fileseq.FileSequence, frame_number: int) -> meshio.Mesh: - # this renders all the faces(both surface and inside) - # by default, the addon only renders the surface faces - frame_number = frame_number % len(fileseq) - mesh = meshio.read(fileseq[frame_number]) - new_cells = [] - for cell in mesh.cells: - if cell.type=="tetra": - faces = [] - for d in cell.data: - faces.append([d[0],d[1],d[2]]) - faces.append([d[0],d[1],d[3]]) - faces.append([d[0],d[2],d[3]]) - faces.append([d[1],d[2],d[3]]) - new_cells.append(('triangle',np.array(faces, dtype=np.int32))) - else: - new_cells.append((cell.type,cell.data)) - return meshio.Mesh(mesh.points,new_cells,mesh.point_data) \ No newline at end of file From 12dc6187496d784bb5933063f0dc100f7b55222e Mon Sep 17 00:00:00 2001 From: Hantao Hui Date: Wed, 13 Jul 2022 12:11:40 +0200 Subject: [PATCH 3/5] rename simloader to bseq --- __init__.py | 56 +++++++++---------- bseq/__init__.py | 64 +++++++++++++++++++++ {simloader => bseq}/callback.py | 12 ++-- {simloader => bseq}/importer.py | 26 ++++----- {simloader => bseq}/messenger.py | 18 +++--- {simloader => bseq}/operators.py | 92 +++++++++++++++---------------- {simloader => bseq}/panels.py | 72 ++++++++++++------------ {simloader => bseq}/properties.py | 6 +- {simloader => bseq}/utils.py | 2 +- simloader/__init__.py | 64 --------------------- 10 files changed, 206 insertions(+), 206 deletions(-) create mode 100644 bseq/__init__.py rename {simloader => bseq}/callback.py (81%) rename {simloader => bseq}/importer.py (92%) rename {simloader => bseq}/messenger.py (60%) rename {simloader => bseq}/operators.py (78%) rename {simloader => bseq}/panels.py (73%) rename {simloader => bseq}/properties.py (93%) rename {simloader => bseq}/utils.py (94%) delete mode 100644 simloader/__init__.py diff --git a/__init__.py b/__init__.py index 2ebd10a..de20ec2 100644 --- a/__init__.py +++ b/__init__.py @@ -20,53 +20,53 @@ if bpy.context.preferences.filepaths.use_relative_paths == True: bpy.context.preferences.filepaths.use_relative_paths = False -from simloader import * +from bseq import * classes = [ - SIMLOADER_obj_property, - SIMLOADER_scene_property, - SIMLOADER_mesh_property, - SIMLOADER_OT_load, - SIMLOADER_OT_edit, - SIMLOADER_OT_resetpt, - SIMLOADER_OT_resetins, - SIMLOADER_OT_resetmesh, - SIMLOADER_Import, - SIMLOADER_List_Panel, - SIMLOADER_UL_Obj_List, - SIMLOADER_Settings, - SIMLOADER_Templates, - SIMLOADER_UL_Att_List, - SIMLOADER_OT_set_as_split_norm, - SIMLOADER_OT_remove_split_norm, - SIMLOADER_OT_disable_selected, - SIMLOADER_OT_enable_selected, - SIMLOADER_OT_refresh_seq, + BSEQ_obj_property, + BSEQ_scene_property, + BSEQ_mesh_property, + BSEQ_OT_load, + BSEQ_OT_edit, + BSEQ_OT_resetpt, + BSEQ_OT_resetins, + BSEQ_OT_resetmesh, + BSEQ_Import, + BSEQ_List_Panel, + BSEQ_UL_Obj_List, + BSEQ_Settings, + BSEQ_Templates, + BSEQ_UL_Att_List, + BSEQ_OT_set_as_split_norm, + BSEQ_OT_remove_split_norm, + BSEQ_OT_disable_selected, + BSEQ_OT_enable_selected, + BSEQ_OT_refresh_seq, ] def register(): - bpy.app.handlers.load_post.append(SIMLOADER_initialize) + bpy.app.handlers.load_post.append(BSEQ_initialize) for cls in classes: bpy.utils.register_class(cls) bpy.types.TEXT_MT_templates.append(draw_template) - bpy.types.Scene.SIMLOADER = bpy.props.PointerProperty(type=SIMLOADER_scene_property) - bpy.types.Object.SIMLOADER = bpy.props.PointerProperty(type=SIMLOADER_obj_property) - bpy.types.Mesh.SIMLOADER = bpy.props.PointerProperty(type=SIMLOADER_mesh_property) + 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) # manually call this function once # so when addon being installed, it can run correctly # because scene is not used, so pass None into it - SIMLOADER_initialize(None) + BSEQ_initialize(None) def unregister(): for cls in classes: bpy.utils.unregister_class(cls) bpy.types.TEXT_MT_templates.remove(draw_template) - del bpy.types.Scene.SIMLOADER - del bpy.types.Object.SIMLOADER - bpy.app.handlers.load_post.remove(SIMLOADER_initialize) + del bpy.types.Scene.BSEQ + del bpy.types.Object.BSEQ + bpy.app.handlers.load_post.remove(BSEQ_initialize) unsubscribe_to_selected() diff --git a/bseq/__init__.py b/bseq/__init__.py new file mode 100644 index 0000000..abcd742 --- /dev/null +++ b/bseq/__init__.py @@ -0,0 +1,64 @@ +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 .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 +import bpy +from bpy.app.handlers import persistent +from .importer import update_obj +from datetime import datetime + + +def print_information(scene): + if not bpy.context.scene.BSEQ.print: + return + now = datetime.now() + path = bpy.context.scene.render.filepath + path = bpy.path.abspath(path) + filepath = path + '/bseq_' + now.strftime("%Y-%m-%d_%H-%M") + with open(filepath, 'w') as file: + file.write("Render Time: {}\n".format(now.strftime("%Y-%m-%d_%H-%M"))) + file.write("bseq Objects in the scene:\n\n") + for obj in bpy.data.objects: + bseq_prop = obj.BSEQ + 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(bseq_prop.use_relative)) + file.write("\n\n") + + +@persistent +def BSEQ_initialize(scene): + if update_obj not in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.append(update_obj) + subscribe_to_selected() + if print_information not in bpy.app.handlers.render_init: + bpy.app.handlers.render_init.append(print_information) + + +__all__ = [ + "BSEQ_OT_edit", + "BSEQ_OT_load", + "BSEQ_obj_property", + "BSEQ_initialize", + "BSEQ_Import", + "BSEQ_List_Panel", + "BSEQ_UL_Obj_List", + "BSEQ_scene_property", + "BSEQ_Templates", + "BSEQ_Settings", + "BSEQ_UL_Att_List", + "subscribe_to_selected", + "BSEQ_OT_resetpt", + "BSEQ_OT_resetmesh", + "BSEQ_OT_resetins", + "draw_template", + "unsubscribe_to_selected", + "BSEQ_OT_set_as_split_norm", + "BSEQ_mesh_property", + "BSEQ_OT_remove_split_norm", + "BSEQ_OT_disable_selected", + "BSEQ_OT_enable_selected", + "BSEQ_OT_refresh_seq", +] diff --git a/simloader/callback.py b/bseq/callback.py similarity index 81% rename from simloader/callback.py rename to bseq/callback.py index 48a04fc..02b41a9 100644 --- a/simloader/callback.py +++ b/bseq/callback.py @@ -6,9 +6,9 @@ def update_path(self, context): # When the path has been changed, reset the selected sequence to None - context.scene.SIMLOADER['fileseq'] = 1 - context.scene.SIMLOADER.use_pattern = False - context.scene.SIMLOADER.pattern = "" + context.scene.BSEQ['fileseq'] = 1 + context.scene.BSEQ.use_pattern = False + context.scene.BSEQ.pattern = "" def item_fileseq(self, context): @@ -16,7 +16,7 @@ def item_fileseq(self, context): Detects all the file sequences in the directory ''' - p = context.scene.SIMLOADER.path + p = context.scene.BSEQ.path try: f = fileseq.findSequencesOnDisk(p) except: @@ -38,10 +38,10 @@ def item_fileseq(self, context): def update_selected_obj_num(self, context): # Here is when select sequences, then change the corresponding object to active object - index = context.scene.SIMLOADER.selected_obj_num + index = context.scene.BSEQ.selected_obj_num obj = bpy.data.objects[index] - if context.scene.SIMLOADER.selected_obj_deselectall_flag: + if context.scene.BSEQ.selected_obj_deselectall_flag: bpy.ops.object.select_all(action="DESELECT") obj.select_set(True) context.view_layer.objects.active = obj diff --git a/simloader/importer.py b/bseq/importer.py similarity index 92% rename from simloader/importer.py rename to bseq/importer.py index 64f3c8e..972b14b 100644 --- a/simloader/importer.py +++ b/bseq/importer.py @@ -132,7 +132,7 @@ def update_mesh(meshio_mesh, mesh): attribute.data.foreach_set(name_string, v.ravel()) # set as split norm - if mesh.SIMLOADER.split_norm_att_name and mesh.SIMLOADER.split_norm_att_name == k: + 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) @@ -156,13 +156,13 @@ 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.SIMLOADER.use_relative = use_relaitve + object.BSEQ.use_relative = use_relaitve if use_relaitve: - object.SIMLOADER.pattern = bpy.path.relpath(str(fileseq)) + object.BSEQ.pattern = bpy.path.relpath(str(fileseq)) else: - object.SIMLOADER.pattern = str(fileseq) - object.SIMLOADER.init = True - object.SIMLOADER.enabled = enabled + object.BSEQ.pattern = str(fileseq) + object.BSEQ.init = True + object.BSEQ.enabled = enabled object.matrix_world = transform_matrix if enabled: update_mesh(meshio_mesh, object.data) @@ -177,25 +177,25 @@ def update_obj(scene, depsgraph=None): current_frame = bpy.context.scene.frame_current for obj in bpy.data.objects: - if obj.SIMLOADER.init == False: + if obj.BSEQ.init == False: continue - if obj.SIMLOADER.enabled == False: + if obj.BSEQ.enabled == False: continue meshio_mesh = None - pattern = obj.SIMLOADER.pattern - if obj.SIMLOADER.use_relative: + pattern = obj.BSEQ.pattern + if obj.BSEQ.use_relative: 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) - if obj.SIMLOADER.use_advance and obj.SIMLOADER.script_name: - script = bpy.data.texts[obj.SIMLOADER.script_name] + if obj.BSEQ.use_advance and obj.BSEQ.script_name: + script = bpy.data.texts[obj.BSEQ.script_name] try: exec(script.as_string()) except Exception as e: - show_message_box(traceback.format_exc(), "running script: " + obj.SIMLOADER.script_name + " failed: " + str(e), + show_message_box(traceback.format_exc(), "running script: " + obj.BSEQ.script_name + " failed: " + str(e), "ERROR") continue diff --git a/simloader/messenger.py b/bseq/messenger.py similarity index 60% rename from simloader/messenger.py rename to bseq/messenger.py index f9f35a3..e497e74 100644 --- a/simloader/messenger.py +++ b/bseq/messenger.py @@ -7,22 +7,22 @@ def selected_callback(): name = bpy.context.active_object.name idx = bpy.data.objects.find(name) if idx >= 0: - bpy.context.scene.SIMLOADER.selected_obj_deselectall_flag = False - bpy.context.scene.SIMLOADER.selected_obj_num = idx - bpy.context.scene.SIMLOADER.selected_obj_deselectall_flag = True + bpy.context.scene.BSEQ.selected_obj_deselectall_flag = False + bpy.context.scene.BSEQ.selected_obj_num = idx + bpy.context.scene.BSEQ.selected_obj_deselectall_flag = True def subscribe_to_selected(): - import simloader + import bseq # because current implementation may subscribe twice # so clear once to avoid duplication - bpy.msgbus.clear_by_owner(simloader) + bpy.msgbus.clear_by_owner(bseq) bpy.msgbus.subscribe_rna( key=(bpy.types.LayerObjects, 'active'), - # don't know why it needs this owner, so I set owner to this module `simloader` - owner=simloader, + # don't know why it needs this owner, so I set owner to this module `bseq` + owner=bseq, # no args args=(()), notify=selected_callback, @@ -30,5 +30,5 @@ def subscribe_to_selected(): def unsubscribe_to_selected(): - import simloader - bpy.msgbus.clear_by_owner(simloader) + import bseq + bpy.msgbus.clear_by_owner(bseq) diff --git a/simloader/operators.py b/bseq/operators.py similarity index 78% rename from simloader/operators.py rename to bseq/operators.py index 290d4b4..78106e4 100644 --- a/simloader/operators.py +++ b/bseq/operators.py @@ -8,7 +8,7 @@ # Here are load and delete operations -class SIMLOADER_OT_load(bpy.types.Operator): +class BSEQ_OT_load(bpy.types.Operator): '''This operator loads a sequnce''' bl_label = "Load Sequence" bl_idname = "sequence.load" @@ -16,7 +16,7 @@ class SIMLOADER_OT_load(bpy.types.Operator): def execute(self, context): scene = context.scene - importer_prop = scene.SIMLOADER + importer_prop = scene.BSEQ if importer_prop.relative and not bpy.data.is_saved: # use relative but file not saved @@ -45,7 +45,7 @@ def execute(self, context): return {"FINISHED"} -class SIMLOADER_OT_edit(bpy.types.Operator): +class BSEQ_OT_edit(bpy.types.Operator): '''This operator changes a sequnce''' bl_label = "Edit Sequences Path" bl_idname = "sequence.edit" @@ -53,7 +53,7 @@ class SIMLOADER_OT_edit(bpy.types.Operator): def execute(self, context): scene = context.scene - importer_prop = scene.SIMLOADER + importer_prop = scene.BSEQ if importer_prop.relative and not bpy.data.is_saved: # use relative but file not saved @@ -78,27 +78,27 @@ def execute(self, context): show_message_box(traceback.format_exc(), "Can't find sequence: " + str(fs), "ERROR") return {"CANCELLED"} - sim_loader = context.scene.SIMLOADER + sim_loader = context.scene.BSEQ # it seems quite simple task, no need to create a function(for now) if sim_loader.selected_obj_num >= len(bpy.data.objects): return {"CANCELLED"} obj = bpy.data.objects[sim_loader.selected_obj_num] if importer_prop.relative: - obj.SIMLOADER.pattern = bpy.path.relpath(str(fs)) + obj.BSEQ.pattern = bpy.path.relpath(str(fs)) else: - obj.SIMLOADER.pattern = str(fs) - obj.SIMLOADER.use_relative = importer_prop.relative + obj.BSEQ.pattern = str(fs) + obj.BSEQ.use_relative = importer_prop.relative return {"FINISHED"} -class SIMLOADER_OT_resetpt(bpy.types.Operator): +class BSEQ_OT_resetpt(bpy.types.Operator): '''This operator reset the geometry nodes of the sequence as a point cloud''' bl_label = "Reset Geometry Nodes as Point Cloud" - bl_idname = "simloader.resetpt" + bl_idname = "bseq.resetpt" bl_options = {"UNDO"} def execute(self, context): - sim_loader = context.scene.SIMLOADER + sim_loader = context.scene.BSEQ obj = bpy.data.objects[sim_loader.selected_obj_num] warn = False for modifier in obj.modifiers: @@ -107,14 +107,14 @@ def execute(self, context): obj.modifiers.remove(modifier) if warn: show_message_box("Exising geoemtry nodes of {} has been removed".format(obj.name), "Warning") - gn = obj.modifiers.new("SIMLOADER_GeometryNodse", "NODES") + gn = obj.modifiers.new("BSEQ_GeometryNodse", "NODES") # change starting from blender 3.2 # https://developer.blender.org/rB08b4b657b64f if bpy.app.version >= (3,2,0): bpy.ops.node.new_geometry_node_group_assign() gn.node_group.nodes.new('GeometryNodeMeshToPoints') set_material = gn.node_group.nodes.new('GeometryNodeSetMaterial') - set_material.inputs[2].default_value = context.scene.SIMLOADER.material + set_material.inputs[2].default_value = context.scene.BSEQ.material node0 = gn.node_group.nodes[0] node1 = gn.node_group.nodes[1] @@ -126,14 +126,14 @@ def execute(self, context): return {"FINISHED"} -class SIMLOADER_OT_resetmesh(bpy.types.Operator): +class BSEQ_OT_resetmesh(bpy.types.Operator): '''This operator reset the geometry nodes of the sequence as a point cloud''' bl_label = "Reset Geometry Nodes as Mesh" - bl_idname = "simloader.resetmesh" + bl_idname = "bseq.resetmesh" bl_options = {"UNDO"} def execute(self, context): - sim_loader = context.scene.SIMLOADER + sim_loader = context.scene.BSEQ obj = bpy.data.objects[sim_loader.selected_obj_num] warn = False for modifier in obj.modifiers: @@ -142,7 +142,7 @@ def execute(self, context): obj.modifiers.remove(modifier) if warn: show_message_box("Exising geoemtry nodes of {} has been removed".format(obj.name), "Warning") - gn = obj.modifiers.new("SIMLOADER_GeometryNodse", "NODES") + gn = obj.modifiers.new("BSEQ_GeometryNodse", "NODES") # change starting from blender 3.2 # https://developer.blender.org/rB08b4b657b64f if bpy.app.version >= (3,2,0): @@ -151,14 +151,14 @@ def execute(self, context): return {"FINISHED"} -class SIMLOADER_OT_resetins(bpy.types.Operator): +class BSEQ_OT_resetins(bpy.types.Operator): '''This operator reset the geometry nodes of the sequence as a point cloud''' bl_label = "Reset Geometry Nodes as Instances" - bl_idname = "simloader.resetins" + bl_idname = "bseq.resetins" bl_options = {"UNDO"} def execute(self, context): - sim_loader = context.scene.SIMLOADER + sim_loader = context.scene.BSEQ obj = bpy.data.objects[sim_loader.selected_obj_num] warn = False for modifier in obj.modifiers: @@ -167,7 +167,7 @@ def execute(self, context): obj.modifiers.remove(modifier) if warn: show_message_box("Exising geoemtry nodes of {} has been removed".format(obj.name), "Warning") - gn = obj.modifiers.new("SIMLOADER_GeometryNodse", "NODES") + gn = obj.modifiers.new("BSEQ_GeometryNodse", "NODES") # change starting from blender 3.2 # https://developer.blender.org/rB08b4b657b64f if bpy.app.version >= (3,2,0): @@ -187,7 +187,7 @@ def execute(self, context): 0.05, 0.05, ] - set_material.inputs[2].default_value = context.scene.SIMLOADER.material + set_material.inputs[2].default_value = context.scene.BSEQ.material links.new(input_node.outputs[0], instance_on_points.inputs['Points']) links.new(cube.outputs[0], instance_on_points.inputs['Instance']) @@ -199,84 +199,84 @@ def execute(self, context): return {"FINISHED"} -class SIMLOADER_OT_set_as_split_norm(bpy.types.Operator): +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" - bl_idname = "simloader.setsplitnorm" + bl_idname = "bseq.setsplitnorm" bl_options = {"UNDO"} def execute(self, context): - sim_loader = context.scene.SIMLOADER + sim_loader = context.scene.BSEQ obj = bpy.data.objects[sim_loader.selected_obj_num] mesh = obj.data attr_index = sim_loader.selected_attribute_num if attr_index >= len(mesh.attributes): show_message_box("Please select the attribute") return {"CANCELLED"} - mesh.SIMLOADER.split_norm_att_name = mesh.attributes[attr_index].name + mesh.BSEQ.split_norm_att_name = mesh.attributes[attr_index].name return {"FINISHED"} -class SIMLOADER_OT_remove_split_norm(bpy.types.Operator): +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" - bl_idname = "simloader.removesplitnorm" + bl_idname = "bseq.removesplitnorm" bl_options = {"UNDO"} def execute(self, context): - sim_loader = context.scene.SIMLOADER + sim_loader = context.scene.BSEQ obj = bpy.data.objects[sim_loader.selected_obj_num] mesh = obj.data - if mesh.SIMLOADER.split_norm_att_name: - mesh.SIMLOADER.split_norm_att_name = "" + if mesh.BSEQ.split_norm_att_name: + mesh.BSEQ.split_norm_att_name = "" return {"FINISHED"} -class SIMLOADER_OT_disable_selected(bpy.types.Operator): +class BSEQ_OT_disable_selected(bpy.types.Operator): '''This operator disable all selected sequence''' bl_label = "Disable Selected Sequence" - bl_idname = "simloader.disableselected" + bl_idname = "bseq.disableselected" bl_options = {"UNDO"} def execute(self, context): for obj in bpy.context.selected_objects: - if obj.SIMLOADER.init and obj.SIMLOADER.enabled: - obj.SIMLOADER.enabled = False + if obj.BSEQ.init and obj.BSEQ.enabled: + obj.BSEQ.enabled = False return {"FINISHED"} -class SIMLOADER_OT_enable_selected(bpy.types.Operator): +class BSEQ_OT_enable_selected(bpy.types.Operator): '''This operator enable all selected sequence''' bl_label = "Enable Selected Sequence" - bl_idname = "simloader.enableselected" + bl_idname = "bseq.enableselected" bl_options = {"UNDO"} def execute(self, context): for obj in bpy.context.selected_objects: - if obj.SIMLOADER.init and not obj.SIMLOADER.enabled: - obj.SIMLOADER.enabled = True + if obj.BSEQ.init and not obj.BSEQ.enabled: + obj.BSEQ.enabled = True return {"FINISHED"} -class SIMLOADER_OT_refresh_seq(bpy.types.Operator): +class BSEQ_OT_refresh_seq(bpy.types.Operator): '''This operator refresh the sequence''' bl_label = "Refresh Sequence" - bl_idname = "simloader.refresh" + bl_idname = "bseq.refresh" def execute(self, context): scene = context.scene - obj = bpy.data.objects[scene.SIMLOADER.selected_obj_num] + obj = bpy.data.objects[scene.BSEQ.selected_obj_num] - fs = obj.SIMLOADER.pattern - if obj.SIMLOADER.use_relative: + fs = obj.BSEQ.pattern + if obj.BSEQ.use_relative: fs = bpy.path.abspath(fs) fs = fileseq.findSequenceOnDisk(fs) fs = fileseq.findSequenceOnDisk(fs.dirname() + fs.basename() + "@" + fs.extension()) fs = str(fs) - if obj.SIMLOADER.use_relative: + if obj.BSEQ.use_relative: fs = bpy.path.relpath(fs) - obj.SIMLOADER.pattern = fs + obj.BSEQ.pattern = fs return {"FINISHED"} diff --git a/simloader/panels.py b/bseq/panels.py similarity index 73% rename from simloader/panels.py rename to bseq/panels.py index 55b3bdd..b009476 100644 --- a/simloader/panels.py +++ b/bseq/panels.py @@ -2,7 +2,7 @@ import os -class SIMLOADER_UL_Obj_List(bpy.types.UIList): +class BSEQ_UL_Obj_List(bpy.types.UIList): ''' This controls the list of imported sequences. ''' @@ -13,7 +13,7 @@ def filter_items(self, context, data, property): # not sure if I understand correctly about this # see reference from https://docs.blender.org/api/current/bpy.types.UIList.html#advanced-uilist-example-filtering-and-reordering for o in objs: - if o.SIMLOADER.init: + if o.BSEQ.init: flt_flags.append(self.bitflag_filter_item) else: flt_flags.append(0) @@ -24,16 +24,16 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn if item: row = layout.row() row.prop(item, "name", text='Name ', emboss=False) - if item.SIMLOADER.enabled: - row.prop(item.SIMLOADER, "enabled", text = "ENABLED", icon="PLAY") + if item.BSEQ.enabled: + row.prop(item.BSEQ, "enabled", text = "ENABLED", icon="PLAY") else: - row.prop(item.SIMLOADER, "enabled", text = "DISABLED", icon="PAUSE") + row.prop(item.BSEQ, "enabled", text = "DISABLED", icon="PAUSE") else: # actually, I guess this line of code won't be executed? layout.label(text="", translate=False, icon_value=icon) -class SIMLOADER_UL_Att_List(bpy.types.UIList): +class BSEQ_UL_Att_List(bpy.types.UIList): ''' This controls the list of attributes available for this sequence ''' @@ -42,9 +42,9 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn if item: layout.enabled = False layout.prop(item, "name", text='Name ', emboss=False) - obj = bpy.data.objects[context.scene.SIMLOADER.selected_obj_num] + obj = bpy.data.objects[context.scene.BSEQ.selected_obj_num] mesh = obj.data - if mesh.SIMLOADER.split_norm_att_name and mesh.SIMLOADER.split_norm_att_name == item.name: + if mesh.BSEQ.split_norm_att_name and mesh.BSEQ.split_norm_att_name == item.name: layout.label(text="using as split norm") else: @@ -52,12 +52,12 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn layout.label(text="", translate=False, icon_value=icon) -class SIMLOADER_List_Panel(bpy.types.Panel): +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 = "SIMLOADER_PT_list" + bl_idname = "BSEQ_PT_list" bl_space_type = 'VIEW_3D' bl_region_type = "UI" bl_category = "Sequence Loader" @@ -65,23 +65,23 @@ class SIMLOADER_List_Panel(bpy.types.Panel): def draw(self, context): layout = self.layout - sim_loader = context.scene.SIMLOADER + sim_loader = context.scene.BSEQ row = layout.row() - row.template_list("SIMLOADER_UL_Obj_List", "", bpy.data, "objects", sim_loader, "selected_obj_num", rows=2) + row.template_list("BSEQ_UL_Obj_List", "", bpy.data, "objects", sim_loader, "selected_obj_num", rows=2) row = layout.row() - row.operator("simloader.enableselected", text="Enable Selected") - row.operator("simloader.disableselected", text="Disable Selected") + row.operator("bseq.enableselected", text="Enable Selected") + row.operator("bseq.disableselected", text="Disable Selected") row = layout.row() row.operator("sequence.edit", text="Edit Path") - row.operator("simloader.refresh", text="Refresh") + row.operator("bseq.refresh", text="Refresh") -class SIMLOADER_Settings(bpy.types.Panel): +class BSEQ_Settings(bpy.types.Panel): ''' This is the panel of settings of selected sequence ''' bl_label = "Sequence Settings" - bl_idname = "SIMLOADER_PT_settings" + bl_idname = "BSEQ_PT_settings" bl_space_type = 'VIEW_3D' bl_region_type = "UI" bl_category = "Sequence Loader" @@ -90,11 +90,11 @@ class SIMLOADER_Settings(bpy.types.Panel): def draw(self, context): layout = self.layout - sim_loader = context.scene.SIMLOADER + sim_loader = 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.SIMLOADER.init: + if not obj.BSEQ.init: return # path settings @@ -108,9 +108,9 @@ def draw(self, context): col2.enabled = False col1.label(text='Relative') - col2.prop(obj.SIMLOADER, 'use_relative', text="") + col2.prop(obj.BSEQ, 'use_relative', text="") col1.label(text='Pattern') - col2.prop(obj.SIMLOADER, 'pattern', text="") + col2.prop(obj.BSEQ, 'pattern', text="") # geometry nodes settings layout.label(text="Geometry Nodes") @@ -128,18 +128,18 @@ def draw(self, context): col1 = split.column() col2 = split.column() col3 = split.column() - col1.operator('SIMLOADER.resetpt', text="Point Cloud") - col2.operator('SIMLOADER.resetmesh', text="Mesh") - col3.operator('SIMLOADER.resetins', text="Instances") + col1.operator('bseq.resetpt', text="Point Cloud") + col2.operator('bseq.resetmesh', text="Mesh") + col3.operator('bseq.resetins', text="Instances") # attributes settings layout.label(text="Attributes") box = layout.box() row = box.row() - row.template_list("SIMLOADER_UL_Att_List", "", obj.data, "attributes", sim_loader, "selected_attribute_num", rows=2) - box.operator("SIMLOADER.setsplitnorm", text="Set selected as normal") - box.operator("SIMLOADER.removesplitnorm", text="Clear normal") + row.template_list("BSEQ_UL_Att_List", "", obj.data, "attributes", sim_loader, "selected_attribute_num", rows=2) + box.operator("bseq.setsplitnorm", text="Set selected as normal") + box.operator("bseq.removesplitnorm", text="Clear normal") # advance settings layout.label(text="Advanced") @@ -149,18 +149,18 @@ def draw(self, context): col1.alignment = 'RIGHT' col2 = split.column(align=False) col1.label(text="Show Settings") - col2.prop(obj.SIMLOADER, 'use_advance', text="") - if obj.SIMLOADER.use_advance: + col2.prop(obj.BSEQ, 'use_advance', text="") + if obj.BSEQ.use_advance: col1.label(text='Script') - col2.prop_search(obj.SIMLOADER, 'script_name', bpy.data, 'texts', text="") + col2.prop_search(obj.BSEQ, 'script_name', bpy.data, 'texts', text="") -class SIMLOADER_Import(bpy.types.Panel): +class BSEQ_Import(bpy.types.Panel): ''' This is the panel of main addon interface. see images/1.jpg ''' bl_label = "Sequence Loader" - bl_idname = "SIMLOADER_PT_panel" + bl_idname = "BSEQ_PT_panel" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Sequence Loader" @@ -169,7 +169,7 @@ class SIMLOADER_Import(bpy.types.Panel): def draw(self, context): layout = self.layout scene = context.scene - importer_prop = scene.SIMLOADER + importer_prop = scene.BSEQ layout.label(text="Basic Import Settings") box = layout.box() @@ -205,12 +205,12 @@ def draw(self, context): col2.prop(importer_prop, "print", text="") -class SIMLOADER_Templates(bpy.types.Menu): +class BSEQ_Templates(bpy.types.Menu): ''' Here is the template panel, shown in the text editor -> templates ''' bl_label = "Sequence Loader" - bl_idname = "SIMLOADER_MT_template" + bl_idname = "BSEQ_MT_template" def draw(self, context): current_folder = os.path.dirname(os.path.abspath(__file__)) @@ -227,4 +227,4 @@ def draw_template(self, context): Here it function call to integrate template panel into blender template interface ''' layout = self.layout - layout.menu(SIMLOADER_Templates.bl_idname) \ No newline at end of file + layout.menu(BSEQ_Templates.bl_idname) \ No newline at end of file diff --git a/simloader/properties.py b/bseq/properties.py similarity index 93% rename from simloader/properties.py rename to bseq/properties.py index dbf5911..5cfcb9f 100644 --- a/simloader/properties.py +++ b/bseq/properties.py @@ -2,7 +2,7 @@ from .callback import * -class SIMLOADER_scene_property(bpy.types.PropertyGroup): +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\". ", @@ -37,7 +37,7 @@ class SIMLOADER_scene_property(bpy.types.PropertyGroup): default=True) -class SIMLOADER_obj_property(bpy.types.PropertyGroup): +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") @@ -48,5 +48,5 @@ class SIMLOADER_obj_property(bpy.types.PropertyGroup): # set this property for mesh, not object (maybe change later?) -class SIMLOADER_mesh_property(bpy.types.PropertyGroup): +class BSEQ_mesh_property(bpy.types.PropertyGroup): split_norm_att_name: bpy.props.StringProperty(default="") diff --git a/simloader/utils.py b/bseq/utils.py similarity index 94% rename from simloader/utils.py rename to bseq/utils.py index c45027f..7896675 100644 --- a/simloader/utils.py +++ b/bseq/utils.py @@ -13,7 +13,7 @@ def draw(self, context): print("Information: ", title) print(message) - print('End of simloader message box') + print('End of bseq message box') print() stop_animation() bpy.context.window_manager.popup_menu(draw, title=title, icon=icon) diff --git a/simloader/__init__.py b/simloader/__init__.py deleted file mode 100644 index cdf071d..0000000 --- a/simloader/__init__.py +++ /dev/null @@ -1,64 +0,0 @@ -from .operators import SIMLOADER_OT_load, SIMLOADER_OT_edit, SIMLOADER_OT_resetpt, SIMLOADER_OT_resetmesh, SIMLOADER_OT_resetins,SIMLOADER_OT_set_as_split_norm,SIMLOADER_OT_remove_split_norm,SIMLOADER_OT_disable_selected,SIMLOADER_OT_enable_selected,SIMLOADER_OT_refresh_seq -from .properties import SIMLOADER_scene_property, SIMLOADER_obj_property,SIMLOADER_mesh_property -from .panels import SIMLOADER_UL_Obj_List, SIMLOADER_List_Panel, SIMLOADER_Settings, SIMLOADER_Import, SIMLOADER_Templates, SIMLOADER_UL_Att_List, draw_template -from .messenger import subscribe_to_selected, unsubscribe_to_selected -import bpy -from bpy.app.handlers import persistent -from .importer import update_obj -from datetime import datetime - - -def print_information(scene): - if not bpy.context.scene.SIMLOADER.print: - return - now = datetime.now() - path = bpy.context.scene.render.filepath - path = bpy.path.abspath(path) - filepath = path + '/simloader_' + now.strftime("%Y-%m-%d_%H-%M") - with open(filepath, 'w') as file: - file.write("Render Time: {}\n".format(now.strftime("%Y-%m-%d_%H-%M"))) - file.write("Simloader Objects in the scene:\n\n") - for obj in bpy.data.objects: - simloader_prop = obj.SIMLOADER - if simloader_prop.init: - file.write("Object name: {}\n".format(obj.name)) - file.write("Is it being animated: {}\n".format(simloader_prop.enabled)) - file.write("Filepath: {}\n".format(simloader_prop.pattern)) - file.write("Is it relative path: {}\n".format(simloader_prop.use_relative)) - file.write("\n\n") - - -@persistent -def SIMLOADER_initialize(scene): - if update_obj not in bpy.app.handlers.frame_change_post: - bpy.app.handlers.frame_change_post.append(update_obj) - subscribe_to_selected() - if print_information not in bpy.app.handlers.render_init: - bpy.app.handlers.render_init.append(print_information) - - -__all__ = [ - "SIMLOADER_OT_edit", - "SIMLOADER_OT_load", - "SIMLOADER_obj_property", - "SIMLOADER_initialize", - "SIMLOADER_Import", - "SIMLOADER_List_Panel", - "SIMLOADER_UL_Obj_List", - "SIMLOADER_scene_property", - "SIMLOADER_Templates", - "SIMLOADER_Settings", - "SIMLOADER_UL_Att_List", - "subscribe_to_selected", - "SIMLOADER_OT_resetpt", - "SIMLOADER_OT_resetmesh", - "SIMLOADER_OT_resetins", - "draw_template", - "unsubscribe_to_selected", - "SIMLOADER_OT_set_as_split_norm", - "SIMLOADER_mesh_property", - "SIMLOADER_OT_remove_split_norm", - "SIMLOADER_OT_disable_selected", - "SIMLOADER_OT_enable_selected", - "SIMLOADER_OT_refresh_seq", -] From cf0adcbbf03a57cb8b92145889c2086b8fd2c98f Mon Sep 17 00:00:00 2001 From: Hantao Hui Date: Wed, 13 Jul 2022 13:51:01 +0200 Subject: [PATCH 4/5] minor fix --- build_addon.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build_addon.py b/build_addon.py index 93fe126..db5d1b6 100644 --- a/build_addon.py +++ b/build_addon.py @@ -2,14 +2,14 @@ import os from datetime import date - -addondirectory = 'simloader' +addondirectory = 'bseq' templatedirectory = 'template' additionaldirectory = 'additional_file_formats' meshiodirectory = 'extern/meshio/src/meshio' fileseqdirectory = 'extern/fileseq/src/fileseq' futuredirectory = 'extern/python-future/src/future' richdirectory = 'extern/rich/rich' +foldername = 'blendersequenceloader/' dirs = { addondirectory: addondirectory, @@ -32,8 +32,8 @@ filepath = os.path.join(subdir, file) relative_path = os.path.relpath(filepath, k) endpath = os.path.join(v, relative_path) - endpath = os.path.join('simloaderaddon/', endpath) + endpath = os.path.join(foldername, endpath) addonzip.write(filepath, endpath) # write init.py - addonzip.write('__init__.py', 'simloaderaddon/__init__.py') + addonzip.write('__init__.py', foldername + '__init__.py') From 9e344155b63134c2d4982dba2e2554e4534b0ccc Mon Sep 17 00:00:00 2001 From: Hantao Hui Date: Wed, 13 Jul 2022 13:54:45 +0200 Subject: [PATCH 5/5] add notes --- template/dim3.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/template/dim3.py b/template/dim3.py index 7537943..c58ab70 100644 --- a/template/dim3.py +++ b/template/dim3.py @@ -1,6 +1,8 @@ # Here is an template to load 3-d mesh # By default, the addon only renders the surface faces # The template here will render all the faces, including the fase inside the mesh + +# NOTE: this might break the `shade smooth` in blender import fileseq import meshio import numpy as np