diff --git a/.gitignore b/.gitignore index 59a5874..fe9c7f9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ .DS_Store .vscode/ *.zip +*.whl # the following ignores are used to ignore the local softlink files # the extern folder won't be affected by this @@ -10,4 +11,4 @@ meshio future fileseq -docs/_build/* \ No newline at end of file +docs/_build/* diff --git a/__init__.py b/__init__.py index 97535c0..d98c67b 100644 --- a/__init__.py +++ b/__init__.py @@ -1,35 +1,24 @@ -bl_info = { - "name": "Sequence Loader", - "description": "Loader for meshio supported mesh files/ simulation sequences", - "author": "Interactive Computer Graphics", - "version": (0, 3, 2), - "blender": (4, 0, 0), - "warning": "", - "support": "COMMUNITY", - "category": "Import-Export", -} - import bpy import os import sys -current_folder = os.path.dirname(os.path.abspath(__file__)) -if current_folder not in sys.path: - sys.path.append(current_folder) -# add paths of external libraries to sys.path -if os.path.exists(os.path.join(current_folder, "extern")): - external_libs = ["fileseq/src", "meshio/src", "python-future/src", "rich"] - for lib in external_libs: - lib_path = os.path.join(current_folder, "extern", lib) - if lib_path not in sys.path: - sys.path.append(lib_path) +# current_folder = os.path.dirname(os.path.abspath(__file__)) +# if current_folder not in sys.path: +# sys.path.append(current_folder) +# # add paths of external libraries to sys.path +# if os.path.exists(os.path.join(current_folder, "extern")): +# external_libs = ["fileseq/src", "meshio/src", "python-future/src", "rich"] +# for lib in external_libs: +# lib_path = os.path.join(current_folder, "extern", lib) +# if lib_path not in sys.path: +# sys.path.append(lib_path) -if bpy.context.preferences.filepaths.use_relative_paths == True: - bpy.context.preferences.filepaths.use_relative_paths = False +# if bpy.context.preferences.filepaths.use_relative_paths == True: +# bpy.context.preferences.filepaths.use_relative_paths = False -from bseq import * -from bseq.operators import menu_func_import, add_keymap, delete_keymap +from .bseq import * +from .bseq.operators import menu_func_import, add_keymap, delete_keymap classes = [ BSEQ_obj_property, diff --git a/blender_manifest.toml b/blender_manifest.toml new file mode 100644 index 0000000..3867861 --- /dev/null +++ b/blender_manifest.toml @@ -0,0 +1,80 @@ +schema_version = "1.0.0" + +# Example of manifest file for a Blender extension +# Change the values according to your extension +id = "sequence_loader" +version = "0.3.3" +name = "Blender Sequence Loader" +tagline = "Just-in-time loader for meshio-supported mesh file sequences" +maintainer = "Stefan Rhys Jeske " +# Supported types: "add-on", "theme" +type = "add-on" + +# # Optional: link to documentation, support, source files, etc +website = "https://github.com/InteractiveComputerGraphics/blender-sequence-loader" + +# # Optional: tag list defined by Blender and server, see: +# # https://docs.blender.org/manual/en/dev/advanced/extensions/tags.html +tags = ["Animation", "Object"] + +blender_version_min = "4.2.0" +# # Optional: Blender version that the extension does not support, earlier versions are supported. +# # This can be omitted and defined later on the extensions platform if an issue is found. +# blender_version_max = "5.1.0" + +# License conforming to https://spdx.org/licenses/ (use "SPDX: prefix) +# https://docs.blender.org/manual/en/dev/advanced/extensions/licenses.html +license = [ + "SPDX:MIT", +] +# # Optional: required by some licenses. +# copyright = [ +# "2002-2024 Developer Name", +# "1998 Company Name", +# ] + +# # Optional: list of supported platforms. If omitted, the extension will be available in all operating systems. +# platforms = ["windows-x64", "macos-arm64", "linux-x64"] +# # Other supported platforms: "windows-arm64", "macos-x64" + +# # Optional: bundle 3rd party Python modules. +# # https://docs.blender.org/manual/en/dev/advanced/extensions/python_wheels.html +wheels = [ + "./wheels/Fileseq-1.15.2-py3-none-any.whl", + "./wheels/future-0.18.3-py3-none-any.whl", + "./wheels/meshio-5.3.4-py3-none-any.whl", + "./wheels/rich-13.7.0-py3-none-any.whl", +] + +# # Optional: add-ons can list which resources they will require: +# # * files (for access of any filesystem operations) +# # * network (for internet access) +# # * clipboard (to read and/or write the system clipboard) +# # * camera (to capture photos and videos) +# # * microphone (to capture audio) +# # +# # If using network, remember to also check `bpy.app.online_access` +# # https://docs.blender.org/manual/en/dev/advanced/extensions/addons.html#internet-access +# # +# # For each permission it is important to also specify the reason why it is required. +# # Keep this a single short sentence without a period (.) at the end. +# # For longer explanations use the documentation or detail page. +# +[permissions] +files = "Core functionality to load files from disk" + +# # Optional: advanced build settings. +# # https://docs.blender.org/manual/en/dev/advanced/extensions/command_line_arguments.html#command-line-args-extension-build +[build] +# These are the default build excluded patterns. +# You only need to edit them if you want different options. +paths_exclude_pattern = [ + "__pycache__/", + "/.git/", + "/*.zip", + "/extern/", + "/docs/", + "/images/", + "build_addon.py", + "download_wheels.sh" +] \ No newline at end of file diff --git a/bseq/__init__.py b/bseq/__init__.py index 17cb3bf..13ca993 100644 --- a/bseq/__init__.py +++ b/bseq/__init__.py @@ -1,13 +1,14 @@ -from bseq.utils import refresh_obj +from .utils import refresh_obj from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, BSEQ_OT_set_start_end_frames, BSEQ_OT_batch_sequences, BSEQ_PT_batch_sequences_settings, BSEQ_OT_meshio_object, BSEQ_OT_import_zip, BSEQ_OT_delete_zips, BSEQ_addon_preferences, BSEQ_OT_load_all, BSEQ_OT_load_all_recursive from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_PT_Import, BSEQ_PT_Import_Child1, BSEQ_PT_Import_Child2, BSEQ_Globals_Panel, BSEQ_Advanced_Panel, BSEQ_Templates, BSEQ_UL_Att_List, draw_template from .messenger import subscribe_to_selected, unsubscribe_to_selected -import bpy -from bpy.app.handlers import persistent from .importer import update_obj from .globals import * +import bpy +from bpy.app.handlers import persistent + @persistent def BSEQ_initialize(scene): diff --git a/additional_file_formats/README.md b/bseq/additional_file_formats/README.md similarity index 100% rename from additional_file_formats/README.md rename to bseq/additional_file_formats/README.md diff --git a/additional_file_formats/__init__.py b/bseq/additional_file_formats/__init__.py similarity index 100% rename from additional_file_formats/__init__.py rename to bseq/additional_file_formats/__init__.py diff --git a/additional_file_formats/bgeo.py b/bseq/additional_file_formats/bgeo.py similarity index 100% rename from additional_file_formats/bgeo.py rename to bseq/additional_file_formats/bgeo.py diff --git a/additional_file_formats/mzd.py b/bseq/additional_file_formats/mzd.py similarity index 100% rename from additional_file_formats/mzd.py rename to bseq/additional_file_formats/mzd.py diff --git a/additional_file_formats/obj.py b/bseq/additional_file_formats/obj.py similarity index 100% rename from additional_file_formats/obj.py rename to bseq/additional_file_formats/obj.py diff --git a/additional_file_formats/table.py b/bseq/additional_file_formats/table.py similarity index 100% rename from additional_file_formats/table.py rename to bseq/additional_file_formats/table.py diff --git a/bseq/importer.py b/bseq/importer.py index 9c67c52..73b1d01 100644 --- a/bseq/importer.py +++ b/bseq/importer.py @@ -9,7 +9,7 @@ from mathutils import Matrix import time # this import is not useless -import additional_file_formats +from .additional_file_formats import * def extract_edges(cell: meshio.CellBlock): if cell.type == "line": diff --git a/bseq/messenger.py b/bseq/messenger.py index 0409779..25cceab 100644 --- a/bseq/messenger.py +++ b/bseq/messenger.py @@ -20,7 +20,8 @@ def selected_callback(): bpy.context.scene.BSEQ.edit_obj = bpy.context.active_object def subscribe_to_selected(): - import bseq + # import bseq + bseq = __loader__ # because current implementation may subscribe twice # so clear once to avoid duplication @@ -37,5 +38,6 @@ def subscribe_to_selected(): def unsubscribe_to_selected(): - import bseq + # import bseq + bseq = __loader__ bpy.msgbus.clear_by_owner(bseq) diff --git a/bseq/operators.py b/bseq/operators.py index 525f407..5028048 100644 --- a/bseq/operators.py +++ b/bseq/operators.py @@ -440,7 +440,7 @@ def draw(self, context): # layout.prop(importer_prop, "root_path", text="Root Directory") class BSEQ_addon_preferences(bpy.types.AddonPreferences): - bl_idname = addon_name + bl_idname = __package__ zips_folder: bpy.props.StringProperty( name="Zips Folder", @@ -556,6 +556,9 @@ def execute(self, context): return relative_path_error() root_dir = importer_prop.path + root_coll = bpy.context.scene.collection + root_layer_collection = bpy.context.view_layer.layer_collection + unlinked_collections = [] # Recurse through subdirectories for root, dirs, files in os.walk(bpy.path.abspath(root_dir)): for dir in sorted(dirs): @@ -570,14 +573,33 @@ def execute(self, context): coll_list = bpy.path.relpath(subdirectory, start=root_dir).strip("//").split("/") # Get or create a nested collection starting from the root - last_coll = bpy.context.scene.collection - layer_collection = bpy.context.view_layer.layer_collection + last_coll = root_coll + layer_collection = root_layer_collection for coll in coll_list: - cur_coll = bpy.data.collections.get(coll) if bpy.data.collections.get(coll) is not None else bpy.data.collections.new(coll) - if last_coll is not None and cur_coll.name not in last_coll.children: + # If it already exists and is not in the children of the last collection, then the prefix has changed + cur_coll = bpy.data.collections.get(coll) + if cur_coll is not None and last_coll is not None: + if cur_coll.name not in last_coll.children: + # Get the old parent of the existing collection and move the children to the old parent + parent = [c for c in bpy.data.collections if bpy.context.scene.user_of_id(cur_coll) and cur_coll.name in c.children] + if len(parent) > 0: + for child in cur_coll.children: + parent[0].children.link(child) + for obj in cur_coll.objects: + parent[0].objects.link(obj) + parent[0].children.unlink(cur_coll) + unlinked_collections.append(cur_coll) + else: + layer_collection = layer_collection.children[cur_coll.name] + last_coll = cur_coll + + + # If it was newly created, link it to the last collection + if cur_coll is None and last_coll is not None: + cur_coll = bpy.data.collections.new(coll) last_coll.children.link(cur_coll) - layer_collection = layer_collection.children[cur_coll.name] - last_coll = cur_coll + layer_collection = layer_collection.children[cur_coll.name] + last_coll = cur_coll # Set the last collection as the active collection by recursing through the collections context.view_layer.active_layer_collection = layer_collection @@ -587,6 +609,10 @@ def execute(self, context): for s in seqs: create_obj_wrapper(s, importer_prop) + + # Make sure unused datablocks are freed + for coll in unlinked_collections: + bpy.data.collections.remove(coll) return {'FINISHED'} diff --git a/bseq/panels.py b/bseq/panels.py index be1c142..5c9484e 100644 --- a/bseq/panels.py +++ b/bseq/panels.py @@ -241,6 +241,11 @@ def draw(self, context): col1.label(text="Relative Paths") col2.prop(importer_prop, "use_relative", text="") + if importer_prop.use_relative: + col1.label(text="Relative Root") + col2.prop(importer_prop, "root_path", text="") + + col1.label(text="Import Normals") col2.prop(importer_prop, "use_imported_normals", text="") diff --git a/docs/conf.py b/docs/conf.py index bf923be..d81a6a8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,7 +9,7 @@ project = 'blender-sequence-loader' copyright = '2024, InteractiveComputerGraphics' author = 'InteractiveComputerGraphics' -release = '0.3.2' +release = '0.3.3' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/download_wheels.sh b/download_wheels.sh new file mode 100755 index 0000000..1abd6b0 --- /dev/null +++ b/download_wheels.sh @@ -0,0 +1,4 @@ +pip wheel fileseq==1.15.2 -w ./wheels --no-deps +pip wheel meshio==5.3.4 -w ./wheels --no-deps +pip wheel future==0.18.3 -w ./wheels --no-deps +pip wheel rich==13.7.0 -w ./wheels --no-deps \ No newline at end of file