# SPDX-FileCopyrightText: 2025 Blender Studio Tools Authors # # SPDX-License-Identifier: GPL-3.0-or-later import bpy from typing import List from . import constants from . import config from pathlib import Path from .prefs import get_addon_prefs from .asset_catalog import get_asset_catalog_items, get_asset_name, get_asset_id """ NOTE Items in these properties groups should be generated by a function that finds the avaliable task layers from the task_layer.json file that needs to be created. """ def get_safely_string_prop(self, name: str) -> str: """Return Value of String Property, and return "" if value isn't set""" try: return self[name] except KeyError: return "" def get_task_layer_presets(self, context): prefs = get_addon_prefs() user_tls = Path(prefs.custom_task_layers_dir) presets_dir = config.get_task_layer_presets_path() items = [] for file in presets_dir.glob('*.json'): items.append((file.__str__(), file.name.replace(".json", ""), file.name)) if user_tls.exists() and user_tls.is_dir(): for file in user_tls.glob('*.json'): items.append((file.__str__(), file.name.replace(".json", ""), file.name)) return items class AssetTransferData(bpy.types.PropertyGroup): """Properties to track transferable data on an object""" owner: bpy.props.StringProperty(name="Owner", default="NONE") type: bpy.props.EnumProperty( name="Transferable Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) surrender: bpy.props.BoolProperty(name="Surrender Ownership", default=False) @property def obj_name(self): return self.id_data.name class AssetTransferDataTemp(bpy.types.PropertyGroup): """Class used when finding new ownership data so it can be drawn with the same method as the existing ownership data from ASSET_TRANSFER_DATA""" owner: bpy.props.StringProperty(name="Owner", default="NONE") type: bpy.props.EnumProperty( name="Transferable Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) surrender: bpy.props.BoolProperty(name="Surrender Ownership", default=False) obj_name: bpy.props.StringProperty(name="Object Name", default="") class TaskLayerSettings(bpy.types.PropertyGroup): is_local: bpy.props.BoolProperty(name="Task Layer is Local", default=False) class AssetPipeline(bpy.types.PropertyGroup): """Properties to manage the status of asset pipeline files""" is_asset_pipeline_file: bpy.props.BoolProperty( name="Asset Pipeline File", description="Asset Pipeline Files are used in the asset pipeline, if file is not asset pipeline file user will be prompted to create a new asset", default=False, ) is_depreciated: bpy.props.BoolProperty( name="Depreciated", description="Depreciated files do not recieve any updates when syncing from a task layer", default=False, ) def get_is_published(self): return bool(Path(bpy.data.filepath).parent.name in constants.PUBLISH_KEYS) is_published: bpy.props.BoolProperty( name="Is Published", description="File is Published", get=lambda self: Path(bpy.data.filepath).parent.name in constants.PUBLISH_KEYS, ) @property def asset_collection(self): return bpy.data.collections.get(self.asset_collection_name) or bpy.data.collections.get( self.asset_collection_name + "." + constants.LOCAL_SUFFIX ) @asset_collection.setter def asset_collection(self, coll): self.asset_collection_name = coll.name asset_collection_name: bpy.props.StringProperty( name="Asset", default="", description="Top Level Collection of the Asset, all other collections of the asset will be children of this collection", ) # Commented out - Let's use a weak ref for now because this causes the collection to evaluate even when hidden, causing performance nightmares # asset_collection: bpy.props.PointerProperty( # type=bpy.types.Collection, # name="Asset", # description="Top Level Collection of the Asset, all other collections of the # asset will be children of this collection", # ) temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) def add_temp_transfer_data(self, name, owner, type, obj_name, surrender) -> 'AssetTransferDataTemp': new_transfer_data = self.temp_transfer_data transfer_data_item = new_transfer_data.add() transfer_data_item.name = name transfer_data_item.owner = owner transfer_data_item.type = type transfer_data_item.obj_name = obj_name transfer_data_item.surrender = surrender return transfer_data_item ## NEW FILE new_file_mode: bpy.props.EnumProperty( name="New File Mode", items=( ('KEEP', "Current File", "Setup the Existing File/Directory as an Asset"), ('BLANK', "Blank File", "Create a New Blank Asset in a New Directory"), ), ) dir: bpy.props.StringProperty( name="Directory", description="Target Path for new asset files", subtype="DIR_PATH", ) name: bpy.props.StringProperty(name="Name", description="Name for new Asset") prefix: bpy.props.StringProperty(name="Prefix", description="Prefix for new Asset", default="") task_layer_config_type: bpy.props.EnumProperty( name="Task Layer Preset", items=get_task_layer_presets, ) # type: ignore temp_file: bpy.props.StringProperty(name="Pre-Sync Backup") source_file: bpy.props.StringProperty(name="File that started Sync") sync_error: bpy.props.BoolProperty(name="Sync Error", default=False) all_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) local_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) def set_local_task_layers(self, task_layer_keys: List[str]): # Update Local Task Layers for New File self.local_task_layers.clear() for task_layer in self.all_task_layers: if task_layer.name in task_layer_keys: new_local_task_layer = self.local_task_layers.add() new_local_task_layer.name = task_layer.name def get_local_task_layers(self) -> list[str]: return [task_layer.name for task_layer in self.local_task_layers] def set_asset_catalog_name(self, input): task_layer_dict = config.get_task_layer_dict() task_layer_dict["ASSET_CATALOG_ID"] = get_asset_id(input) config.update_task_layer_json_data(task_layer_dict) self['asset_catalog_name'] = input def get_asset_catalog_name(self): if config.ASSET_CATALOG_ID != "": asset_name = get_asset_name(config.ASSET_CATALOG_ID) if asset_name is None: return "" return asset_name return get_safely_string_prop(self, 'asset_catalog_name') def get_asset_catalogs_search(self, context, edit_text: str): return get_asset_catalog_items() asset_catalog_name: bpy.props.StringProperty( name="Catalog", get=get_asset_catalog_name, set=set_asset_catalog_name, search=get_asset_catalogs_search, search_options={'SORT'}, description="Select Asset Library Catalog for the current Asset, this value will be updated each time you Push to an 'Active' Publish", ) # type: ignore @bpy.app.handlers.persistent def set_asset_collection_name_post_file_load(_): # Version the PointerProperty to the StringProperty, and the left-over pointer. for scene in bpy.data.scenes: if 'asset_collection' not in scene.asset_pipeline: continue coll = scene.asset_pipeline['asset_collection'] if coll: scene.asset_pipeline.asset_collection_name = coll.name del scene.asset_pipeline['asset_collection'] @bpy.app.handlers.persistent def refresh_asset_catalog(_): get_asset_catalog_items() config.verify_task_layer_json_data() classes = ( AssetTransferData, AssetTransferDataTemp, TaskLayerSettings, AssetPipeline, ) def register(): for i in classes: bpy.utils.register_class(i) bpy.types.Object.transfer_data_ownership = bpy.props.CollectionProperty(type=AssetTransferData) bpy.types.Scene.asset_pipeline = bpy.props.PointerProperty(type=AssetPipeline) bpy.types.ID.asset_id_owner = bpy.props.StringProperty(name="Owner", default="NONE") bpy.types.ID.asset_id_surrender = bpy.props.BoolProperty( name="Surrender Ownership", default=False ) bpy.app.handlers.load_post.append(set_asset_collection_name_post_file_load) bpy.app.handlers.load_post.append(refresh_asset_catalog) def unregister(): for i in classes: bpy.utils.unregister_class(i) del bpy.types.Object.transfer_data_ownership del bpy.types.Scene.asset_pipeline del bpy.types.ID.asset_id_owner bpy.app.handlers.load_post.remove(set_asset_collection_name_post_file_load) bpy.app.handlers.load_post.remove(refresh_asset_catalog)