diff --git a/README.md b/README.md new file mode 100644 index 0000000..244f698 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Asset IO +Asset IO is a Blender addon that gives an interface to the [Blib library](https://github.com/LucaRood/Blib). + +This addon is not supposed to be an advanced asset library manager, but rather a simple import/export interface, to allow preliminary usage of the Blib asset file standard. It is expected that more asset managers in Blender will use the Blib standard in the future, but until then, this simple solution is provided. + +For the reasons stated above, this addon's development will be limited to potential bug fixes and minor features at most. On the other hand, the Blib library itself, will see continued development and feature additions. + +If you find yourself interested in developing more advanced interfaces using the Blib standard, you may refer to [contributing to Blib](https://github.com/LucaRood/Blib#contributing), for instructions, usage examples, and the full API specification. + +**Warning:** The Blib library is currently in beta stage, and thus might contain bugs. Furthermore, breaking changes may occur until the consolidating 1.0.0 release, thus files exported with the current version, might not be importable by future releases. (Though they should be importable by the version they were exported with, so you could import them back into Blender, and re-export them with the newer version) + +* [Getting Asset IO](#getting-asset-io) +* [Using Asset IO](#using-asset-io) + * [Import](#import) + * [Export](#export) +* [Reporting issues](#reporting-issues) + +## Getting Asset IO +Download file entitled **Asset_IO.zip** from the [latest release](../../releases/latest) page. (Do not download the other .zip or .tar.gz provided by GitHub) + +To install Asset IO, you can just follow the standard addon installation process in Blender, detailed here: +0. In Blender, go to **File > User Preferences...** +0. Under the **Add-ons** tab, click **Install from File...** and find the downloaded .zip +0. Type "Asset IO" in the search bar at the top left corner of the settings window +0. Click the check-box next to the addon name, to activate it +0. Optionally, click **Save User Settings**, so you don't have to activate it every time you open Blender + +## Using Asset IO +### Import +You can access the Asset IO importer, at **File > Import > Blib Assets (.blib)**. +Then, in the import options, you can choose what type of asset you want to import, and all import options for that type will appear. +Now, you just set the desired options (defaults are fine in most cases), and select the files to import, and you're set. + +### Export +You can access the Asset IO exporter, at **File > Export > Blib Assets (.blib)**. +Then, in the export options, you can choose what type of asset you want to export, and a list with all exportable assets of that type will appear, wherein you can select all the assets to be exported. +Now, you just set the desired export options below the asset list (defaults are fine in most cases), and you're set. + +## Reporting issues +If you encounter a bug or issue with Blib, before reporting it make sure you are using the latest released version of Asset IO (you can check the latest version on the [latest release page](../../releases/latest)), and Blender version 2.76 or later, as no previous versions are officially supported. + +Once you verified all of the above, you can head to the [issues page](../../issues), and click the "New issue" button. +Start by giving it a short and descriptive title. Then you write your report in the following format, replacing the fields with the appropriate info: +``` +* **Platform:** Linux/OSX/Windows +* **Blender version:** 2.76 +* **Steps to reproduce:** + 0. Do this + 0. Do that + 0. Do this other thing +* **Expected behavior:** This is what should actually happen. + +If you feel like the title and the above info are not enough to describe the issue, +here you can write a more thorough description of the issue. +``` +Note that the list of steps to reproduce, should be numbered with zeroes ("0"), and will automatically be replaced by the correct numbers. \ No newline at end of file diff --git a/__init__.py b/__init__.py index 5ac5c0b..1802b69 100644 --- a/__init__.py +++ b/__init__.py @@ -20,11 +20,11 @@ bl_info = { "name": "Asset IO", - "description": "Import and export blender assets (.blib)", + "description": "Import and export Blender assets (.blib)", "author": "Luca Rood", "version": (0, 1, 0), "blender": (2, 76, 0), - "location": "File > Import-Export > Export Assets (.blib)", + "location": "File > Import-Export > Blib Assets (.blib)", "warning": "Beta version, might contain bugs.", "category": "Import-Export" } @@ -52,52 +52,55 @@ else: from blib.exceptions import BlibException -class ExportBlib(bpy.types.Operator, ExportHelper): - bl_idname = "blib.export" - bl_label = "Blib Assets (.blib)" - bl_description = "Save assets to .blib files" +def uniquify_name(filepath): + num = 1 + newpath = filepath + parts = path.splitext(filepath) + while path.isfile(newpath): + newpath = parts[0] + str(num) + parts[1] + num += 1 + return newpath - filename_ext = ".blib" - filter_glob = StringProperty(default="*.blib", options={'HIDDEN'}) - directory = StringProperty(options={'HIDDEN'}) - filepath = StringProperty() - filename = StringProperty(default="untitled") +class ExportConfirmation(bpy.types.Operator): + bl_idname = "blib.export_confirm" + bl_label = "Some files to be exported already exist" + bl_description = "Select what to do with duplicate files" - asset_index = IntProperty(default=0) + directory = StringProperty() + filename = StringProperty() + + ### All the code commented out in this class is not working because of a Blender bug, and shall be reactivated once fixed ### + # dup_assets = CollectionProperty(type=AssetItem) + # asset_index = IntProperty(default=0) def draw(self, context): - asset_type = context.scene.blib.export_type - asset = getattr(context.scene.blib.assets, asset_type) layout = self.layout - layout.prop(context.scene.blib, "export_type") - - asset_info = context.scene.blib.asset_types[asset_type] + layout.label("Action to be taken:") + layout.prop(context.scene.blib, "action", expand=True) - if asset_info["list_type"] is not None: - layout.template_list(asset_info["list_type"], "", asset, "assets", self, "asset_index") - row = layout.row(align=True) - row.operator("blib.sel_all").asset_type = asset_type - row.operator("blib.sel_none").asset_type = asset_type - - for prop in asset_info["exp_props"]: - layout.prop(asset.export_props, prop) + # layout.template_list(asset_info["list_type"], "", self, "dup_assets", self, "asset_index") def invoke(self, context, event): - self.filepath = "untitled.blib" + asset_type = context.scene.blib.export_type + asset = getattr(context.scene.blib.assets, asset_type) + assets = asset.assets - for a_type, asset_info in context.scene.blib.asset_types.items(): - data = getattr(bpy.data, asset_info["data"]) - assets = getattr(context.scene.blib.assets, a_type).assets - assets.clear() - - for item in data: - if asset_info["check_asset_func"](item): - asset = assets.add() - asset.name = item.name + for asset in assets: + if asset.state == True: + filepath = path.join(self.directory, "{}_{}.blib".format(path.splitext(self.filename)[0], asset.name)) + if path.isfile(filepath): + return context.window_manager.invoke_props_dialog(self) - context.window_manager.fileselect_add(self) - return {'RUNNING_MODAL'} + # for asset in assets: + # if asset.state == True: + # filepath = path.join(self.directory, "{}_{}.blib".format(path.splitext(self.filename)[0], asset.name)) + # if path.isfile(filepath): + # dup_asset = self.dup_assets.add() + # dup_asset.name = asset.name + # dup_asset.state = True + + return self.execute(context) def execute(self, context): asset_type = context.scene.blib.export_type @@ -115,6 +118,12 @@ def execute(self, context): for asset in assets: if asset.state == True: filepath = path.join(self.directory, "{}_{}.blib".format(path.splitext(self.filename)[0], asset.name)) + if context.scene.blib.action == "rename": + filepath = uniquify_name(filepath) + elif context.scene.blib.action == "ignore": + if path.isfile(filepath): + continue + print() print("Initiating export of '{}'".format(asset.name)) try: @@ -133,14 +142,64 @@ def execute(self, context): print("{} of {} successful exports.".format(success, success + failed)) return {'FINISHED'} +class ExportBlib(bpy.types.Operator, ExportHelper): + bl_idname = "blib.export" + bl_label = "Export BLIB" + bl_description = "Save assets to .blib files" + + filename_ext = ".blib" + filter_glob = StringProperty(default="*.blib") + directory = StringProperty() + filepath = StringProperty() + filename = StringProperty(default="untitled") + + asset_index = IntProperty(default=0) + + def draw(self, context): + asset_type = context.scene.blib.export_type + asset = getattr(context.scene.blib.assets, asset_type) + layout = self.layout + + layout.prop(context.scene.blib, "export_type") + + asset_info = context.scene.blib.asset_types[asset_type] + + if asset_info["list_type"] is not None: + layout.template_list(asset_info["list_type"], "", asset, "assets", self, "asset_index") + row = layout.row(align=True) + row.operator("blib.sel_all").asset_type = asset_type + row.operator("blib.sel_none").asset_type = asset_type + + for prop in asset_info["exp_props"]: + layout.prop(asset.export_props, prop) + + def invoke(self, context, event): + self.filepath = "untitled.blib" + + for a_type, asset_info in context.scene.blib.asset_types.items(): + data = getattr(bpy.data, asset_info["data"]) + assets = getattr(context.scene.blib.assets, a_type).assets + assets.clear() + + for item in data: + if asset_info["check_asset_func"](item): + asset = assets.add() + asset.name = item.name + + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + return bpy.ops.blib.export_confirm('INVOKE_DEFAULT', directory=self.directory, filename=self.filename) + class ImportBlib(bpy.types.Operator, ImportHelper): bl_idname = "blib.import" - bl_label = "Blib Assets (.blib)" + bl_label = "Import BLIB" bl_description = "Load assets from .blib files" filename_ext = ".blib" - filter_glob = StringProperty(default="*.blib", options={'HIDDEN'}) - directory = StringProperty(options={'HIDDEN'}) + filter_glob = StringProperty(default="*.blib") + directory = StringProperty() files = CollectionProperty(type=bpy.types.PropertyGroup) def draw(self, context): @@ -208,10 +267,10 @@ def execute(self, context): return {'FINISHED'} def menu_func_export(self, context): - self.layout.operator(ExportBlib.bl_idname) + self.layout.operator(ExportBlib.bl_idname, text="Blib Assets (.blib)") def menu_func_import(self, context): - self.layout.operator(ImportBlib.bl_idname) + self.layout.operator(ImportBlib.bl_idname, text="Blib Assets (.blib)") def register(): #Generic asset item @@ -230,6 +289,7 @@ def register(): #Operator registration bpy.utils.register_class(ExportBlib) bpy.utils.register_class(ImportBlib) + bpy.utils.register_class(ExportConfirmation) bpy.utils.register_class(BLIB_OT_select_all) bpy.utils.register_class(BLIB_OT_select_none) @@ -260,5 +320,6 @@ def unregister(): #Operator unregistration bpy.utils.unregister_class(ExportBlib) bpy.utils.unregister_class(ImportBlib) + bpy.utils.unregister_class(ExportConfirmation) bpy.utils.unregister_class(BLIB_OT_select_all) bpy.utils.unregister_class(BLIB_OT_select_none) diff --git a/blib/cycles/bimport.py b/blib/cycles/bimport.py index 06b6144..02bfae0 100644 --- a/blib/cycles/bimport.py +++ b/blib/cycles/bimport.py @@ -122,7 +122,7 @@ def set_attributes(asset, xelement, failed): fail(failed, "attributes", "set attribute '{}' on object '{}'".format(attr, asset.name)) def make_sockets(tree, inp, out, xinpn, xoutn): - types = ('VALUE', 'INT', 'BOOLEAN', 'VECTOR', 'STRING', 'RGBA', 'SHADER') + types = ['VALUE', 'INT', 'BOOLEAN', 'VECTOR', 'STRING', 'RGBA', 'SHADER'] routes = {} outs = {} @@ -143,9 +143,9 @@ def make_sockets(tree, inp, out, xinpn, xoutn): xouts = xinpn.find("outputs") if xouts is not None: for i, xout in enumerate(xouts): - tree.links.new(inp.outputs[i + 7], routes[xout.attrib["type"]].inputs[0]) + tree.links.new(inp.outputs[i + len(types)], routes[xout.attrib["type"]].inputs[0]) tree.links.new(outs[xout.attrib["type"]], routes[xout.attrib["type"]].inputs[0]) - tree.inputs[i + 7].name = xout.attrib["name"] + tree.inputs[i + len(types)].name = xout.attrib["name"] if xoutn is not None: xinps = xoutn.find("inputs") diff --git a/blib/cycles/utils.py b/blib/cycles/utils.py index f57f3a1..bfa5996 100644 --- a/blib/cycles/utils.py +++ b/blib/cycles/utils.py @@ -120,6 +120,19 @@ def get_sub_type(f_path): return None def check_file(f_path, sub=None): + """ + Check if file is a 'cycles' type blib file. + Optionally check if file is of a specific subtype. + + Args: + f_path (str): Path to the file to be checked. + sub (str or None): If a str is provided, it should be the subtype to check against, + if None is given, no subtype check is performed. + + Returns: + bool: True if the file is of type 'cycles', and if it matches the optional subtype. + """ + if get_file_type(f_path) == "cycles": if sub is not None: return get_sub_type(f_path) == sub diff --git a/blib/cycles/version.py b/blib/cycles/version.py index 79489f4..108a17e 100644 --- a/blib/cycles/version.py +++ b/blib/cycles/version.py @@ -29,5 +29,5 @@ from ..utils import Version -version = Version("0.1.4", "beta") +version = Version("0.1.5", "beta") compatible = Version("0.1.2", "beta") diff --git a/blib/version.py b/blib/version.py index c022209..b3ac5b6 100644 --- a/blib/version.py +++ b/blib/version.py @@ -28,4 +28,4 @@ from .utils import Version -version = Version("0.1.4", "beta") +version = Version("0.1.5", "beta") diff --git a/props.py b/props.py index 801a16a..6f81e19 100644 --- a/props.py +++ b/props.py @@ -52,8 +52,9 @@ class AssetItem(bpy.types.PropertyGroup): name = StringProperty() state = BoolProperty( - description="Toggle export", - default=False) + description="Toggle export", + default=False + ) ### PER ASSET TYPE EXPORT/IMPORT PROPERTIES ### @@ -114,43 +115,43 @@ class CyclesImportProps(bpy.types.PropertyGroup): name="Import packed images", description="Import images/textures that were packed in the .blend file", default=True - ) + ) imge_import = BoolProperty( name="Import external images", description="Import images/textures that were separately saved on disc", default=True - ) + ) seq_import = BoolProperty( name="Import image sequences", description="Import image sequences", default=True - ) + ) mov_import = BoolProperty( name="Import movies", description="Import movies", default=True - ) + ) txti_import = BoolProperty( name="Import packed texts", description="Import text blocks that were packed in the .blend file", default=True - ) + ) txte_import = BoolProperty( name="Import external texts", description="Import text blocks that were separately saved on disc", default=True - ) + ) script_import = BoolProperty( name="Import external scripts", description='Import scripts that are only referenced by their path in a "script" node', default=True - ) + ) img_embed = EnumProperty( name="Pack Images", @@ -158,9 +159,9 @@ class CyclesImportProps(bpy.types.PropertyGroup): ("True", "Yes", "Pack all images into .blend file"), ("False", "No", "Store all images externally"), ("None", "Auto", "Maintain setup from exported material") - ), + ), default="False" - ) + ) txt_embed = EnumProperty( name="Pack Texts", @@ -168,28 +169,28 @@ class CyclesImportProps(bpy.types.PropertyGroup): ("True", "Yes", "Pack all texts into .blend file"), ("False", "No", "Store all texts externally"), ("None", "Auto", "Maintain setup from exported material") - ), + ), default="None" - ) + ) img_merge = BoolProperty( name="Reuse existing images", description="For duplicate images, use those already in your resource library instead of creating new a instance", default=True - ) + ) resource_path = StringProperty( name="Resource path", description="Directory to save external resources (images, texts...)", default=gen_resource_path(), subtype='DIR_PATH' - ) + ) skip_sha1 = BoolProperty( name="Skip checksum", description="Skip file corruption verification (only use if you manually edited the file, and know what you're doing)", default=False - ) + ) ### ASSET TYPE CONTAINERS ### @@ -224,6 +225,16 @@ class BlibThings(bpy.types.PropertyGroup): ("cycles_grp", "Cycles Node Group", "Node groups for Cycles renderer materials")] ) + action = EnumProperty( + name = "Action to be taken", + items = ( + ("replace", "Replace", "Replace existing files"), + ("rename", "Keep Both", "Append number at the end of file names"), + ("ignore", "Ignore", "Only export those files that don't yet exist") + ), + default = "replace" + ) + assets = PointerProperty(type=AssetList) asset_types = {