From fbb2aec93752cd0b1d1e14646ab165d6a4eb6499 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 3 Aug 2023 17:10:31 +0200 Subject: [PATCH 001/429] Asset Pipe: Add Temp Test Files --- .gitattributes | 3 ++- .gitignore | 3 +++ .../addons/asset_pipeline_2/task_layer_merge_test.blend | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend diff --git a/.gitattributes b/.gitattributes index 719efb30..9e90bc2d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,4 +2,5 @@ *.png filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text *.whl filter=lfs diff=lfs merge=lfs -text -*.gif filter=lfs diff=lfs merge=lfs -text \ No newline at end of file +*.gif filter=lfs diff=lfs merge=lfs -text +*.blend filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.gitignore b/.gitignore index d1f9c1af..052d3450 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,6 @@ ENV/ # Docs node_modules/ docs/.vitepress/cache + +# Temp +*.blend1 \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend new file mode 100644 index 00000000..e535489c --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb034f7920e413371c56f11d73735f36cf7c2c1302e6696b46b701b9dbde4b31 +size 891200 -- 2.30.2 From 750d619422862f123b6a89c3f45bef6b8f3ccba5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 3 Aug 2023 20:46:38 +0200 Subject: [PATCH 002/429] Asset Pipe: Initial Commit --- .../addons/asset_pipeline_2/__init__.py | 44 ++++++++++++++ .../addons/asset_pipeline_2/ops.py | 58 +++++++++++++++++++ .../addons/asset_pipeline_2/props.py | 30 ++++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 35 +++++++++++ 4 files changed, 167 insertions(+) create mode 100644 scripts-blender/addons/asset_pipeline_2/__init__.py create mode 100644 scripts-blender/addons/asset_pipeline_2/ops.py create mode 100644 scripts-blender/addons/asset_pipeline_2/props.py create mode 100644 scripts-blender/addons/asset_pipeline_2/ui.py diff --git a/scripts-blender/addons/asset_pipeline_2/__init__.py b/scripts-blender/addons/asset_pipeline_2/__init__.py new file mode 100644 index 00000000..3fdd8d54 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/__init__.py @@ -0,0 +1,44 @@ +import importlib + +from . import ui, ops, props + +bl_info = { + "name": "Asset Pipeline 2", + "author": "Nick Alberelli", + "description": "Blender Studio Asset Pipeline Add-on", + "blender": (3, 1, 0), + "version": (0, 1, 2), + "location": "View3D", + "warning": "", + "doc_url": "", + "tracker_url": "", + "category": "Generic", +} + + +def reload() -> None: + global ui + global ops + global props + importlib.reload(ui) + importlib.reload(ops) + importlib.reload(props) + + +_need_reload = "ui" in locals() +if _need_reload: + reload() + +# ----------------REGISTER--------------. + + +def register() -> None: + ui.register() + ops.register() + props.register() + + +def unregister() -> None: + ui.unregister() + ops.unregister() + props.unregister() diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py new file mode 100644 index 00000000..b7402cca --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -0,0 +1,58 @@ +import bpy + + +class ASSETPIPE_OT_update_ownership(bpy.types.Operator): + bl_idname = "assetpipe.update_ownership" + bl_label = 'Update Ownership' + + def execute(self, context): + obj = context.active_object + ownership = context.active_object.asset_ownership + task_layer_name = obj.name.split(".")[-1] + for vertex_group in obj.vertex_groups: + if not vertex_group.name in [item.name for item in ownership]: + item = ownership.add() + item.name = vertex_group.name + item.owner = task_layer_name.upper() + return {'FINISHED'} + + +class ASSETPIPE_OT_push_test(bpy.types.Operator): + bl_idname = "assetpipe.push_test" + bl_label = 'Push to Publish' + + def execute(self, context): + if self.text != "test": + self.report({'ERROR'}, "Failure") + return {'CANCELLED'} + self.report({'INFO'}, "Success") + return {'FINISHED'} + + +class ASSETPIPE_OT_pull_test(bpy.types.Operator): + bl_idname = "assetpipe.pull_test" + bl_label = 'Pull from Publish' + + def execute(self, context): + if self.text != "test": + self.report({'ERROR'}, "Failure") + return {'CANCELLED'} + self.report({'INFO'}, "Success") + return {'FINISHED'} + + +classes = ( + ASSETPIPE_OT_push_test, + ASSETPIPE_OT_pull_test, + ASSETPIPE_OT_update_ownership, +) + + +def register(): + for i in classes: + bpy.utils.register_class(i) + + +def unregister(): + for i in classes: + bpy.utils.unregister_class(i) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py new file mode 100644 index 00000000..d50a6014 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -0,0 +1,30 @@ +import bpy + + +""" NOTE Items in these properties groups should be generated by a function that finds the +avaliable task layers from the task_layer_defaults.json file that needs to be created. +""" + + +# items=[("MODEL", "Rigging", ""), ("RIG", "Modeling", "")], +class ASSETOWNERSHIP(bpy.types.PropertyGroup): + owner: bpy.props.StringProperty(name="ID Owner", default="") + + +classes = (ASSETOWNERSHIP,) + + +def register(): + for i in classes: + bpy.utils.register_class(i) + bpy.types.Object.asset_ownership = bpy.props.CollectionProperty(type=ASSETOWNERSHIP) + bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( + name="ID Owner", + items=[("NONE", "None", ""), ("MODEL", "Rigging", ""), ("RIG", "Modeling", "")], + ) + + +def unregister(): + for i in classes: + bpy.utils.unregister_class(i) + del bpy.types.Object.asset_ownership diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py new file mode 100644 index 00000000..a2ff4b18 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -0,0 +1,35 @@ +import bpy + + +class ASSETPIPE_PT_TestUI(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'Asset Pipe 2' + bl_label = "Test UI" + + def draw(self, context: bpy.types.Context) -> None: + self.layout.label(text="Test UI") + self.layout.operator("assetpipe.update_ownership") + self.layout.operator("assetpipe.push_test", icon="TRIA_UP") + self.layout.operator("assetpipe.pull_test", icon="TRIA_DOWN") + + if not context.active_object: + return + obj = context.active_object + ownership = obj.asset_ownership + self.layout.prop(obj, "asset_id_owner") + for my_item in ownership: + self.layout.label(text=f"{my_item.name} : {my_item.owner}") + + +classes = (ASSETPIPE_PT_TestUI,) + + +def register(): + for i in classes: + bpy.utils.register_class(i) + + +def unregister(): + for i in classes: + bpy.utils.unregister_class(i) -- 2.30.2 From bd8dca64b1db341250a5eca9b79d1db2529a66c8 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 4 Aug 2023 11:48:40 +0200 Subject: [PATCH 003/429] Asset Pipe: Create Test Push Operator --- .../addons/asset_pipeline_2/ops.py | 44 +++++++++++++++++-- .../addons/asset_pipeline_2/props.py | 2 +- .../task_layer_merge_test.blend | 4 +- scripts-blender/addons/asset_pipeline_2/ui.py | 3 ++ 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index b7402cca..bb1e0ce0 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -22,10 +22,46 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): bl_label = 'Push to Publish' def execute(self, context): - if self.text != "test": - self.report({'ERROR'}, "Failure") - return {'CANCELLED'} - self.report({'INFO'}, "Success") + # Find current task Layer + obj = context.active_object + task_layer_col = context.collection + current_task_layer = task_layer_col.name.split('.')[-1] + + # Find PUBLUSH Collection + for col in context.scene.collection.children_recursive: + if "PUB" in col.name: + publish_col = col + + # Find Obj owned by Current Task Layer + push_obj = [] + for obj in task_layer_col.objects: + if obj.asset_id_owner == current_task_layer: + push_obj.append(obj) + + # Delete existing root OBJ + for obj in publish_col.objects: + obj_root_name = obj.name.split('.')[0] + if f"{obj_root_name}.PUB" in obj.name: + bpy.data.objects.remove(obj) + + # Link new obj to collection + for obj in push_obj: + obj_root_name = obj.name.split('.')[0] + new_obj = obj.copy() + new_obj.data = obj.data.copy() + new_obj.name = f"{obj_root_name}.PUB" + publish_col.objects.link(new_obj) + + # Cosmetically move the obj for easier organization + # TODO Remove this step + new_obj.location[0] = 0 + new_obj.location[2] = 3 + + # TODO Move transferrable data onto obj owned by others + # Find Transfer-Data in current TL + # Find matching Target Object in Publish + # Copy Transfer-Data + return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index d50a6014..9c2966d8 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -20,7 +20,7 @@ def register(): bpy.types.Object.asset_ownership = bpy.props.CollectionProperty(type=ASSETOWNERSHIP) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", - items=[("NONE", "None", ""), ("MODEL", "Rigging", ""), ("RIG", "Modeling", "")], + items=[("NONE", "None", ""), ("MODEL", "Modeling", ""), ("RIG", "Rigging", "")], ) diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend index e535489c..c2c0cd33 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb034f7920e413371c56f11d73735f36cf7c2c1302e6696b46b701b9dbde4b31 -size 891200 +oid sha256:9224d4dbb2b5f169ca9cf98d3da89dfd550167c7384575ecdc1a473adbb9e814 +size 901508 diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index a2ff4b18..5e98ecd4 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -8,6 +8,9 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): bl_label = "Test UI" def draw(self, context: bpy.types.Context) -> None: + self.layout.label( + text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" + ) self.layout.label(text="Test UI") self.layout.operator("assetpipe.update_ownership") self.layout.operator("assetpipe.push_test", icon="TRIA_UP") -- 2.30.2 From 18bf74e2b37a3f8804c9ca6bbcfc5a8b7132f3d7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 4 Aug 2023 15:07:17 +0200 Subject: [PATCH 004/429] Asset Pipe: Add test Transfer Data on Push --- .../addons/asset_pipeline_2/core.py | 11 +++++ .../addons/asset_pipeline_2/ops.py | 43 ++++++++++++++----- .../addons/asset_pipeline_2/props.py | 10 +++-- .../task_layer_merge_test.blend | 4 +- .../asset_pipeline_2/transfer_functions.py | 18 ++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 6 ++- 6 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/core.py create mode 100644 scripts-blender/addons/asset_pipeline_2/transfer_functions.py diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py new file mode 100644 index 00000000..4d80b9b3 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -0,0 +1,11 @@ +import bpy + + +def update_transfer_data(transfer_data_item, target_obj): + transfer_data_ownership = target_obj.transfer_data_ownership + if transfer_data_item not in transfer_data_ownership.items(): + new_item = transfer_data_ownership.add() + new_item.name = transfer_data_item.name + new_item.owner = transfer_data_item.owner + new_item.type = transfer_data_item.type + new_item.id = transfer_data_item.id diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index bb1e0ce0..0956d2c4 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -1,5 +1,7 @@ import bpy +from . import transfer_functions, core + class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_idname = "assetpipe.update_ownership" @@ -7,13 +9,17 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): def execute(self, context): obj = context.active_object - ownership = context.active_object.asset_ownership + ownership = context.active_object.transfer_data_ownership task_layer_name = obj.name.split(".")[-1] for vertex_group in obj.vertex_groups: if not vertex_group.name in [item.name for item in ownership]: item = ownership.add() item.name = vertex_group.name item.owner = task_layer_name.upper() + item.type = "VERTEX_GROUP" # TODO Make procedural + item.id = bpy.data.objects[ + f"{obj.name.split('.')[0]}.{obj.asset_id_owner}" + ] return {'FINISHED'} @@ -33,19 +39,25 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): publish_col = col # Find Obj owned by Current Task Layer - push_obj = [] + push_objs = [] + transfer_data = [] for obj in task_layer_col.objects: if obj.asset_id_owner == current_task_layer: - push_obj.append(obj) + push_objs.append(obj) + # Find Transfer-Data in current TL + for item in obj.transfer_data_ownership: + if item.owner == current_task_layer: + transfer_data.append(item) # Delete existing root OBJ for obj in publish_col.objects: obj_root_name = obj.name.split('.')[0] - if f"{obj_root_name}.PUB" in obj.name: - bpy.data.objects.remove(obj) + for push_obj in push_objs: + if f"{obj_root_name}.PUB" in push_obj.name: + bpy.data.objects.remove(obj) # Link new obj to collection - for obj in push_obj: + for obj in push_objs: obj_root_name = obj.name.split('.')[0] new_obj = obj.copy() new_obj.data = obj.data.copy() @@ -58,10 +70,21 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): new_obj.location[2] = 3 # TODO Move transferrable data onto obj owned by others - # Find Transfer-Data in current TL - # Find matching Target Object in Publish - # Copy Transfer-Data - + for item in transfer_data: + for obj in publish_col.objects: + if obj.name.split(".")[0] == item.id.name.split(".")[0]: + transfer_functions.transfer_vertex_group( + context=context, + vertex_group_name=item.name, + target_obj=obj, + source_obj=bpy.data.objects[ + f"{item.id.name.split('.')[0]}.{current_task_layer}" + ], + ) + core.update_transfer_data( + transfer_data_item=item, + target_obj=obj, + ) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 9c2966d8..14eb5523 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -8,7 +8,9 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr # items=[("MODEL", "Rigging", ""), ("RIG", "Modeling", "")], class ASSETOWNERSHIP(bpy.types.PropertyGroup): - owner: bpy.props.StringProperty(name="ID Owner", default="") + owner: bpy.props.StringProperty(name="Transfer Data Owner", default="") + type: bpy.props.StringProperty(name="Transfer Data Type", default="") + id: bpy.props.PointerProperty(type=bpy.types.Object) classes = (ASSETOWNERSHIP,) @@ -17,7 +19,9 @@ classes = (ASSETOWNERSHIP,) def register(): for i in classes: bpy.utils.register_class(i) - bpy.types.Object.asset_ownership = bpy.props.CollectionProperty(type=ASSETOWNERSHIP) + bpy.types.Object.transfer_data_ownership = bpy.props.CollectionProperty( + type=ASSETOWNERSHIP + ) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", items=[("NONE", "None", ""), ("MODEL", "Modeling", ""), ("RIG", "Rigging", "")], @@ -27,4 +31,4 @@ def register(): def unregister(): for i in classes: bpy.utils.unregister_class(i) - del bpy.types.Object.asset_ownership + del bpy.types.Object.transfer_data_ownership diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend index c2c0cd33..fa187119 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9224d4dbb2b5f169ca9cf98d3da89dfd550167c7384575ecdc1a473adbb9e814 -size 901508 +oid sha256:64c2d168bfc8b4c7234cbf48c1b0fdf36cafa0386ec236eaace4a02a0e3d2dae +size 920988 diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py new file mode 100644 index 00000000..47c3baf1 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -0,0 +1,18 @@ +import bpy +from bpy import context + +def transfer_vertex_group(context, vertex_group_name:str, target_obj:bpy.types.Object, source_obj:bpy.types.Object): + source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] + override = context.copy() + override["selected_editable_objects"] = [target_obj, source_obj] + override["active_object"] = source_obj + override["object"] = source_obj #TODO test if needed + with context.temp_override(**override ): + bpy.ops.object.data_transfer( + data_type="VGROUP_WEIGHTS", + use_create=True, + vert_mapping='POLYINTERP_NEAREST', + layers_select_src="ACTIVE", + layers_select_dst="NAME", + mix_mode="REPLACE", + ) \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 5e98ecd4..408cf0a9 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -19,10 +19,12 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): if not context.active_object: return obj = context.active_object - ownership = obj.asset_ownership + ownership = obj.transfer_data_ownership self.layout.prop(obj, "asset_id_owner") for my_item in ownership: - self.layout.label(text=f"{my_item.name} : {my_item.owner}") + self.layout.label( + text=f"{my_item.name} : {my_item.owner} : {my_item.id.name}" + ) classes = (ASSETPIPE_PT_TestUI,) -- 2.30.2 From a07866ec4152c2a12d917317ebff04967809101e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 4 Aug 2023 15:50:14 +0200 Subject: [PATCH 005/429] Asset Pipe: Add test Transfer Data on Pull --- .../addons/asset_pipeline_2/core.py | 2 +- .../addons/asset_pipeline_2/ops.py | 62 +++++++++++++++++-- .../task_layer_merge_test.blend | 4 +- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 4d80b9b3..59985c64 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -3,7 +3,7 @@ import bpy def update_transfer_data(transfer_data_item, target_obj): transfer_data_ownership = target_obj.transfer_data_ownership - if transfer_data_item not in transfer_data_ownership.items(): + if transfer_data_item.name not in transfer_data_ownership.items(): new_item = transfer_data_ownership.add() new_item.name = transfer_data_item.name new_item.owner = transfer_data_item.owner diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 0956d2c4..6b090e5e 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -93,10 +93,64 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_label = 'Pull from Publish' def execute(self, context): - if self.text != "test": - self.report({'ERROR'}, "Failure") - return {'CANCELLED'} - self.report({'INFO'}, "Success") + # Find current task Layer + obj = context.active_object + task_layer_col = context.collection + current_task_layer = task_layer_col.name.split('.')[-1] + + # Find PUBLUSH Collection + for col in context.scene.collection.children_recursive: + if "PUB" in col.name: + publish_col = col + + # Find Obj owned by other Current Task Layer + pull_objs = [] + transfer_data = [] + for obj in publish_col.objects: + if obj.asset_id_owner != current_task_layer: + pull_objs.append(obj) + # Find Transfer-Data in other Task Layers + for item in obj.transfer_data_ownership: + if item.owner != current_task_layer: + transfer_data.append(item) + + # Delete existing root OBJ + # TODO FIX + # for obj in publish_col.objects: + # obj_root_name = obj.name.split('.')[0] + # for pull_obj in pull_objs: + # if f"{obj_root_name}.{current_task_layer}" == pull_obj.name: + # bpy.data.objects.remove(obj) + + # # Link new obj to collection + for obj in pull_objs: + obj_root_name = obj.name.split('.')[0] + new_obj = obj.copy() + new_obj.data = obj.data.copy() + new_obj.name = f"{obj_root_name}.{current_task_layer}" + task_layer_col.objects.link(new_obj) + + # Cosmetically move the obj for easier organization + # TODO Remove this step + new_obj.location[0] = 3 + new_obj.location[2] = 0 + + # TODO Move transferrable data onto obj owned by others + for item in transfer_data: + for obj in task_layer_col.objects: + if obj.name.split(".")[0] == item.id.name.split(".")[0]: + transfer_functions.transfer_vertex_group( + context=context, + vertex_group_name=item.name, + source_obj=bpy.data.objects[ + f"{item.id.name.split('.')[0]}.PUB" + ], + target_obj=obj, + ) + core.update_transfer_data( + transfer_data_item=item, + target_obj=obj, + ) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend index fa187119..c32050f7 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64c2d168bfc8b4c7234cbf48c1b0fdf36cafa0386ec236eaace4a02a0e3d2dae -size 920988 +oid sha256:86f69fe0a46e5fda1969db982365d9f83ce623c381b45cdc581c646f8ecc2bf5 +size 890968 -- 2.30.2 From 68e9a23a710c7d3d2949c9e2eb9052a6a434f844 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 5 Aug 2023 11:47:04 -0400 Subject: [PATCH 006/429] Asset Pipe: Move Push to Core Function --- .../addons/asset_pipeline_2/core.py | 57 +++++++++++++++++- .../addons/asset_pipeline_2/ops.py | 59 +++---------------- 2 files changed, 63 insertions(+), 53 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 59985c64..e4ee8acc 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,11 +1,66 @@ import bpy +from . import transfer_functions + def update_transfer_data(transfer_data_item, target_obj): transfer_data_ownership = target_obj.transfer_data_ownership - if transfer_data_item.name not in transfer_data_ownership.items(): + transfer_items_names = [item.name for item in transfer_data_ownership] + if transfer_data_item.name not in transfer_items_names: new_item = transfer_data_ownership.add() new_item.name = transfer_data_item.name new_item.owner = transfer_data_item.owner new_item.type = transfer_data_item.type new_item.id = transfer_data_item.id + + +def push_task_layer( + context, source_col, target_col, source_task_layer, target_task_layer +): + # Find Obj owned by Current Task Layer + transfer_objs = [] + transfer_data = [] + for source_obj in source_col.objects: + if source_obj.asset_id_owner == source_task_layer: + transfer_objs.append(source_obj) + # Find Transfer-Data in current TL + for item in source_obj.transfer_data_ownership: + if item.owner == source_task_layer: + transfer_data.append(item) + + # Delete existing root OBJ + for target_obj in target_col.objects: + obj_root_name = target_obj.name.split('.')[0] + for push_obj in transfer_objs: + if f"{obj_root_name}.{target_task_layer}" in push_obj.name: + bpy.data.objects.remove(target_obj) + + # Link new obj to collection + for transfer_obj in transfer_objs: + obj_root_name = transfer_obj.name.split('.')[0] + new_obj = transfer_obj.copy() + new_obj.data = transfer_obj.data.copy() + new_obj.name = f"{obj_root_name}.{target_task_layer}" + target_col.objects.link(new_obj) + + # Cosmetically move the obj for easier organization + # TODO Remove this step + new_obj.location[0] = 0 + new_obj.location[2] = 3 + + # Move transferrable data onto obj owned by others + for item in transfer_data: + for target_obj in target_col.objects: + if target_obj.name.split(".")[0] == item.id.name.split(".")[0]: + transfer_functions.transfer_vertex_group( + context=context, + vertex_group_name=item.name, + target_obj=target_obj, + source_obj=bpy.data.objects[ + f"{item.id.name.split('.')[0]}.{source_task_layer}" + ], + ) + update_transfer_data( + transfer_data_item=item, + target_obj=target_obj, + ) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 6b090e5e..4f47b1d3 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -29,62 +29,17 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): def execute(self, context): # Find current task Layer - obj = context.active_object - task_layer_col = context.collection - current_task_layer = task_layer_col.name.split('.')[-1] + source_col = context.collection + source_task_layer = source_col.name.split('.')[-1] + target_task_layer = "PUB" # Find PUBLUSH Collection for col in context.scene.collection.children_recursive: if "PUB" in col.name: - publish_col = col - - # Find Obj owned by Current Task Layer - push_objs = [] - transfer_data = [] - for obj in task_layer_col.objects: - if obj.asset_id_owner == current_task_layer: - push_objs.append(obj) - # Find Transfer-Data in current TL - for item in obj.transfer_data_ownership: - if item.owner == current_task_layer: - transfer_data.append(item) - - # Delete existing root OBJ - for obj in publish_col.objects: - obj_root_name = obj.name.split('.')[0] - for push_obj in push_objs: - if f"{obj_root_name}.PUB" in push_obj.name: - bpy.data.objects.remove(obj) - - # Link new obj to collection - for obj in push_objs: - obj_root_name = obj.name.split('.')[0] - new_obj = obj.copy() - new_obj.data = obj.data.copy() - new_obj.name = f"{obj_root_name}.PUB" - publish_col.objects.link(new_obj) - - # Cosmetically move the obj for easier organization - # TODO Remove this step - new_obj.location[0] = 0 - new_obj.location[2] = 3 - - # TODO Move transferrable data onto obj owned by others - for item in transfer_data: - for obj in publish_col.objects: - if obj.name.split(".")[0] == item.id.name.split(".")[0]: - transfer_functions.transfer_vertex_group( - context=context, - vertex_group_name=item.name, - target_obj=obj, - source_obj=bpy.data.objects[ - f"{item.id.name.split('.')[0]}.{current_task_layer}" - ], - ) - core.update_transfer_data( - transfer_data_item=item, - target_obj=obj, - ) + target_col = col + core.push_task_layer( + context, source_col, target_col, source_task_layer, target_task_layer + ) return {'FINISHED'} -- 2.30.2 From a186ea8fa306d3b32412cdcd092a0ce63d7d27e4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 5 Aug 2023 12:35:18 -0400 Subject: [PATCH 007/429] Asset Pipe: Move Pull to Core Functions --- .../addons/asset_pipeline_2/core.py | 123 ++++++++++++------ .../addons/asset_pipeline_2/ops.py | 58 ++------- 2 files changed, 93 insertions(+), 88 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index e4ee8acc..cf355080 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -3,7 +3,7 @@ import bpy from . import transfer_functions -def update_transfer_data(transfer_data_item, target_obj): +def update_transfer_data_ownership(transfer_data_item, target_obj): transfer_data_ownership = target_obj.transfer_data_ownership transfer_items_names = [item.name for item in transfer_data_ownership] if transfer_data_item.name not in transfer_items_names: @@ -14,42 +14,8 @@ def update_transfer_data(transfer_data_item, target_obj): new_item.id = transfer_data_item.id -def push_task_layer( - context, source_col, target_col, source_task_layer, target_task_layer -): - # Find Obj owned by Current Task Layer - transfer_objs = [] - transfer_data = [] - for source_obj in source_col.objects: - if source_obj.asset_id_owner == source_task_layer: - transfer_objs.append(source_obj) - # Find Transfer-Data in current TL - for item in source_obj.transfer_data_ownership: - if item.owner == source_task_layer: - transfer_data.append(item) - - # Delete existing root OBJ - for target_obj in target_col.objects: - obj_root_name = target_obj.name.split('.')[0] - for push_obj in transfer_objs: - if f"{obj_root_name}.{target_task_layer}" in push_obj.name: - bpy.data.objects.remove(target_obj) - - # Link new obj to collection - for transfer_obj in transfer_objs: - obj_root_name = transfer_obj.name.split('.')[0] - new_obj = transfer_obj.copy() - new_obj.data = transfer_obj.data.copy() - new_obj.name = f"{obj_root_name}.{target_task_layer}" - target_col.objects.link(new_obj) - - # Cosmetically move the obj for easier organization - # TODO Remove this step - new_obj.location[0] = 0 - new_obj.location[2] = 3 - - # Move transferrable data onto obj owned by others - for item in transfer_data: +def apply_transfer_data(context, transfer_data_list, target_col, source_task_layer): + for item in transfer_data_list: for target_obj in target_col.objects: if target_obj.name.split(".")[0] == item.id.name.split(".")[0]: transfer_functions.transfer_vertex_group( @@ -60,7 +26,88 @@ def push_task_layer( f"{item.id.name.split('.')[0]}.{source_task_layer}" ], ) - update_transfer_data( + update_transfer_data_ownership( transfer_data_item=item, target_obj=target_obj, ) + + +def push_task_layer( + context: bpy.types.Collection, + source_col: bpy.types.Collection, + target_col: bpy.types.Collection, + source_tl: str, + target_tl: str, +): + # Find Obj owned by Current Task Layer + transfer_objs = [] + transfer_data_list = [] + for source_obj in source_col.objects: + if source_obj.asset_id_owner == source_tl: + transfer_objs.append(source_obj) + # Find Transfer-Data in current TL + for item in source_obj.transfer_data_ownership: + if item.owner == source_tl: + transfer_data_list.append(item) + + # Delete existing root OBJ + for target_obj in target_col.objects: + obj_root_name = target_obj.name.split('.')[0] + for push_obj in transfer_objs: + if f"{obj_root_name}.{target_tl}" in push_obj.name: + bpy.data.objects.remove(target_obj) + + # Link new obj to collection + for transfer_obj in transfer_objs: + obj_root_name = transfer_obj.name.split('.')[0] + new_obj = transfer_obj.copy() + new_obj.data = transfer_obj.data.copy() + new_obj.name = f"{obj_root_name}.{target_tl}" + target_col.objects.link(new_obj) + + # Cosmetically move the obj for easier organization + # TODO Remove this step + new_obj.location[0] = 0 + new_obj.location[2] = 3 + + # Move transferrable data onto obj owned by others + apply_transfer_data(context, transfer_data_list, target_col, source_tl) + + +def pull_task_layer( + context: bpy.types.Collection, + source_col: bpy.types.Collection, + target_col: bpy.types.Collection, + source_tl: str, + target_tl: str, +): + # Find Obj owned by other Current Task Layer + pull_objs = [] + transfer_data = [] + for obj in source_col.objects: + if obj.asset_id_owner != source_tl: + pull_objs.append(obj) + # Find Transfer-Data in other Task Layers + for item in obj.transfer_data_ownership: + if item.owner != source_tl: + transfer_data.append(item) + + # TODO FIX SO I CAN REPLACE OBJ NOT JUST UPDATE IT + + # Delete existing root OBJ + # for obj in publish_col.objects: + # obj_root_name = obj.name.split('.')[0] + # for pull_obj in pull_objs: + # if f"{obj_root_name}.{current_task_layer}" == pull_obj.name: + # bpy.data.objects.remove(obj) + + # # # Link new obj to collection + # for obj in pull_objs: + # obj_root_name = obj.name.split('.')[0] + # new_obj = obj.copy() + # new_obj.data = obj.data.copy() + # new_obj.name = f"{obj_root_name}.{target_tl}" + # target_col.objects.link(new_obj) + + # TODO Move transferrable data onto obj owned by others + apply_transfer_data(context, transfer_data, target_col, source_tl) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 4f47b1d3..a223dbda 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -1,6 +1,6 @@ import bpy -from . import transfer_functions, core +from . import core class ASSETPIPE_OT_update_ownership(bpy.types.Operator): @@ -49,7 +49,6 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): def execute(self, context): # Find current task Layer - obj = context.active_object task_layer_col = context.collection current_task_layer = task_layer_col.name.split('.')[-1] @@ -58,54 +57,13 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): if "PUB" in col.name: publish_col = col - # Find Obj owned by other Current Task Layer - pull_objs = [] - transfer_data = [] - for obj in publish_col.objects: - if obj.asset_id_owner != current_task_layer: - pull_objs.append(obj) - # Find Transfer-Data in other Task Layers - for item in obj.transfer_data_ownership: - if item.owner != current_task_layer: - transfer_data.append(item) - - # Delete existing root OBJ - # TODO FIX - # for obj in publish_col.objects: - # obj_root_name = obj.name.split('.')[0] - # for pull_obj in pull_objs: - # if f"{obj_root_name}.{current_task_layer}" == pull_obj.name: - # bpy.data.objects.remove(obj) - - # # Link new obj to collection - for obj in pull_objs: - obj_root_name = obj.name.split('.')[0] - new_obj = obj.copy() - new_obj.data = obj.data.copy() - new_obj.name = f"{obj_root_name}.{current_task_layer}" - task_layer_col.objects.link(new_obj) - - # Cosmetically move the obj for easier organization - # TODO Remove this step - new_obj.location[0] = 3 - new_obj.location[2] = 0 - - # TODO Move transferrable data onto obj owned by others - for item in transfer_data: - for obj in task_layer_col.objects: - if obj.name.split(".")[0] == item.id.name.split(".")[0]: - transfer_functions.transfer_vertex_group( - context=context, - vertex_group_name=item.name, - source_obj=bpy.data.objects[ - f"{item.id.name.split('.')[0]}.PUB" - ], - target_obj=obj, - ) - core.update_transfer_data( - transfer_data_item=item, - target_obj=obj, - ) + core.pull_task_layer( + context, + source_col=publish_col, + target_col=task_layer_col, + source_tl="PUB", + target_tl=current_task_layer, + ) return {'FINISHED'} -- 2.30.2 From 153120bd9aa52c61efe49a4281d244f9b6e0bfc5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 5 Aug 2023 12:50:17 -0400 Subject: [PATCH 008/429] Asset Pipe: Update Objects in Core Function --- .../addons/asset_pipeline_2/core.py | 74 +++++++++---------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index cf355080..e2cd189a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -3,7 +3,7 @@ import bpy from . import transfer_functions -def update_transfer_data_ownership(transfer_data_item, target_obj): +def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): transfer_data_ownership = target_obj.transfer_data_ownership transfer_items_names = [item.name for item in transfer_data_ownership] if transfer_data_item.name not in transfer_items_names: @@ -14,7 +14,12 @@ def update_transfer_data_ownership(transfer_data_item, target_obj): new_item.id = transfer_data_item.id -def apply_transfer_data(context, transfer_data_list, target_col, source_task_layer): +def apply_transfer_data( + context: bpy.types.Context, + transfer_data_list, + target_col: bpy.types.Collection, + source_task_layer: str, +): for item in transfer_data_list: for target_obj in target_col.objects: if target_obj.name.split(".")[0] == item.id.name.split(".")[0]: @@ -32,6 +37,28 @@ def apply_transfer_data(context, transfer_data_list, target_col, source_task_lay ) +def update_task_layer_objects( + target_col: bpy.types.Collection, + transfer_objs: list[bpy.types.Object], + target_tl: str, +): + # TODO CHECK WHY MAKE DUPLICATES + # Delete existing root OBJ + for target_obj in target_col.objects: + obj_root_name = target_obj.name.split('.')[0] + for push_obj in transfer_objs: + if f"{obj_root_name}.{target_tl}" in push_obj.name: + bpy.data.objects.remove(target_obj) + + # Link new obj to collection + for transfer_obj in transfer_objs: + obj_root_name = transfer_obj.name.split('.')[0] + new_obj = transfer_obj.copy() + new_obj.data = transfer_obj.data.copy() + new_obj.name = f"{obj_root_name}.{target_tl}" + target_col.objects.link(new_obj) + + def push_task_layer( context: bpy.types.Collection, source_col: bpy.types.Collection, @@ -50,25 +77,7 @@ def push_task_layer( if item.owner == source_tl: transfer_data_list.append(item) - # Delete existing root OBJ - for target_obj in target_col.objects: - obj_root_name = target_obj.name.split('.')[0] - for push_obj in transfer_objs: - if f"{obj_root_name}.{target_tl}" in push_obj.name: - bpy.data.objects.remove(target_obj) - - # Link new obj to collection - for transfer_obj in transfer_objs: - obj_root_name = transfer_obj.name.split('.')[0] - new_obj = transfer_obj.copy() - new_obj.data = transfer_obj.data.copy() - new_obj.name = f"{obj_root_name}.{target_tl}" - target_col.objects.link(new_obj) - - # Cosmetically move the obj for easier organization - # TODO Remove this step - new_obj.location[0] = 0 - new_obj.location[2] = 3 + update_task_layer_objects(target_col, transfer_objs, target_tl) # Move transferrable data onto obj owned by others apply_transfer_data(context, transfer_data_list, target_col, source_tl) @@ -82,32 +91,17 @@ def pull_task_layer( target_tl: str, ): # Find Obj owned by other Current Task Layer - pull_objs = [] + transfer_objs = [] transfer_data = [] for obj in source_col.objects: - if obj.asset_id_owner != source_tl: - pull_objs.append(obj) + if obj.asset_id_owner != target_tl: + transfer_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: if item.owner != source_tl: transfer_data.append(item) - # TODO FIX SO I CAN REPLACE OBJ NOT JUST UPDATE IT - - # Delete existing root OBJ - # for obj in publish_col.objects: - # obj_root_name = obj.name.split('.')[0] - # for pull_obj in pull_objs: - # if f"{obj_root_name}.{current_task_layer}" == pull_obj.name: - # bpy.data.objects.remove(obj) - - # # # Link new obj to collection - # for obj in pull_objs: - # obj_root_name = obj.name.split('.')[0] - # new_obj = obj.copy() - # new_obj.data = obj.data.copy() - # new_obj.name = f"{obj_root_name}.{target_tl}" - # target_col.objects.link(new_obj) + update_task_layer_objects(target_col, transfer_objs, target_tl) # TODO Move transferrable data onto obj owned by others apply_transfer_data(context, transfer_data, target_col, source_tl) -- 2.30.2 From b982caf531ba75d1b1b925898bd8dc2e186598cc Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 6 Aug 2023 19:11:27 -0400 Subject: [PATCH 009/429] Asset Pipe: Update TODO --- scripts-blender/addons/asset_pipeline_2/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index e2cd189a..b94ff2d1 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -42,7 +42,8 @@ def update_task_layer_objects( transfer_objs: list[bpy.types.Object], target_tl: str, ): - # TODO CHECK WHY MAKE DUPLICATES + # TODO CHECK WHY MAKE DUPLICATES ON PULL + # Delete existing root OBJ for target_obj in target_col.objects: obj_root_name = target_obj.name.split('.')[0] -- 2.30.2 From 1730f082d379db3d8143913d9c76af11a6a56b56 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 14 Aug 2023 11:18:41 -0400 Subject: [PATCH 010/429] Asset Pipe: Expand example file to multiple files --- .../addons/asset_pipeline_2/chr_test/chr_test.modeling.blend | 3 +++ .../addons/asset_pipeline_2/chr_test/chr_test.rigging.blend | 3 +++ .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 3 +++ .../addons/asset_pipeline_2/task_layer_merge_test.blend | 3 --- 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend create mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend create mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend delete mode 100644 scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend new file mode 100644 index 00000000..cd0c3ee9 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcfc0cd4259e295bdf10466fa0270e02f8d1480543bbdcb6c691283e7d743101 +size 910448 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend new file mode 100644 index 00000000..bb76f520 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3cd5bc58eaac2ce8fdd8c38abeab0f18d35c7ed60b4d4beff73f61da5649c3c +size 927348 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend new file mode 100644 index 00000000..65aa8553 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15f7d98ce8f9470788b93f00eec4ccd47c47dbb95e239d7e17795fcbfdb75c88 +size 927348 diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend b/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend deleted file mode 100644 index c32050f7..00000000 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_merge_test.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:86f69fe0a46e5fda1969db982365d9f83ce623c381b45cdc581c646f8ecc2bf5 -size 890968 -- 2.30.2 From 33f5aaa1300d9d039501af7e20b71e8bcfac0ee0 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 14 Aug 2023 12:45:57 -0400 Subject: [PATCH 011/429] Asset Pipe: Re-Use Asset Suffix functions from previous add-on --- .../addons/asset_pipeline_2/asset_suffix.py | 68 ++++++++++++++ .../addons/asset_pipeline_2/utils.py | 93 +++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 scripts-blender/addons/asset_pipeline_2/asset_suffix.py create mode 100644 scripts-blender/addons/asset_pipeline_2/utils.py diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py new file mode 100644 index 00000000..28cd5983 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -0,0 +1,68 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# +# (c) 2021, Blender Foundation - Paul Golter +from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator + +import bpy +from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids + +from .utils import get_storage_of_id + +DELIMITER = "." + + +def remove_suffix_from_hierarchy( + collection: bpy.types.Collection, delimiter: str = DELIMITER +): + """Removes the suffix after a set delimiter from all datablocks + referenced by a collection, itself included""" + + ref_map = get_id_reference_map() + datablocks = get_all_referenced_ids(collection, ref_map) + datablocks.add(collection) + for db in datablocks: + if db.library: + # Don't rename linked datablocks. + continue + try: + db.name = delimiter.join(db.name.split(delimiter)[:-1]) + except: + pass + + +def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str): + """Add a suffix to the names of all datablocks referenced by a collection, + itself included.""" + + suffix = f"{DELIMITER}{suffix_base}" + + ref_map = get_id_reference_map() + datablocks = get_all_referenced_ids(collection, ref_map) + datablocks.add(collection) + for db in datablocks: + if db.library: + # Don't rename linked datablocks. + continue + collision_db = get_storage_of_id(db).get(db.name + suffix) + if collision_db: + collision_db.name += f'{DELIMITER}OLD' + try: + db.name += suffix + except: + pass diff --git a/scripts-blender/addons/asset_pipeline_2/utils.py b/scripts-blender/addons/asset_pipeline_2/utils.py new file mode 100644 index 00000000..9aadee47 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/utils.py @@ -0,0 +1,93 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# +# (c) 2021, Blender Foundation - Paul Golter + +from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator + +import bpy +from bpy import types + +ID_INFO = { + (types.WindowManager, 'WINDOWMANAGER', 'window_managers'), + (types.Scene, 'SCENE', 'scenes'), + (types.World, 'WORLD', 'worlds'), + (types.Collection, 'COLLECTION', 'collections'), + (types.Armature, 'ARMATURE', 'armatures'), + (types.Mesh, 'MESH', 'meshes'), + (types.Camera, 'CAMERA', 'cameras'), + (types.Lattice, 'LATTICE', 'lattices'), + (types.Light, 'LIGHT', 'lights'), + (types.Speaker, 'SPEAKER', 'speakers'), + (types.Volume, 'VOLUME', 'volumes'), + (types.GreasePencil, 'GREASEPENCIL', 'grease_pencils'), + (types.Curve, 'CURVE', 'curves'), + (types.LightProbe, 'LIGHT_PROBE', 'lightprobes'), + (types.MetaBall, 'METABALL', 'metaballs'), + (types.Object, 'OBJECT', 'objects'), + (types.Action, 'ACTION', 'actions'), + (types.Key, 'KEY', 'shape_keys'), + (types.Sound, 'SOUND', 'sounds'), + (types.Material, 'MATERIAL', 'materials'), + (types.NodeTree, 'NODETREE', 'node_groups'), + (types.Image, 'IMAGE', 'images'), + (types.Mask, 'MASK', 'masks'), + (types.FreestyleLineStyle, 'LINESTYLE', 'linestyles'), + (types.Library, 'LIBRARY', 'libraries'), + (types.VectorFont, 'FONT', 'fonts'), + (types.CacheFile, 'CACHE_FILE', 'cache_files'), + (types.PointCloud, 'POINT_CLOUD', 'pointclouds'), + (types.Curves, 'HAIR_CURVES', 'hair_curves'), + (types.Text, 'TEXT', 'texts'), + # (types.Simulation, 'SIMULATION', 'simulations'), + (types.ParticleSettings, 'PARTICLE', 'particles'), + (types.Palette, 'PALETTE', 'palettes'), + (types.PaintCurve, 'PAINT_CURVE', 'paint_curves'), + (types.MovieClip, 'MOVIE_CLIP', 'movieclips'), + (types.WorkSpace, 'WORKSPACE', 'workspaces'), + (types.Screen, 'SCREEN', 'screens'), + (types.Brush, 'BRUSH', 'brushes'), + (types.Texture, 'TEXTURE', 'textures'), +} + +# Map datablock Python classes to their string representation. +ID_CLASS_TO_IDENTIFIER: Dict[type, Tuple[str, int]] = dict( + [(tup[0], (tup[1])) for tup in ID_INFO] +) + +# Map datablock Python classes to the name of their bpy.data container. +ID_CLASS_TO_STORAGE_NAME: Dict[type, str] = dict( + [(tup[0], (tup[2])) for tup in ID_INFO] +) + + +def get_fundamental_id_type(datablock: bpy.types.ID) -> Any: + """Certain datablocks have very specific types. + This function should return their fundamental type, ie. parent class.""" + for id_type in ID_CLASS_TO_IDENTIFIER.keys(): + if isinstance(datablock, id_type): + return id_type + + +def get_storage_of_id(datablock: bpy.types.ID) -> 'bpy_prop_collection': + """Return the storage collection property of the datablock. + Eg. for an object, returns bpy.data.objects. + """ + + fundamental_type = get_fundamental_id_type(datablock) + return getattr(bpy.data, ID_CLASS_TO_STORAGE_NAME[fundamental_type]) -- 2.30.2 From dcee6fcba6ae2833578958d55ff398fbcb2efce0 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 14 Aug 2023 12:48:03 -0400 Subject: [PATCH 012/429] Asset Pipe: Append Data on Pull from Publish File - Add Function to find latest Publish - Re-use import_data function from pervious add-on - Adapt Pull Operator to Append Data - Add TODO to re-implement pull core function for this context --- .../addons/asset_pipeline_2/core.py | 55 +++++++++++++++++++ .../addons/asset_pipeline_2/ops.py | 24 ++++++-- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index b94ff2d1..9c6e6722 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,6 +1,7 @@ import bpy from . import transfer_functions +from pathlib import Path def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): @@ -106,3 +107,57 @@ def pull_task_layer( # TODO Move transferrable data onto obj owned by others apply_transfer_data(context, transfer_data, target_col, source_tl) + + +def find_published_file_version(file): + return int(file.name.split(".")[1].replace("v", "")) + + +def find_published_file(current_file: Path): + publish_dir = current_file.parent.joinpath("publish") + if not publish_dir.exists(): + return + published_files = list(current_file.parent.joinpath("publish").glob('*.blend')) + published_files.sort(key=find_published_file_version) + return published_files[-1] + + +def import_data_from_lib( + libpath: Path, + data_category: str, + data_name: str, + link: bool = False, +): + noun = "Appended" + if link: + noun = "Linked" + + with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( + data_from, + data_to, + ): + if data_name not in eval(f"data_from.{data_category}"): + print( + f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", + ) + + # Check if datablock with same name already exists in blend file. + try: + eval(f"bpy.data.{data_category}['{data_name}']") + except KeyError: + pass + else: + print( + f"{data_name} already in bpy.data.{data_category} of this blendfile.", + ) + + # Append data block. + eval(f"data_to.{data_category}.append('{data_name}')") + print(f"{noun}:{data_name} from library: {libpath.as_posix()}") + + if link: + return eval( + f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" + ) + + return eval(f"bpy.data.{data_category}['{data_name}']") diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index a223dbda..43fbf062 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -1,6 +1,8 @@ import bpy from . import core +from pathlib import Path +from . import asset_suffix class ASSETPIPE_OT_update_ownership(bpy.types.Operator): @@ -48,15 +50,26 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_label = 'Pull from Publish' def execute(self, context): + task_col = bpy.data.collections["CH-chr_test"] # TODO replace hard coded value + asset_suffix.add_suffix_to_hierarchy(task_col, "TASK") + + current_file = Path(bpy.data.filepath) + pub_file = core.find_published_file(current_file) + col_name = "CH-chr_test" # TODO replace hard coded value + core.import_data_from_lib(pub_file, "collections", col_name) + appended_col = bpy.data.collections["CH-chr_test"] # TODO find appended data + asset_suffix.add_suffix_to_hierarchy(appended_col, "PUBLISH") + + task_layer_col = bpy.data.collections["CH-chr_test.TASK"] + publish_col = bpy.data.collections["CH-chr_test.PUBLISH"] + + # TODO fix pull function to work with multiple files + return {'FINISHED'} + # Find current task Layer task_layer_col = context.collection current_task_layer = task_layer_col.name.split('.')[-1] - # Find PUBLUSH Collection - for col in context.scene.collection.children_recursive: - if "PUB" in col.name: - publish_col = col - core.pull_task_layer( context, source_col=publish_col, @@ -64,7 +77,6 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): source_tl="PUB", target_tl=current_task_layer, ) - return {'FINISHED'} classes = ( -- 2.30.2 From e819c89eb1b03bd2ac199288c07005784a57b423 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 14 Aug 2023 14:50:50 -0400 Subject: [PATCH 013/429] Asset Pipe: Update Test Files --- .../addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend | 3 +++ .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 3 +++ .../addons/asset_pipeline_2/chr_test/chr_test.modeling.blend | 3 --- .../addons/asset_pipeline_2/chr_test/chr_test.rigging.blend | 3 --- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend create mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend delete mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend delete mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend new file mode 100644 index 00000000..8bdc1652 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e84b096378ce5949ff387b73f6194d49acfe0376955b4da19db8683f0d1b00dd +size 884856 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend new file mode 100644 index 00000000..a135040f --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:091b9f2ed80aa32a2510f50d323cd6a620d7310039136c5322d13eeb9391e275 +size 941076 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend deleted file mode 100644 index cd0c3ee9..00000000 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.modeling.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fcfc0cd4259e295bdf10466fa0270e02f8d1480543bbdcb6c691283e7d743101 -size 910448 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend deleted file mode 100644 index bb76f520..00000000 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.rigging.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c3cd5bc58eaac2ce8fdd8c38abeab0f18d35c7ed60b4d4beff73f61da5649c3c -size 927348 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index 65aa8553..e4229b31 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15f7d98ce8f9470788b93f00eec4ccd47c47dbb95e239d7e17795fcbfdb75c88 -size 927348 +oid sha256:2b8aecd0f4c2b62fac9f19ccc2db2996dcef784f35dc98146da50aa291826ea0 +size 941076 -- 2.30.2 From 6825e692dd755f23d06f38531ce33fa311b85ec5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 14 Aug 2023 14:51:12 -0400 Subject: [PATCH 014/429] Asset Pipe: Add Core Function to Get Asset Basename --- scripts-blender/addons/asset_pipeline_2/asset_suffix.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index 28cd5983..7c10cae0 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -26,9 +26,11 @@ from .utils import get_storage_of_id DELIMITER = "." +def get_asset_basename(name): + return DELIMITER.join(name.split(DELIMITER)[:-1]) def remove_suffix_from_hierarchy( - collection: bpy.types.Collection, delimiter: str = DELIMITER + collection: bpy.types.Collection ): """Removes the suffix after a set delimiter from all datablocks referenced by a collection, itself included""" @@ -41,7 +43,7 @@ def remove_suffix_from_hierarchy( # Don't rename linked datablocks. continue try: - db.name = delimiter.join(db.name.split(delimiter)[:-1]) + db.name = get_asset_basename(db.name) except: pass -- 2.30.2 From 7b3f6a17e3c1c572826379aa5537c5cb0ae8958f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 14 Aug 2023 14:55:01 -0400 Subject: [PATCH 015/429] Asset Pipe: Fix Appending Data on Pull from Publish File --- .../addons/asset_pipeline_2/core.py | 112 ++++++++++++------ .../addons/asset_pipeline_2/ops.py | 38 ++---- 2 files changed, 83 insertions(+), 67 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 9c6e6722..20b3195a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,6 +1,6 @@ import bpy -from . import transfer_functions +from . import transfer_functions, asset_suffix from pathlib import Path @@ -8,29 +8,30 @@ def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Obj transfer_data_ownership = target_obj.transfer_data_ownership transfer_items_names = [item.name for item in transfer_data_ownership] if transfer_data_item.name not in transfer_items_names: + id_name = asset_suffix.get_asset_basename(transfer_data_item.id.name) new_item = transfer_data_ownership.add() new_item.name = transfer_data_item.name new_item.owner = transfer_data_item.owner new_item.type = transfer_data_item.type - new_item.id = transfer_data_item.id + new_item.id = bpy.data.objects[ + id_name + ] # TODO replace this pointer with a a string instead def apply_transfer_data( context: bpy.types.Context, transfer_data_list, target_col: bpy.types.Collection, - source_task_layer: str, ): for item in transfer_data_list: for target_obj in target_col.objects: - if target_obj.name.split(".")[0] == item.id.name.split(".")[0]: + if target_obj.name == asset_suffix.get_asset_basename(item.id.name): + print(f"{target_obj.name}: READY TO TRANSFER BABY!") transfer_functions.transfer_vertex_group( context=context, vertex_group_name=item.name, target_obj=target_obj, - source_obj=bpy.data.objects[ - f"{item.id.name.split('.')[0]}.{source_task_layer}" - ], + source_obj=item.id, ) update_transfer_data_ownership( transfer_data_item=item, @@ -41,33 +42,22 @@ def apply_transfer_data( def update_task_layer_objects( target_col: bpy.types.Collection, transfer_objs: list[bpy.types.Object], - target_tl: str, ): - # TODO CHECK WHY MAKE DUPLICATES ON PULL - - # Delete existing root OBJ - for target_obj in target_col.objects: - obj_root_name = target_obj.name.split('.')[0] - for push_obj in transfer_objs: - if f"{obj_root_name}.{target_tl}" in push_obj.name: - bpy.data.objects.remove(target_obj) - # Link new obj to collection for transfer_obj in transfer_objs: - obj_root_name = transfer_obj.name.split('.')[0] - new_obj = transfer_obj.copy() - new_obj.data = transfer_obj.data.copy() - new_obj.name = f"{obj_root_name}.{target_tl}" - target_col.objects.link(new_obj) + obj_root_name = asset_suffix.get_asset_basename(transfer_obj.name) + transfer_obj.name = f"{obj_root_name}" + target_col.objects.link(transfer_obj) def push_task_layer( context: bpy.types.Collection, source_col: bpy.types.Collection, - target_col: bpy.types.Collection, + current_col: bpy.types.Collection, source_tl: str, - target_tl: str, ): + # TODO REFACTOR based on new PULL FUNCTION + # NOTE PUSHING MAY BE AS SIMPLE AS OPENING THE PUBLISH FILE AND PULLING THE CURRENT TASK LAYER IN # Find Obj owned by Current Task Layer transfer_objs = [] transfer_data_list = [] @@ -79,34 +69,78 @@ def push_task_layer( if item.owner == source_tl: transfer_data_list.append(item) - update_task_layer_objects(target_col, transfer_objs, target_tl) + update_task_layer_objects( + current_col, + transfer_objs, + ) # Move transferrable data onto obj owned by others - apply_transfer_data(context, transfer_data_list, target_col, source_tl) + apply_transfer_data(context, transfer_data_list, current_col) def pull_task_layer( - context: bpy.types.Collection, - source_col: bpy.types.Collection, + context: bpy.types.Context, + current_task_col: bpy.types.Collection, + col_base_name: str, + current_tl: str, target_col: bpy.types.Collection, - source_tl: str, - target_tl: str, ): + current_suffix = "TASK" + source_suffix = "PUBLISH" + asset_suffix.add_suffix_to_hierarchy(current_task_col, current_suffix) + + current_file = Path(bpy.data.filepath) + pub_file = find_published_file( + current_file + ) # TODO if this function is used in PULL then this needs to be a variable set by the operator instead + import_data_from_lib(pub_file, "collections", col_base_name) + appended_col = bpy.data.collections[col_base_name] # find appended data + asset_suffix.add_suffix_to_hierarchy(appended_col, source_suffix) + + current_col = bpy.data.collections[f"{col_base_name}.{current_suffix}"] + source_col = bpy.data.collections[f"{col_base_name}.{source_suffix}"] + target_col = bpy.data.collections.new(col_base_name) + + # Link Target as new Active Collection + context.scene.collection.children.link(target_col) + context.scene.collection.children.unlink(current_col) + # Find Obj owned by other Current Task Layer - transfer_objs = [] - transfer_data = [] + source_transfer_objs = [] + source_transfer_data = [] for obj in source_col.objects: - if obj.asset_id_owner != target_tl: - transfer_objs.append(obj) + if obj.asset_id_owner != current_tl: + source_transfer_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: - if item.owner != source_tl: - transfer_data.append(item) + if item.owner != current_tl: + source_transfer_data.append(item) + update_task_layer_objects(target_col, source_transfer_objs) - update_task_layer_objects(target_col, transfer_objs, target_tl) + current_transfer_objs = [] + current_transfer_data = [] + for obj in current_col.objects: + if obj.asset_id_owner == current_tl: + current_transfer_objs.append(obj) + # Find Transfer-Data in other Task Layers + for item in obj.transfer_data_ownership: + if item.owner == current_tl: + current_transfer_data.append(item) + update_task_layer_objects(target_col, current_transfer_objs) - # TODO Move transferrable data onto obj owned by others - apply_transfer_data(context, transfer_data, target_col, source_tl) + apply_transfer_data( + context, + source_transfer_data, + target_col, + ) + apply_transfer_data( + context, + current_transfer_data, + target_col, + ) # TODO test if only one list of transfer data is needed + bpy.ops.outliner.orphans_purge( + do_local_ids=True, do_linked_ids=False, do_recursive=True + ) def find_published_file_version(file): diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 43fbf062..7a149389 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -12,16 +12,15 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): def execute(self, context): obj = context.active_object ownership = context.active_object.transfer_data_ownership - task_layer_name = obj.name.split(".")[-1] + file_name = bpy.path.basename(bpy.context.blend_data.filepath) + task_layer_name = file_name.split(".")[-2] for vertex_group in obj.vertex_groups: if not vertex_group.name in [item.name for item in ownership]: item = ownership.add() item.name = vertex_group.name item.owner = task_layer_name.upper() item.type = "VERTEX_GROUP" # TODO Make procedural - item.id = bpy.data.objects[ - f"{obj.name.split('.')[0]}.{obj.asset_id_owner}" - ] + item.id = bpy.data.objects[f"{obj.name.split('.')[-1]}"] return {'FINISHED'} @@ -50,33 +49,16 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_label = 'Pull from Publish' def execute(self, context): - task_col = bpy.data.collections["CH-chr_test"] # TODO replace hard coded value - asset_suffix.add_suffix_to_hierarchy(task_col, "TASK") - - current_file = Path(bpy.data.filepath) - pub_file = core.find_published_file(current_file) - col_name = "CH-chr_test" # TODO replace hard coded value - core.import_data_from_lib(pub_file, "collections", col_name) - appended_col = bpy.data.collections["CH-chr_test"] # TODO find appended data - asset_suffix.add_suffix_to_hierarchy(appended_col, "PUBLISH") - - task_layer_col = bpy.data.collections["CH-chr_test.TASK"] - publish_col = bpy.data.collections["CH-chr_test.PUBLISH"] - - # TODO fix pull function to work with multiple files - return {'FINISHED'} - - # Find current task Layer - task_layer_col = context.collection - current_task_layer = task_layer_col.name.split('.')[-1] - + # TODO move some of this logic into the core pull function + col_base_name = "CH-chr_test" # TODO replace hard coded value + current_task_col = bpy.data.collections[col_base_name] core.pull_task_layer( context, - source_col=publish_col, - target_col=task_layer_col, - source_tl="PUB", - target_tl=current_task_layer, + current_task_col=current_task_col, + col_base_name=col_base_name, + current_tl="MODEL", ) + return {'FINISHED'} classes = ( -- 2.30.2 From 7df6d5a6df4f635ba3163b1b55b80bc0af377e2f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 15 Aug 2023 18:38:19 -0400 Subject: [PATCH 016/429] Asset Pipe: Fix Hierarchy Suffix --- .../addons/asset_pipeline_2/core.py | 24 +++++++++---------- .../addons/asset_pipeline_2/ops.py | 3 ++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 20b3195a..d1a3b100 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -8,14 +8,11 @@ def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Obj transfer_data_ownership = target_obj.transfer_data_ownership transfer_items_names = [item.name for item in transfer_data_ownership] if transfer_data_item.name not in transfer_items_names: - id_name = asset_suffix.get_asset_basename(transfer_data_item.id.name) new_item = transfer_data_ownership.add() new_item.name = transfer_data_item.name new_item.owner = transfer_data_item.owner new_item.type = transfer_data_item.type - new_item.id = bpy.data.objects[ - id_name - ] # TODO replace this pointer with a a string instead + new_item.id = bpy.data.objects[target_obj.name] def apply_transfer_data( @@ -25,7 +22,9 @@ def apply_transfer_data( ): for item in transfer_data_list: for target_obj in target_col.objects: - if target_obj.name == asset_suffix.get_asset_basename(item.id.name): + if asset_suffix.get_asset_basename( + target_obj.name + ) == asset_suffix.get_asset_basename(item.id.name): print(f"{target_obj.name}: READY TO TRANSFER BABY!") transfer_functions.transfer_vertex_group( context=context, @@ -45,7 +44,7 @@ def update_task_layer_objects( ): # Link new obj to collection for transfer_obj in transfer_objs: - obj_root_name = asset_suffix.get_asset_basename(transfer_obj.name) + obj_root_name = transfer_obj.name transfer_obj.name = f"{obj_root_name}" target_col.objects.link(transfer_obj) @@ -82,7 +81,7 @@ def pull_task_layer( context: bpy.types.Context, current_task_col: bpy.types.Collection, col_base_name: str, - current_tl: str, + current_tls: list[str], target_col: bpy.types.Collection, ): current_suffix = "TASK" @@ -99,7 +98,7 @@ def pull_task_layer( current_col = bpy.data.collections[f"{col_base_name}.{current_suffix}"] source_col = bpy.data.collections[f"{col_base_name}.{source_suffix}"] - target_col = bpy.data.collections.new(col_base_name) + target_col = bpy.data.collections.new(f"{col_base_name}.{target_suffix}") # Link Target as new Active Collection context.scene.collection.children.link(target_col) @@ -109,22 +108,22 @@ def pull_task_layer( source_transfer_objs = [] source_transfer_data = [] for obj in source_col.objects: - if obj.asset_id_owner != current_tl: + if obj.asset_id_owner not in current_tls: source_transfer_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: - if item.owner != current_tl: + if item.owner not in current_tls: source_transfer_data.append(item) update_task_layer_objects(target_col, source_transfer_objs) current_transfer_objs = [] current_transfer_data = [] for obj in current_col.objects: - if obj.asset_id_owner == current_tl: + if obj.asset_id_owner in current_tls: current_transfer_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: - if item.owner == current_tl: + if item.owner in current_tls: current_transfer_data.append(item) update_task_layer_objects(target_col, current_transfer_objs) @@ -141,6 +140,7 @@ def pull_task_layer( bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) + asset_suffix.remove_suffix_from_hierarchy(target_col) def find_published_file_version(file): diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 7a149389..be5cd8bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -49,13 +49,14 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_label = 'Pull from Publish' def execute(self, context): - # TODO move some of this logic into the core pull function + current_tl = current_file.name.split('.')[-2] col_base_name = "CH-chr_test" # TODO replace hard coded value current_task_col = bpy.data.collections[col_base_name] core.pull_task_layer( context, current_task_col=current_task_col, col_base_name=col_base_name, + current_tls=[current_tl], current_tl="MODEL", ) return {'FINISHED'} -- 2.30.2 From 2bbb5a3ab65405ff141d964f401812908d80b4dd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 15 Aug 2023 19:11:51 -0400 Subject: [PATCH 017/429] Asset Pipe: Make target_file a variable --- scripts-blender/addons/asset_pipeline_2/core.py | 11 ++++------- scripts-blender/addons/asset_pipeline_2/ops.py | 6 +++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index d1a3b100..5cd75778 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -79,20 +79,17 @@ def push_task_layer( def pull_task_layer( context: bpy.types.Context, - current_task_col: bpy.types.Collection, col_base_name: str, current_tls: list[str], - target_col: bpy.types.Collection, + target_file: Path, ): + current_task_col = bpy.data.collections[col_base_name] current_suffix = "TASK" source_suffix = "PUBLISH" + target_suffix = "TARGET" asset_suffix.add_suffix_to_hierarchy(current_task_col, current_suffix) - current_file = Path(bpy.data.filepath) - pub_file = find_published_file( - current_file - ) # TODO if this function is used in PULL then this needs to be a variable set by the operator instead - import_data_from_lib(pub_file, "collections", col_base_name) + import_data_from_lib(target_file, "collections", col_base_name) appended_col = bpy.data.collections[col_base_name] # find appended data asset_suffix.add_suffix_to_hierarchy(appended_col, source_suffix) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index be5cd8bc..dbd10a5b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -49,15 +49,15 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_label = 'Pull from Publish' def execute(self, context): + current_file = Path(bpy.data.filepath) current_tl = current_file.name.split('.')[-2] + pub_file = core.find_published_file(current_file) col_base_name = "CH-chr_test" # TODO replace hard coded value - current_task_col = bpy.data.collections[col_base_name] core.pull_task_layer( context, - current_task_col=current_task_col, col_base_name=col_base_name, current_tls=[current_tl], - current_tl="MODEL", + target_file=pub_file, ) return {'FINISHED'} -- 2.30.2 From c87100b34de270b1a63aba7420d10df2775bfed1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 15 Aug 2023 20:01:13 -0400 Subject: [PATCH 018/429] Asset Pipe: Update Test Files for Push Test --- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index e4229b31..2e54d456 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b8aecd0f4c2b62fac9f19ccc2db2996dcef784f35dc98146da50aa291826ea0 -size 941076 +oid sha256:e8b06e4f3cf7bba01a1859b3fcd44b104a5ca5df777eb28b1bee07b58a22143a +size 875112 -- 2.30.2 From 49700033aaad30917668c9068f82266e5653a62d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 15 Aug 2023 20:22:34 -0400 Subject: [PATCH 019/429] Asset Pipe: Update Push Function to Use same code as Pull - Rename Pull Function to Merge_Task_Layer - Open Publish on Push and Pull in Data from 'pushing' layer --- .../addons/asset_pipeline_2/core.py | 66 ++++++------------- .../addons/asset_pipeline_2/ops.py | 33 ++++++---- 2 files changed, 41 insertions(+), 58 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 5cd75778..4016421a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -49,80 +49,52 @@ def update_task_layer_objects( target_col.objects.link(transfer_obj) -def push_task_layer( - context: bpy.types.Collection, - source_col: bpy.types.Collection, - current_col: bpy.types.Collection, - source_tl: str, -): - # TODO REFACTOR based on new PULL FUNCTION - # NOTE PUSHING MAY BE AS SIMPLE AS OPENING THE PUBLISH FILE AND PULLING THE CURRENT TASK LAYER IN - # Find Obj owned by Current Task Layer - transfer_objs = [] - transfer_data_list = [] - for source_obj in source_col.objects: - if source_obj.asset_id_owner == source_tl: - transfer_objs.append(source_obj) - # Find Transfer-Data in current TL - for item in source_obj.transfer_data_ownership: - if item.owner == source_tl: - transfer_data_list.append(item) - - update_task_layer_objects( - current_col, - transfer_objs, - ) - - # Move transferrable data onto obj owned by others - apply_transfer_data(context, transfer_data_list, current_col) - - -def pull_task_layer( +def merge_task_layer( context: bpy.types.Context, col_base_name: str, - current_tls: list[str], + local_tls: list[str], target_file: Path, ): - current_task_col = bpy.data.collections[col_base_name] - current_suffix = "TASK" - source_suffix = "PUBLISH" + local_col = bpy.data.collections[col_base_name] + local_suffix = "LOCAL" + source_suffix = "SOURCE" target_suffix = "TARGET" - asset_suffix.add_suffix_to_hierarchy(current_task_col, current_suffix) + asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) import_data_from_lib(target_file, "collections", col_base_name) appended_col = bpy.data.collections[col_base_name] # find appended data asset_suffix.add_suffix_to_hierarchy(appended_col, source_suffix) - current_col = bpy.data.collections[f"{col_base_name}.{current_suffix}"] + local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] source_col = bpy.data.collections[f"{col_base_name}.{source_suffix}"] target_col = bpy.data.collections.new(f"{col_base_name}.{target_suffix}") # Link Target as new Active Collection context.scene.collection.children.link(target_col) - context.scene.collection.children.unlink(current_col) + context.scene.collection.children.unlink(local_col) # Find Obj owned by other Current Task Layer source_transfer_objs = [] source_transfer_data = [] for obj in source_col.objects: - if obj.asset_id_owner not in current_tls: + if obj.asset_id_owner not in local_tls: source_transfer_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: - if item.owner not in current_tls: + if item.owner not in local_tls: source_transfer_data.append(item) update_task_layer_objects(target_col, source_transfer_objs) - current_transfer_objs = [] - current_transfer_data = [] - for obj in current_col.objects: - if obj.asset_id_owner in current_tls: - current_transfer_objs.append(obj) + local_objs = [] + local_transfer_data = [] + for obj in local_col.objects: + if obj.asset_id_owner in local_tls: + local_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: - if item.owner in current_tls: - current_transfer_data.append(item) - update_task_layer_objects(target_col, current_transfer_objs) + if item.owner in local_tls: + local_transfer_data.append(item) + update_task_layer_objects(target_col, local_objs) apply_transfer_data( context, @@ -131,7 +103,7 @@ def pull_task_layer( ) apply_transfer_data( context, - current_transfer_data, + local_transfer_data, target_col, ) # TODO test if only one list of transfer data is needed bpy.ops.outliner.orphans_purge( diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index dbd10a5b..14008768 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -4,6 +4,11 @@ from . import core from pathlib import Path from . import asset_suffix +TASK_LAYER_TYPES = [ + 'RIG', + 'MODEL', +] # TODO Tie this into props and generate based on JSON file instead + class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_idname = "assetpipe.update_ownership" @@ -30,17 +35,23 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): def execute(self, context): # Find current task Layer - source_col = context.collection - source_task_layer = source_col.name.split('.')[-1] - target_task_layer = "PUB" + col_base_name = "CH-chr_test" # TODO replace hard coded value + current_file = Path(bpy.data.filepath) + current_tl = current_file.name.split('.')[-2] + pub_file = core.find_published_file(current_file) + pub_file_path = pub_file.__str__() + bpy.ops.wm.open_mainfile(filepath=pub_file_path) + local_tls = [tl for tl in TASK_LAYER_TYPES if tl != current_tl] - # Find PUBLUSH Collection - for col in context.scene.collection.children_recursive: - if "PUB" in col.name: - target_col = col - core.push_task_layer( - context, source_col, target_col, source_task_layer, target_task_layer + core.merge_task_layer( + context, + col_base_name=col_base_name, + local_tls=local_tls, + target_file=current_file, ) + bpy.ops.wm.save_as_mainfile(filepath=pub_file_path) + bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) + return {'FINISHED'} @@ -53,10 +64,10 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): current_tl = current_file.name.split('.')[-2] pub_file = core.find_published_file(current_file) col_base_name = "CH-chr_test" # TODO replace hard coded value - core.pull_task_layer( + core.merge_task_layer( context, col_base_name=col_base_name, - current_tls=[current_tl], + local_tls=[current_tl], target_file=pub_file, ) return {'FINISHED'} -- 2.30.2 From db3e076e524976f0f662c33b05e578695d3ee71b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 11:14:33 -0400 Subject: [PATCH 020/429] Asset Pipe: Clean-up Transfer Data & Update TODOs --- scripts-blender/addons/asset_pipeline_2/core.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 4016421a..8ac86b5a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -3,6 +3,8 @@ import bpy from . import transfer_functions, asset_suffix from pathlib import Path +# TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 + def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): transfer_data_ownership = target_obj.transfer_data_ownership @@ -43,6 +45,7 @@ def update_task_layer_objects( transfer_objs: list[bpy.types.Object], ): # Link new obj to collection + # TODO implement remap ids for transfer_obj in transfer_objs: obj_root_name = transfer_obj.name transfer_obj.name = f"{obj_root_name}" @@ -75,37 +78,31 @@ def merge_task_layer( # Find Obj owned by other Current Task Layer source_transfer_objs = [] - source_transfer_data = [] + transfer_data = [] for obj in source_col.objects: if obj.asset_id_owner not in local_tls: source_transfer_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: if item.owner not in local_tls: - source_transfer_data.append(item) + transfer_data.append(item) update_task_layer_objects(target_col, source_transfer_objs) local_objs = [] - local_transfer_data = [] for obj in local_col.objects: if obj.asset_id_owner in local_tls: local_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: if item.owner in local_tls: - local_transfer_data.append(item) + transfer_data.append(item) update_task_layer_objects(target_col, local_objs) apply_transfer_data( context, - source_transfer_data, + transfer_data, target_col, ) - apply_transfer_data( - context, - local_transfer_data, - target_col, - ) # TODO test if only one list of transfer data is needed bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) -- 2.30.2 From 3b7ec663d89c76631e5d733bfb546bcaea5df30a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 11:14:56 -0400 Subject: [PATCH 021/429] Asset Pipe: Clean-up Asset Suffix --- scripts-blender/addons/asset_pipeline_2/asset_suffix.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index 7c10cae0..c4da1d59 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -22,16 +22,16 @@ from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids -from .utils import get_storage_of_id +from .util import get_storage_of_id DELIMITER = "." + def get_asset_basename(name): return DELIMITER.join(name.split(DELIMITER)[:-1]) -def remove_suffix_from_hierarchy( - collection: bpy.types.Collection -): + +def remove_suffix_from_hierarchy(collection: bpy.types.Collection): """Removes the suffix after a set delimiter from all datablocks referenced by a collection, itself included""" -- 2.30.2 From 433fc97b0627c5fdf70c77d11a518fccf67108e7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 11:19:49 -0400 Subject: [PATCH 022/429] Asset Pipe: Rename Util File --- scripts-blender/addons/asset_pipeline_2/{utils.py => util.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts-blender/addons/asset_pipeline_2/{utils.py => util.py} (100%) diff --git a/scripts-blender/addons/asset_pipeline_2/utils.py b/scripts-blender/addons/asset_pipeline_2/util.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/utils.py rename to scripts-blender/addons/asset_pipeline_2/util.py -- 2.30.2 From 1b9975f33acce22f2fe44dfe794d7c38cd2d7513 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 12:57:14 -0400 Subject: [PATCH 023/429] Asset Pipe: Add Object to Model Test File --- .../addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend index 8bdc1652..37e302ab 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e84b096378ce5949ff387b73f6194d49acfe0376955b4da19db8683f0d1b00dd -size 884856 +oid sha256:d15692a9e27a5f43eded30fe32e7112a6d038898249ddbc4e4af3364c66de727 +size 945120 -- 2.30.2 From 6b9932ddf9da55f6eba451fe049b9c5d5e11395f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 17:45:40 -0400 Subject: [PATCH 024/429] Asset Pipe: Fix Transfer Data Checking --- .../addons/asset_pipeline_2/core.py | 38 +------------ .../addons/asset_pipeline_2/ops.py | 11 +--- .../asset_pipeline_2/transferable_data.py | 55 +++++++++++++++++++ 3 files changed, 59 insertions(+), 45 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/transferable_data.py diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 8ac86b5a..e91f5724 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,45 +1,11 @@ import bpy -from . import transfer_functions, asset_suffix +from . import asset_suffix, transferable_data from pathlib import Path # TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 -def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): - transfer_data_ownership = target_obj.transfer_data_ownership - transfer_items_names = [item.name for item in transfer_data_ownership] - if transfer_data_item.name not in transfer_items_names: - new_item = transfer_data_ownership.add() - new_item.name = transfer_data_item.name - new_item.owner = transfer_data_item.owner - new_item.type = transfer_data_item.type - new_item.id = bpy.data.objects[target_obj.name] - - -def apply_transfer_data( - context: bpy.types.Context, - transfer_data_list, - target_col: bpy.types.Collection, -): - for item in transfer_data_list: - for target_obj in target_col.objects: - if asset_suffix.get_asset_basename( - target_obj.name - ) == asset_suffix.get_asset_basename(item.id.name): - print(f"{target_obj.name}: READY TO TRANSFER BABY!") - transfer_functions.transfer_vertex_group( - context=context, - vertex_group_name=item.name, - target_obj=target_obj, - source_obj=item.id, - ) - update_transfer_data_ownership( - transfer_data_item=item, - target_obj=target_obj, - ) - - def update_task_layer_objects( target_col: bpy.types.Collection, transfer_objs: list[bpy.types.Object], @@ -98,7 +64,7 @@ def merge_task_layer( transfer_data.append(item) update_task_layer_objects(target_col, local_objs) - apply_transfer_data( + transferable_data.apply_transfer_data( context, transfer_data, target_col, diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 14008768..5eb05753 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,7 +2,7 @@ import bpy from . import core from pathlib import Path -from . import asset_suffix +from . import transferable_data TASK_LAYER_TYPES = [ 'RIG', @@ -16,16 +16,9 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): def execute(self, context): obj = context.active_object - ownership = context.active_object.transfer_data_ownership file_name = bpy.path.basename(bpy.context.blend_data.filepath) task_layer_name = file_name.split(".")[-2] - for vertex_group in obj.vertex_groups: - if not vertex_group.name in [item.name for item in ownership]: - item = ownership.add() - item.name = vertex_group.name - item.owner = task_layer_name.upper() - item.type = "VERTEX_GROUP" # TODO Make procedural - item.id = bpy.data.objects[f"{obj.name.split('.')[-1]}"] + transferable_data.vertex_groups_update(obj, task_layer_name) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py new file mode 100644 index 00000000..3cf2482d --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -0,0 +1,55 @@ +import bpy + +from . import transfer_functions, asset_suffix + + +def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): + transfer_data_ownership = target_obj.transfer_data_ownership + transfer_items_names = [item.name for item in transfer_data_ownership] + if transfer_data_item.name not in transfer_items_names: + new_item = transfer_data_ownership.add() + new_item.name = transfer_data_item.name + new_item.owner = transfer_data_item.owner + new_item.type = transfer_data_item.type + new_item.id = bpy.data.objects[target_obj.name] + + +def apply_transfer_data( + context: bpy.types.Context, + transfer_data_list, + target_col: bpy.types.Collection, +): + for item in transfer_data_list: + for target_obj in target_col.objects: + if asset_suffix.get_asset_basename( + target_obj.name + ) == asset_suffix.get_asset_basename(item.id.name): + print(f"{target_obj.name}: READY TO TRANSFER BABY!") + transfer_functions.transfer_vertex_group( + context=context, + vertex_group_name=item.name, + target_obj=target_obj, + source_obj=item.id, + ) + update_transfer_data_ownership( + transfer_data_item=item, + target_obj=target_obj, + ) + + +## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES + + +# VERTEX GROUPS +def vertex_groups_update(obj, task_layer_name): + ownership = obj.transfer_data_ownership + for vertex_group in obj.vertex_groups: + # Only add new ownership item if vertex group doesn't have an owner + existing_items = [item.name for item in ownership] + matches = set([vertex_group.name]).intersection(set(existing_items)) + if len(matches) == 0: + item = ownership.add() + item.name = vertex_group.name + item.owner = task_layer_name.upper() + item.type = "VERTEX_GROUP" + item.id = bpy.data.objects[f"{obj.name.split('.')[-1]}"] -- 2.30.2 From e341c84269acc1d46eee241d7340c8f6e23b7472 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 18:19:18 -0400 Subject: [PATCH 025/429] Asset Pipe: Remap Data-Block Users during Task Layer Merge --- .../addons/asset_pipeline_2/core.py | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index e91f5724..ada48694 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,6 +1,6 @@ import bpy -from . import asset_suffix, transferable_data +from . import asset_suffix, transferable_data, util from pathlib import Path # TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 @@ -11,7 +11,6 @@ def update_task_layer_objects( transfer_objs: list[bpy.types.Object], ): # Link new obj to collection - # TODO implement remap ids for transfer_obj in transfer_objs: obj_root_name = transfer_obj.name transfer_obj.name = f"{obj_root_name}" @@ -69,6 +68,9 @@ def merge_task_layer( transfer_data, target_col, ) + + remap_users(context) + bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) @@ -127,3 +129,50 @@ def import_data_from_lib( ) return eval(f"bpy.data.{data_category}['{data_name}']") + + +### REMAPPING IDS + + +def remap_users(context, suf=".SOURCE"): + """ + When objects inside the asset collection reference datablocks outside of + the asset collection or vice versa, some duplication can occur, as + outside objects end up with a .TASK suffix, and they end up referencing + objects that are no longer linked to the scene. + + Objects inside the asset collection correctly lose their suffix, but + also end up referencing outside objects without the suffix, which are + actually the wrong ones. + + So this function remaps references such that everything inside and outside + the asset collection reference each other once again, and removes + any leftover .TASK suffixes. + """ + + # suf = constants.TASK_SUFFIX + for datablock in bpy.data.user_map(): + has_type = hasattr(datablock, 'type') + if ( + has_type + and datablock.type == 'OBJECT' + and datablock.name not in context.scene.objects + ): + # Objects that aren't in the scene have been replaced by the pull + # process, so we don't want to remap any references to them. + continue + storage = util.get_storage_of_id(datablock) + if not datablock.name.endswith(suf): + continue + + without_suffix = datablock.name.replace(suf, ".LOCAL") + other_db = storage.get(without_suffix) + if not other_db: + continue + + # print(f'REMAP USERS: "{other_db.name}" -> "{datablock.name}"') + other_db.user_remap(datablock) + # Rename the object to make its name available. + # This datablock should get purged soon, otherwise it's a bug. + other_db.name += "_Users_Remapped" + datablock.name = without_suffix -- 2.30.2 From ce3ee011d3056a6387c172f076a8065fa5992b34 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 22:45:42 -0400 Subject: [PATCH 026/429] Asset Pipe: Update Test Files to test Transfer of Modifiers --- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index a135040f..410b1ab3 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:091b9f2ed80aa32a2510f50d323cd6a620d7310039136c5322d13eeb9391e275 -size 941076 +oid sha256:253ec7cd4395deb362f7e6544a34fd3027e224ebabfd9f87785d81e227d31c93 +size 958060 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index 2e54d456..66624187 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8b06e4f3cf7bba01a1859b3fcd44b104a5ca5df777eb28b1bee07b58a22143a -size 875112 +oid sha256:7d302dcc89914d8826bc8ee64a0c74450510b89cf2ac2e05267e376538165c0a +size 922612 -- 2.30.2 From dc5c12e8517958520cd1bb0d8b33c0f6250fea64 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 23:02:53 -0400 Subject: [PATCH 027/429] Asset Pipe: Add Modifiers as Transfer Data Type --- .../addons/asset_pipeline_2/ops.py | 3 +- .../asset_pipeline_2/transfer_functions.py | 69 +++++++++++++++++-- .../asset_pipeline_2/transferable_data.py | 56 ++++++++++----- 3 files changed, 106 insertions(+), 22 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 5eb05753..69fb7865 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,7 +2,7 @@ import bpy from . import core from pathlib import Path -from . import transferable_data +from . import transferable_data, transfer_functions TASK_LAYER_TYPES = [ 'RIG', @@ -19,6 +19,7 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): file_name = bpy.path.basename(bpy.context.blend_data.filepath) task_layer_name = file_name.split(".")[-2] transferable_data.vertex_groups_update(obj, task_layer_name) + transferable_data.modifiers_update(obj, task_layer_name) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index 47c3baf1..beb57b52 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -1,13 +1,19 @@ import bpy from bpy import context -def transfer_vertex_group(context, vertex_group_name:str, target_obj:bpy.types.Object, source_obj:bpy.types.Object): + +def transfer_vertex_group( + context, + vertex_group_name: str, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, +): source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] override = context.copy() override["selected_editable_objects"] = [target_obj, source_obj] override["active_object"] = source_obj - override["object"] = source_obj #TODO test if needed - with context.temp_override(**override ): + override["object"] = source_obj # TODO test if needed + with context.temp_override(**override): bpy.ops.object.data_transfer( data_type="VGROUP_WEIGHTS", use_create=True, @@ -15,4 +21,59 @@ def transfer_vertex_group(context, vertex_group_name:str, target_obj:bpy.types.O layers_select_src="ACTIVE", layers_select_dst="NAME", mix_mode="REPLACE", - ) \ No newline at end of file + ) + + +def transfer_modifier(item, obj_target): + # remove old and sync existing modifiers + obj_source = item.id + old_mod = obj_target.modifiers.get(item.name) + if old_mod: + obj_target.modifiers.remove(old_mod) + + # transfer new modifiers + for i, mod in enumerate(obj_source.modifiers): + if mod.name == item.name: + mod_new = obj_target.modifiers.new(mod.name, mod.type) + # sort new modifier at correct index (default to beginning of the stack) + idx = 0 + if i > 0: + name_prev = obj_source.modifiers[i - 1].name + for target_mod_i, target_mod in enumerate(obj_target.modifiers): + if target_mod.name == name_prev: + idx = target_mod_i + 1 + bpy.ops.object.modifier_move_to_index( + {'object': obj_target}, modifier=mod_new.name, index=idx + ) + mod_target = obj_target.modifiers.get(mod.name) + props = [p.identifier for p in mod.bl_rna.properties if not p.is_readonly] + for prop in props: + value = getattr(mod, prop) + setattr(mod_target, prop, value) + + # rebind modifiers (corr. smooth, surf. deform, mesh deform) + for mod in obj_target.modifiers: + if mod.type == 'SURFACE_DEFORM': + if not mod.is_bound: + continue + for i in range(2): + bpy.ops.object.surfacedeform_bind( + {"object": obj_target, "active_object": obj_target}, + modifier=mod.name, + ) + elif mod.type == 'MESH_DEFORM': + if not mod.is_bound: + continue + for i in range(2): + bpy.ops.object.meshdeform_bind( + {"object": obj_target, "active_object": obj_target}, + modifier=mod.name, + ) + elif mod.type == 'CORRECTIVE_SMOOTH': + if not mod.is_bind: + continue + for i in range(2): + bpy.ops.object.correctivesmooth_bind( + {"object": obj_target, "active_object": obj_target}, + modifier=mod.name, + ) diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index 3cf2482d..391eb172 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -11,7 +11,7 @@ def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Obj new_item.name = transfer_data_item.name new_item.owner = transfer_data_item.owner new_item.type = transfer_data_item.type - new_item.id = bpy.data.objects[target_obj.name] + new_item.id = target_obj def apply_transfer_data( @@ -24,32 +24,54 @@ def apply_transfer_data( if asset_suffix.get_asset_basename( target_obj.name ) == asset_suffix.get_asset_basename(item.id.name): - print(f"{target_obj.name}: READY TO TRANSFER BABY!") - transfer_functions.transfer_vertex_group( - context=context, - vertex_group_name=item.name, - target_obj=target_obj, - source_obj=item.id, - ) + if item.type == "VERTEX_GROUP": + transfer_functions.transfer_vertex_group( + context=context, + vertex_group_name=item.name, + target_obj=target_obj, + source_obj=item.id, + ) + if item.type == "MODIFIER": + transfer_functions.transfer_modifier(item, target_obj) update_transfer_data_ownership( transfer_data_item=item, target_obj=target_obj, ) +def check_transfer_data_entry(ownership, key, td_type): + existing_items = [item.name for item in ownership if item.type == td_type] + return set([key]).intersection(set(existing_items)) + + +def transfer_data_add_entry(ownership, name, td_type, task_layer_name): + obj = ownership.id_data + item = ownership.add() + item.name = name + item.owner = task_layer_name.upper() + item.type = td_type + item.id = obj + + ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES - - # VERTEX GROUPS def vertex_groups_update(obj, task_layer_name): ownership = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: # Only add new ownership item if vertex group doesn't have an owner - existing_items = [item.name for item in ownership] - matches = set([vertex_group.name]).intersection(set(existing_items)) + matches = check_transfer_data_entry( + ownership, vertex_group.name, "VERTEX_GROUP" + ) if len(matches) == 0: - item = ownership.add() - item.name = vertex_group.name - item.owner = task_layer_name.upper() - item.type = "VERTEX_GROUP" - item.id = bpy.data.objects[f"{obj.name.split('.')[-1]}"] + transfer_data_add_entry( + ownership, vertex_group.name, "VERTEX_GROUP", task_layer_name + ) + + +# MODIFIERS +def modifiers_update(obj, task_layer_name): + ownership = obj.transfer_data_ownership + for mod in obj.modifiers: + matches = check_transfer_data_entry(ownership, mod.name, "MODIFIER") + if len(matches) == 0: + transfer_data_add_entry(ownership, mod.name, "MODIFIER", task_layer_name) -- 2.30.2 From 45d7f6ba7b172115ea8156fcd01a32e448c56921 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 16 Aug 2023 23:03:26 -0400 Subject: [PATCH 028/429] Asset Pipe: Add TODO for properties --- scripts-blender/addons/asset_pipeline_2/props.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 14eb5523..23c3a3f3 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -12,6 +12,12 @@ class ASSETOWNERSHIP(bpy.types.PropertyGroup): type: bpy.props.StringProperty(name="Transfer Data Type", default="") id: bpy.props.PointerProperty(type=bpy.types.Object) + # TODO USE ENUM FOR TRANSFER DATA TYPE + # type: bpy.props.EnumProperty( + # name="TD TYPE", + # items=[("NONE", "None", ""), ("MODEL", "Modeling", ""), ("RIG", "Rigging", "")], + # ) + classes = (ASSETOWNERSHIP,) -- 2.30.2 From 31495bd4f255996e37c9f415a9cd98d22ebd5ce6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 17 Aug 2023 11:05:46 -0400 Subject: [PATCH 029/429] Asset Pipe: Make Task Layer Items a Constant --- .../addons/asset_pipeline_2/constants.py | 7 +++++++ .../addons/asset_pipeline_2/ops.py | 21 ++++++++++--------- .../addons/asset_pipeline_2/props.py | 5 ++--- 3 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/constants.py diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py new file mode 100644 index 00000000..d89328b1 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -0,0 +1,7 @@ +# TODO Tie this into props and generate based on JSON file instead + +TASK_LAYER_ITEMS = [ + ("NONE", "None", ""), + ("MODEL", "Modeling", ""), + ("RIG", "Rigging", ""), +] diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 69fb7865..6e53cb86 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,12 +2,7 @@ import bpy from . import core from pathlib import Path -from . import transferable_data, transfer_functions - -TASK_LAYER_TYPES = [ - 'RIG', - 'MODEL', -] # TODO Tie this into props and generate based on JSON file instead +from . import transferable_data, transfer_functions, constants class ASSETPIPE_OT_update_ownership(bpy.types.Operator): @@ -17,6 +12,7 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): def execute(self, context): obj = context.active_object file_name = bpy.path.basename(bpy.context.blend_data.filepath) + # TODO check if exists in task_layer_constants task_layer_name = file_name.split(".")[-2] transferable_data.vertex_groups_update(obj, task_layer_name) transferable_data.modifiers_update(obj, task_layer_name) @@ -31,11 +27,15 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): # Find current task Layer col_base_name = "CH-chr_test" # TODO replace hard coded value current_file = Path(bpy.data.filepath) - current_tl = current_file.name.split('.')[-2] + # TODO check if exists in task_layer_constants + task_layer_name = current_file.name.split('.')[-2] pub_file = core.find_published_file(current_file) pub_file_path = pub_file.__str__() bpy.ops.wm.open_mainfile(filepath=pub_file_path) - local_tls = [tl for tl in TASK_LAYER_TYPES if tl != current_tl] + + local_tls = [ + item[0] for item in constants.TASK_LAYER_ITEMS if item[0] != task_layer_name + ] core.merge_task_layer( context, @@ -55,13 +55,14 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): def execute(self, context): current_file = Path(bpy.data.filepath) - current_tl = current_file.name.split('.')[-2] + # TODO check if exists in task_layer_constants + task_layer_name = current_file.name.split('.')[-2] pub_file = core.find_published_file(current_file) col_base_name = "CH-chr_test" # TODO replace hard coded value core.merge_task_layer( context, col_base_name=col_base_name, - local_tls=[current_tl], + local_tls=[task_layer_name], target_file=pub_file, ) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 23c3a3f3..73933e93 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -5,8 +5,7 @@ import bpy avaliable task layers from the task_layer_defaults.json file that needs to be created. """ - -# items=[("MODEL", "Rigging", ""), ("RIG", "Modeling", "")], +from . import constants class ASSETOWNERSHIP(bpy.types.PropertyGroup): owner: bpy.props.StringProperty(name="Transfer Data Owner", default="") type: bpy.props.StringProperty(name="Transfer Data Type", default="") @@ -30,7 +29,7 @@ def register(): ) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", - items=[("NONE", "None", ""), ("MODEL", "Modeling", ""), ("RIG", "Rigging", "")], + items=constants.TASK_LAYER_ITEMS, ) -- 2.30.2 From 41fe7220adecfafc0e1572e8e34373c1771697bd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 17 Aug 2023 11:10:10 -0400 Subject: [PATCH 030/429] Asset Pipe: Replace Strings with Enums --- .../addons/asset_pipeline_2/constants.py | 6 ++++++ .../addons/asset_pipeline_2/props.py | 20 ++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index d89328b1..d37a6d6d 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -5,3 +5,9 @@ TASK_LAYER_ITEMS = [ ("MODEL", "Modeling", ""), ("RIG", "Rigging", ""), ] + +TRANSFER_DATA_TYPES = [ + ("NONE", "None", ""), + ("VERTEX_GROUP", "Vertex Group", ""), + ("MODIFIER", "Modifier", ""), + ] \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 73933e93..f551b785 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -6,16 +6,18 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr """ from . import constants -class ASSETOWNERSHIP(bpy.types.PropertyGroup): - owner: bpy.props.StringProperty(name="Transfer Data Owner", default="") - type: bpy.props.StringProperty(name="Transfer Data Type", default="") - id: bpy.props.PointerProperty(type=bpy.types.Object) - # TODO USE ENUM FOR TRANSFER DATA TYPE - # type: bpy.props.EnumProperty( - # name="TD TYPE", - # items=[("NONE", "None", ""), ("MODEL", "Modeling", ""), ("RIG", "Rigging", "")], - # ) + +class ASSETOWNERSHIP(bpy.types.PropertyGroup): + owner: bpy.props.EnumProperty( + name="Transfer Data Owner", + items=constants.TASK_LAYER_ITEMS, + ) + type: bpy.props.EnumProperty( + name="Transfer Data Type", + items=constants.TRANSFER_DATA_TYPES, + ) + id: bpy.props.PointerProperty(type=bpy.types.Object) classes = (ASSETOWNERSHIP,) -- 2.30.2 From 545eb2fdeb0090c09f1348375849bcc3a3c18be7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 17 Aug 2023 11:11:26 -0400 Subject: [PATCH 031/429] Asset Pipe: Add TODO to Vertex Group Transfer Funct --- scripts-blender/addons/asset_pipeline_2/transfer_functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index beb57b52..4f8f1425 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -8,6 +8,7 @@ def transfer_vertex_group( target_obj: bpy.types.Object, source_obj: bpy.types.Object, ): + # TODO remove old vertex groups before transfer !! source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] override = context.copy() override["selected_editable_objects"] = [target_obj, source_obj] -- 2.30.2 From 9bc898cdae61374d53ba9aeec911e438931a2eeb Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 17 Aug 2023 12:25:40 -0400 Subject: [PATCH 032/429] Asset Pipe: Add TODO for Collections --- scripts-blender/addons/asset_pipeline_2/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index ada48694..2aa9c1e0 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -37,6 +37,7 @@ def merge_task_layer( source_col = bpy.data.collections[f"{col_base_name}.{source_suffix}"] target_col = bpy.data.collections.new(f"{col_base_name}.{target_suffix}") + # TODO Make sure we preserve the collection hirearchies instead of having one flat col # Link Target as new Active Collection context.scene.collection.children.link(target_col) context.scene.collection.children.unlink(local_col) -- 2.30.2 From 6755f0e38d894f3baad714bacf655af8c96e654c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 17 Aug 2023 14:36:36 -0400 Subject: [PATCH 033/429] Asset Pipe: Add Shading task layer --- .../asset_pipeline_2/chr_test/chr_test.MODEL.blend | 4 ++-- .../asset_pipeline_2/chr_test/chr_test.SHADE.blend | 3 +++ .../chr_test/publish/chr_test.v001.blend | 4 ++-- scripts-blender/addons/asset_pipeline_2/constants.py | 9 +++++---- 4 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend index 37e302ab..d5510996 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d15692a9e27a5f43eded30fe32e7112a6d038898249ddbc4e4af3364c66de727 -size 945120 +oid sha256:e9d71cef6d0b6630206df2be7ec6e56e97641204dec5c15d91d5aa9e7e0d9277 +size 925316 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend new file mode 100644 index 00000000..65b91ee4 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e2cfb7e8643a3ba28a42ff141e857569fda58037d09bf99d35f0d24bab33e28 +size 955580 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index 66624187..c369c64f 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d302dcc89914d8826bc8ee64a0c74450510b89cf2ac2e05267e376538165c0a -size 922612 +oid sha256:e829b7c262f57332343b237c13d092ec73ad787c30ff79ad2cc8c0195a65e80a +size 899228 diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index d37a6d6d..68213f18 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -4,10 +4,11 @@ TASK_LAYER_ITEMS = [ ("NONE", "None", ""), ("MODEL", "Modeling", ""), ("RIG", "Rigging", ""), + ("SHADE", "Shading", ""), ] TRANSFER_DATA_TYPES = [ - ("NONE", "None", ""), - ("VERTEX_GROUP", "Vertex Group", ""), - ("MODIFIER", "Modifier", ""), - ] \ No newline at end of file + ("NONE", "None", ""), + ("VERTEX_GROUP", "Vertex Group", ""), + ("MODIFIER", "Modifier", ""), +] -- 2.30.2 From ac76652d97092d296a0769957dd8dd24d1b17bd1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 17 Aug 2023 14:50:21 -0400 Subject: [PATCH 034/429] Asset Pipe: Add Material Slot to Transfer Data Types --- scripts-blender/addons/asset_pipeline_2/constants.py | 1 + scripts-blender/addons/asset_pipeline_2/ops.py | 1 + .../addons/asset_pipeline_2/transferable_data.py | 11 +++++++++++ 3 files changed, 13 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 68213f18..7362e9e9 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -11,4 +11,5 @@ TRANSFER_DATA_TYPES = [ ("NONE", "None", ""), ("VERTEX_GROUP", "Vertex Group", ""), ("MODIFIER", "Modifier", ""), + ("MATERIAL_SLOT", "Material Slot", ""), ] diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 6e53cb86..93329d40 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -16,6 +16,7 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): task_layer_name = file_name.split(".")[-2] transferable_data.vertex_groups_update(obj, task_layer_name) transferable_data.modifiers_update(obj, task_layer_name) + transferable_data.material_slot_update(obj, task_layer_name) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index 391eb172..64d4cc01 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -75,3 +75,14 @@ def modifiers_update(obj, task_layer_name): matches = check_transfer_data_entry(ownership, mod.name, "MODIFIER") if len(matches) == 0: transfer_data_add_entry(ownership, mod.name, "MODIFIER", task_layer_name) + + +# MATERIAL SLOT +def material_slot_update(obj, task_layer_name): + ownership = obj.transfer_data_ownership + for slot in obj.material_slots: + matches = check_transfer_data_entry(ownership, slot.name, "MATERIAL_SLOT") + if len(matches) == 0: + transfer_data_add_entry( + ownership, slot.name, "MATERIAL_SLOT", task_layer_name + ) -- 2.30.2 From be72175f4a45ca03777fcc96fbca0a5965dee6da Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 17 Aug 2023 15:49:31 -0400 Subject: [PATCH 035/429] Asset Pipe: Add Material / Material Slot to Transfer Functions --- .../addons/asset_pipeline_2/core.py | 11 +++-- .../asset_pipeline_2/transfer_functions.py | 43 +++++++++++++++++++ .../asset_pipeline_2/transferable_data.py | 2 + 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 2aa9c1e0..5f44edd4 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -69,12 +69,17 @@ def merge_task_layer( transfer_data, target_col, ) - - remap_users(context) - bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) + + # TODO DEBUG WHY REMAP USERS SOMETIMES REPLACES LOCAL OBJ WITH SOURCE OBJ + # ADDING A PURGE BEFORE THIS FIXES IT FOR SOME REASON? + remap_users(context) + + # bpy.ops.outliner.orphans_purge( + # do_local_ids=True, do_linked_ids=False, do_recursive=True + # ) asset_suffix.remove_suffix_from_hierarchy(target_col) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index 4f8f1425..f5c634ad 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -1,5 +1,6 @@ import bpy from bpy import context +from . import asset_suffix def transfer_vertex_group( @@ -78,3 +79,45 @@ def transfer_modifier(item, obj_target): {"object": obj_target, "active_object": obj_target}, modifier=mod.name, ) + + +def transfer_material_slot(item, obj_target): + obj_source = item.id + # Delete existing material slot if exists + for idx in range(len(obj_source.material_slots)): + slot = obj_source.material_slots[idx] + if asset_suffix.get_asset_basename(slot.material.name) == item.name: + obj_target.active_material_index = idx + bpy.ops.object.material_slot_remove({"object": obj_target}) + + # Transfer material slots + + for idx in range(len(obj_source.material_slots)): + if idx >= len(obj_target.material_slots): + slot = obj_source.material_slots[idx] + if asset_suffix.get_asset_basename(slot.material.name) == item.name: + bpy.ops.object.material_slot_add({"object": obj_target}) + obj_target.material_slots[idx].link = obj_source.material_slots[ + idx + ].link + obj_target.material_slots[idx].material = obj_source.material_slots[ + idx + ].material + + # Transfer active material slot index + obj_target.active_material_index = obj_source.active_material_index + + # Transfer material slot assignments for curve + if obj_target.type == "CURVE": + for spl_to, spl_from in zip(obj_target.data.splines, obj_source.data.splines): + spl_to.material_index = spl_from.material_index + + # TODO MAKE USE_ABLE AGAIN + # # Rest of the loop applies only to meshes. + # if obj_target.type != "MESH": + # continue + + # # Transfer material slot assignments for mesh + # for pol_to, pol_from in zip(obj_target.data.polygons, obj_source.data.polygons): + # pol_to.material_index = pol_from.material_index + # pol_to.use_smooth = pol_from.use_smooth diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index 64d4cc01..efc4b80c 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -33,6 +33,8 @@ def apply_transfer_data( ) if item.type == "MODIFIER": transfer_functions.transfer_modifier(item, target_obj) + if item.type == "MATERIAL_SLOT": + transfer_functions.transfer_material_slot(item, target_obj) update_transfer_data_ownership( transfer_data_item=item, target_obj=target_obj, -- 2.30.2 From de36680359d78c58a78ed93b816427ceba6fbeb4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 15:57:35 -0400 Subject: [PATCH 036/429] Asset Pipe: Rename get_asset_basename to get_basename --- scripts-blender/addons/asset_pipeline_2/asset_suffix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index c4da1d59..d02d5190 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -27,7 +27,7 @@ from .util import get_storage_of_id DELIMITER = "." -def get_asset_basename(name): +def get_basename(name): return DELIMITER.join(name.split(DELIMITER)[:-1]) @@ -43,7 +43,7 @@ def remove_suffix_from_hierarchy(collection: bpy.types.Collection): # Don't rename linked datablocks. continue try: - db.name = get_asset_basename(db.name) + db.name = get_basename(db.name) except: pass -- 2.30.2 From 1397623bc0d66e4fcbd35e14466708cf84994827 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 17:07:24 -0400 Subject: [PATCH 037/429] Asset Pipe: Standardize obj_source and obj_target --- .../addons/asset_pipeline_2/transfer_functions.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index f5c634ad..7b19951b 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -6,15 +6,17 @@ from . import asset_suffix def transfer_vertex_group( context, vertex_group_name: str, - target_obj: bpy.types.Object, - source_obj: bpy.types.Object, + obj_source: bpy.types.Object, + obj_target: bpy.types.Object, ): + if obj_source == obj_target: + return # TODO remove old vertex groups before transfer !! - source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] + obj_target.vertex_groups.active = obj_target.vertex_groups[vertex_group_name] override = context.copy() - override["selected_editable_objects"] = [target_obj, source_obj] - override["active_object"] = source_obj - override["object"] = source_obj # TODO test if needed + override["selected_editable_objects"] = [obj_source, obj_target] + override["active_object"] = obj_target + override["object"] = obj_target # TODO test if needed with context.temp_override(**override): bpy.ops.object.data_transfer( data_type="VGROUP_WEIGHTS", -- 2.30.2 From ed63af71f9fb9b4420e86721910cddb1832c3e28 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 17:07:58 -0400 Subject: [PATCH 038/429] Asset Pipe: Early Return if target is source in transfer functions --- scripts-blender/addons/asset_pipeline_2/transfer_functions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index 7b19951b..519909ea 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -31,6 +31,8 @@ def transfer_vertex_group( def transfer_modifier(item, obj_target): # remove old and sync existing modifiers obj_source = item.id + if obj_source == obj_target: + return old_mod = obj_target.modifiers.get(item.name) if old_mod: obj_target.modifiers.remove(old_mod) @@ -85,6 +87,8 @@ def transfer_modifier(item, obj_target): def transfer_material_slot(item, obj_target): obj_source = item.id + if obj_source == obj_target: + return # Delete existing material slot if exists for idx in range(len(obj_source.material_slots)): slot = obj_source.material_slots[idx] -- 2.30.2 From ec2c10b08563e82370a7e8bcba530048423f3eda Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 17:09:18 -0400 Subject: [PATCH 039/429] Asset Pipe: Fix get_basename renaming --- .../addons/asset_pipeline_2/transfer_functions.py | 4 ++-- .../addons/asset_pipeline_2/transferable_data.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index 519909ea..4fab2b49 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -92,7 +92,7 @@ def transfer_material_slot(item, obj_target): # Delete existing material slot if exists for idx in range(len(obj_source.material_slots)): slot = obj_source.material_slots[idx] - if asset_suffix.get_asset_basename(slot.material.name) == item.name: + if asset_suffix.get_basename(slot.material.name) == item.name: obj_target.active_material_index = idx bpy.ops.object.material_slot_remove({"object": obj_target}) @@ -101,7 +101,7 @@ def transfer_material_slot(item, obj_target): for idx in range(len(obj_source.material_slots)): if idx >= len(obj_target.material_slots): slot = obj_source.material_slots[idx] - if asset_suffix.get_asset_basename(slot.material.name) == item.name: + if asset_suffix.get_basename(slot.material.name) == item.name: bpy.ops.object.material_slot_add({"object": obj_target}) obj_target.material_slots[idx].link = obj_source.material_slots[ idx diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index efc4b80c..070a6146 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -21,9 +21,9 @@ def apply_transfer_data( ): for item in transfer_data_list: for target_obj in target_col.objects: - if asset_suffix.get_asset_basename( - target_obj.name - ) == asset_suffix.get_asset_basename(item.id.name): + if asset_suffix.get_basename(target_obj.name) == asset_suffix.get_basename( + item.id.name + ): if item.type == "VERTEX_GROUP": transfer_functions.transfer_vertex_group( context=context, -- 2.30.2 From 6c6c8669362d83e344eeb7270a606d6e1a83875a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 17:10:21 -0400 Subject: [PATCH 040/429] Asset Pipe: Fix Standardize obj_source and obj_target --- scripts-blender/addons/asset_pipeline_2/transferable_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index 070a6146..4d2173a5 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -28,8 +28,8 @@ def apply_transfer_data( transfer_functions.transfer_vertex_group( context=context, vertex_group_name=item.name, - target_obj=target_obj, - source_obj=item.id, + obj_source=target_obj, + obj_target=item.id, ) if item.type == "MODIFIER": transfer_functions.transfer_modifier(item, target_obj) -- 2.30.2 From b78c48811a0e33d00852a12d7722232ea0e2e029 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 17:11:09 -0400 Subject: [PATCH 041/429] Asset Pipe: Fix Remapping of Data-Blocks on Push & Pull --- .../addons/asset_pipeline_2/core.py | 77 +++++++------------ 1 file changed, 28 insertions(+), 49 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 5f44edd4..b057b3ad 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -2,6 +2,7 @@ import bpy from . import asset_suffix, transferable_data, util from pathlib import Path +from .constants import TASK_LAYER_ITEMS # TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 @@ -64,22 +65,13 @@ def merge_task_layer( transfer_data.append(item) update_task_layer_objects(target_col, local_objs) - transferable_data.apply_transfer_data( - context, - transfer_data, - target_col, - ) + transferable_data.apply_transfer_data(context, transfer_data, target_col) + + remap_external_datablocks(context) + bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) - - # TODO DEBUG WHY REMAP USERS SOMETIMES REPLACES LOCAL OBJ WITH SOURCE OBJ - # ADDING A PURGE BEFORE THIS FIXES IT FOR SOME REASON? - remap_users(context) - - # bpy.ops.outliner.orphans_purge( - # do_local_ids=True, do_linked_ids=False, do_recursive=True - # ) asset_suffix.remove_suffix_from_hierarchy(target_col) @@ -138,47 +130,34 @@ def import_data_from_lib( ### REMAPPING IDS - - -def remap_users(context, suf=".SOURCE"): - """ - When objects inside the asset collection reference datablocks outside of - the asset collection or vice versa, some duplication can occur, as - outside objects end up with a .TASK suffix, and they end up referencing - objects that are no longer linked to the scene. - - Objects inside the asset collection correctly lose their suffix, but - also end up referencing outside objects without the suffix, which are - actually the wrong ones. - - So this function remaps references such that everything inside and outside - the asset collection reference each other once again, and removes - any leftover .TASK suffixes. - """ - - # suf = constants.TASK_SUFFIX +def remap_get_data_blocks(context, suffix): + retun_datablocks = [] for datablock in bpy.data.user_map(): has_type = hasattr(datablock, 'type') - if ( + # TODO IMPROVE FILTERING? + if not ( has_type and datablock.type == 'OBJECT' and datablock.name not in context.scene.objects ): - # Objects that aren't in the scene have been replaced by the pull - # process, so we don't want to remap any references to them. - continue - storage = util.get_storage_of_id(datablock) - if not datablock.name.endswith(suf): - continue + if datablock.name.endswith(suffix): + retun_datablocks.append(datablock) + return retun_datablocks - without_suffix = datablock.name.replace(suf, ".LOCAL") - other_db = storage.get(without_suffix) - if not other_db: - continue - # print(f'REMAP USERS: "{other_db.name}" -> "{datablock.name}"') - other_db.user_remap(datablock) - # Rename the object to make its name available. - # This datablock should get purged soon, otherwise it's a bug. - other_db.name += "_Users_Remapped" - datablock.name = without_suffix +def remap_user(datablock, target_name): + storage = util.get_storage_of_id(datablock) + remap_datablock = storage.get(target_name) + if remap_datablock: + remap_datablock.user_remap(datablock) + remap_datablock.name += "_Users_Remapped" + + +def remap_external_datablocks(context): + source_suffix = ".SOURCE" + target_suffix = ".LOCAL" + + datablocks = remap_get_data_blocks(context, source_suffix) + for datablock in datablocks: + target_name = datablock.name.replace(source_suffix, target_suffix) + remap_user(datablock, target_name) -- 2.30.2 From 80ecf237c082301333fc95ee2c22d099dccfc682 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 17:17:10 -0400 Subject: [PATCH 042/429] Asset Pipe: Rename Source to External --- .../addons/asset_pipeline_2/core.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index b057b3ad..2f537027 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -26,16 +26,16 @@ def merge_task_layer( ): local_col = bpy.data.collections[col_base_name] local_suffix = "LOCAL" - source_suffix = "SOURCE" + external_suffix = "EXTERNAL" target_suffix = "TARGET" asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) import_data_from_lib(target_file, "collections", col_base_name) appended_col = bpy.data.collections[col_base_name] # find appended data - asset_suffix.add_suffix_to_hierarchy(appended_col, source_suffix) + asset_suffix.add_suffix_to_hierarchy(appended_col, external_suffix) local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] - source_col = bpy.data.collections[f"{col_base_name}.{source_suffix}"] + external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] target_col = bpy.data.collections.new(f"{col_base_name}.{target_suffix}") # TODO Make sure we preserve the collection hirearchies instead of having one flat col @@ -44,16 +44,16 @@ def merge_task_layer( context.scene.collection.children.unlink(local_col) # Find Obj owned by other Current Task Layer - source_transfer_objs = [] + external_transfer_objs = [] transfer_data = [] - for obj in source_col.objects: + for obj in external_col.objects: if obj.asset_id_owner not in local_tls: - source_transfer_objs.append(obj) + external_transfer_objs.append(obj) # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: if item.owner not in local_tls: transfer_data.append(item) - update_task_layer_objects(target_col, source_transfer_objs) + update_task_layer_objects(target_col, external_transfer_objs) local_objs = [] for obj in local_col.objects: @@ -154,10 +154,10 @@ def remap_user(datablock, target_name): def remap_external_datablocks(context): - source_suffix = ".SOURCE" + external_suffix = ".EXTERNAL" target_suffix = ".LOCAL" - datablocks = remap_get_data_blocks(context, source_suffix) + datablocks = remap_get_data_blocks(context, external_suffix) for datablock in datablocks: - target_name = datablock.name.replace(source_suffix, target_suffix) + target_name = datablock.name.replace(external_suffix, target_suffix) remap_user(datablock, target_name) -- 2.30.2 From b34f5030061b0896374e0102ed713aa09fc091f6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 18 Aug 2023 17:17:50 -0400 Subject: [PATCH 043/429] Asset Pipe: Update TODOS - Update TODO for Suffix - Update TODO to remove id from prop group --- scripts-blender/addons/asset_pipeline_2/core.py | 2 ++ scripts-blender/addons/asset_pipeline_2/props.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 2f537027..d0abbf84 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -154,6 +154,8 @@ def remap_user(datablock, target_name): def remap_external_datablocks(context): + # TODO STANDARDIZE IF ASSET SHOULD HAVE . IN SUFFIX OR NOT, + # ADD A CORE FUNCTION FOR APPENDING SUFFIX TO SINGLE ITEM external_suffix = ".EXTERNAL" target_suffix = ".LOCAL" diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index f551b785..5378c4b3 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -17,7 +17,9 @@ class ASSETOWNERSHIP(bpy.types.PropertyGroup): name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES, ) - id: bpy.props.PointerProperty(type=bpy.types.Object) + id: bpy.props.PointerProperty( + type=bpy.types.Object + ) # TODO REPLACE WITH ID_DATA AND DELETE classes = (ASSETOWNERSHIP,) -- 2.30.2 From f70f8992b38a53a4b2d88fd0fe61f132ff01f480 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 21 Aug 2023 14:35:56 -0400 Subject: [PATCH 044/429] Asset Pipe: Update Rig Test File --- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index 410b1ab3..9d9f547f 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:253ec7cd4395deb362f7e6544a34fd3027e224ebabfd9f87785d81e227d31c93 -size 958060 +oid sha256:a63a7c27a52d4b54fd0ebcc143a1149017be6375df765f6d55175af21321b814 +size 945944 -- 2.30.2 From 32404502f56e2bf828c3eab442e7b40ca44fd8be Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 21 Aug 2023 17:01:41 -0400 Subject: [PATCH 045/429] Asset Pipe: Improve Remapping of IDs --- .../addons/asset_pipeline_2/core.py | 42 +---------- .../addons/asset_pipeline_2/id_remap.py | 70 +++++++++++++++++++ 2 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/id_remap.py diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index d0abbf84..8ecab866 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,8 +1,8 @@ import bpy -from . import asset_suffix, transferable_data, util +from . import asset_suffix, transferable_data, id_remap from pathlib import Path -from .constants import TASK_LAYER_ITEMS + # TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 @@ -67,7 +67,7 @@ def merge_task_layer( transferable_data.apply_transfer_data(context, transfer_data, target_col) - remap_external_datablocks(context) + id_remap.remap_external_datablocks(context) bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True @@ -127,39 +127,3 @@ def import_data_from_lib( ) return eval(f"bpy.data.{data_category}['{data_name}']") - - -### REMAPPING IDS -def remap_get_data_blocks(context, suffix): - retun_datablocks = [] - for datablock in bpy.data.user_map(): - has_type = hasattr(datablock, 'type') - # TODO IMPROVE FILTERING? - if not ( - has_type - and datablock.type == 'OBJECT' - and datablock.name not in context.scene.objects - ): - if datablock.name.endswith(suffix): - retun_datablocks.append(datablock) - return retun_datablocks - - -def remap_user(datablock, target_name): - storage = util.get_storage_of_id(datablock) - remap_datablock = storage.get(target_name) - if remap_datablock: - remap_datablock.user_remap(datablock) - remap_datablock.name += "_Users_Remapped" - - -def remap_external_datablocks(context): - # TODO STANDARDIZE IF ASSET SHOULD HAVE . IN SUFFIX OR NOT, - # ADD A CORE FUNCTION FOR APPENDING SUFFIX TO SINGLE ITEM - external_suffix = ".EXTERNAL" - target_suffix = ".LOCAL" - - datablocks = remap_get_data_blocks(context, external_suffix) - for datablock in datablocks: - target_name = datablock.name.replace(external_suffix, target_suffix) - remap_user(datablock, target_name) diff --git a/scripts-blender/addons/asset_pipeline_2/id_remap.py b/scripts-blender/addons/asset_pipeline_2/id_remap.py new file mode 100644 index 00000000..294459ee --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/id_remap.py @@ -0,0 +1,70 @@ +import bpy + +from . import util +from bpy.types import bpy_prop_collection + +# TODO CHECK OTHER DATA TYPES OUTSIDE OF OBJECTS + + +### REMAPPING IDS +def get_users(col, ID): + ret = tuple(repr(o) for o in col if o.user_of_id(ID)) + return [eval(item) for item in ret] if ret else None + + +def get_users_of_id(ID): + users_of_id = [] + for p in dir(bpy.data): + if isinstance(getattr(bpy.data, p, None), bpy_prop_collection): + users = get_users(getattr(bpy.data, p), ID) + if users: + for user in users: + users_of_id.append(user) + return users_of_id + + +def get_users_in_scene(context, ID): + users_in_scene = [] + users = get_users_of_id(ID) + for user in users: + has_type = hasattr(user, 'type') + if has_type and user in list(context.scene.objects): + users_in_scene.append(user) + return users_in_scene + + +def remap_get_data_blocks(context): + retun_datablocks = [] + for obj in bpy.data.objects: + references_in_scene = get_users_in_scene(context, obj) + if references_in_scene and obj not in list(context.scene.objects): + retun_datablocks.append(obj) + return retun_datablocks + + +def remap_user(datablock, target_name): + storage = util.get_storage_of_id(datablock) + remap_datablock = storage.get(target_name) + if remap_datablock: + datablock.user_remap(remap_datablock) + datablock.name += "_Users_Remapped" + + +def get_opposite_suffix(suffix): + # TODO Creating a map would be easier that doing this on the fly + if suffix.endswith("EXTERNAL"): + return ".LOCAL" + if suffix.endswith("LOCAL"): + return ".EXTERNAL" + + +def remap_external_datablocks(context): + # TODO STANDARDIZE IF ASSET SHOULD HAVE . IN SUFFIX OR NOT, + # ADD A CORE FUNCTION FOR APPENDING SUFFIX TO SINGLE ITEM + datablocks = remap_get_data_blocks(context) + for datablock in datablocks: + current_suffix = "." + datablock.name.split(".")[-1] + target_suffix = get_opposite_suffix(datablock.name) + target_name = datablock.name.replace(current_suffix, target_suffix) + print(f"Will remap {datablock.name} to {target_name}") + remap_user(datablock, target_name) -- 2.30.2 From 80a1b8813241b8647d8f58341e4d4ded9f6ab890 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 21 Aug 2023 23:00:22 -0400 Subject: [PATCH 046/429] Asset Pipe: Refactor Remapping of IDs --- .../addons/asset_pipeline_2/core.py | 4 +- .../addons/asset_pipeline_2/datablocks.py | 87 +++++++++++++++++++ .../addons/asset_pipeline_2/id_remap.py | 70 --------------- 3 files changed, 89 insertions(+), 72 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/datablocks.py delete mode 100644 scripts-blender/addons/asset_pipeline_2/id_remap.py diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 8ecab866..49f9c339 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,6 +1,6 @@ import bpy -from . import asset_suffix, transferable_data, id_remap +from . import asset_suffix, datablocks, transferable_data from pathlib import Path @@ -67,7 +67,7 @@ def merge_task_layer( transferable_data.apply_transfer_data(context, transfer_data, target_col) - id_remap.remap_external_datablocks(context) + datablocks.remap_datablocks_outside_scene(context.scene) bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True diff --git a/scripts-blender/addons/asset_pipeline_2/datablocks.py b/scripts-blender/addons/asset_pipeline_2/datablocks.py new file mode 100644 index 00000000..097a5b99 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/datablocks.py @@ -0,0 +1,87 @@ +import bpy + +from . import util + + +def get_type_datablocks(datablocks): + """Filters for items in a list of datablocks that have a 'type'""" + return [datablock for datablock in datablocks if hasattr(datablock, 'type')] + + +def get_users_of_id(datablock): + """Returns datablocks that are using/referencing a give datablock""" + return bpy.data.user_map()[datablock] + + +def recursively_find_parent_objs(datablock): + """Recursively loop through ID parents and return a parent obj + of a given datablock""" + datablocks = bpy.data.user_map()[datablock] + if datablock not in list(bpy.data.objects): + for datablock in datablocks: + recursively_find_parent_objs(datablock) + else: + return datablocks + + +def referenced_in_scene(scene: bpy.types.Scene, datablock): + """Returns datablocks that are being references by data within + the scene""" + users = get_users_of_id(datablock) + for user in users: + if user in list(scene.objects): + return True + + # FOR DATABLOCKS THAT ARE NOT OBJ BUT ARE REFERENCES BY OBJS IN SCENE + parent_users = recursively_find_parent_objs(datablock) + if parent_users is None: + return + for parent_user in parent_users: + if parent_user in list(scene.objects): + return True + + +def remap_get_data_blocks(scene: bpy.types.Scene): + """Returns list of datablocks that aren't in the scene + but are referenced by data within the scene""" + retun_datablocks = [] + datablocks = [db for db in bpy.data.user_map() if hasattr(db, 'type')] + for datablock in datablocks: + if referenced_in_scene(scene, datablock) and datablock not in list( + scene.objects + ): + retun_datablocks.append(datablock) + return retun_datablocks + + +def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): + """Remap datablock and append name to datablock that has been remapped""" + source_datablock.user_remap(target_datablock) + source_datablock.name += "_Users_Remapped" + + +def get_opposite_suffix(suffix: str): + # TODO FIX HACK that is used until I have transfer mapping + # TODO Creating a map would be easier that doing this on the fly + if suffix.endswith("EXTERNAL"): + return ".LOCAL" + if suffix.endswith("LOCAL"): + return ".EXTERNAL" + + +def remap_datablocks_outside_scene(scene: bpy.types.Scene): + """Remap Datablocks that are used in the scene but will be purged + because they are not references by the items within the scene""" + # TODO STANDARDIZE IF ASSET SHOULD HAVE . IN SUFFIX OR NOT, + # ADD A CORE FUNCTION FOR APPENDING SUFFIX TO SINGLE ITEM + datablocks = remap_get_data_blocks(scene) + for datablock in datablocks: + # FIND TARGET DATA-BLOCK BY SUFFIX + current_suffix = "." + datablock.name.split(".")[-1] + target_suffix = get_opposite_suffix(current_suffix) + target_name = datablock.name.replace(current_suffix, target_suffix) + storage = util.get_storage_of_id(datablock) + target_datablock = storage.get(target_name) + print(f"Will remap {datablock.name} to {target_datablock.name}") + if target_datablock: + remap_user(datablock, target_datablock) diff --git a/scripts-blender/addons/asset_pipeline_2/id_remap.py b/scripts-blender/addons/asset_pipeline_2/id_remap.py deleted file mode 100644 index 294459ee..00000000 --- a/scripts-blender/addons/asset_pipeline_2/id_remap.py +++ /dev/null @@ -1,70 +0,0 @@ -import bpy - -from . import util -from bpy.types import bpy_prop_collection - -# TODO CHECK OTHER DATA TYPES OUTSIDE OF OBJECTS - - -### REMAPPING IDS -def get_users(col, ID): - ret = tuple(repr(o) for o in col if o.user_of_id(ID)) - return [eval(item) for item in ret] if ret else None - - -def get_users_of_id(ID): - users_of_id = [] - for p in dir(bpy.data): - if isinstance(getattr(bpy.data, p, None), bpy_prop_collection): - users = get_users(getattr(bpy.data, p), ID) - if users: - for user in users: - users_of_id.append(user) - return users_of_id - - -def get_users_in_scene(context, ID): - users_in_scene = [] - users = get_users_of_id(ID) - for user in users: - has_type = hasattr(user, 'type') - if has_type and user in list(context.scene.objects): - users_in_scene.append(user) - return users_in_scene - - -def remap_get_data_blocks(context): - retun_datablocks = [] - for obj in bpy.data.objects: - references_in_scene = get_users_in_scene(context, obj) - if references_in_scene and obj not in list(context.scene.objects): - retun_datablocks.append(obj) - return retun_datablocks - - -def remap_user(datablock, target_name): - storage = util.get_storage_of_id(datablock) - remap_datablock = storage.get(target_name) - if remap_datablock: - datablock.user_remap(remap_datablock) - datablock.name += "_Users_Remapped" - - -def get_opposite_suffix(suffix): - # TODO Creating a map would be easier that doing this on the fly - if suffix.endswith("EXTERNAL"): - return ".LOCAL" - if suffix.endswith("LOCAL"): - return ".EXTERNAL" - - -def remap_external_datablocks(context): - # TODO STANDARDIZE IF ASSET SHOULD HAVE . IN SUFFIX OR NOT, - # ADD A CORE FUNCTION FOR APPENDING SUFFIX TO SINGLE ITEM - datablocks = remap_get_data_blocks(context) - for datablock in datablocks: - current_suffix = "." + datablock.name.split(".")[-1] - target_suffix = get_opposite_suffix(datablock.name) - target_name = datablock.name.replace(current_suffix, target_suffix) - print(f"Will remap {datablock.name} to {target_name}") - remap_user(datablock, target_name) -- 2.30.2 From 84b854ea8e67ab1285d6d07030967eda0b9793db Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 16:21:26 -0400 Subject: [PATCH 047/429] Asset Pipe: Implement Asset Mapping for Objects --- .../addons/asset_pipeline_2/asset_mapping.py | 129 ++++++++++++++++++ .../addons/asset_pipeline_2/core.py | 37 +++-- .../addons/asset_pipeline_2/datablocks.py | 1 + .../addons/asset_pipeline_2/util.py | 8 ++ 4 files changed, 156 insertions(+), 19 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/asset_mapping.py diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py new file mode 100644 index 00000000..509341c8 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -0,0 +1,129 @@ +import bpy +from typing import List, Dict, Union, Any, Set, Optional, Tuple + +from . import util + + +def get_opposite_suffix(suffix: str): + # TODO FIX HACK that is used until I have transfer mapping + # TODO Creating a map would be easier that doing this on the fly + if suffix.endswith("EXTERNAL"): + return "LOCAL" + if suffix.endswith("LOCAL"): + return "EXTERNAL" + + +def rreplace(s: str, occurrence=1) -> str: + old = s.split(".")[-1] + new = get_opposite_suffix(old) + li = s.rsplit(old, occurrence) + return new.join(li) + + +class AssetTransferMapping: + """ + The AssetTranfserMapping class represents a mapping between a source and a target. + It contains an object mapping which connects each source object with a target + object as well as a collection mapping. + The mapping process relies heavily on suffixes, which is why we use + MergeCollections as input that store a suffix. + + Instances of this class will be pased TaskLayer data transfer function so Users + can easily write their merge instructions. + """ + + def __init__( + self, + local_coll: bpy.types.Collection, + external_coll: bpy.types.Collection, + local_tls: Set[str], + ): + self._local_col = local_coll + self._external_col = external_coll + self._local_tls = local_tls + + self.local_obj_to_remove: Set[bpy.types.Object] = set() + self.external_obj_to_add: Set[bpy.types.Object] = set() + self._no_match_source_objs: Set[bpy.types.Object] = set() + self._no_match_target_objs: Set[bpy.types.Object] = set() + + self._no_match_source_colls: Set[bpy.types.Object] = set() + self._no_match_target_colls: Set[bpy.types.Object] = set() + + self.generate_mapping() + + def generate_mapping(self) -> None: + self.object_map = self._gen_object_map() + self.collection_map = self._gen_collection_map() + + def _get_external_object(self, local_obj): + external_obj_name = rreplace( + local_obj.name, + ) + external_obj = self._external_col.all_objects.get(external_obj_name) + if not external_obj: + print(f"Failed to find match obj {external_obj_name} for {local_obj.name}") + self._no_match_source_objs.add(local_obj) + return + return external_obj + + def _gen_object_map(self) -> Dict[bpy.types.Object, bpy.types.Object]: + """ + Tries to link all objects in source collection to an object in + target collection. Uses suffixes to match them up. + """ + object_map: Dict[bpy.types.Object, bpy.types.Object] = {} + for local_obj in self._local_col.all_objects: + # IF ITEM IS OWNED BY LOCAL TASK LAYERS + if local_obj.asset_id_owner in self._local_tls: + external_obj = self._get_external_object(local_obj) + if external_obj: + object_map[external_obj] = local_obj + + # IF ITEM IS NOT OWNED BY LOCAL TASK LAYERS + else: + external_obj = self._get_external_object(local_obj) + if external_obj: + object_map[local_obj] = external_obj + else: + # REMOVE OBJ NOT OWNED BY LOCAL TASK LAYER THAT HAS NO MATCH + self.local_obj_to_remove.add(local_obj) + + # Find new objects to add to local_col + for external_obj in self._external_col.all_objects: + obj = self._local_col.all_objects.get(rreplace(external_obj.name)) + if not obj and external_obj.asset_id_owner not in self._local_tls: + self.external_obj_to_add.add(external_obj) + return object_map + + def _gen_collection_map(self) -> Dict[bpy.types.Collection, bpy.types.Collection]: + """ + Tries to link all source collections to a target collection. + Uses suffixes to match them up. + """ + coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} + + # Link top most parents. + coll_map[self._local_col] = self._external_col + + # Link up all children. + for s_coll in util.traverse_collection_tree(self._local_col): + # assert source_obj.name.endswith(self._source_merge_coll.suffix) + + # Replace source object suffix with target suffix to get target object. + external_col = rreplace(s_coll.name) + t_coll = bpy.data.collections.get(external_col) + if t_coll: + coll_map[s_coll] = t_coll + else: + print( + f"Failed to find match collection {s_coll.name} for {external_col}" + ) + self._no_match_source_colls.add(s_coll) + + all_tgt_colls = set(self._external_col.children_recursive) + all_tgt_colls.add(self._external_col) + match_target_colls = set([coll for coll in coll_map.values()]) + self._no_match_target_colls = all_tgt_colls - match_target_colls + + return coll_map diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 49f9c339..25a41b97 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -3,6 +3,8 @@ import bpy from . import asset_suffix, datablocks, transferable_data from pathlib import Path +from .asset_mapping import AssetTransferMapping + # TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 @@ -27,7 +29,6 @@ def merge_task_layer( local_col = bpy.data.collections[col_base_name] local_suffix = "LOCAL" external_suffix = "EXTERNAL" - target_suffix = "TARGET" asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) import_data_from_lib(target_file, "collections", col_base_name) @@ -36,43 +37,41 @@ def merge_task_layer( local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] - target_col = bpy.data.collections.new(f"{col_base_name}.{target_suffix}") # TODO Make sure we preserve the collection hirearchies instead of having one flat col - # Link Target as new Active Collection - context.scene.collection.children.link(target_col) - context.scene.collection.children.unlink(local_col) + mapping_task_target = AssetTransferMapping(local_col, external_col, local_tls) - # Find Obj owned by other Current Task Layer - external_transfer_objs = [] + # Find Transfer Data transfer_data = [] for obj in external_col.objects: - if obj.asset_id_owner not in local_tls: - external_transfer_objs.append(obj) - # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: if item.owner not in local_tls: transfer_data.append(item) - update_task_layer_objects(target_col, external_transfer_objs) - local_objs = [] for obj in local_col.objects: - if obj.asset_id_owner in local_tls: - local_objs.append(obj) - # Find Transfer-Data in other Task Layers for item in obj.transfer_data_ownership: if item.owner in local_tls: transfer_data.append(item) - update_task_layer_objects(target_col, local_objs) - transferable_data.apply_transfer_data(context, transfer_data, target_col) + transferable_data.apply_transfer_data(context, transfer_data, local_col) + transferable_data.apply_transfer_data(context, transfer_data, external_col) - datablocks.remap_datablocks_outside_scene(context.scene) + for old_obj in mapping_task_target.local_obj_to_remove: + # TODO Support collection hirearchies + local_col.objects.unlink(old_obj) + + for new_obj in mapping_task_target.external_obj_to_add: + # TODO Support collection hirearchies + local_col.objects.link(new_obj) + + for source_obj in mapping_task_target._object_map: + target_obj = mapping_task_target._object_map[source_obj] + datablocks.remap_user(source_obj, target_obj) bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) - asset_suffix.remove_suffix_from_hierarchy(target_col) + asset_suffix.remove_suffix_from_hierarchy(local_col) def find_published_file_version(file): diff --git a/scripts-blender/addons/asset_pipeline_2/datablocks.py b/scripts-blender/addons/asset_pipeline_2/datablocks.py index 097a5b99..6afef204 100644 --- a/scripts-blender/addons/asset_pipeline_2/datablocks.py +++ b/scripts-blender/addons/asset_pipeline_2/datablocks.py @@ -56,6 +56,7 @@ def remap_get_data_blocks(scene: bpy.types.Scene): def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): """Remap datablock and append name to datablock that has been remapped""" + print(f"REMAPPING {source_datablock.name} to {target_datablock.name}") source_datablock.user_remap(target_datablock) source_datablock.name += "_Users_Remapped" diff --git a/scripts-blender/addons/asset_pipeline_2/util.py b/scripts-blender/addons/asset_pipeline_2/util.py index 9aadee47..c142f85b 100644 --- a/scripts-blender/addons/asset_pipeline_2/util.py +++ b/scripts-blender/addons/asset_pipeline_2/util.py @@ -91,3 +91,11 @@ def get_storage_of_id(datablock: bpy.types.ID) -> 'bpy_prop_collection': fundamental_type = get_fundamental_id_type(datablock) return getattr(bpy.data, ID_CLASS_TO_STORAGE_NAME[fundamental_type]) + + +def traverse_collection_tree( + collection: bpy.types.Collection, +) -> Generator[bpy.types.Collection, None, None]: + yield collection + for child in collection.children: + yield from traverse_collection_tree(child) -- 2.30.2 From 76a00ff9975e52233755840b9f48dacc398cdefe Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 17:07:18 -0400 Subject: [PATCH 048/429] Asset Pipe: Standardize getting Target Name via Suffix --- .../addons/asset_pipeline_2/asset_mapping.py | 24 ++++--------------- .../addons/asset_pipeline_2/asset_suffix.py | 16 ++++++++++++- .../addons/asset_pipeline_2/constants.py | 3 +++ .../addons/asset_pipeline_2/core.py | 5 ++-- .../addons/asset_pipeline_2/datablocks.py | 20 ++++------------ 5 files changed, 30 insertions(+), 38 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 509341c8..a0c1af9e 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -2,22 +2,7 @@ import bpy from typing import List, Dict, Union, Any, Set, Optional, Tuple from . import util - - -def get_opposite_suffix(suffix: str): - # TODO FIX HACK that is used until I have transfer mapping - # TODO Creating a map would be easier that doing this on the fly - if suffix.endswith("EXTERNAL"): - return "LOCAL" - if suffix.endswith("LOCAL"): - return "EXTERNAL" - - -def rreplace(s: str, occurrence=1) -> str: - old = s.split(".")[-1] - new = get_opposite_suffix(old) - li = s.rsplit(old, occurrence) - return new.join(li) +from . import asset_suffix class AssetTransferMapping: @@ -57,7 +42,7 @@ class AssetTransferMapping: self.collection_map = self._gen_collection_map() def _get_external_object(self, local_obj): - external_obj_name = rreplace( + external_obj_name = asset_suffix.get_target_name( local_obj.name, ) external_obj = self._external_col.all_objects.get(external_obj_name) @@ -91,7 +76,8 @@ class AssetTransferMapping: # Find new objects to add to local_col for external_obj in self._external_col.all_objects: - obj = self._local_col.all_objects.get(rreplace(external_obj.name)) + local_col_objs = self._local_col.all_objects + obj = local_col_objs.get(asset_suffix.get_target_name(external_obj.name)) if not obj and external_obj.asset_id_owner not in self._local_tls: self.external_obj_to_add.add(external_obj) return object_map @@ -111,7 +97,7 @@ class AssetTransferMapping: # assert source_obj.name.endswith(self._source_merge_coll.suffix) # Replace source object suffix with target suffix to get target object. - external_col = rreplace(s_coll.name) + external_col = asset_suffix.get_target_name(s_coll.name) t_coll = bpy.data.collections.get(external_col) if t_coll: coll_map[s_coll] = t_coll diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index d02d5190..2addd366 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -21,12 +21,26 @@ from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids - +from . import constants from .util import get_storage_of_id DELIMITER = "." +def get_target_suffix(suffix: str): + if suffix.endswith(constants.EXTERNAL_SUFFIX): + return constants.LOCAL_SUFFIX + if suffix.endswith(constants.LOCAL_SUFFIX): + return constants.EXTERNAL_SUFFIX + + +def get_target_name(name: str, occurrence=1) -> str: + old = name.split(DELIMITER)[-1] + new = get_target_suffix(old) + li = name.rsplit(old, occurrence) + return new.join(li) + + def get_basename(name): return DELIMITER.join(name.split(DELIMITER)[:-1]) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 7362e9e9..cfe29d6a 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -13,3 +13,6 @@ TRANSFER_DATA_TYPES = [ ("MODIFIER", "Modifier", ""), ("MATERIAL_SLOT", "Material Slot", ""), ] + +LOCAL_SUFFIX = "LOCAL" +EXTERNAL_SUFFIX = "EXTERNAL" diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 25a41b97..459c3416 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -5,6 +5,7 @@ from pathlib import Path from .asset_mapping import AssetTransferMapping +from . import constants # TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 @@ -27,8 +28,8 @@ def merge_task_layer( target_file: Path, ): local_col = bpy.data.collections[col_base_name] - local_suffix = "LOCAL" - external_suffix = "EXTERNAL" + local_suffix = constants.LOCAL_SUFFIX + external_suffix = constants.EXTERNAL_SUFFIX asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) import_data_from_lib(target_file, "collections", col_base_name) diff --git a/scripts-blender/addons/asset_pipeline_2/datablocks.py b/scripts-blender/addons/asset_pipeline_2/datablocks.py index 6afef204..7443664f 100644 --- a/scripts-blender/addons/asset_pipeline_2/datablocks.py +++ b/scripts-blender/addons/asset_pipeline_2/datablocks.py @@ -1,6 +1,8 @@ import bpy -from . import util +from . import util, asset_suffix + +# TODO REMOVE UNUSED FUNCTIONS def get_type_datablocks(datablocks): @@ -61,28 +63,14 @@ def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): source_datablock.name += "_Users_Remapped" -def get_opposite_suffix(suffix: str): - # TODO FIX HACK that is used until I have transfer mapping - # TODO Creating a map would be easier that doing this on the fly - if suffix.endswith("EXTERNAL"): - return ".LOCAL" - if suffix.endswith("LOCAL"): - return ".EXTERNAL" - - def remap_datablocks_outside_scene(scene: bpy.types.Scene): """Remap Datablocks that are used in the scene but will be purged because they are not references by the items within the scene""" - # TODO STANDARDIZE IF ASSET SHOULD HAVE . IN SUFFIX OR NOT, - # ADD A CORE FUNCTION FOR APPENDING SUFFIX TO SINGLE ITEM datablocks = remap_get_data_blocks(scene) for datablock in datablocks: # FIND TARGET DATA-BLOCK BY SUFFIX - current_suffix = "." + datablock.name.split(".")[-1] - target_suffix = get_opposite_suffix(current_suffix) - target_name = datablock.name.replace(current_suffix, target_suffix) + target_name = asset_suffix.get_target_name(datablock.name) storage = util.get_storage_of_id(datablock) target_datablock = storage.get(target_name) - print(f"Will remap {datablock.name} to {target_datablock.name}") if target_datablock: remap_user(datablock, target_datablock) -- 2.30.2 From b5e0767c0f07b3bd8743b929637b70e8ee61f85c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 17:30:02 -0400 Subject: [PATCH 049/429] Asset Pipe: Improve Applying Transfer Data --- scripts-blender/addons/asset_pipeline_2/core.py | 14 +++++++------- .../addons/asset_pipeline_2/transferable_data.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 459c3416..6b05c9a9 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -40,7 +40,7 @@ def merge_task_layer( external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] # TODO Make sure we preserve the collection hirearchies instead of having one flat col - mapping_task_target = AssetTransferMapping(local_col, external_col, local_tls) + map = AssetTransferMapping(local_col, external_col, local_tls) # Find Transfer Data transfer_data = [] @@ -54,19 +54,19 @@ def merge_task_layer( if item.owner in local_tls: transfer_data.append(item) - transferable_data.apply_transfer_data(context, transfer_data, local_col) - transferable_data.apply_transfer_data(context, transfer_data, external_col) + target_objs = [map.object_map[obj] for obj in map.object_map] + transferable_data.apply_transfer_data(context, transfer_data, target_objs) - for old_obj in mapping_task_target.local_obj_to_remove: + for old_obj in map.local_obj_to_remove: # TODO Support collection hirearchies local_col.objects.unlink(old_obj) - for new_obj in mapping_task_target.external_obj_to_add: + for new_obj in map.external_obj_to_add: # TODO Support collection hirearchies local_col.objects.link(new_obj) - for source_obj in mapping_task_target._object_map: - target_obj = mapping_task_target._object_map[source_obj] + for source_obj in map.object_map: + target_obj = map.object_map[source_obj] datablocks.remap_user(source_obj, target_obj) bpy.ops.outliner.orphans_purge( diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index 4d2173a5..ae9a6b9e 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -17,10 +17,10 @@ def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Obj def apply_transfer_data( context: bpy.types.Context, transfer_data_list, - target_col: bpy.types.Collection, + target_objs: set[bpy.types.Object], ): for item in transfer_data_list: - for target_obj in target_col.objects: + for target_obj in target_objs: if asset_suffix.get_basename(target_obj.name) == asset_suffix.get_basename( item.id.name ): -- 2.30.2 From b3ecbcd49b37685f69d671e5f8f2a2786382e4a1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 17:46:27 -0400 Subject: [PATCH 050/429] Asset Pipe: Replace ID with id_data --- .../addons/asset_pipeline_2/core.py | 2 -- .../addons/asset_pipeline_2/props.py | 3 --- .../asset_pipeline_2/transfer_functions.py | 4 ++-- .../asset_pipeline_2/transferable_data.py | 21 +++++++++---------- scripts-blender/addons/asset_pipeline_2/ui.py | 2 +- 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 6b05c9a9..0f563627 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -7,8 +7,6 @@ from .asset_mapping import AssetTransferMapping from . import constants -# TODO refactor merge functions into a class based on AssetBuilder class of Asset Pipeline 1 - def update_task_layer_objects( target_col: bpy.types.Collection, diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 5378c4b3..6d71d2a4 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -17,9 +17,6 @@ class ASSETOWNERSHIP(bpy.types.PropertyGroup): name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES, ) - id: bpy.props.PointerProperty( - type=bpy.types.Object - ) # TODO REPLACE WITH ID_DATA AND DELETE classes = (ASSETOWNERSHIP,) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index 4fab2b49..4bef1335 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -30,7 +30,7 @@ def transfer_vertex_group( def transfer_modifier(item, obj_target): # remove old and sync existing modifiers - obj_source = item.id + obj_source = item.id_data if obj_source == obj_target: return old_mod = obj_target.modifiers.get(item.name) @@ -86,7 +86,7 @@ def transfer_modifier(item, obj_target): def transfer_material_slot(item, obj_target): - obj_source = item.id + obj_source = item.id_data if obj_source == obj_target: return # Delete existing material slot if exists diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index ae9a6b9e..a9b18131 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -4,14 +4,15 @@ from . import transfer_functions, asset_suffix def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): - transfer_data_ownership = target_obj.transfer_data_ownership - transfer_items_names = [item.name for item in transfer_data_ownership] + ownership = target_obj.transfer_data_ownership + transfer_items_names = [item.name for item in ownership] if transfer_data_item.name not in transfer_items_names: - new_item = transfer_data_ownership.add() - new_item.name = transfer_data_item.name - new_item.owner = transfer_data_item.owner - new_item.type = transfer_data_item.type - new_item.id = target_obj + transfer_data_add_entry( + ownership, + transfer_data_item.name, + transfer_data_item.type, + transfer_data_item.owner, + ) def apply_transfer_data( @@ -22,14 +23,14 @@ def apply_transfer_data( for item in transfer_data_list: for target_obj in target_objs: if asset_suffix.get_basename(target_obj.name) == asset_suffix.get_basename( - item.id.name + item.id_data.name ): if item.type == "VERTEX_GROUP": transfer_functions.transfer_vertex_group( context=context, vertex_group_name=item.name, obj_source=target_obj, - obj_target=item.id, + obj_target=item.id_data, ) if item.type == "MODIFIER": transfer_functions.transfer_modifier(item, target_obj) @@ -47,12 +48,10 @@ def check_transfer_data_entry(ownership, key, td_type): def transfer_data_add_entry(ownership, name, td_type, task_layer_name): - obj = ownership.id_data item = ownership.add() item.name = name item.owner = task_layer_name.upper() item.type = td_type - item.id = obj ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 408cf0a9..d4be615f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -23,7 +23,7 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): self.layout.prop(obj, "asset_id_owner") for my_item in ownership: self.layout.label( - text=f"{my_item.name} : {my_item.owner} : {my_item.id.name}" + text=f"{my_item.name} : {my_item.owner} : {my_item.id_data.name}" ) -- 2.30.2 From 151b6a6354ae9b747015ce2d8a5d48ebac5e5ea7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 17:53:08 -0400 Subject: [PATCH 051/429] Asset Pipe: Check for Task Layer in Update Ownership --- scripts-blender/addons/asset_pipeline_2/constants.py | 2 ++ scripts-blender/addons/asset_pipeline_2/ops.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index cfe29d6a..559743c6 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -7,6 +7,8 @@ TASK_LAYER_ITEMS = [ ("SHADE", "Shading", ""), ] +TASK_LAYER_KEYS = [item[0] for item in TASK_LAYER_ITEMS] + TRANSFER_DATA_TYPES = [ ("NONE", "None", ""), ("VERTEX_GROUP", "Vertex Group", ""), diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 93329d40..edb6cd42 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -12,8 +12,10 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): def execute(self, context): obj = context.active_object file_name = bpy.path.basename(bpy.context.blend_data.filepath) - # TODO check if exists in task_layer_constants task_layer_name = file_name.split(".")[-2] + if task_layer_name not in constants.TASK_LAYER_KEYS: + self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + return {'CANCELLED'} transferable_data.vertex_groups_update(obj, task_layer_name) transferable_data.modifiers_update(obj, task_layer_name) transferable_data.material_slot_update(obj, task_layer_name) @@ -35,7 +37,7 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): bpy.ops.wm.open_mainfile(filepath=pub_file_path) local_tls = [ - item[0] for item in constants.TASK_LAYER_ITEMS if item[0] != task_layer_name + item for item in constants.TASK_LAYER_KEYS if item != task_layer_name ] core.merge_task_layer( -- 2.30.2 From d118a165665b63a01542b0932a0f00a63e6d0047 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 17:55:28 -0400 Subject: [PATCH 052/429] Asset Pipe: Cleanup Imports --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 2 +- scripts-blender/addons/asset_pipeline_2/asset_suffix.py | 1 - scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- scripts-blender/addons/asset_pipeline_2/util.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index a0c1af9e..0fc6ad3c 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -1,5 +1,5 @@ import bpy -from typing import List, Dict, Union, Any, Set, Optional, Tuple +from typing import Dict, Set from . import util from . import asset_suffix diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index 2addd366..1b655281 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -17,7 +17,6 @@ # ***** END GPL LICENCE BLOCK ***** # # (c) 2021, Blender Foundation - Paul Golter -from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index edb6cd42..9b56f05e 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,7 +2,7 @@ import bpy from . import core from pathlib import Path -from . import transferable_data, transfer_functions, constants +from . import transferable_data, constants class ASSETPIPE_OT_update_ownership(bpy.types.Operator): diff --git a/scripts-blender/addons/asset_pipeline_2/util.py b/scripts-blender/addons/asset_pipeline_2/util.py index c142f85b..26f590d5 100644 --- a/scripts-blender/addons/asset_pipeline_2/util.py +++ b/scripts-blender/addons/asset_pipeline_2/util.py @@ -18,7 +18,7 @@ # # (c) 2021, Blender Foundation - Paul Golter -from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator +from typing import Dict, Any, Tuple, Generator import bpy from bpy import types -- 2.30.2 From 32c6cf0890d03fb104fb84bf187d7ea95f796995 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 18:13:32 -0400 Subject: [PATCH 053/429] Asset Pipe: Add Function to Get Task Layer Name --- .../addons/asset_pipeline_2/ops.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 9b56f05e..3549b9e8 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -5,17 +5,22 @@ from pathlib import Path from . import transferable_data, constants +def get_task_layer_name_from_file(self): + file_name = bpy.path.basename(bpy.context.blend_data.filepath) + task_layer_name = file_name.split(".")[-2] + if task_layer_name not in constants.TASK_LAYER_KEYS: + self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + return {'CANCELLED'} + return task_layer_name + + class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_idname = "assetpipe.update_ownership" bl_label = 'Update Ownership' def execute(self, context): obj = context.active_object - file_name = bpy.path.basename(bpy.context.blend_data.filepath) - task_layer_name = file_name.split(".")[-2] - if task_layer_name not in constants.TASK_LAYER_KEYS: - self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") - return {'CANCELLED'} + task_layer_name = get_task_layer_name_from_file(self) transferable_data.vertex_groups_update(obj, task_layer_name) transferable_data.modifiers_update(obj, task_layer_name) transferable_data.material_slot_update(obj, task_layer_name) @@ -30,8 +35,7 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): # Find current task Layer col_base_name = "CH-chr_test" # TODO replace hard coded value current_file = Path(bpy.data.filepath) - # TODO check if exists in task_layer_constants - task_layer_name = current_file.name.split('.')[-2] + task_layer_name = get_task_layer_name_from_file(self) pub_file = core.find_published_file(current_file) pub_file_path = pub_file.__str__() bpy.ops.wm.open_mainfile(filepath=pub_file_path) @@ -57,10 +61,8 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_label = 'Pull from Publish' def execute(self, context): - current_file = Path(bpy.data.filepath) - # TODO check if exists in task_layer_constants - task_layer_name = current_file.name.split('.')[-2] - pub_file = core.find_published_file(current_file) + task_layer_name = get_task_layer_name_from_file(self) + pub_file = core.find_published_file(Path(bpy.data.filepath)) col_base_name = "CH-chr_test" # TODO replace hard coded value core.merge_task_layer( context, -- 2.30.2 From 621849ca1d9e78da402650aadab98ccb6a6abb19 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 19:46:21 -0400 Subject: [PATCH 054/429] Asset Pipe: Improve Error Handling on Operators --- .../addons/asset_pipeline_2/core.py | 4 +- .../addons/asset_pipeline_2/ops.py | 39 +++++++++++++------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 0f563627..659774e6 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -25,7 +25,9 @@ def merge_task_layer( local_tls: list[str], target_file: Path, ): - local_col = bpy.data.collections[col_base_name] + local_col = bpy.data.collections.get(col_base_name) + if not local_col: + return "Current File Name doesn't contain valid task layer" local_suffix = constants.LOCAL_SUFFIX external_suffix = constants.EXTERNAL_SUFFIX asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 3549b9e8..fc952e91 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -5,13 +5,15 @@ from pathlib import Path from . import transferable_data, constants +def get_parent_col_name(): + return "CH-chr_test" # TODO Replace Hard Coded Value + + def get_task_layer_name_from_file(self): file_name = bpy.path.basename(bpy.context.blend_data.filepath) task_layer_name = file_name.split(".")[-2] - if task_layer_name not in constants.TASK_LAYER_KEYS: - self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") - return {'CANCELLED'} - return task_layer_name + if task_layer_name in constants.TASK_LAYER_KEYS: + return task_layer_name class ASSETPIPE_OT_update_ownership(bpy.types.Operator): @@ -21,6 +23,9 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): def execute(self, context): obj = context.active_object task_layer_name = get_task_layer_name_from_file(self) + if not task_layer_name: + self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + return {'CANCELLED'} transferable_data.vertex_groups_update(obj, task_layer_name) transferable_data.modifiers_update(obj, task_layer_name) transferable_data.material_slot_update(obj, task_layer_name) @@ -33,9 +38,11 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): def execute(self, context): # Find current task Layer - col_base_name = "CH-chr_test" # TODO replace hard coded value current_file = Path(bpy.data.filepath) task_layer_name = get_task_layer_name_from_file(self) + if not task_layer_name: + self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + return {'CANCELLED'} pub_file = core.find_published_file(current_file) pub_file_path = pub_file.__str__() bpy.ops.wm.open_mainfile(filepath=pub_file_path) @@ -44,15 +51,19 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): item for item in constants.TASK_LAYER_KEYS if item != task_layer_name ] - core.merge_task_layer( + error_msg = core.merge_task_layer( context, - col_base_name=col_base_name, + col_base_name=get_parent_col_name(), local_tls=local_tls, target_file=current_file, ) + if error_msg: + bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) + self.report({'ERROR'}, error_msg) + return {'CANCELLED'} + bpy.ops.wm.save_as_mainfile(filepath=pub_file_path) bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) - return {'FINISHED'} @@ -62,14 +73,20 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): def execute(self, context): task_layer_name = get_task_layer_name_from_file(self) + if not task_layer_name: + self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + return {'CANCELLED'} pub_file = core.find_published_file(Path(bpy.data.filepath)) - col_base_name = "CH-chr_test" # TODO replace hard coded value - core.merge_task_layer( + error_msg = core.merge_task_layer( context, - col_base_name=col_base_name, + col_base_name=get_parent_col_name(), local_tls=[task_layer_name], target_file=pub_file, ) + + if error_msg: + self.report({'ERROR'}, error_msg) + return {'CANCELLED'} return {'FINISHED'} -- 2.30.2 From 13445bb643690825427af733f5b677950f8ed7c8 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 19:52:00 -0400 Subject: [PATCH 055/429] Asset Pipe: Clear Testing TODO - Object key is needed for temp override in vertex group transfer --- scripts-blender/addons/asset_pipeline_2/transfer_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index 4bef1335..046b4796 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -16,7 +16,7 @@ def transfer_vertex_group( override = context.copy() override["selected_editable_objects"] = [obj_source, obj_target] override["active_object"] = obj_target - override["object"] = obj_target # TODO test if needed + override["object"] = obj_target with context.temp_override(**override): bpy.ops.object.data_transfer( data_type="VGROUP_WEIGHTS", -- 2.30.2 From 321c233d9063dfe142d6941400a4afb7dac7d438 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 20:03:36 -0400 Subject: [PATCH 056/429] Asset Pipe: Improve Vertex Group Transfer Function - Rename target and source object for accuracy - Remove vertex group before transfer --- .../asset_pipeline_2/transfer_functions.py | 17 ++++++++++------- .../asset_pipeline_2/transferable_data.py | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index 046b4796..d8671eba 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -6,17 +6,20 @@ from . import asset_suffix def transfer_vertex_group( context, vertex_group_name: str, - obj_source: bpy.types.Object, obj_target: bpy.types.Object, + obj_source: bpy.types.Object, ): - if obj_source == obj_target: + if obj_target == obj_source: return - # TODO remove old vertex groups before transfer !! - obj_target.vertex_groups.active = obj_target.vertex_groups[vertex_group_name] + + if obj_target.vertex_groups.get(vertex_group_name): + obj_target.vertex_groups.remove(obj_target.vertex_groups.get(vertex_group_name)) + + obj_source.vertex_groups.active = obj_source.vertex_groups[vertex_group_name] override = context.copy() - override["selected_editable_objects"] = [obj_source, obj_target] - override["active_object"] = obj_target - override["object"] = obj_target + override["selected_editable_objects"] = [obj_target, obj_source] + override["active_object"] = obj_source + override["object"] = obj_source with context.temp_override(**override): bpy.ops.object.data_transfer( data_type="VGROUP_WEIGHTS", diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index a9b18131..54b112e0 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -29,8 +29,8 @@ def apply_transfer_data( transfer_functions.transfer_vertex_group( context=context, vertex_group_name=item.name, - obj_source=target_obj, - obj_target=item.id_data, + obj_target=target_obj, + obj_source=item.id_data, ) if item.type == "MODIFIER": transfer_functions.transfer_modifier(item, target_obj) -- 2.30.2 From 6bed87102312d42684f3db9cb247c63706c24308 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 20:07:21 -0400 Subject: [PATCH 057/429] Asset Pipe: Remove Un-used Functions --- .../addons/asset_pipeline_2/core.py | 9 ++- .../addons/asset_pipeline_2/datablocks.py | 76 ------------------- 2 files changed, 8 insertions(+), 77 deletions(-) delete mode 100644 scripts-blender/addons/asset_pipeline_2/datablocks.py diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 659774e6..9b3c490d 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -8,6 +8,13 @@ from .asset_mapping import AssetTransferMapping from . import constants +def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): + """Remap datablock and append name to datablock that has been remapped""" + print(f"REMAPPING {source_datablock.name} to {target_datablock.name}") + source_datablock.user_remap(target_datablock) + source_datablock.name += "_Users_Remapped" + + def update_task_layer_objects( target_col: bpy.types.Collection, transfer_objs: list[bpy.types.Object], @@ -67,7 +74,7 @@ def merge_task_layer( for source_obj in map.object_map: target_obj = map.object_map[source_obj] - datablocks.remap_user(source_obj, target_obj) + remap_user(source_obj, target_obj) bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True diff --git a/scripts-blender/addons/asset_pipeline_2/datablocks.py b/scripts-blender/addons/asset_pipeline_2/datablocks.py deleted file mode 100644 index 7443664f..00000000 --- a/scripts-blender/addons/asset_pipeline_2/datablocks.py +++ /dev/null @@ -1,76 +0,0 @@ -import bpy - -from . import util, asset_suffix - -# TODO REMOVE UNUSED FUNCTIONS - - -def get_type_datablocks(datablocks): - """Filters for items in a list of datablocks that have a 'type'""" - return [datablock for datablock in datablocks if hasattr(datablock, 'type')] - - -def get_users_of_id(datablock): - """Returns datablocks that are using/referencing a give datablock""" - return bpy.data.user_map()[datablock] - - -def recursively_find_parent_objs(datablock): - """Recursively loop through ID parents and return a parent obj - of a given datablock""" - datablocks = bpy.data.user_map()[datablock] - if datablock not in list(bpy.data.objects): - for datablock in datablocks: - recursively_find_parent_objs(datablock) - else: - return datablocks - - -def referenced_in_scene(scene: bpy.types.Scene, datablock): - """Returns datablocks that are being references by data within - the scene""" - users = get_users_of_id(datablock) - for user in users: - if user in list(scene.objects): - return True - - # FOR DATABLOCKS THAT ARE NOT OBJ BUT ARE REFERENCES BY OBJS IN SCENE - parent_users = recursively_find_parent_objs(datablock) - if parent_users is None: - return - for parent_user in parent_users: - if parent_user in list(scene.objects): - return True - - -def remap_get_data_blocks(scene: bpy.types.Scene): - """Returns list of datablocks that aren't in the scene - but are referenced by data within the scene""" - retun_datablocks = [] - datablocks = [db for db in bpy.data.user_map() if hasattr(db, 'type')] - for datablock in datablocks: - if referenced_in_scene(scene, datablock) and datablock not in list( - scene.objects - ): - retun_datablocks.append(datablock) - return retun_datablocks - - -def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): - """Remap datablock and append name to datablock that has been remapped""" - print(f"REMAPPING {source_datablock.name} to {target_datablock.name}") - source_datablock.user_remap(target_datablock) - source_datablock.name += "_Users_Remapped" - - -def remap_datablocks_outside_scene(scene: bpy.types.Scene): - """Remap Datablocks that are used in the scene but will be purged - because they are not references by the items within the scene""" - datablocks = remap_get_data_blocks(scene) - for datablock in datablocks: - # FIND TARGET DATA-BLOCK BY SUFFIX - target_name = asset_suffix.get_target_name(datablock.name) - storage = util.get_storage_of_id(datablock) - target_datablock = storage.get(target_name) - if target_datablock: - remap_user(datablock, target_datablock) -- 2.30.2 From fccb06aaca534592fc00c994a76f6189276e525a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 20:11:04 -0400 Subject: [PATCH 058/429] Asset Pipe: Remove no-op code --- scripts-blender/addons/asset_pipeline_2/core.py | 2 +- scripts-blender/addons/asset_pipeline_2/ops.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 9b3c490d..2e2e91b8 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,6 +1,6 @@ import bpy -from . import asset_suffix, datablocks, transferable_data +from . import asset_suffix, transferable_data from pathlib import Path from .asset_mapping import AssetTransferMapping diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index fc952e91..d554ce01 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -9,7 +9,7 @@ def get_parent_col_name(): return "CH-chr_test" # TODO Replace Hard Coded Value -def get_task_layer_name_from_file(self): +def get_task_layer_name_from_file(): file_name = bpy.path.basename(bpy.context.blend_data.filepath) task_layer_name = file_name.split(".")[-2] if task_layer_name in constants.TASK_LAYER_KEYS: @@ -22,7 +22,7 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): def execute(self, context): obj = context.active_object - task_layer_name = get_task_layer_name_from_file(self) + task_layer_name = get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} @@ -39,7 +39,7 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): def execute(self, context): # Find current task Layer current_file = Path(bpy.data.filepath) - task_layer_name = get_task_layer_name_from_file(self) + task_layer_name = get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} @@ -72,7 +72,7 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_label = 'Pull from Publish' def execute(self, context): - task_layer_name = get_task_layer_name_from_file(self) + task_layer_name = get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} -- 2.30.2 From 0760b6f13d82693570e15efdf88b07ff68a03004 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 22 Aug 2023 20:16:27 -0400 Subject: [PATCH 059/429] Asset Pipe: Clear TODO on material slot transfer function --- .../addons/asset_pipeline_2/transfer_functions.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index d8671eba..d7c8b9bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -121,12 +121,9 @@ def transfer_material_slot(item, obj_target): for spl_to, spl_from in zip(obj_target.data.splines, obj_source.data.splines): spl_to.material_index = spl_from.material_index - # TODO MAKE USE_ABLE AGAIN - # # Rest of the loop applies only to meshes. - # if obj_target.type != "MESH": - # continue - - # # Transfer material slot assignments for mesh - # for pol_to, pol_from in zip(obj_target.data.polygons, obj_source.data.polygons): - # pol_to.material_index = pol_from.material_index - # pol_to.use_smooth = pol_from.use_smooth + # Rest of the loop applies only to meshes. + if obj_target.type == "MESH": + # Transfer material slot assignments for mesh + for pol_to, pol_from in zip(obj_target.data.polygons, obj_source.data.polygons): + pol_to.material_index = pol_from.material_index + pol_to.use_smooth = pol_from.use_smooth -- 2.30.2 From 342ba2c4a7d2ef51e2b60885bffdf755c0e40b0e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 14:02:20 -0400 Subject: [PATCH 060/429] Asset Pipe: Update TODOs --- scripts-blender/addons/asset_pipeline_2/core.py | 1 + scripts-blender/addons/asset_pipeline_2/transferable_data.py | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 2e2e91b8..f8cf78eb 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -47,6 +47,7 @@ def merge_task_layer( external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] # TODO Make sure we preserve the collection hirearchies instead of having one flat col + # TODO Consider re-adding the TARGET collection map = AssetTransferMapping(local_col, external_col, local_tls) # Find Transfer Data diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index 54b112e0..c0c4e1a7 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -3,6 +3,7 @@ import bpy from . import transfer_functions, asset_suffix +# TODO Consider using transfer mapping here instead of matching suffixes def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): ownership = target_obj.transfer_data_ownership transfer_items_names = [item.name for item in ownership] -- 2.30.2 From e5255018c762d8897d8be0d24ffd83b8586ab606 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 14:08:00 -0400 Subject: [PATCH 061/429] Asset Pipe: Add Collections corresponding to Task Layers --- .../addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend | 4 ++-- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend index d5510996..9e14de8f 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9d71cef6d0b6630206df2be7ec6e56e97641204dec5c15d91d5aa9e7e0d9277 -size 925316 +oid sha256:4c8967066230440863ba8de96787e553594d4aeb1d3db3c49591cdd5085420cd +size 936268 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index 9d9f547f..1f4a6e02 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a63a7c27a52d4b54fd0ebcc143a1149017be6375df765f6d55175af21321b814 -size 945944 +oid sha256:0bfc7e86d668f4820533d5830fa9846266b55debffbf59fe129d5fef32dec099 +size 949736 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend index 65b91ee4..dd40d26a 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e2cfb7e8643a3ba28a42ff141e857569fda58037d09bf99d35f0d24bab33e28 -size 955580 +oid sha256:3bd727c70d81152a7e7a3e20ac75f8ae4ac1d898b69cc480295f3430796df777 +size 969004 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index c369c64f..a89599ad 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e829b7c262f57332343b237c13d092ec73ad787c30ff79ad2cc8c0195a65e80a -size 899228 +oid sha256:0d90137df71d20e853382533eeda0beb9792e39c4222024032baf4745e9a5438 +size 936036 -- 2.30.2 From 48fdd9100ac7146d6aac6f4a63e6761e9c2ad02b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 15:03:46 -0400 Subject: [PATCH 062/429] Asset Pipe: Maintain Collection hierarchy during transfer --- .../addons/asset_pipeline_2/asset_mapping.py | 35 ++++++++++++------- .../addons/asset_pipeline_2/core.py | 16 +++------ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 0fc6ad3c..3be90891 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -3,6 +3,7 @@ from typing import Dict, Set from . import util from . import asset_suffix +from . import constants class AssetTransferMapping: @@ -90,22 +91,30 @@ class AssetTransferMapping: coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} # Link top most parents. - coll_map[self._local_col] = self._external_col + # coll_map[self._local_col] = self._external_col - # Link up all children. - for s_coll in util.traverse_collection_tree(self._local_col): - # assert source_obj.name.endswith(self._source_merge_coll.suffix) + # Get user facing names for local task layers + local_tl_names = [ + item[1] for item in constants.TASK_LAYER_ITEMS if item[0] in self._local_tls + ] - # Replace source object suffix with target suffix to get target object. - external_col = asset_suffix.get_target_name(s_coll.name) - t_coll = bpy.data.collections.get(external_col) - if t_coll: - coll_map[s_coll] = t_coll - else: - print( - f"Failed to find match collection {s_coll.name} for {external_col}" + for local_task_layer_col in self._local_col.children: + if ( + asset_suffix.get_basename(local_task_layer_col.name) + not in local_tl_names + ): + # Replace source object suffix with target suffix to get target object. + external_col_name = asset_suffix.get_target_name( + local_task_layer_col.name ) - self._no_match_source_colls.add(s_coll) + external_col = bpy.data.collections.get(external_col_name) + if external_col: + coll_map[local_task_layer_col] = external_col + else: + print( + f"Failed to find match collection {local_task_layer_col.name} for {external_col_name}" + ) + self._no_match_source_colls.add(local_task_layer_col) all_tgt_colls = set(self._external_col.children_recursive) all_tgt_colls.add(self._external_col) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index f8cf78eb..3b1aac1b 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -46,18 +46,17 @@ def merge_task_layer( local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] - # TODO Make sure we preserve the collection hirearchies instead of having one flat col # TODO Consider re-adding the TARGET collection map = AssetTransferMapping(local_col, external_col, local_tls) # Find Transfer Data transfer_data = [] - for obj in external_col.objects: + for obj in external_col.all_objects: for item in obj.transfer_data_ownership: if item.owner not in local_tls: transfer_data.append(item) - for obj in local_col.objects: + for obj in local_col.all_objects: for item in obj.transfer_data_ownership: if item.owner in local_tls: transfer_data.append(item) @@ -65,18 +64,13 @@ def merge_task_layer( target_objs = [map.object_map[obj] for obj in map.object_map] transferable_data.apply_transfer_data(context, transfer_data, target_objs) - for old_obj in map.local_obj_to_remove: - # TODO Support collection hirearchies - local_col.objects.unlink(old_obj) - - for new_obj in map.external_obj_to_add: - # TODO Support collection hirearchies - local_col.objects.link(new_obj) - for source_obj in map.object_map: target_obj = map.object_map[source_obj] remap_user(source_obj, target_obj) + for col in map.collection_map: + remap_user(col, map.collection_map[col]) + bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) -- 2.30.2 From e0dad500d7a7ee70adc956c100db3c2212913750 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 15:06:18 -0400 Subject: [PATCH 063/429] Asset Pipe: Clear TODO --- scripts-blender/addons/asset_pipeline_2/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 3b1aac1b..a01ce862 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -46,7 +46,6 @@ def merge_task_layer( local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] - # TODO Consider re-adding the TARGET collection map = AssetTransferMapping(local_col, external_col, local_tls) # Find Transfer Data -- 2.30.2 From d9c4dd1ad27bd140573c095836735012e1e0e329 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 16:53:54 -0400 Subject: [PATCH 064/429] Asset Pipe: Get/Set transfer data in individual functions --- .../addons/asset_pipeline_2/core.py | 17 +++++++++- .../addons/asset_pipeline_2/ops.py | 13 ++++--- .../asset_pipeline_2/transferable_data.py | 34 ++++++++++++++----- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index a01ce862..ebbdf6fe 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -2,12 +2,27 @@ import bpy from . import asset_suffix, transferable_data from pathlib import Path - +from typing import Dict from .asset_mapping import AssetTransferMapping from . import constants +def get_ownership(local_col: str, task_layer_name: str): + new_transfer_data: Dict[bpy.props.CollectionProperty, str, str, str, str] = {} + for obj in local_col.all_objects: + transferable_data.get_vertex_groups(obj, task_layer_name, new_transfer_data) + transferable_data.get_material_slots(obj, task_layer_name, new_transfer_data) + transferable_data.get_modifiers(obj, task_layer_name, new_transfer_data) + return new_transfer_data + + +def set_ownership(new_transfer_data): + for data in new_transfer_data: + item = new_transfer_data[data] + transferable_data.transfer_data_add_entry(item[0], item[1], item[2], item[3]) + + def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): """Remap datablock and append name to datablock that has been remapped""" print(f"REMAPPING {source_datablock.name} to {target_datablock.name}") diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index d554ce01..16820c8b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -21,14 +21,19 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_label = 'Update Ownership' def execute(self, context): - obj = context.active_object + # TODO LOOP OVER ALl COllECTION OBJECTS + # TODO CREATE UI TO CONFIRM NEW OWNERSHIP ITEMS + local_col = bpy.data.collections.get(get_parent_col_name()) + if not local_col: + self.report({'ERROR'}, "Top level collection could not be found") + return {'CANCELLED'} task_layer_name = get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - transferable_data.vertex_groups_update(obj, task_layer_name) - transferable_data.modifiers_update(obj, task_layer_name) - transferable_data.material_slot_update(obj, task_layer_name) + + new_data = core.get_ownership(local_col, task_layer_name) + core.set_ownership(new_data) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index c0c4e1a7..c0c1a6ea 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -1,9 +1,12 @@ import bpy from . import transfer_functions, asset_suffix +from typing import Dict # TODO Consider using transfer mapping here instead of matching suffixes + + def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): ownership = target_obj.transfer_data_ownership transfer_items_names = [item.name for item in ownership] @@ -57,7 +60,7 @@ def transfer_data_add_entry(ownership, name, td_type, task_layer_name): ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES # VERTEX GROUPS -def vertex_groups_update(obj, task_layer_name): +def get_vertex_groups(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: # Only add new ownership item if vertex group doesn't have an owner @@ -65,26 +68,41 @@ def vertex_groups_update(obj, task_layer_name): ownership, vertex_group.name, "VERTEX_GROUP" ) if len(matches) == 0: - transfer_data_add_entry( - ownership, vertex_group.name, "VERTEX_GROUP", task_layer_name + # NEED UNIQUE NAME INCASE OF DUPLICATES + name = vertex_group.name + '_' + obj.name + new_transfer_data[name] = ( + ownership, + vertex_group.name, + "VERTEX_GROUP", + task_layer_name, ) # MODIFIERS -def modifiers_update(obj, task_layer_name): +def get_modifiers(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for mod in obj.modifiers: matches = check_transfer_data_entry(ownership, mod.name, "MODIFIER") if len(matches) == 0: - transfer_data_add_entry(ownership, mod.name, "MODIFIER", task_layer_name) + name = mod.name + '_' + obj.name + new_transfer_data[name] = ( + ownership, + mod.name, + "MODIFIER", + task_layer_name, + ) # MATERIAL SLOT -def material_slot_update(obj, task_layer_name): +def get_material_slots(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for slot in obj.material_slots: matches = check_transfer_data_entry(ownership, slot.name, "MATERIAL_SLOT") if len(matches) == 0: - transfer_data_add_entry( - ownership, slot.name, "MATERIAL_SLOT", task_layer_name + name = slot.name + '_' + obj.name + new_transfer_data[name] = ( + ownership, + slot.name, + "MATERIAL_SLOT", + task_layer_name, ) -- 2.30.2 From bc1f79794238ee3ccc24f1496b3935ef1ab057a2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 16:57:49 -0400 Subject: [PATCH 065/429] Asset Pipe: Move transfer data getters to transfer_functions file --- .../addons/asset_pipeline_2/core.py | 8 +-- .../asset_pipeline_2/transfer_functions.py | 58 ++++++++++++++++++- .../asset_pipeline_2/transferable_data.py | 50 ---------------- 3 files changed, 61 insertions(+), 55 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index ebbdf6fe..336f1335 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,6 +1,6 @@ import bpy -from . import asset_suffix, transferable_data +from . import asset_suffix, transferable_data, transfer_functions from pathlib import Path from typing import Dict from .asset_mapping import AssetTransferMapping @@ -11,9 +11,9 @@ from . import constants def get_ownership(local_col: str, task_layer_name: str): new_transfer_data: Dict[bpy.props.CollectionProperty, str, str, str, str] = {} for obj in local_col.all_objects: - transferable_data.get_vertex_groups(obj, task_layer_name, new_transfer_data) - transferable_data.get_material_slots(obj, task_layer_name, new_transfer_data) - transferable_data.get_modifiers(obj, task_layer_name, new_transfer_data) + transfer_functions.get_vertex_groups(obj, task_layer_name, new_transfer_data) + transfer_functions.get_material_slots(obj, task_layer_name, new_transfer_data) + transfer_functions.get_modifiers(obj, task_layer_name, new_transfer_data) return new_transfer_data diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py index d7c8b9bc..7d0335f4 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_functions.py @@ -1,6 +1,28 @@ import bpy from bpy import context -from . import asset_suffix +from . import asset_suffix, transferable_data + + +## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES + + +# VERTEX GROUPS +def get_vertex_groups(obj, task_layer_name, new_transfer_data): + ownership = obj.transfer_data_ownership + for vertex_group in obj.vertex_groups: + # Only add new ownership item if vertex group doesn't have an owner + matches = transferable_data.check_transfer_data_entry( + ownership, vertex_group.name, "VERTEX_GROUP" + ) + if len(matches) == 0: + # NEED UNIQUE NAME INCASE OF DUPLICATES + name = vertex_group.name + '_' + obj.name + new_transfer_data[name] = ( + ownership, + vertex_group.name, + "VERTEX_GROUP", + task_layer_name, + ) def transfer_vertex_group( @@ -31,6 +53,23 @@ def transfer_vertex_group( ) +# MODIFIERS +def get_modifiers(obj, task_layer_name, new_transfer_data): + ownership = obj.transfer_data_ownership + for mod in obj.modifiers: + matches = transferable_data.check_transfer_data_entry( + ownership, mod.name, "MODIFIER" + ) + if len(matches) == 0: + name = mod.name + '_' + obj.name + new_transfer_data[name] = ( + ownership, + mod.name, + "MODIFIER", + task_layer_name, + ) + + def transfer_modifier(item, obj_target): # remove old and sync existing modifiers obj_source = item.id_data @@ -88,6 +127,23 @@ def transfer_modifier(item, obj_target): ) +# MATERIAL SLOT +def get_material_slots(obj, task_layer_name, new_transfer_data): + ownership = obj.transfer_data_ownership + for slot in obj.material_slots: + matches = transferable_data.check_transfer_data_entry( + ownership, slot.name, "MATERIAL_SLOT" + ) + if len(matches) == 0: + name = slot.name + '_' + obj.name + new_transfer_data[name] = ( + ownership, + slot.name, + "MATERIAL_SLOT", + task_layer_name, + ) + + def transfer_material_slot(item, obj_target): obj_source = item.id_data if obj_source == obj_target: diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transferable_data.py index c0c1a6ea..c9d8d1fc 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transferable_data.py @@ -56,53 +56,3 @@ def transfer_data_add_entry(ownership, name, td_type, task_layer_name): item.name = name item.owner = task_layer_name.upper() item.type = td_type - - -## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES -# VERTEX GROUPS -def get_vertex_groups(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership - for vertex_group in obj.vertex_groups: - # Only add new ownership item if vertex group doesn't have an owner - matches = check_transfer_data_entry( - ownership, vertex_group.name, "VERTEX_GROUP" - ) - if len(matches) == 0: - # NEED UNIQUE NAME INCASE OF DUPLICATES - name = vertex_group.name + '_' + obj.name - new_transfer_data[name] = ( - ownership, - vertex_group.name, - "VERTEX_GROUP", - task_layer_name, - ) - - -# MODIFIERS -def get_modifiers(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership - for mod in obj.modifiers: - matches = check_transfer_data_entry(ownership, mod.name, "MODIFIER") - if len(matches) == 0: - name = mod.name + '_' + obj.name - new_transfer_data[name] = ( - ownership, - mod.name, - "MODIFIER", - task_layer_name, - ) - - -# MATERIAL SLOT -def get_material_slots(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership - for slot in obj.material_slots: - matches = check_transfer_data_entry(ownership, slot.name, "MATERIAL_SLOT") - if len(matches) == 0: - name = slot.name + '_' + obj.name - new_transfer_data[name] = ( - ownership, - slot.name, - "MATERIAL_SLOT", - task_layer_name, - ) -- 2.30.2 From 8190c5fa1f3cd6e56eeebdf58112882ec34ae248 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 17:06:37 -0400 Subject: [PATCH 066/429] Asset Pipe: Move Transfer Data into Folder --- scripts-blender/addons/asset_pipeline_2/core.py | 13 +++++++------ scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- .../transfer_core.py} | 3 ++- .../{ => transfer_data}/transfer_functions.py | 9 +++++---- 4 files changed, 15 insertions(+), 12 deletions(-) rename scripts-blender/addons/asset_pipeline_2/{transferable_data.py => transfer_data/transfer_core.py} (94%) rename scripts-blender/addons/asset_pipeline_2/{ => transfer_data}/transfer_functions.py (96%) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 336f1335..8ab4ee1c 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,6 +1,7 @@ import bpy +from .transfer_data import transfer_core, transfer_functions -from . import asset_suffix, transferable_data, transfer_functions +from . import asset_suffix from pathlib import Path from typing import Dict from .asset_mapping import AssetTransferMapping @@ -20,7 +21,7 @@ def get_ownership(local_col: str, task_layer_name: str): def set_ownership(new_transfer_data): for data in new_transfer_data: item = new_transfer_data[data] - transferable_data.transfer_data_add_entry(item[0], item[1], item[2], item[3]) + transfer_core.transfer_data_add_entry(item[0], item[1], item[2], item[3]) def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): @@ -64,19 +65,19 @@ def merge_task_layer( map = AssetTransferMapping(local_col, external_col, local_tls) # Find Transfer Data - transfer_data = [] + transfer_data_items = [] for obj in external_col.all_objects: for item in obj.transfer_data_ownership: if item.owner not in local_tls: - transfer_data.append(item) + transfer_data_items.append(item) for obj in local_col.all_objects: for item in obj.transfer_data_ownership: if item.owner in local_tls: - transfer_data.append(item) + transfer_data_items.append(item) target_objs = [map.object_map[obj] for obj in map.object_map] - transferable_data.apply_transfer_data(context, transfer_data, target_objs) + transfer_core.apply_transfer_data(context, transfer_data_items, target_objs) for source_obj in map.object_map: target_obj = map.object_map[source_obj] diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 16820c8b..e6710e68 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,7 +2,7 @@ import bpy from . import core from pathlib import Path -from . import transferable_data, constants +from . import constants def get_parent_col_name(): diff --git a/scripts-blender/addons/asset_pipeline_2/transferable_data.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py similarity index 94% rename from scripts-blender/addons/asset_pipeline_2/transferable_data.py rename to scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index c9d8d1fc..893b5d27 100644 --- a/scripts-blender/addons/asset_pipeline_2/transferable_data.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -1,6 +1,7 @@ import bpy +from . import transfer_functions -from . import transfer_functions, asset_suffix +from .. import asset_suffix from typing import Dict diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py similarity index 96% rename from scripts-blender/addons/asset_pipeline_2/transfer_functions.py rename to scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 7d0335f4..aefc0f3a 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,6 +1,7 @@ import bpy from bpy import context -from . import asset_suffix, transferable_data +from . import transfer_core +from .. import asset_suffix ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES @@ -11,7 +12,7 @@ def get_vertex_groups(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: # Only add new ownership item if vertex group doesn't have an owner - matches = transferable_data.check_transfer_data_entry( + matches = transfer_core.check_transfer_data_entry( ownership, vertex_group.name, "VERTEX_GROUP" ) if len(matches) == 0: @@ -57,7 +58,7 @@ def transfer_vertex_group( def get_modifiers(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for mod in obj.modifiers: - matches = transferable_data.check_transfer_data_entry( + matches = transfer_core.check_transfer_data_entry( ownership, mod.name, "MODIFIER" ) if len(matches) == 0: @@ -131,7 +132,7 @@ def transfer_modifier(item, obj_target): def get_material_slots(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for slot in obj.material_slots: - matches = transferable_data.check_transfer_data_entry( + matches = transfer_core.check_transfer_data_entry( ownership, slot.name, "MATERIAL_SLOT" ) if len(matches) == 0: -- 2.30.2 From 3d9dc82cdff74ae0c96e7aa0377ebe12bd3f2224 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 17:47:37 -0400 Subject: [PATCH 067/429] Asset Pipe: Remove no-op code --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 3be90891..aeae69bf 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -1,7 +1,6 @@ import bpy from typing import Dict, Set -from . import util from . import asset_suffix from . import constants @@ -90,10 +89,6 @@ class AssetTransferMapping: """ coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - # Link top most parents. - # coll_map[self._local_col] = self._external_col - - # Get user facing names for local task layers local_tl_names = [ item[1] for item in constants.TASK_LAYER_ITEMS if item[0] in self._local_tls ] -- 2.30.2 From 945204a64d6a498944ead0433f957ec18fac61a8 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 18:33:08 -0400 Subject: [PATCH 068/429] Asset Pipe: Remove no-op Code --- .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 893b5d27..87ea9e8f 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -2,7 +2,6 @@ import bpy from . import transfer_functions from .. import asset_suffix -from typing import Dict # TODO Consider using transfer mapping here instead of matching suffixes -- 2.30.2 From 3b6c397433604ae4c3d5c88e7cb2fb8593e54f3f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 18:33:58 -0400 Subject: [PATCH 069/429] Asset Pipe: Add UI to Review New Transfer Data --- .../addons/asset_pipeline_2/ops.py | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index e6710e68..ee9521e4 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -20,9 +20,9 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_idname = "assetpipe.update_ownership" bl_label = 'Update Ownership' - def execute(self, context): - # TODO LOOP OVER ALl COllECTION OBJECTS - # TODO CREATE UI TO CONFIRM NEW OWNERSHIP ITEMS + _new_transfer_data = {} + + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): local_col = bpy.data.collections.get(get_parent_col_name()) if not local_col: self.report({'ERROR'}, "Top level collection could not be found") @@ -31,9 +31,38 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} + self._new_transfer_data = core.get_ownership(local_col, task_layer_name) + return context.window_manager.invoke_props_dialog(self, width=400) - new_data = core.get_ownership(local_col, task_layer_name) - core.set_ownership(new_data) + def get_transfer_data(self, name): + return [ + self._new_transfer_data[item] + for item in self._new_transfer_data + if self._new_transfer_data[item][2] == name + ] + + def draw_transfer_data_items(self, layout, name): + items = self.get_transfer_data(name) + if items: + box = layout.box() + box.label(text=name) + for item in items: + box.label(text=f"OBJ:{item[0].id_data.name} - {item[1]}") + + def draw(self, context: bpy.types.Context): + layout = self.layout + + if self._new_transfer_data: + layout.label(text="New Transfer Data will be Pushed to Publish") + else: + layout.label(text="No New Transfer Data found") + + self.draw_transfer_data_items(layout, "VERTEX_GROUP") + self.draw_transfer_data_items(layout, "MODIFIER") + self.draw_transfer_data_items(layout, "MATERIAL_SLOT") + + def execute(self, context: bpy.types.Context): + core.set_ownership(self._new_transfer_data) return {'FINISHED'} @@ -41,7 +70,7 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): bl_idname = "assetpipe.push_test" bl_label = 'Push to Publish' - def execute(self, context): + def execute(self, context: bpy.types.Context): # Find current task Layer current_file = Path(bpy.data.filepath) task_layer_name = get_task_layer_name_from_file() @@ -76,7 +105,7 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): bl_idname = "assetpipe.pull_test" bl_label = 'Pull from Publish' - def execute(self, context): + def execute(self, context: bpy.types.Context): task_layer_name = get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") -- 2.30.2 From f17a6fa203a8837189905ecffa30fd2af2ccdb11 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 19:43:08 -0400 Subject: [PATCH 070/429] Asset Pipe: Move Transfer Data Keys to Constants --- .../addons/asset_pipeline_2/constants.py | 6 ++++++ scripts-blender/addons/asset_pipeline_2/ops.py | 5 ++--- .../transfer_data/transfer_core.py | 9 ++++----- .../transfer_data/transfer_functions.py | 16 +++++++++------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 559743c6..e5270625 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -16,5 +16,11 @@ TRANSFER_DATA_TYPES = [ ("MATERIAL_SLOT", "Material Slot", ""), ] +TRANSFER_DATA_KEYS = [item[0] for item in TRANSFER_DATA_TYPES] + +VERTEX_GROUP_KEY = TRANSFER_DATA_KEYS[1] +MODIFIER_KEY = TRANSFER_DATA_KEYS[2] +MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[3] + LOCAL_SUFFIX = "LOCAL" EXTERNAL_SUFFIX = "EXTERNAL" diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index ee9521e4..0ba7314d 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -57,9 +57,8 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): else: layout.label(text="No New Transfer Data found") - self.draw_transfer_data_items(layout, "VERTEX_GROUP") - self.draw_transfer_data_items(layout, "MODIFIER") - self.draw_transfer_data_items(layout, "MATERIAL_SLOT") + for key in constants.TRANSFER_DATA_KEYS: + self.draw_transfer_data_items(layout, key) def execute(self, context: bpy.types.Context): core.set_ownership(self._new_transfer_data) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 87ea9e8f..22161478 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -1,8 +1,7 @@ import bpy from . import transfer_functions -from .. import asset_suffix - +from .. import asset_suffix, constants # TODO Consider using transfer mapping here instead of matching suffixes @@ -29,16 +28,16 @@ def apply_transfer_data( if asset_suffix.get_basename(target_obj.name) == asset_suffix.get_basename( item.id_data.name ): - if item.type == "VERTEX_GROUP": + if item.type == constants.VERTEX_GROUP_KEY: transfer_functions.transfer_vertex_group( context=context, vertex_group_name=item.name, obj_target=target_obj, obj_source=item.id_data, ) - if item.type == "MODIFIER": + if item.type == constants.MODIFIER_KEY: transfer_functions.transfer_modifier(item, target_obj) - if item.type == "MATERIAL_SLOT": + if item.type == constants.MATERIAL_SLOT_KEY: transfer_functions.transfer_material_slot(item, target_obj) update_transfer_data_ownership( transfer_data_item=item, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index aefc0f3a..636ae510 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,7 +1,7 @@ import bpy from bpy import context from . import transfer_core -from .. import asset_suffix +from .. import asset_suffix, constants ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES @@ -13,7 +13,7 @@ def get_vertex_groups(obj, task_layer_name, new_transfer_data): for vertex_group in obj.vertex_groups: # Only add new ownership item if vertex group doesn't have an owner matches = transfer_core.check_transfer_data_entry( - ownership, vertex_group.name, "VERTEX_GROUP" + ownership, vertex_group.name, constants.VERTEX_GROUP_KEY ) if len(matches) == 0: # NEED UNIQUE NAME INCASE OF DUPLICATES @@ -21,7 +21,7 @@ def get_vertex_groups(obj, task_layer_name, new_transfer_data): new_transfer_data[name] = ( ownership, vertex_group.name, - "VERTEX_GROUP", + constants.VERTEX_GROUP_KEY, task_layer_name, ) @@ -59,14 +59,15 @@ def get_modifiers(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for mod in obj.modifiers: matches = transfer_core.check_transfer_data_entry( - ownership, mod.name, "MODIFIER" + ownership, mod.name, constants.MODIFIER_KEY ) if len(matches) == 0: + # NEED UNIQUE NAME INCASE OF DUPLICATES name = mod.name + '_' + obj.name new_transfer_data[name] = ( ownership, mod.name, - "MODIFIER", + constants.MODIFIER_KEY, task_layer_name, ) @@ -133,14 +134,15 @@ def get_material_slots(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for slot in obj.material_slots: matches = transfer_core.check_transfer_data_entry( - ownership, slot.name, "MATERIAL_SLOT" + ownership, slot.name, constants.MATERIAL_SLOT_KEY ) if len(matches) == 0: + # NEED UNIQUE NAME INCASE OF DUPLICATES name = slot.name + '_' + obj.name new_transfer_data[name] = ( ownership, slot.name, - "MATERIAL_SLOT", + constants.MATERIAL_SLOT_KEY, task_layer_name, ) -- 2.30.2 From 33a4277224b7c1d60a6c9ef83f9c4c66ec95f3f7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 19:45:57 -0400 Subject: [PATCH 071/429] Asset Pipe: Move Ownership into Push Operator --- .../addons/asset_pipeline_2/ops.py | 19 ++++++------------- scripts-blender/addons/asset_pipeline_2/ui.py | 1 - 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 0ba7314d..56b24ec0 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -16,9 +16,9 @@ def get_task_layer_name_from_file(): return task_layer_name -class ASSETPIPE_OT_update_ownership(bpy.types.Operator): - bl_idname = "assetpipe.update_ownership" - bl_label = 'Update Ownership' +class ASSETPIPE_OT_push_test(bpy.types.Operator): + bl_idname = "assetpipe.push_test" + bl_label = 'Push to Publish' _new_transfer_data = {} @@ -60,22 +60,16 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): for key in constants.TRANSFER_DATA_KEYS: self.draw_transfer_data_items(layout, key) - def execute(self, context: bpy.types.Context): - core.set_ownership(self._new_transfer_data) - return {'FINISHED'} - - -class ASSETPIPE_OT_push_test(bpy.types.Operator): - bl_idname = "assetpipe.push_test" - bl_label = 'Push to Publish' - def execute(self, context: bpy.types.Context): # Find current task Layer + + core.set_ownership(self._new_transfer_data) current_file = Path(bpy.data.filepath) task_layer_name = get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} + pub_file = core.find_published_file(current_file) pub_file_path = pub_file.__str__() bpy.ops.wm.open_mainfile(filepath=pub_file_path) @@ -126,7 +120,6 @@ class ASSETPIPE_OT_pull_test(bpy.types.Operator): classes = ( ASSETPIPE_OT_push_test, ASSETPIPE_OT_pull_test, - ASSETPIPE_OT_update_ownership, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index d4be615f..b723edc6 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -12,7 +12,6 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) self.layout.label(text="Test UI") - self.layout.operator("assetpipe.update_ownership") self.layout.operator("assetpipe.push_test", icon="TRIA_UP") self.layout.operator("assetpipe.pull_test", icon="TRIA_DOWN") -- 2.30.2 From 560531ba39044bbb89b81d894f0d4c7db4d4c43e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 19:51:38 -0400 Subject: [PATCH 072/429] Asset Pipe: Add Option to Save During Push --- scripts-blender/addons/asset_pipeline_2/ops.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 56b24ec0..0ae3a88b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -22,6 +22,12 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): _new_transfer_data = {} + save: bpy.props.BoolProperty( + name="Save Current File", + default=True, + description="Save the Current File (after pulling if enabled) before Pushing to Publish", + ) + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): local_col = bpy.data.collections.get(get_parent_col_name()) if not local_col: @@ -51,6 +57,8 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): def draw(self, context: bpy.types.Context): layout = self.layout + row = layout.row() + row.prop(self, "save") if self._new_transfer_data: layout.label(text="New Transfer Data will be Pushed to Publish") @@ -70,6 +78,9 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} + if self.save: + bpy.ops.wm.save_as_mainfile(filepath=current_file.__str__()) + pub_file = core.find_published_file(current_file) pub_file_path = pub_file.__str__() bpy.ops.wm.open_mainfile(filepath=pub_file_path) -- 2.30.2 From 9ce89c2be90d3d1aa8bd948b133df1de26215ae1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 19:54:27 -0400 Subject: [PATCH 073/429] Asset Pipe: Add Option to Pull Before Pushing --- .../addons/asset_pipeline_2/ops.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 0ae3a88b..1a8185f6 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -28,6 +28,12 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): description="Save the Current File (after pulling if enabled) before Pushing to Publish", ) + pull: bpy.props.BoolProperty( + name="Pull before Pushing", + default=True, + description="Pull in any new data from the Published file before Pushing", + ) + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): local_col = bpy.data.collections.get(get_parent_col_name()) if not local_col: @@ -57,7 +63,9 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): def draw(self, context: bpy.types.Context): layout = self.layout + row = layout.row() + row.prop(self, "pull") row.prop(self, "save") if self._new_transfer_data: @@ -70,7 +78,6 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): def execute(self, context: bpy.types.Context): # Find current task Layer - core.set_ownership(self._new_transfer_data) current_file = Path(bpy.data.filepath) task_layer_name = get_task_layer_name_from_file() @@ -78,11 +85,24 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} + pub_file = core.find_published_file(current_file) + pub_file_path = pub_file.__str__() + + if self.pull: + error_msg = core.merge_task_layer( + context, + col_base_name=get_parent_col_name(), + local_tls=[task_layer_name], + target_file=pub_file, + ) + + if error_msg: + self.report({'ERROR'}, error_msg) + return {'CANCELLED'} + if self.save: bpy.ops.wm.save_as_mainfile(filepath=current_file.__str__()) - pub_file = core.find_published_file(current_file) - pub_file_path = pub_file.__str__() bpy.ops.wm.open_mainfile(filepath=pub_file_path) local_tls = [ -- 2.30.2 From 06d0e25e8bda8bcd91a804265205326bec434ea9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 21:28:53 -0400 Subject: [PATCH 074/429] Asset Pipe: Add Transfer Data to Asset Mapping --- .../addons/asset_pipeline_2/asset_mapping.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index aeae69bf..1c95e5ed 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -40,6 +40,7 @@ class AssetTransferMapping: def generate_mapping(self) -> None: self.object_map = self._gen_object_map() self.collection_map = self._gen_collection_map() + self.transfer_data_map = self._gen_transfer_data_map() def _get_external_object(self, local_obj): external_obj_name = asset_suffix.get_target_name( @@ -117,3 +118,23 @@ class AssetTransferMapping: self._no_match_target_colls = all_tgt_colls - match_target_colls return coll_map + + def _gen_transfer_data_map(self): + transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} + + for obj in self._external_col.all_objects: + for item in obj.transfer_data_ownership: + if item.owner not in self._local_tls: + name = item.name + '_' + obj.name + target_obj_name = asset_suffix.get_target_name(obj.name) + target_obj = self._local_col.all_objects.get(target_obj_name) + transfer_data_map[name] = (item, target_obj) + + for obj in self._local_col.all_objects: + for item in obj.transfer_data_ownership: + if item.owner in self._local_tls: + name = item.name + '_' + obj.name + target_obj_name = asset_suffix.get_target_name(obj.name) + target_obj = self._external_col.all_objects.get(target_obj_name) + transfer_data_map[name] = (item, target_obj) + return transfer_data_map -- 2.30.2 From 510af2c8cf46a0b409c0f4f4324ca244fe56e5ac Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 21:30:09 -0400 Subject: [PATCH 075/429] Asset Pipe: Use Transfer Data Map in Apply Transfer Data --- .../addons/asset_pipeline_2/core.py | 15 +----- .../transfer_data/transfer_core.py | 48 +++++++++---------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 8ab4ee1c..7cafeb6a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -64,20 +64,7 @@ def merge_task_layer( map = AssetTransferMapping(local_col, external_col, local_tls) - # Find Transfer Data - transfer_data_items = [] - for obj in external_col.all_objects: - for item in obj.transfer_data_ownership: - if item.owner not in local_tls: - transfer_data_items.append(item) - - for obj in local_col.all_objects: - for item in obj.transfer_data_ownership: - if item.owner in local_tls: - transfer_data_items.append(item) - - target_objs = [map.object_map[obj] for obj in map.object_map] - transfer_core.apply_transfer_data(context, transfer_data_items, target_objs) + transfer_core.apply_transfer_data(context, map.transfer_data_map) for source_obj in map.object_map: target_obj = map.object_map[source_obj] diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 22161478..0912256f 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -18,31 +18,29 @@ def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Obj ) -def apply_transfer_data( - context: bpy.types.Context, - transfer_data_list, - target_objs: set[bpy.types.Object], -): - for item in transfer_data_list: - for target_obj in target_objs: - if asset_suffix.get_basename(target_obj.name) == asset_suffix.get_basename( - item.id_data.name - ): - if item.type == constants.VERTEX_GROUP_KEY: - transfer_functions.transfer_vertex_group( - context=context, - vertex_group_name=item.name, - obj_target=target_obj, - obj_source=item.id_data, - ) - if item.type == constants.MODIFIER_KEY: - transfer_functions.transfer_modifier(item, target_obj) - if item.type == constants.MATERIAL_SLOT_KEY: - transfer_functions.transfer_material_slot(item, target_obj) - update_transfer_data_ownership( - transfer_data_item=item, - target_obj=target_obj, - ) +def apply_transfer_data(context, transfer_data_map): + for name in transfer_data_map: + transfer_data = transfer_data_map[name] + item = transfer_data[0] + target_obj = transfer_data[1] + if item.type == constants.VERTEX_GROUP_KEY: + print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") + transfer_functions.transfer_vertex_group( + context=context, + vertex_group_name=item.name, + obj_target=target_obj, + obj_source=item.id_data, + ) + if item.type == constants.MODIFIER_KEY: + print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") + transfer_functions.transfer_modifier(item, target_obj) + if item.type == constants.MATERIAL_SLOT_KEY: + print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") + transfer_functions.transfer_material_slot(item, target_obj) + update_transfer_data_ownership( + transfer_data_item=item, + target_obj=target_obj, + ) def check_transfer_data_entry(ownership, key, td_type): -- 2.30.2 From 7c6a6166a945f2a16fc2b79f36adf5cb8a6f9171 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 23 Aug 2023 21:31:21 -0400 Subject: [PATCH 076/429] Asset Pipe: Clear TODO & Remove no-op code --- .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 0912256f..aeabd555 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -1,9 +1,7 @@ import bpy from . import transfer_functions -from .. import asset_suffix, constants - -# TODO Consider using transfer mapping here instead of matching suffixes +from .. import constants def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): -- 2.30.2 From 1bbba9ac5759542beae3b22edf34ae4a44f7a12f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 11:31:39 -0400 Subject: [PATCH 077/429] Asset Pipe: Consolidate Push and Pull into single Sync Operator --- .../addons/asset_pipeline_2/ops.py | 45 ++++++------------- scripts-blender/addons/asset_pipeline_2/ui.py | 8 +++- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 1a8185f6..d25f19f9 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -16,9 +16,10 @@ def get_task_layer_name_from_file(): return task_layer_name -class ASSETPIPE_OT_push_test(bpy.types.Operator): - bl_idname = "assetpipe.push_test" - bl_label = 'Push to Publish' +class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): + bl_idname = "assetpipe.sync_with_publish" + bl_label = "Sync with Publish" + bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" _new_transfer_data = {} @@ -34,6 +35,10 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): description="Pull in any new data from the Published file before Pushing", ) + push: bpy.props.BoolProperty( + name="Push", default=True, description="Push any new data to Published file" + ) + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): local_col = bpy.data.collections.get(get_parent_col_name()) if not local_col: @@ -65,7 +70,8 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): layout = self.layout row = layout.row() - row.prop(self, "pull") + if self.push: + row.prop(self, "pull") row.prop(self, "save") if self._new_transfer_data: @@ -103,6 +109,9 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): if self.save: bpy.ops.wm.save_as_mainfile(filepath=current_file.__str__()) + if not self.push: + return {'FINISHED'} + bpy.ops.wm.open_mainfile(filepath=pub_file_path) local_tls = [ @@ -125,33 +134,7 @@ class ASSETPIPE_OT_push_test(bpy.types.Operator): return {'FINISHED'} -class ASSETPIPE_OT_pull_test(bpy.types.Operator): - bl_idname = "assetpipe.pull_test" - bl_label = 'Pull from Publish' - - def execute(self, context: bpy.types.Context): - task_layer_name = get_task_layer_name_from_file() - if not task_layer_name: - self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") - return {'CANCELLED'} - pub_file = core.find_published_file(Path(bpy.data.filepath)) - error_msg = core.merge_task_layer( - context, - col_base_name=get_parent_col_name(), - local_tls=[task_layer_name], - target_file=pub_file, - ) - - if error_msg: - self.report({'ERROR'}, error_msg) - return {'CANCELLED'} - return {'FINISHED'} - - -classes = ( - ASSETPIPE_OT_push_test, - ASSETPIPE_OT_pull_test, -) +classes = (ASSETPIPE_OT_sync_with_publish,) def register(): diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index b723edc6..69724d21 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -12,8 +12,12 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) self.layout.label(text="Test UI") - self.layout.operator("assetpipe.push_test", icon="TRIA_UP") - self.layout.operator("assetpipe.pull_test", icon="TRIA_DOWN") + self.layout.operator( + "assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP" + ).push = True + self.layout.operator( + "assetpipe.sync_with_publish", text="Pull from Publish", icon="TRIA_DOWN" + ).push = False if not context.active_object: return -- 2.30.2 From 0641acc166317d417fe5540b720f8d1b19cf59b0 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 14:47:47 -0400 Subject: [PATCH 078/429] Asset Pipe: Clear old ownership items --- .../addons/asset_pipeline_2/core.py | 17 ++++++++++++++++ .../transfer_data/transfer_functions.py | 20 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 7cafeb6a..34d1347f 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -9,9 +9,26 @@ from .asset_mapping import AssetTransferMapping from . import constants +def remove_old_ownership(obj, task_layer_name): + ownership = obj.transfer_data_ownership + to_remove = [] + for item in ownership: + if constants.TASK_LAYER_KEYS[item["owner"]] == task_layer_name: + if not ( + transfer_functions.verify_vertex_group(item) + or transfer_functions.verify_modifiers(item) + or transfer_functions.verify_modifiers(item) + ): + to_remove.append(item.name) + + for name in to_remove: + ownership.remove(ownership.keys().index(name)) + + def get_ownership(local_col: str, task_layer_name: str): new_transfer_data: Dict[bpy.props.CollectionProperty, str, str, str, str] = {} for obj in local_col.all_objects: + remove_old_ownership(obj, task_layer_name) transfer_functions.get_vertex_groups(obj, task_layer_name, new_transfer_data) transfer_functions.get_material_slots(obj, task_layer_name, new_transfer_data) transfer_functions.get_modifiers(obj, task_layer_name, new_transfer_data) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 636ae510..b60be0ce 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -8,6 +8,12 @@ from .. import asset_suffix, constants # VERTEX GROUPS +def verify_vertex_group(item): + obj = item.id_data + if item.type == constants.VERTEX_GROUP_KEY and obj.vertex_groups.get(item["name"]): + return True + + def get_vertex_groups(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: @@ -55,6 +61,12 @@ def transfer_vertex_group( # MODIFIERS +def verify_modifiers(item): + obj = item.id_data + if item.type == constants.MODIFIER_KEY and obj.modifiers.get(item["name"]): + return True + + def get_modifiers(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for mod in obj.modifiers: @@ -130,6 +142,14 @@ def transfer_modifier(item, obj_target): # MATERIAL SLOT +def verify_material_slot(item): + obj = item.id_data + if item.type == constants.MATERIAL_SLOT_KEY and obj.material_slots.get( + item["name"] + ): + return True + + def get_material_slots(obj, task_layer_name, new_transfer_data): ownership = obj.transfer_data_ownership for slot in obj.material_slots: -- 2.30.2 From 3888a318734b41eb41037f5fabf36f8110c4645a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 14:57:12 -0400 Subject: [PATCH 079/429] Asset Pipe: Use Sync Operator to Only Update Ownership --- scripts-blender/addons/asset_pipeline_2/ops.py | 6 ++++-- scripts-blender/addons/asset_pipeline_2/ui.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index d25f19f9..c48b1cbc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -31,12 +31,12 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): pull: bpy.props.BoolProperty( name="Pull before Pushing", - default=True, + default=False, description="Pull in any new data from the Published file before Pushing", ) push: bpy.props.BoolProperty( - name="Push", default=True, description="Push any new data to Published file" + name="Push", default=False, description="Push any new data to Published file" ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): @@ -49,6 +49,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} self._new_transfer_data = core.get_ownership(local_col, task_layer_name) + if self.push: + self.pull = True return context.window_manager.invoke_props_dialog(self, width=400) def get_transfer_data(self, name): diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 69724d21..075eb797 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -12,12 +12,13 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) self.layout.label(text="Test UI") + self.layout.operator("assetpipe.sync_with_publish", text="Update Ownership") self.layout.operator( "assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP" ).push = True self.layout.operator( "assetpipe.sync_with_publish", text="Pull from Publish", icon="TRIA_DOWN" - ).push = False + ).pull = True if not context.active_object: return -- 2.30.2 From 1273e079943071e30ad03b420c6215ac7c4e5c61 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 15:22:09 -0400 Subject: [PATCH 080/429] Asset Pipe: Fix bug in remove_old_ownership() --- scripts-blender/addons/asset_pipeline_2/core.py | 8 ++++---- .../transfer_data/transfer_functions.py | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 34d1347f..fc00431b 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -14,10 +14,10 @@ def remove_old_ownership(obj, task_layer_name): to_remove = [] for item in ownership: if constants.TASK_LAYER_KEYS[item["owner"]] == task_layer_name: - if not ( - transfer_functions.verify_vertex_group(item) - or transfer_functions.verify_modifiers(item) - or transfer_functions.verify_modifiers(item) + if ( + transfer_functions.vertex_group_is_missing(item) + or transfer_functions.modifiers_is_missing(item) + or transfer_functions.modifiers_is_missing(item) ): to_remove.append(item.name) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index b60be0ce..9fc8caa2 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -8,9 +8,11 @@ from .. import asset_suffix, constants # VERTEX GROUPS -def verify_vertex_group(item): +def vertex_group_is_missing(item): obj = item.id_data - if item.type == constants.VERTEX_GROUP_KEY and obj.vertex_groups.get(item["name"]): + if item.type == constants.VERTEX_GROUP_KEY and not obj.vertex_groups.get( + item["name"] + ): return True @@ -61,9 +63,9 @@ def transfer_vertex_group( # MODIFIERS -def verify_modifiers(item): +def modifiers_is_missing(item): obj = item.id_data - if item.type == constants.MODIFIER_KEY and obj.modifiers.get(item["name"]): + if item.type == constants.MODIFIER_KEY and not obj.modifiers.get(item["name"]): return True @@ -142,9 +144,9 @@ def transfer_modifier(item, obj_target): # MATERIAL SLOT -def verify_material_slot(item): +def material_slot_is_missing(item): obj = item.id_data - if item.type == constants.MATERIAL_SLOT_KEY and obj.material_slots.get( + if item.type == constants.MATERIAL_SLOT_KEY and not obj.material_slots.get( item["name"] ): return True -- 2.30.2 From f263f35805de571c4f3e1e3717a8cef6c957c973 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 15:26:04 -0400 Subject: [PATCH 081/429] Asset Pipe: Add Comment for Clarification --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index c48b1cbc..28dbf652 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -49,6 +49,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} self._new_transfer_data = core.get_ownership(local_col, task_layer_name) + + # Default behaviour is to pull before pushing if self.push: self.pull = True return context.window_manager.invoke_props_dialog(self, width=400) -- 2.30.2 From 7d193d33e61ad6c1116ebc36c8210876c1b2ec46 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 16:48:33 -0400 Subject: [PATCH 082/429] Asset Pipe: Fix Typo --- scripts-blender/addons/asset_pipeline_2/core.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index fc00431b..4f1e7ac1 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -17,7 +17,7 @@ def remove_old_ownership(obj, task_layer_name): if ( transfer_functions.vertex_group_is_missing(item) or transfer_functions.modifiers_is_missing(item) - or transfer_functions.modifiers_is_missing(item) + or transfer_functions.material_slot_is_missing(item) ): to_remove.append(item.name) @@ -100,6 +100,15 @@ def find_published_file_version(file): return int(file.name.split(".")[1].replace("v", "")) +def get_next_published_file(current_file: Path): + last_publish = find_published_file(current_file) + new_version_number = find_published_file_version(last_publish) + 1 + new_version = "{0:0=3d}".format(new_version_number) + return last_publish.parent.joinpath( + last_publish.name.split(".v")[0] + f".v" + new_version + ".blend" + ) + + def find_published_file(current_file: Path): publish_dir = current_file.parent.joinpath("publish") if not publish_dir.exists(): -- 2.30.2 From 9370fd4ff0ce4059e759443a59926740b1082384 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 16:49:38 -0400 Subject: [PATCH 083/429] Asset Pipe: Add Basic Publish New Version Operator --- .../addons/asset_pipeline_2/constants.py | 19 +++++++++++++ .../addons/asset_pipeline_2/ops.py | 27 ++++++++++++++++++- scripts-blender/addons/asset_pipeline_2/ui.py | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index e5270625..0096c8b9 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -22,5 +22,24 @@ VERTEX_GROUP_KEY = TRANSFER_DATA_KEYS[1] MODIFIER_KEY = TRANSFER_DATA_KEYS[2] MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[3] +PUBLISH_TYPES = [ + ( + "ACTIVE", + "Active", + "Publish a new active version that will become the latest published version", + ), + ( + "STAGE", + "Staged", + """Publish a staged version that will replace the last active version as the Push/Pull target. + Used for internal asset pipeline use only""", + ), + ( + "REVIEW", + "Review", + "Test the results that will be published in the review area, will not be used as Push/Pull target", + ), +] + LOCAL_SUFFIX = "LOCAL" EXTERNAL_SUFFIX = "EXTERNAL" diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 28dbf652..9f5a7c8a 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -138,7 +138,32 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): return {'FINISHED'} -classes = (ASSETPIPE_OT_sync_with_publish,) +class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): + bl_idname = "assetpipe.publish_new_version" + bl_label = "Publish New Version" + bl_description = """Create a new Published Version in the Publish Area""" + + publish_types: bpy.props.EnumProperty( + name="Transfer Data Owner", + items=constants.PUBLISH_TYPES, + ) + + # TODO use published types to publish to different folders + + def execute(self, context: bpy.types.Context): + if bpy.data.is_dirty: + self.report( + {'ERROR'}, + "Please save the current file and/or Pull from last publish before creating new Publish", + ) + return {'CANCELLED'} + current_file = Path(bpy.data.filepath) + new_file_path = core.get_next_published_file(current_file) + bpy.ops.wm.save_as_mainfile(filepath=new_file_path.__str__(), copy=True) + return {'FINISHED'} + + +classes = (ASSETPIPE_OT_sync_with_publish, ASSETPIPE_OT_publish_new_version) def register(): diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 075eb797..3379a98b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -12,6 +12,7 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) self.layout.label(text="Test UI") + self.layout.operator("assetpipe.publish_new_version") self.layout.operator("assetpipe.sync_with_publish", text="Update Ownership") self.layout.operator( "assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP" -- 2.30.2 From 6daada483ff9dfaaf819e0f293a32ea488f615c4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 17:12:25 -0400 Subject: [PATCH 084/429] Asset Pipe: Used Staged File as Sync Target if any exist --- .../addons/asset_pipeline_2/constants.py | 6 ++-- .../addons/asset_pipeline_2/core.py | 36 ++++++++++++------- .../addons/asset_pipeline_2/ops.py | 13 +++++-- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 0096c8b9..7ced121f 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -24,18 +24,18 @@ MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[3] PUBLISH_TYPES = [ ( - "ACTIVE", + "publish", "Active", "Publish a new active version that will become the latest published version", ), ( - "STAGE", + "staged", "Staged", """Publish a staged version that will replace the last active version as the Push/Pull target. Used for internal asset pipeline use only""", ), ( - "REVIEW", + "review", "Review", "Test the results that will be published in the review area, will not be used as Push/Pull target", ), diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 4f1e7ac1..4ff7d9d0 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -96,26 +96,38 @@ def merge_task_layer( asset_suffix.remove_suffix_from_hierarchy(local_col) -def find_published_file_version(file): +def find_file_version(file): return int(file.name.split(".")[1].replace("v", "")) -def get_next_published_file(current_file: Path): - last_publish = find_published_file(current_file) - new_version_number = find_published_file_version(last_publish) + 1 +def get_next_published_file(current_file: Path, publish_type="publish"): + last_publish = find_published(current_file, publish_type) + base_name = current_file.name.split(".")[0] + publish_dir = current_file.parent.joinpath(publish_type) + if not last_publish: + new_version_number = 1 + + else: + new_version_number = find_file_version(last_publish) + 1 new_version = "{0:0=3d}".format(new_version_number) - return last_publish.parent.joinpath( - last_publish.name.split(".v")[0] + f".v" + new_version + ".blend" - ) + return publish_dir.joinpath(base_name + f".v" + new_version + ".blend") -def find_published_file(current_file: Path): - publish_dir = current_file.parent.joinpath("publish") +def find_published(current_file: Path, publish_type="publish"): + publish_dir = current_file.parent.joinpath(publish_type) if not publish_dir.exists(): return - published_files = list(current_file.parent.joinpath("publish").glob('*.blend')) - published_files.sort(key=find_published_file_version) - return published_files[-1] + published_files = list(publish_dir.glob('*.blend')) + published_files.sort(key=find_file_version) + if published_files: + return published_files[-1] + + +def find_sync_target(current_file: Path): + latest_staged = find_published(current_file, publish_type="staged") + if latest_staged: + return latest_staged + return find_published(current_file, publish_type="publish") def import_data_from_lib( diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 9f5a7c8a..22d0ba2c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -95,7 +95,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - pub_file = core.find_published_file(current_file) + pub_file = core.find_sync_target(current_file) pub_file_path = pub_file.__str__() if self.pull: @@ -144,12 +144,19 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): bl_description = """Create a new Published Version in the Publish Area""" publish_types: bpy.props.EnumProperty( - name="Transfer Data Owner", + name="Type", items=constants.PUBLISH_TYPES, ) # TODO use published types to publish to different folders + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + layout = self.layout + layout.prop(self, "publish_types") + def execute(self, context: bpy.types.Context): if bpy.data.is_dirty: self.report( @@ -158,7 +165,7 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): ) return {'CANCELLED'} current_file = Path(bpy.data.filepath) - new_file_path = core.get_next_published_file(current_file) + new_file_path = core.get_next_published_file(current_file, self.publish_types) bpy.ops.wm.save_as_mainfile(filepath=new_file_path.__str__(), copy=True) return {'FINISHED'} -- 2.30.2 From 129a2936dbbfad2b7a486e85d9d56844851d811c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 18:02:15 -0400 Subject: [PATCH 085/429] Asset Pipe: Add Loop to Push to All Active Published Files --- .../addons/asset_pipeline_2/core.py | 13 +++++-- .../addons/asset_pipeline_2/ops.py | 37 ++++++++++--------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 4ff7d9d0..bf262f89 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -101,7 +101,7 @@ def find_file_version(file): def get_next_published_file(current_file: Path, publish_type="publish"): - last_publish = find_published(current_file, publish_type) + last_publish = find_latest_publish(current_file, publish_type) base_name = current_file.name.split(".")[0] publish_dir = current_file.parent.joinpath(publish_type) if not last_publish: @@ -113,21 +113,26 @@ def get_next_published_file(current_file: Path, publish_type="publish"): return publish_dir.joinpath(base_name + f".v" + new_version + ".blend") -def find_published(current_file: Path, publish_type="publish"): +def find_all_published(current_file, publish_type): publish_dir = current_file.parent.joinpath(publish_type) if not publish_dir.exists(): return published_files = list(publish_dir.glob('*.blend')) published_files.sort(key=find_file_version) + return published_files + + +def find_latest_publish(current_file: Path, publish_type="publish"): + published_files = find_all_published(current_file, publish_type) if published_files: return published_files[-1] def find_sync_target(current_file: Path): - latest_staged = find_published(current_file, publish_type="staged") + latest_staged = find_latest_publish(current_file, publish_type="staged") if latest_staged: return latest_staged - return find_published(current_file, publish_type="publish") + return find_latest_publish(current_file, publish_type="publish") def import_data_from_lib( diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 22d0ba2c..817c7702 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -96,8 +96,6 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): return {'CANCELLED'} pub_file = core.find_sync_target(current_file) - pub_file_path = pub_file.__str__() - if self.pull: error_msg = core.merge_task_layer( context, @@ -116,25 +114,28 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not self.push: return {'FINISHED'} - bpy.ops.wm.open_mainfile(filepath=pub_file_path) + # TODO Include staged file if it exists in this loop + for file in core.find_all_published(current_file, "publish"): + file_path = file.__str__() + bpy.ops.wm.open_mainfile(filepath=file_path) - local_tls = [ - item for item in constants.TASK_LAYER_KEYS if item != task_layer_name - ] + local_tls = [ + item for item in constants.TASK_LAYER_KEYS if item != task_layer_name + ] - error_msg = core.merge_task_layer( - context, - col_base_name=get_parent_col_name(), - local_tls=local_tls, - target_file=current_file, - ) - if error_msg: + error_msg = core.merge_task_layer( + context, + col_base_name=get_parent_col_name(), + local_tls=local_tls, + target_file=current_file, + ) + if error_msg: + bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) + self.report({'ERROR'}, error_msg) + return {'CANCELLED'} + + bpy.ops.wm.save_as_mainfile(filepath=file_path) bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) - self.report({'ERROR'}, error_msg) - return {'CANCELLED'} - - bpy.ops.wm.save_as_mainfile(filepath=pub_file_path) - bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) return {'FINISHED'} -- 2.30.2 From 17e56b0d96c642dec463952a11c06b10436ed013 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 18:09:46 -0400 Subject: [PATCH 086/429] Asset Pipe: Include Sync Target in Push Loop --- scripts-blender/addons/asset_pipeline_2/ops.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 817c7702..2e5f6e96 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -95,13 +95,13 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - pub_file = core.find_sync_target(current_file) + sync_target = core.find_sync_target(current_file) if self.pull: error_msg = core.merge_task_layer( context, col_base_name=get_parent_col_name(), local_tls=[task_layer_name], - target_file=pub_file, + target_file=sync_target, ) if error_msg: @@ -114,8 +114,11 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not self.push: return {'FINISHED'} - # TODO Include staged file if it exists in this loop - for file in core.find_all_published(current_file, "publish"): + push_targets = core.find_all_published(current_file, "publish") + if sync_target not in push_targets: + push_targets.append(sync_target) + + for file in push_targets: file_path = file.__str__() bpy.ops.wm.open_mainfile(filepath=file_path) -- 2.30.2 From f0cab0d867833235deb4aa47df1138d7f3d167ae Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 18:26:03 -0400 Subject: [PATCH 087/429] Asset Pipe: Add Option to Skip Depreciated Files to Push Loop --- scripts-blender/addons/asset_pipeline_2/core.py | 7 +++++++ scripts-blender/addons/asset_pipeline_2/ops.py | 15 ++++++--------- .../addons/asset_pipeline_2/props.py | 17 +++++++++++++++-- scripts-blender/addons/asset_pipeline_2/ui.py | 11 +++++++++++ 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index bf262f89..bd0f9e5c 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -174,3 +174,10 @@ def import_data_from_lib( ) return eval(f"bpy.data.{data_category}['{data_name}']") + + +def get_task_layer_name_from_file(): + file_name = bpy.path.basename(bpy.context.blend_data.filepath) + task_layer_name = file_name.split(".")[-2] + if task_layer_name in constants.TASK_LAYER_KEYS: + return task_layer_name diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 2e5f6e96..bb165580 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -9,13 +9,6 @@ def get_parent_col_name(): return "CH-chr_test" # TODO Replace Hard Coded Value -def get_task_layer_name_from_file(): - file_name = bpy.path.basename(bpy.context.blend_data.filepath) - task_layer_name = file_name.split(".")[-2] - if task_layer_name in constants.TASK_LAYER_KEYS: - return task_layer_name - - class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bl_idname = "assetpipe.sync_with_publish" bl_label = "Sync with Publish" @@ -44,7 +37,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not local_col: self.report({'ERROR'}, "Top level collection could not be found") return {'CANCELLED'} - task_layer_name = get_task_layer_name_from_file() + task_layer_name = core.get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} @@ -90,7 +83,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): # Find current task Layer core.set_ownership(self._new_transfer_data) current_file = Path(bpy.data.filepath) - task_layer_name = get_task_layer_name_from_file() + task_layer_name = core.get_task_layer_name_from_file() if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} @@ -122,6 +115,10 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): file_path = file.__str__() bpy.ops.wm.open_mainfile(filepath=file_path) + # SKIP DEPRECIATED FILES + if context.scene.asset_status.is_depreciated: + continue + local_tls = [ item for item in constants.TASK_LAYER_KEYS if item != task_layer_name ] diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 6d71d2a4..6abfc321 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -1,10 +1,19 @@ import bpy - +from . import constants """ NOTE Items in these properties groups should be generated by a function that finds the avaliable task layers from the task_layer_defaults.json file that needs to be created. """ + +class ASSET_STATUS(bpy.types.PropertyGroup): + is_depreciated: bpy.props.BoolProperty( + name="Depreciated", + description="Depreciated files do not recieve any updates when syncing from a task layer", + default=False, + ) + + from . import constants @@ -19,7 +28,10 @@ class ASSETOWNERSHIP(bpy.types.PropertyGroup): ) -classes = (ASSETOWNERSHIP,) +classes = ( + ASSETOWNERSHIP, + ASSET_STATUS, +) def register(): @@ -28,6 +40,7 @@ def register(): bpy.types.Object.transfer_data_ownership = bpy.props.CollectionProperty( type=ASSETOWNERSHIP ) + bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=ASSET_STATUS) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", items=constants.TASK_LAYER_ITEMS, diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 3379a98b..05061e1c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -1,5 +1,7 @@ import bpy +from . import core, constants + class ASSETPIPE_PT_TestUI(bpy.types.Panel): bl_space_type = 'VIEW_3D' @@ -8,6 +10,7 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): bl_label = "Test UI" def draw(self, context: bpy.types.Context) -> None: + layout = self.layout self.layout.label( text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) @@ -31,6 +34,14 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): text=f"{my_item.name} : {my_item.owner} : {my_item.id_data.name}" ) + # UI ONLY FOR PUBLISHED FILES + task_layer_name = core.get_task_layer_name_from_file() + if task_layer_name not in constants.TASK_LAYER_KEYS: + status = context.scene.asset_status + box = layout.box() + box.label(text="Published File Settings") + box.prop(status, "is_depreciated") + classes = (ASSETPIPE_PT_TestUI,) -- 2.30.2 From 2aa7461b6e6488a06ff87ae9b5e186ec0bdcdfc6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 18:26:33 -0400 Subject: [PATCH 088/429] Asset Pipe: Clean-up UI --- scripts-blender/addons/asset_pipeline_2/ui.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 05061e1c..79a2b70c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -11,16 +11,16 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout - self.layout.label( + layout.label( text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) - self.layout.label(text="Test UI") - self.layout.operator("assetpipe.publish_new_version") - self.layout.operator("assetpipe.sync_with_publish", text="Update Ownership") - self.layout.operator( + layout.label(text="Test UI") + layout.operator("assetpipe.publish_new_version") + layout.operator("assetpipe.sync_with_publish", text="Update Ownership") + layout.operator( "assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP" ).push = True - self.layout.operator( + layout.operator( "assetpipe.sync_with_publish", text="Pull from Publish", icon="TRIA_DOWN" ).pull = True @@ -28,9 +28,9 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): return obj = context.active_object ownership = obj.transfer_data_ownership - self.layout.prop(obj, "asset_id_owner") + layout.prop(obj, "asset_id_owner") for my_item in ownership: - self.layout.label( + layout.label( text=f"{my_item.name} : {my_item.owner} : {my_item.id_data.name}" ) -- 2.30.2 From 0ca61bccc582849b97144fd36fb3260ea5c36679 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 18:31:23 -0400 Subject: [PATCH 089/429] Asset Pipe: Update TODOs --- scripts-blender/addons/asset_pipeline_2/core.py | 1 + scripts-blender/addons/asset_pipeline_2/ui.py | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index bd0f9e5c..19082b78 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -26,6 +26,7 @@ def remove_old_ownership(obj, task_layer_name): def get_ownership(local_col: str, task_layer_name: str): + # TODO Collect ID owners of newly added objects and set during set_ownership new_transfer_data: Dict[bpy.props.CollectionProperty, str, str, str, str] = {} for obj in local_col.all_objects: remove_old_ownership(obj, task_layer_name) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 79a2b70c..c87c70f4 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -9,6 +9,7 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): bl_category = 'Asset Pipe 2' bl_label = "Test UI" + # TODO Move UI for inspecting Object Ownership to seperate panel def draw(self, context: bpy.types.Context) -> None: layout = self.layout layout.label( -- 2.30.2 From 39b1b0163973aa9c490c434e55b7f5da055c0c38 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 18:34:28 -0400 Subject: [PATCH 090/429] Asset Pipe: Clear old TODO --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index bb165580..1856554b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -149,8 +149,6 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): items=constants.PUBLISH_TYPES, ) - # TODO use published types to publish to different folders - def invoke(self, context: bpy.types.Context, event: bpy.types.Event): return context.window_manager.invoke_props_dialog(self, width=400) -- 2.30.2 From a17f50e5fad7c907b179f16f946e8904aea3a5fe Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 19:35:52 -0400 Subject: [PATCH 091/429] Asset Pipe: Rename Constant Keys to Match Icon Names --- scripts-blender/addons/asset_pipeline_2/constants.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 7ced121f..2a9c1315 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -9,11 +9,12 @@ TASK_LAYER_ITEMS = [ TASK_LAYER_KEYS = [item[0] for item in TASK_LAYER_ITEMS] +# KEYS FOR TRANSFER DATA TYPE MATCH NAME OF ICON TRANSFER_DATA_TYPES = [ ("NONE", "None", ""), - ("VERTEX_GROUP", "Vertex Group", ""), + ("GROUP_VERTEX", "Vertex Group", ""), ("MODIFIER", "Modifier", ""), - ("MATERIAL_SLOT", "Material Slot", ""), + ("MATERIAL", "Material Slot", ""), ] TRANSFER_DATA_KEYS = [item[0] for item in TRANSFER_DATA_TYPES] -- 2.30.2 From 89cd17787b208b154b6cfc344d7467435297fd2a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 19:36:21 -0400 Subject: [PATCH 092/429] Asset Pipe: Create new Panel just for Inspecting Ownership --- .../addons/asset_pipeline_2/core.py | 6 +++ scripts-blender/addons/asset_pipeline_2/ui.py | 50 +++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 19082b78..a0bced3c 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -182,3 +182,9 @@ def get_task_layer_name_from_file(): task_layer_name = file_name.split(".")[-2] if task_layer_name in constants.TASK_LAYER_KEYS: return task_layer_name + + +def get_enum_item(enum, key): + for item in enum: + if item[0] == key: + return item diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index c87c70f4..39fb11d2 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -3,13 +3,12 @@ import bpy from . import core, constants -class ASSETPIPE_PT_TestUI(bpy.types.Panel): +class ASSETPIPE_sync(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Asset Pipe 2' - bl_label = "Test UI" + bl_label = "Sync" - # TODO Move UI for inspecting Object Ownership to seperate panel def draw(self, context: bpy.types.Context) -> None: layout = self.layout layout.label( @@ -25,17 +24,45 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): "assetpipe.sync_with_publish", text="Pull from Publish", icon="TRIA_DOWN" ).pull = True + +class ASSETPIPE_ownership_inspector(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'Asset Pipe 2' + bl_label = "Ownership Inspector" + + def draw_transfer_data_type(self, layout, items): + name = core.get_enum_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] + box = layout.box() + box.label(text=name, icon=items[0].type) + for item in items: + owner = core.get_enum_item(constants.TASK_LAYER_ITEMS, item.owner)[1] + box.label(text=f"{item.name}: '{owner}'") + + def draw_transfer_data(self, ownership, layout) -> None: + vertex_groups = [ + item for item in ownership if item.type == constants.VERTEX_GROUP_KEY + ] + material_slots = [ + item for item in ownership if item.type == constants.MATERIAL_SLOT_KEY + ] + modifiers = [item for item in ownership if item.type == constants.MODIFIER_KEY] + self.draw_transfer_data_type(layout, vertex_groups) + self.draw_transfer_data_type(layout, modifiers) + self.draw_transfer_data_type(layout, material_slots) + + def draw(self, context: bpy.types.Context) -> None: + layout = self.layout if not context.active_object: + layout.label(text="Set an Active Object to Inspect") return obj = context.active_object ownership = obj.transfer_data_ownership - layout.prop(obj, "asset_id_owner") - for my_item in ownership: - layout.label( - text=f"{my_item.name} : {my_item.owner} : {my_item.id_data.name}" - ) + layout = layout.box() + owner = core.get_enum_item(constants.TASK_LAYER_ITEMS, obj.asset_id_owner)[1] + layout.label(text=f"{obj.name}: '{owner}'", icon="OBJECT_DATA") + self.draw_transfer_data(ownership, layout) - # UI ONLY FOR PUBLISHED FILES task_layer_name = core.get_task_layer_name_from_file() if task_layer_name not in constants.TASK_LAYER_KEYS: status = context.scene.asset_status @@ -44,7 +71,10 @@ class ASSETPIPE_PT_TestUI(bpy.types.Panel): box.prop(status, "is_depreciated") -classes = (ASSETPIPE_PT_TestUI,) +classes = ( + ASSETPIPE_sync, + ASSETPIPE_ownership_inspector, +) def register(): -- 2.30.2 From 95788baed20f7bb70ebcbe6cf61681ac535ba032 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 19:44:06 -0400 Subject: [PATCH 093/429] Asset Pipe: Centralize Transfer Data UI --- .../addons/asset_pipeline_2/ops.py | 2 ++ .../transfer_data/transfer_ui.py | 23 +++++++++++++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 23 ++----------------- 3 files changed, 27 insertions(+), 21 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 1856554b..eb3339cc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -3,6 +3,7 @@ import bpy from . import core from pathlib import Path from . import constants +from .transfer_data import transfer_ui def get_parent_col_name(): @@ -76,6 +77,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): else: layout.label(text="No New Transfer Data found") + # TODO re-use transfer UI here for key in constants.TRANSFER_DATA_KEYS: self.draw_transfer_data_items(layout, key) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py new file mode 100644 index 00000000..35c2d739 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -0,0 +1,23 @@ +from .. import constants, core + + +def draw_transfer_data_type(layout, items): + name = core.get_enum_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] + box = layout.box() + box.label(text=name, icon=items[0].type) + for item in items: + owner = core.get_enum_item(constants.TASK_LAYER_ITEMS, item.owner)[1] + box.label(text=f"{item.name}: '{owner}'") + + +def draw_transfer_data(ownership, layout) -> None: + vertex_groups = [ + item for item in ownership if item.type == constants.VERTEX_GROUP_KEY + ] + material_slots = [ + item for item in ownership if item.type == constants.MATERIAL_SLOT_KEY + ] + modifiers = [item for item in ownership if item.type == constants.MODIFIER_KEY] + draw_transfer_data_type(layout, vertex_groups) + draw_transfer_data_type(layout, modifiers) + draw_transfer_data_type(layout, material_slots) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 39fb11d2..37719be6 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -1,6 +1,7 @@ import bpy from . import core, constants +from .transfer_data import transfer_ui class ASSETPIPE_sync(bpy.types.Panel): @@ -31,26 +32,6 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): bl_category = 'Asset Pipe 2' bl_label = "Ownership Inspector" - def draw_transfer_data_type(self, layout, items): - name = core.get_enum_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] - box = layout.box() - box.label(text=name, icon=items[0].type) - for item in items: - owner = core.get_enum_item(constants.TASK_LAYER_ITEMS, item.owner)[1] - box.label(text=f"{item.name}: '{owner}'") - - def draw_transfer_data(self, ownership, layout) -> None: - vertex_groups = [ - item for item in ownership if item.type == constants.VERTEX_GROUP_KEY - ] - material_slots = [ - item for item in ownership if item.type == constants.MATERIAL_SLOT_KEY - ] - modifiers = [item for item in ownership if item.type == constants.MODIFIER_KEY] - self.draw_transfer_data_type(layout, vertex_groups) - self.draw_transfer_data_type(layout, modifiers) - self.draw_transfer_data_type(layout, material_slots) - def draw(self, context: bpy.types.Context) -> None: layout = self.layout if not context.active_object: @@ -61,7 +42,7 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): layout = layout.box() owner = core.get_enum_item(constants.TASK_LAYER_ITEMS, obj.asset_id_owner)[1] layout.label(text=f"{obj.name}: '{owner}'", icon="OBJECT_DATA") - self.draw_transfer_data(ownership, layout) + transfer_ui.draw_transfer_data(ownership, layout) task_layer_name = core.get_task_layer_name_from_file() if task_layer_name not in constants.TASK_LAYER_KEYS: -- 2.30.2 From 531bfa23a9bdb69617786f0a0527a1eb6513b38e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 20:58:57 -0400 Subject: [PATCH 094/429] Asset Pipe: Use Collection Property to collect New Transfer Items --- .../addons/asset_pipeline_2/core.py | 12 +++--- .../addons/asset_pipeline_2/ops.py | 8 +++- .../addons/asset_pipeline_2/props.py | 21 ++++++++-- .../transfer_data/transfer_functions.py | 39 +++++++------------ 4 files changed, 44 insertions(+), 36 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index a0bced3c..7ec9f136 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -25,21 +25,21 @@ def remove_old_ownership(obj, task_layer_name): ownership.remove(ownership.keys().index(name)) -def get_ownership(local_col: str, task_layer_name: str): +def get_ownership(local_col: str, task_layer_name: str, new_transfer_data): # TODO Collect ID owners of newly added objects and set during set_ownership - new_transfer_data: Dict[bpy.props.CollectionProperty, str, str, str, str] = {} for obj in local_col.all_objects: remove_old_ownership(obj, task_layer_name) transfer_functions.get_vertex_groups(obj, task_layer_name, new_transfer_data) transfer_functions.get_material_slots(obj, task_layer_name, new_transfer_data) transfer_functions.get_modifiers(obj, task_layer_name, new_transfer_data) - return new_transfer_data def set_ownership(new_transfer_data): - for data in new_transfer_data: - item = new_transfer_data[data] - transfer_core.transfer_data_add_entry(item[0], item[1], item[2], item[3]) + for item in new_transfer_data: + ownership = item.obj.transfer_data_ownership + transfer_core.transfer_data_add_entry( + ownership, item.name, item.type, item.owner + ) def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index eb3339cc..00c0db8c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -34,6 +34,9 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + ownership = context.scene.temp_transfer_data_ownership + ownership.clear() + local_col = bpy.data.collections.get(get_parent_col_name()) if not local_col: self.report({'ERROR'}, "Top level collection could not be found") @@ -42,7 +45,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - self._new_transfer_data = core.get_ownership(local_col, task_layer_name) + core.get_ownership(local_col, task_layer_name, ownership) # Default behaviour is to pull before pushing if self.push: @@ -83,7 +86,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): def execute(self, context: bpy.types.Context): # Find current task Layer - core.set_ownership(self._new_transfer_data) + ownership = context.scene.temp_transfer_data_ownership + core.set_ownership(ownership) current_file = Path(bpy.data.filepath) task_layer_name = core.get_task_layer_name_from_file() if not task_layer_name: diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 6abfc321..178b338e 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -28,10 +28,19 @@ class ASSETOWNERSHIP(bpy.types.PropertyGroup): ) -classes = ( - ASSETOWNERSHIP, - ASSET_STATUS, -) +class TEMP_ASSETOWNERSHIP(bpy.types.PropertyGroup): + owner: bpy.props.EnumProperty( + name="Transfer Data Owner", + items=constants.TASK_LAYER_ITEMS, + ) + type: bpy.props.EnumProperty( + name="Transfer Data Type", + items=constants.TRANSFER_DATA_TYPES, + ) + obj: bpy.props.PointerProperty(type=bpy.types.Object) + + +classes = (ASSETOWNERSHIP, ASSET_STATUS, TEMP_ASSETOWNERSHIP) def register(): @@ -40,6 +49,9 @@ def register(): bpy.types.Object.transfer_data_ownership = bpy.props.CollectionProperty( type=ASSETOWNERSHIP ) + bpy.types.Scene.temp_transfer_data_ownership = bpy.props.CollectionProperty( + type=TEMP_ASSETOWNERSHIP + ) bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=ASSET_STATUS) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", @@ -51,3 +63,4 @@ def unregister(): for i in classes: bpy.utils.unregister_class(i) del bpy.types.Object.transfer_data_ownership + del bpy.types.Scene.temp_transfer_data_ownership diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 9fc8caa2..456fdcf9 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -24,14 +24,11 @@ def get_vertex_groups(obj, task_layer_name, new_transfer_data): ownership, vertex_group.name, constants.VERTEX_GROUP_KEY ) if len(matches) == 0: - # NEED UNIQUE NAME INCASE OF DUPLICATES - name = vertex_group.name + '_' + obj.name - new_transfer_data[name] = ( - ownership, - vertex_group.name, - constants.VERTEX_GROUP_KEY, - task_layer_name, - ) + item = new_transfer_data.add() + item.name = vertex_group.name + item.owner = task_layer_name + item.type = constants.VERTEX_GROUP_KEY + item.obj = obj def transfer_vertex_group( @@ -76,14 +73,11 @@ def get_modifiers(obj, task_layer_name, new_transfer_data): ownership, mod.name, constants.MODIFIER_KEY ) if len(matches) == 0: - # NEED UNIQUE NAME INCASE OF DUPLICATES - name = mod.name + '_' + obj.name - new_transfer_data[name] = ( - ownership, - mod.name, - constants.MODIFIER_KEY, - task_layer_name, - ) + item = new_transfer_data.add() + item.name = mod.name + item.owner = task_layer_name + item.type = constants.MODIFIER_KEY + item.obj = obj def transfer_modifier(item, obj_target): @@ -159,14 +153,11 @@ def get_material_slots(obj, task_layer_name, new_transfer_data): ownership, slot.name, constants.MATERIAL_SLOT_KEY ) if len(matches) == 0: - # NEED UNIQUE NAME INCASE OF DUPLICATES - name = slot.name + '_' + obj.name - new_transfer_data[name] = ( - ownership, - slot.name, - constants.MATERIAL_SLOT_KEY, - task_layer_name, - ) + item = new_transfer_data.add() + item.name = slot.name + item.owner = task_layer_name + item.type = constants.MATERIAL_SLOT_KEY + item.obj = obj def transfer_material_slot(item, obj_target): -- 2.30.2 From f09d9534baeb8bdca829dddfa3c790958c33bdb3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 20:59:37 -0400 Subject: [PATCH 095/429] Asset Pipe: Re-use Transfer Data UI for Sync UI --- scripts-blender/addons/asset_pipeline_2/ops.py | 14 +++++++------- .../asset_pipeline_2/transfer_data/transfer_ui.py | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 00c0db8c..34926b3d 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -69,20 +69,20 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): def draw(self, context: bpy.types.Context): layout = self.layout + ownership = context.scene.temp_transfer_data_ownership row = layout.row() if self.push: row.prop(self, "pull") row.prop(self, "save") - if self._new_transfer_data: - layout.label(text="New Transfer Data will be Pushed to Publish") - else: + if len(ownership) == 0: layout.label(text="No New Transfer Data found") - - # TODO re-use transfer UI here - for key in constants.TRANSFER_DATA_KEYS: - self.draw_transfer_data_items(layout, key) + else: + layout.label(text="New Transfer Data will be Pushed to Publish") + # TODO SORT PER OBJECT AND DRAW FOR EACH OBJ + # TODO Store ownership in self for easier access + transfer_ui.draw_transfer_data(ownership, layout) def execute(self, context: bpy.types.Context): # Find current task Layer diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 35c2d739..553c35bd 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -2,6 +2,8 @@ from .. import constants, core def draw_transfer_data_type(layout, items): + if items == []: + return name = core.get_enum_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] box = layout.box() box.label(text=name, icon=items[0].type) -- 2.30.2 From 05bea97ccfba15e1d5be20a4d197768f59a9523b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 21:00:47 -0400 Subject: [PATCH 096/429] Asset Pipe: Remove No-Op Code --- scripts-blender/addons/asset_pipeline_2/ops.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 34926b3d..9e4a8ac4 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -52,21 +52,6 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.pull = True return context.window_manager.invoke_props_dialog(self, width=400) - def get_transfer_data(self, name): - return [ - self._new_transfer_data[item] - for item in self._new_transfer_data - if self._new_transfer_data[item][2] == name - ] - - def draw_transfer_data_items(self, layout, name): - items = self.get_transfer_data(name) - if items: - box = layout.box() - box.label(text=name) - for item in items: - box.label(text=f"OBJ:{item[0].id_data.name} - {item[1]}") - def draw(self, context: bpy.types.Context): layout = self.layout ownership = context.scene.temp_transfer_data_ownership -- 2.30.2 From 2d812d962c1b9d7db95d5ffd40c7ace2b856abfd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 21:07:31 -0400 Subject: [PATCH 097/429] Asset Pipe: Add Docstring to Temp Asset Ownership Class --- scripts-blender/addons/asset_pipeline_2/props.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 178b338e..053b2564 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -29,6 +29,9 @@ class ASSETOWNERSHIP(bpy.types.PropertyGroup): class TEMP_ASSETOWNERSHIP(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 ASSETOWNERSHIP""" + owner: bpy.props.EnumProperty( name="Transfer Data Owner", items=constants.TASK_LAYER_ITEMS, -- 2.30.2 From 57876b15485a9631b935b08525fd76d40d434891 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 21:14:53 -0400 Subject: [PATCH 098/429] Asset Pipe: Display New Ownership Per Object --- scripts-blender/addons/asset_pipeline_2/ops.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 9e4a8ac4..f6c64884 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -15,8 +15,6 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bl_label = "Sync with Publish" bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" - _new_transfer_data = {} - save: bpy.props.BoolProperty( name="Save Current File", default=True, @@ -34,6 +32,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + # TODO Store ownership in self for easier access? ownership = context.scene.temp_transfer_data_ownership ownership.clear() @@ -65,9 +64,14 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): layout.label(text="No New Transfer Data found") else: layout.label(text="New Transfer Data will be Pushed to Publish") - # TODO SORT PER OBJECT AND DRAW FOR EACH OBJ - # TODO Store ownership in self for easier access - transfer_ui.draw_transfer_data(ownership, layout) + + objs = [item.obj for item in ownership] + + for obj in set(objs): + obj_ownership = [item for item in ownership if item.obj == obj] + box = layout.box() + box.label(text=obj.name, icon="OBJECT_DATA") + transfer_ui.draw_transfer_data(obj_ownership, box) def execute(self, context: bpy.types.Context): # Find current task Layer -- 2.30.2 From 5b8b7903977fd4f6759cd9567a715929ee40701d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 24 Aug 2023 21:23:55 -0400 Subject: [PATCH 099/429] Asset Pipe: Clear TODO --- scripts-blender/addons/asset_pipeline_2/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 7ec9f136..d5e58228 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -26,7 +26,6 @@ def remove_old_ownership(obj, task_layer_name): def get_ownership(local_col: str, task_layer_name: str, new_transfer_data): - # TODO Collect ID owners of newly added objects and set during set_ownership for obj in local_col.all_objects: remove_old_ownership(obj, task_layer_name) transfer_functions.get_vertex_groups(obj, task_layer_name, new_transfer_data) -- 2.30.2 From 9315bfb26fb16de0791eb6a79dc76ecf4f591441 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 10:44:17 -0400 Subject: [PATCH 100/429] Asset Pipe: Store ownership in self for access in Sync operator --- scripts-blender/addons/asset_pipeline_2/ops.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index f6c64884..729c47b7 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -15,6 +15,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bl_label = "Sync with Publish" bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" + _temp_ownership = None + save: bpy.props.BoolProperty( name="Save Current File", default=True, @@ -32,9 +34,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): - # TODO Store ownership in self for easier access? - ownership = context.scene.temp_transfer_data_ownership - ownership.clear() + self._temp_ownership = context.scene.temp_transfer_data_ownership + self._temp_ownership.clear() local_col = bpy.data.collections.get(get_parent_col_name()) if not local_col: @@ -44,7 +45,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - core.get_ownership(local_col, task_layer_name, ownership) + core.get_ownership(local_col, task_layer_name, self._temp_ownership) # Default behaviour is to pull before pushing if self.push: @@ -53,22 +54,21 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): def draw(self, context: bpy.types.Context): layout = self.layout - ownership = context.scene.temp_transfer_data_ownership row = layout.row() if self.push: row.prop(self, "pull") row.prop(self, "save") - if len(ownership) == 0: + if len(self._temp_ownership) == 0: layout.label(text="No New Transfer Data found") else: layout.label(text="New Transfer Data will be Pushed to Publish") - objs = [item.obj for item in ownership] + objs = [item.obj for item in self._temp_ownership] for obj in set(objs): - obj_ownership = [item for item in ownership if item.obj == obj] + obj_ownership = [item for item in self._temp_ownership if item.obj == obj] box = layout.box() box.label(text=obj.name, icon="OBJECT_DATA") transfer_ui.draw_transfer_data(obj_ownership, box) -- 2.30.2 From 1773c04f7f0729175e2aa17a1787f44bce093717 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 10:45:22 -0400 Subject: [PATCH 101/429] Asset PIpe: Optimize Draw Transfer Data into Single Loop --- .../transfer_data/transfer_ui.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 553c35bd..d9430ddb 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -13,13 +13,18 @@ def draw_transfer_data_type(layout, items): def draw_transfer_data(ownership, layout) -> None: - vertex_groups = [ - item for item in ownership if item.type == constants.VERTEX_GROUP_KEY - ] - material_slots = [ - item for item in ownership if item.type == constants.MATERIAL_SLOT_KEY - ] - modifiers = [item for item in ownership if item.type == constants.MODIFIER_KEY] + vertex_groups = [] + material_slots = [] + modifiers = [] + + for item in ownership: + if item.type == constants.VERTEX_GROUP_KEY: + vertex_groups.append(item) + if item.type == constants.MATERIAL_SLOT_KEY: + material_slots.append(item) + if item.type == constants.MODIFIER_KEY: + modifiers.append(item) + draw_transfer_data_type(layout, vertex_groups) draw_transfer_data_type(layout, modifiers) draw_transfer_data_type(layout, material_slots) -- 2.30.2 From 30abc7a220f3da5352e62900364554b294ec6874 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 10:54:53 -0400 Subject: [PATCH 102/429] Asset Pipe: Update Collection Pointer in Test Files --- .../addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend | 4 ++-- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend index 9e14de8f..01e11d0f 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c8967066230440863ba8de96787e553594d4aeb1d3db3c49591cdd5085420cd -size 936268 +oid sha256:3432665419addfae50c0edd58deb58c0a0a24cc091072fd226a5b9b8043ed6f7 +size 962236 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index 1f4a6e02..866f48f7 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bfc7e86d668f4820533d5830fa9846266b55debffbf59fe129d5fef32dec099 -size 949736 +oid sha256:bb1a58ac494b08d705086cc55721821b2abf00225e230eecd32b8fab76957c11 +size 975704 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend index dd40d26a..a0bfde0d 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bd727c70d81152a7e7a3e20ac75f8ae4ac1d898b69cc480295f3430796df777 -size 969004 +oid sha256:86b3eb5ec0dd9ef6dff3794f0813f2a2f1e6bacea4b96e2078d7bdae711964b0 +size 994972 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index a89599ad..0a6c4cef 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d90137df71d20e853382533eeda0beb9792e39c4222024032baf4745e9a5438 -size 936036 +oid sha256:656731d43a988ef395fa4cd4254ea450bf7451cff5181e89ddd4a26db6112544 +size 962004 -- 2.30.2 From 787408aae0e11430bcac69c652a1c20aad5a135b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 10:55:37 -0400 Subject: [PATCH 103/429] Asset Pipe: Add UI for Asset Collection Pointer --- scripts-blender/addons/asset_pipeline_2/props.py | 1 + scripts-blender/addons/asset_pipeline_2/ui.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 053b2564..69920e6e 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -12,6 +12,7 @@ class ASSET_STATUS(bpy.types.PropertyGroup): description="Depreciated files do not recieve any updates when syncing from a task layer", default=False, ) + asset_collection: bpy.props.PointerProperty(type=bpy.types.Collection) from . import constants diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 37719be6..3a242e1a 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -12,9 +12,11 @@ class ASSETPIPE_sync(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout + status = context.scene.asset_status layout.label( text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) + layout.prop(status, "asset_collection", text="Asset") layout.label(text="Test UI") layout.operator("assetpipe.publish_new_version") layout.operator("assetpipe.sync_with_publish", text="Update Ownership") -- 2.30.2 From 9de9f5ff31cb73007e37f8059fd7aec6d45bd99f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 11:03:31 -0400 Subject: [PATCH 104/429] Asset Pipe: Use Asset Collection in Merge Core Function --- scripts-blender/addons/asset_pipeline_2/core.py | 8 +++++--- scripts-blender/addons/asset_pipeline_2/ops.py | 8 +------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index d5e58228..377426ff 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -8,6 +8,8 @@ from .asset_mapping import AssetTransferMapping from . import constants +# TODO Add UI to warn about un-assigned objects + def remove_old_ownership(obj, task_layer_name): ownership = obj.transfer_data_ownership @@ -61,13 +63,13 @@ def update_task_layer_objects( def merge_task_layer( context: bpy.types.Context, - col_base_name: str, local_tls: list[str], target_file: Path, ): - local_col = bpy.data.collections.get(col_base_name) + local_col = context.scene.asset_status.asset_collection if not local_col: - return "Current File Name doesn't contain valid task layer" + return "Unable to find Asset Collection" + col_base_name = local_col.name local_suffix = constants.LOCAL_SUFFIX external_suffix = constants.EXTERNAL_SUFFIX asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 729c47b7..351d4a7d 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -6,10 +6,6 @@ from . import constants from .transfer_data import transfer_ui -def get_parent_col_name(): - return "CH-chr_test" # TODO Replace Hard Coded Value - - class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bl_idname = "assetpipe.sync_with_publish" bl_label = "Sync with Publish" @@ -37,7 +33,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self._temp_ownership = context.scene.temp_transfer_data_ownership self._temp_ownership.clear() - local_col = bpy.data.collections.get(get_parent_col_name()) + local_col = context.scene.asset_status.asset_collection if not local_col: self.report({'ERROR'}, "Top level collection could not be found") return {'CANCELLED'} @@ -87,7 +83,6 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if self.pull: error_msg = core.merge_task_layer( context, - col_base_name=get_parent_col_name(), local_tls=[task_layer_name], target_file=sync_target, ) @@ -120,7 +115,6 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): error_msg = core.merge_task_layer( context, - col_base_name=get_parent_col_name(), local_tls=local_tls, target_file=current_file, ) -- 2.30.2 From ce36223e29ea8d9598257427d24e7d7777002343 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 13:02:01 -0400 Subject: [PATCH 105/429] Asset Pipe: Warn about Objects with no Ownership --- .../addons/asset_pipeline_2/asset_mapping.py | 5 ++++- .../addons/asset_pipeline_2/core.py | 20 +++++++++++++++---- .../addons/asset_pipeline_2/ops.py | 18 +++++++++++++++-- .../transfer_data/transfer_core.py | 3 +++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 1c95e5ed..27e144cf 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -60,6 +60,9 @@ class AssetTransferMapping: """ object_map: Dict[bpy.types.Object, bpy.types.Object] = {} for local_obj in self._local_col.all_objects: + # Skip items with no owner + if local_obj.asset_id_owner == "NONE": + continue # IF ITEM IS OWNED BY LOCAL TASK LAYERS if local_obj.asset_id_owner in self._local_tls: external_obj = self._get_external_object(local_obj) @@ -124,7 +127,7 @@ class AssetTransferMapping: for obj in self._external_col.all_objects: for item in obj.transfer_data_ownership: - if item.owner not in self._local_tls: + if item.owner not in self._local_tls and item.owner != "NONE": name = item.name + '_' + obj.name target_obj_name = asset_suffix.get_target_name(obj.name) target_obj = self._local_col.all_objects.get(target_obj_name) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 377426ff..e376dbc2 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -11,7 +11,7 @@ from . import constants # TODO Add UI to warn about un-assigned objects -def remove_old_ownership(obj, task_layer_name): +def ownership_cleanup(obj, task_layer_name): ownership = obj.transfer_data_ownership to_remove = [] for item in ownership: @@ -27,15 +27,27 @@ def remove_old_ownership(obj, task_layer_name): ownership.remove(ownership.keys().index(name)) -def get_ownership(local_col: str, task_layer_name: str, new_transfer_data): +def ownership_get(local_col: str, task_layer_name: str, new_transfer_data): + invalid_objs = [] + task_layer_col_name = get_enum_item(constants.TASK_LAYER_ITEMS, task_layer_name)[1] + task_layer_col = local_col.children.get(task_layer_col_name) for obj in local_col.all_objects: - remove_old_ownership(obj, task_layer_name) + # Mark Asset ID Owner for objects in the current task layers collection + if obj.asset_id_owner == "NONE" and obj in list(task_layer_col.all_objects): + obj.asset_id_owner = task_layer_name + # Skip items that have no owner + if obj.asset_id_owner == "NONE": + invalid_objs.append(obj) + continue + ownership_cleanup(obj, task_layer_name) transfer_functions.get_vertex_groups(obj, task_layer_name, new_transfer_data) transfer_functions.get_material_slots(obj, task_layer_name, new_transfer_data) transfer_functions.get_modifiers(obj, task_layer_name, new_transfer_data) + return invalid_objs -def set_ownership(new_transfer_data): + +def ownership_set(new_transfer_data): for item in new_transfer_data: ownership = item.obj.transfer_data_ownership transfer_core.transfer_data_add_entry( diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 351d4a7d..1e47ba09 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -12,6 +12,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" _temp_ownership = None + _invalid_objs = [] save: bpy.props.BoolProperty( name="Save Current File", @@ -32,6 +33,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): def invoke(self, context: bpy.types.Context, event: bpy.types.Event): self._temp_ownership = context.scene.temp_transfer_data_ownership self._temp_ownership.clear() + self._invalid_objs.clear() local_col = context.scene.asset_status.asset_collection if not local_col: @@ -41,7 +43,12 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not task_layer_name: self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - core.get_ownership(local_col, task_layer_name, self._temp_ownership) + + self._invalid_objs = core.ownership_get( + local_col, + task_layer_name, + self._temp_ownership, + ) # Default behaviour is to pull before pushing if self.push: @@ -56,6 +63,13 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): row.prop(self, "pull") row.prop(self, "save") + if len(self._invalid_objs) != 0: + box = layout.box() + box.alert = True + box.label(text="Sync will clear Invalid Objects:", icon="ERROR") + for obj in self._invalid_objs: + box.label(text=obj.name, icon="OBJECT_DATA") + if len(self._temp_ownership) == 0: layout.label(text="No New Transfer Data found") else: @@ -72,7 +86,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): def execute(self, context: bpy.types.Context): # Find current task Layer ownership = context.scene.temp_transfer_data_ownership - core.set_ownership(ownership) + core.ownership_set(ownership) current_file = Path(bpy.data.filepath) task_layer_name = core.get_task_layer_name_from_file() if not task_layer_name: diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index aeabd555..f3cacf95 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -21,6 +21,9 @@ def apply_transfer_data(context, transfer_data_map): transfer_data = transfer_data_map[name] item = transfer_data[0] target_obj = transfer_data[1] + if target_obj is None: + print(f"Failed to Transfer data for {item.id_data.name}") + continue if item.type == constants.VERTEX_GROUP_KEY: print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") transfer_functions.transfer_vertex_group( -- 2.30.2 From cb9b910bc0ec1106e0dd7c19f58d160192beeb3c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 13:19:03 -0400 Subject: [PATCH 106/429] Asset Pipe: Add Property to Check if File is Asset Pipeline --- scripts-blender/addons/asset_pipeline_2/props.py | 5 +++++ scripts-blender/addons/asset_pipeline_2/ui.py | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 69920e6e..885ce408 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -7,6 +7,11 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr class ASSET_STATUS(bpy.types.PropertyGroup): + 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", diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 3a242e1a..1651b12a 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -8,11 +8,15 @@ class ASSETPIPE_sync(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Asset Pipe 2' - bl_label = "Sync" + bl_label = "Asset Managment" def draw(self, context: bpy.types.Context) -> None: layout = self.layout status = context.scene.asset_status + if not status.is_asset_pipeline_file: + layout.label(text="Isn't Asset Pipeline File") + # layout.operator("") + return layout.label( text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) -- 2.30.2 From ac7497ae9a1e2bb353e2dcb852f68b3653c9877b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 13:19:34 -0400 Subject: [PATCH 107/429] Asset Pipe: Set is Asset Pipeline in Test Files --- .../addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend | 4 ++-- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend index 01e11d0f..35d17096 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3432665419addfae50c0edd58deb58c0a0a24cc091072fd226a5b9b8043ed6f7 -size 962236 +oid sha256:c71ffac74d17bcb6ed532646bde5e969f3a728c67ae1d7c51f04795a77e3c556 +size 937556 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index 866f48f7..7be01e2c 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb1a58ac494b08d705086cc55721821b2abf00225e230eecd32b8fab76957c11 -size 975704 +oid sha256:df7194bd64dbc5c16a075ac98076e84cb17f9e197fe228e99c1792815181beec +size 950888 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend index a0bfde0d..85d91522 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86b3eb5ec0dd9ef6dff3794f0813f2a2f1e6bacea4b96e2078d7bdae711964b0 -size 994972 +oid sha256:439f2b6692538b799c305dd2ca849c9ce8b937c0ca037d5b7fb2d8bf4c06fa56 +size 970156 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index 0a6c4cef..71582e16 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:656731d43a988ef395fa4cd4254ea450bf7451cff5181e89ddd4a26db6112544 -size 962004 +oid sha256:8931f453faaa71d14e42a41356a2fb8acaa337702e8c58f3dd82a9b5de2e2142 +size 937188 -- 2.30.2 From 0f799567eb33d6134933f5e25915180546f71369 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 13:25:11 -0400 Subject: [PATCH 108/429] Asset PIpe: Add Warning to UI if No Asset FIle is found --- scripts-blender/addons/asset_pipeline_2/ui.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 1651b12a..ec88a39b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -40,6 +40,10 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout + status = context.scene.asset_status + if not status.is_asset_pipeline_file: + layout.label(text="Open valid 'Asset Pipeline' file", icon="ERROR") + return if not context.active_object: layout.label(text="Set an Active Object to Inspect") return -- 2.30.2 From 5e01227ce37beed032948a25e487c6a4e0a904ba Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 14:01:30 -0400 Subject: [PATCH 109/429] Asset Pipe: Update TODOs --- scripts-blender/addons/asset_pipeline_2/ops.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 1e47ba09..7645c0ae 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -5,8 +5,20 @@ from pathlib import Path from . import constants from .transfer_data import transfer_ui +# TODO Add operator to create new Asset +# # User Input Directory where Folder is +# # User Input Name of Asset +# # Create Asset Parent Folder at Directory +# # Create Directories for Piublish/Staged/Review +# # Create a file per task Layer +# # Create Asset Col +# # Set Asset Col Pointer +# # Set is Asset Pipe File Prop +# # Creata intial publish based on task layers + class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): + # TODO Early Return no Sync Target is found bl_idname = "assetpipe.sync_with_publish" bl_label = "Sync with Publish" bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" -- 2.30.2 From 04d31d4230ce8810fa9b2339edaad1fa5669c99f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 16:09:53 -0400 Subject: [PATCH 110/429] Asset Pipe: Add Property to store New Asset Name/Dir --- .../addons/asset_pipeline_2/props.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 885ce408..32a633e3 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -6,6 +6,15 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr """ +class NEW_ASSET(bpy.types.PropertyGroup): + 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") + + class ASSET_STATUS(bpy.types.PropertyGroup): is_asset_pipeline_file: bpy.props.BoolProperty( name="Asset Pipeline File", @@ -49,7 +58,12 @@ class TEMP_ASSETOWNERSHIP(bpy.types.PropertyGroup): obj: bpy.props.PointerProperty(type=bpy.types.Object) -classes = (ASSETOWNERSHIP, ASSET_STATUS, TEMP_ASSETOWNERSHIP) +classes = ( + ASSETOWNERSHIP, + ASSET_STATUS, + TEMP_ASSETOWNERSHIP, + NEW_ASSET, +) def register(): @@ -66,6 +80,7 @@ def register(): name="ID Owner", items=constants.TASK_LAYER_ITEMS, ) + bpy.types.Scene.asset_new = bpy.props.PointerProperty(type=NEW_ASSET) def unregister(): @@ -73,3 +88,6 @@ def unregister(): bpy.utils.unregister_class(i) del bpy.types.Object.transfer_data_ownership del bpy.types.Scene.temp_transfer_data_ownership + del bpy.types.Scene.asset_status + del bpy.types.Object.asset_id_owner + del bpy.types.Scene.asset_new -- 2.30.2 From 032311c9fdcdddf19b255d1c5cc9d96074c38e71 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 16:10:14 -0400 Subject: [PATCH 111/429] Asset Pipe: Add Constants for easier access --- scripts-blender/addons/asset_pipeline_2/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 2a9c1315..92c52afe 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -8,6 +8,7 @@ TASK_LAYER_ITEMS = [ ] TASK_LAYER_KEYS = [item[0] for item in TASK_LAYER_ITEMS] +TASK_LAYER_NAMES = [item[1] for item in TASK_LAYER_ITEMS] # KEYS FOR TRANSFER DATA TYPE MATCH NAME OF ICON TRANSFER_DATA_TYPES = [ @@ -41,6 +42,7 @@ PUBLISH_TYPES = [ "Test the results that will be published in the review area, will not be used as Push/Pull target", ), ] +PUBLISH_KEYS = [item[0] for item in PUBLISH_TYPES] LOCAL_SUFFIX = "LOCAL" EXTERNAL_SUFFIX = "EXTERNAL" -- 2.30.2 From 0b5894a2266f614611cf543008163c8d8e6b0a1d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 25 Aug 2023 16:11:26 -0400 Subject: [PATCH 112/429] Asset Pipe: Add Operator to Create a New Asset --- .../addons/asset_pipeline_2/ops.py | 82 ++++++++++++++++--- scripts-blender/addons/asset_pipeline_2/ui.py | 5 +- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 7645c0ae..80dbfd21 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -4,17 +4,73 @@ from . import core from pathlib import Path from . import constants from .transfer_data import transfer_ui +import os -# TODO Add operator to create new Asset -# # User Input Directory where Folder is -# # User Input Name of Asset -# # Create Asset Parent Folder at Directory -# # Create Directories for Piublish/Staged/Review -# # Create a file per task Layer -# # Create Asset Col -# # Set Asset Col Pointer -# # Set is Asset Pipe File Prop -# # Creata intial publish based on task layers + +class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): + bl_idname = "assetpipe.create_new_asset" + bl_label = "Create New Asset" + bl_description = """Create a new Asset""" # TODO Improve description + + _name = None + _dir = None + + # TODO add poll method to check if name and directory are valid before running + + def execute(self, context: bpy.types.Context): + # New File is Createed so Props need to be saved + new_asset = context.scene.asset_new + self._name = new_asset.name + self._dir = new_asset.dir + + # Create Asset Folder at Directory + asset_path = os.path.join(self._dir, self._name) + os.mkdir(asset_path) + + for publish_type in constants.PUBLISH_KEYS: + os.mkdir(os.path.join(asset_path, publish_type)) + + # Prepare New File + bpy.ops.wm.read_homefile(app_template="") + + # Remove All Data + for col in bpy.data.collections: + bpy.data.collections.remove(col) + for obj in bpy.data.objects: + bpy.data.objects.remove(obj) + + bpy.ops.outliner.orphans_purge( + do_local_ids=True, do_linked_ids=False, do_recursive=True + ) + + # Setup New File + asset_status = context.scene.asset_status + asset_status.is_asset_pipeline_file = True + bpy.data.collections.new(self._name) + asset_col = bpy.data.collections.get(self._name) + context.scene.collection.children.link(asset_col) + asset_status.asset_collection = asset_col + + for task_layer_name in constants.TASK_LAYER_NAMES: + if task_layer_name == "None": + continue + bpy.data.collections.new(task_layer_name) + asset_col.children.link(bpy.data.collections.get(task_layer_name)) + + for task_layer_key in reversed(constants.TASK_LAYER_KEYS): + if task_layer_key == "NONE": + continue + name = self._name + "." + task_layer_key + ".blend" + task_layer_file = os.path.join(asset_path, name) + bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) + + # Creata intial publish based on task layers + publish_path = os.path.join(asset_path, "publish") # TODO FIX HARD CODE VALUE + name = self._name + "." + "v001" + ".blend" + publish_file = os.path.join(publish_path, name) + bpy.ops.wm.save_as_mainfile(filepath=publish_file, copy=True) + bpy.ops.wm.open_mainfile(filepath=task_layer_file) + return {'FINISHED'} class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): @@ -184,7 +240,11 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): return {'FINISHED'} -classes = (ASSETPIPE_OT_sync_with_publish, ASSETPIPE_OT_publish_new_version) +classes = ( + ASSETPIPE_OT_sync_with_publish, + ASSETPIPE_OT_publish_new_version, + ASSETPIPE_OT_create_new_asset, +) def register(): diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index ec88a39b..1161a3bf 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -14,7 +14,10 @@ class ASSETPIPE_sync(bpy.types.Panel): layout = self.layout status = context.scene.asset_status if not status.is_asset_pipeline_file: - layout.label(text="Isn't Asset Pipeline File") + new_asset = context.scene.asset_new + layout.prop(new_asset, "dir") + layout.prop(new_asset, "name") + layout.operator("assetpipe.create_new_asset") # layout.operator("") return layout.label( -- 2.30.2 From 02163d71c3e84b366d417856d4c74403e5408952 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 12:05:20 -0400 Subject: [PATCH 113/429] Asset Pipe: Add Poll Method to Create New Asset Op --- scripts-blender/addons/asset_pipeline_2/ops.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 80dbfd21..22e5f5e1 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -15,7 +15,16 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): _name = None _dir = None - # TODO add poll method to check if name and directory are valid before running + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + new_asset = context.scene.asset_new + if new_asset.name == "" or new_asset.dir == "": + cls.poll_message_set("Asset Name and Directory must be valid") + return False + if os.path.exists(os.path.join(new_asset.dir, new_asset.name)): + cls.poll_message_set("Asset Folder already exists") + return False + return True def execute(self, context: bpy.types.Context): # New File is Createed so Props need to be saved -- 2.30.2 From 66edcc8c9a4bd9749fa54bdf93f97e6c9173398c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 12:24:19 -0400 Subject: [PATCH 114/429] Asset Pipe: Early Return if Sync Target doesn't exist --- scripts-blender/addons/asset_pipeline_2/ops.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 22e5f5e1..934190c5 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -171,6 +171,10 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): return {'CANCELLED'} sync_target = core.find_sync_target(current_file) + if not sync_target.exists(): + self.report({'ERROR'}, "Sync Target could not be determined") + return {'CANCELLED'} + if self.pull: error_msg = core.merge_task_layer( context, -- 2.30.2 From bd2e94d1deb9d0ece41198785ba3ca27f693d44e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 12:27:09 -0400 Subject: [PATCH 115/429] Asset Pipe: Clear Old TODO --- scripts-blender/addons/asset_pipeline_2/ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 934190c5..0170227a 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -83,7 +83,6 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): - # TODO Early Return no Sync Target is found bl_idname = "assetpipe.sync_with_publish" bl_label = "Sync with Publish" bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" -- 2.30.2 From 2e737daae5e5328d9adfb988fa7ad72c7c76726a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 12:34:52 -0400 Subject: [PATCH 116/429] Asset Pipe: Clean-up Hard Coded values --- scripts-blender/addons/asset_pipeline_2/constants.py | 2 ++ scripts-blender/addons/asset_pipeline_2/core.py | 12 ++++++++---- scripts-blender/addons/asset_pipeline_2/ops.py | 6 ++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 92c52afe..a2fc296c 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -43,6 +43,8 @@ PUBLISH_TYPES = [ ), ] PUBLISH_KEYS = [item[0] for item in PUBLISH_TYPES] +ACTIVE_PUBLISH_KEY = PUBLISH_KEYS[0] +STAGED_PUBLISH_KEY = PUBLISH_KEYS[1] LOCAL_SUFFIX = "LOCAL" EXTERNAL_SUFFIX = "EXTERNAL" diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index e376dbc2..1308d41a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -114,7 +114,9 @@ def find_file_version(file): return int(file.name.split(".")[1].replace("v", "")) -def get_next_published_file(current_file: Path, publish_type="publish"): +def get_next_published_file( + current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY +): last_publish = find_latest_publish(current_file, publish_type) base_name = current_file.name.split(".")[0] publish_dir = current_file.parent.joinpath(publish_type) @@ -136,17 +138,19 @@ def find_all_published(current_file, publish_type): return published_files -def find_latest_publish(current_file: Path, publish_type="publish"): +def find_latest_publish(current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY): published_files = find_all_published(current_file, publish_type) if published_files: return published_files[-1] def find_sync_target(current_file: Path): - latest_staged = find_latest_publish(current_file, publish_type="staged") + latest_staged = find_latest_publish( + current_file, publish_type=constants.STAGED_PUBLISH_KEY + ) if latest_staged: return latest_staged - return find_latest_publish(current_file, publish_type="publish") + return find_latest_publish(current_file, publish_type=constants.ACTIVE_PUBLISH_KEY) def import_data_from_lib( diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 0170227a..42bdcc9c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -74,7 +74,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) # Creata intial publish based on task layers - publish_path = os.path.join(asset_path, "publish") # TODO FIX HARD CODE VALUE + publish_path = os.path.join(asset_path, constants.ACTIVE_PUBLISH_KEY) name = self._name + "." + "v001" + ".blend" publish_file = os.path.join(publish_path, name) bpy.ops.wm.save_as_mainfile(filepath=publish_file, copy=True) @@ -191,7 +191,9 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not self.push: return {'FINISHED'} - push_targets = core.find_all_published(current_file, "publish") + push_targets = core.find_all_published( + current_file, constants.ACTIVE_PUBLISH_KEY + ) if sync_target not in push_targets: push_targets.append(sync_target) -- 2.30.2 From a71f55f17221a3f21a6f067ad54a177dbb9dfd6e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 12:35:23 -0400 Subject: [PATCH 117/429] Asset Pipe: Clear old TODO --- scripts-blender/addons/asset_pipeline_2/core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 1308d41a..4e82610c 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -8,8 +8,6 @@ from .asset_mapping import AssetTransferMapping from . import constants -# TODO Add UI to warn about un-assigned objects - def ownership_cleanup(obj, task_layer_name): ownership = obj.transfer_data_ownership -- 2.30.2 From 601b3d981069c5d318ced3af7e6c9593794e3d05 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 15:18:34 -0400 Subject: [PATCH 118/429] Asset Pipe: Fix Create New Asset Description --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 42bdcc9c..68b2a90c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -10,7 +10,7 @@ import os class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): bl_idname = "assetpipe.create_new_asset" bl_label = "Create New Asset" - bl_description = """Create a new Asset""" # TODO Improve description + bl_description = """Create a new Asset Files and Folders at a given directory""" _name = None _dir = None -- 2.30.2 From 5ff6381949f5dfdb1ac3ad68d6b828434a06ff85 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 15:49:45 -0400 Subject: [PATCH 119/429] Asset Pipe: Use return from import_data_from_lib --- scripts-blender/addons/asset_pipeline_2/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 4e82610c..5e07e192 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -84,8 +84,7 @@ def merge_task_layer( external_suffix = constants.EXTERNAL_SUFFIX asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) - import_data_from_lib(target_file, "collections", col_base_name) - appended_col = bpy.data.collections[col_base_name] # find appended data + appended_col = import_data_from_lib(target_file, "collections", col_base_name) asset_suffix.add_suffix_to_hierarchy(appended_col, external_suffix) local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] -- 2.30.2 From 0e2ce0de821e5620bfdd2a546b6b869e845da93b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 17:12:55 -0400 Subject: [PATCH 120/429] Asset Pipe: Rename get_enum_item to get_dict_item --- scripts-blender/addons/asset_pipeline_2/core.py | 4 ++-- scripts-blender/addons/asset_pipeline_2/ops.py | 4 ++-- .../addons/asset_pipeline_2/transfer_data/transfer_ui.py | 4 ++-- scripts-blender/addons/asset_pipeline_2/ui.py | 4 +++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 5e07e192..48e868b0 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -198,7 +198,7 @@ def get_task_layer_name_from_file(): return task_layer_name -def get_enum_item(enum, key): - for item in enum: +def get_dict_tuple_item(dict: dict, key: str) -> tuple: + for item in dict: if item[0] == key: return item diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 68b2a90c..f75943ef 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -178,7 +178,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): error_msg = core.merge_task_layer( context, local_tls=[task_layer_name], - target_file=sync_target, + external_file=sync_target, ) if error_msg: @@ -212,7 +212,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): error_msg = core.merge_task_layer( context, local_tls=local_tls, - target_file=current_file, + external_file=current_file, ) if error_msg: bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index d9430ddb..e816be24 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -4,11 +4,11 @@ from .. import constants, core def draw_transfer_data_type(layout, items): if items == []: return - name = core.get_enum_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] + name = core.get_dict_tuple_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] box = layout.box() box.label(text=name, icon=items[0].type) for item in items: - owner = core.get_enum_item(constants.TASK_LAYER_ITEMS, item.owner)[1] + owner = core.get_dict_tuple_item(constants.TASK_LAYER_ITEMS, item.owner)[1] box.label(text=f"{item.name}: '{owner}'") diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 1161a3bf..e8f80f3b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -53,7 +53,9 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): obj = context.active_object ownership = obj.transfer_data_ownership layout = layout.box() - owner = core.get_enum_item(constants.TASK_LAYER_ITEMS, obj.asset_id_owner)[1] + owner = core.get_dict_tuple_item( + constants.TASK_LAYER_ITEMS, obj.asset_id_owner + )[1] layout.label(text=f"{obj.name}: '{owner}'", icon="OBJECT_DATA") transfer_ui.draw_transfer_data(ownership, layout) -- 2.30.2 From 3bc3fa4bed2cf567b30ca3e876b3acc96382847d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 17:17:21 -0400 Subject: [PATCH 121/429] Asset Pipe: Add Docstrings, return types and type hints to Core Functions --- .../addons/asset_pipeline_2/core.py | 173 ++++++++++++++---- 1 file changed, 140 insertions(+), 33 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 48e868b0..04118f8a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -9,7 +9,15 @@ from .asset_mapping import AssetTransferMapping from . import constants -def ownership_cleanup(obj, task_layer_name): +def ownership_transfer_data_cleanup( + obj: bpy.types.Object, task_layer_name: str +) -> None: + """Remove Transfer Data ownership items if the corrisponding data is missing + + Args: + obj (bpy.types.Object): Object that contains the transfer data + task_layer_name (str): Name of the current task layer that owns the data + """ ownership = obj.transfer_data_ownership to_remove = [] for item in ownership: @@ -25,9 +33,29 @@ def ownership_cleanup(obj, task_layer_name): ownership.remove(ownership.keys().index(name)) -def ownership_get(local_col: str, task_layer_name: str, new_transfer_data): +def ownership_get( + local_col: bpy.types.Collection, + task_layer_name: str, + temp_transfer_data: bpy.types.CollectionProperty, +) -> list[bpy.types.Object]: + """Find new transfer data owned by the local task layer. + Marks items as owned by the local task layer if they are in the + corrisponding task layer collection and have no owner. + + Args: + local_col (bpy.types.Collection): The top level asset collection that is local to the file + task_layer_name (str): Name of the current task layer that will be the owner of the data + temp_transfer_data (bpy.types.CollectionProperty): Collection property containing newly found + data and the object that contains this data. + + Returns: + list[bpy.types.Object]: Returns a list of objects that have no owner and will not be included + in the merge process + """ invalid_objs = [] - task_layer_col_name = get_enum_item(constants.TASK_LAYER_ITEMS, task_layer_name)[1] + task_layer_col_name = get_dict_tuple_item( + constants.TASK_LAYER_ITEMS, task_layer_name + )[1] task_layer_col = local_col.children.get(task_layer_col_name) for obj in local_col.all_objects: # Mark Asset ID Owner for objects in the current task layers collection @@ -37,45 +65,61 @@ def ownership_get(local_col: str, task_layer_name: str, new_transfer_data): if obj.asset_id_owner == "NONE": invalid_objs.append(obj) continue - ownership_cleanup(obj, task_layer_name) - transfer_functions.get_vertex_groups(obj, task_layer_name, new_transfer_data) - transfer_functions.get_material_slots(obj, task_layer_name, new_transfer_data) - transfer_functions.get_modifiers(obj, task_layer_name, new_transfer_data) + ownership_transfer_data_cleanup(obj, task_layer_name) + transfer_functions.get_vertex_groups(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_material_slots(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_modifiers(obj, task_layer_name, temp_transfer_data) return invalid_objs -def ownership_set(new_transfer_data): - for item in new_transfer_data: +def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: + """Add new transfer data items on each object found in the + temp transfer data collection property + + Args: + temp_transfer_data (bpy.types.CollectionProperty): Collection property containing newly found + data and the object that contains this data. + """ + for item in temp_transfer_data: ownership = item.obj.transfer_data_ownership transfer_core.transfer_data_add_entry( ownership, item.name, item.type, item.owner ) -def remap_user(source_datablock: bpy.data, target_datablock: bpy.data): - """Remap datablock and append name to datablock that has been remapped""" +def remap_user(source_datablock: bpy.data, target_datablock: bpy.data) -> None: + """Remap datablock and append name to datablock that has been remapped + + Args: + source_datablock (bpy.data): datablock that will be replaced by the target + target_datablock (bpy.data): datablock that will replace the source + """ print(f"REMAPPING {source_datablock.name} to {target_datablock.name}") source_datablock.user_remap(target_datablock) source_datablock.name += "_Users_Remapped" -def update_task_layer_objects( - target_col: bpy.types.Collection, - transfer_objs: list[bpy.types.Object], -): - # Link new obj to collection - for transfer_obj in transfer_objs: - obj_root_name = transfer_obj.name - transfer_obj.name = f"{obj_root_name}" - target_col.objects.link(transfer_obj) - - def merge_task_layer( context: bpy.types.Context, local_tls: list[str], - target_file: Path, -): + external_file: Path, +) -> None: + """Combines data from an external task layer collection in the local + task layer collection. By finding the owner of each collection, + object and transfer data item and keeping each layer of data via a copy + from it's respective owners. + + This ensures that objects owned by an external task layer will always be kept + linked into the scene, and any local transfer data like a modifier will be applied + ontop of that external object of vice versa. Ownership is stored in an objects properties, + and map is created to match each object to it's respective owner. + + Args: + context: (bpy.types.Context): context of current .blend + local_tls: (list[str]): list of task layers that are local to the current file + external_file (Path): external file to pull data into the current file from + """ local_col = context.scene.asset_status.asset_collection if not local_col: return "Unable to find Asset Collection" @@ -84,7 +128,7 @@ def merge_task_layer( external_suffix = constants.EXTERNAL_SUFFIX asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) - appended_col = import_data_from_lib(target_file, "collections", col_base_name) + appended_col = import_data_from_lib(external_file, "collections", col_base_name) asset_suffix.add_suffix_to_hierarchy(appended_col, external_suffix) local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] @@ -107,13 +151,31 @@ def merge_task_layer( asset_suffix.remove_suffix_from_hierarchy(local_col) -def find_file_version(file): - return int(file.name.split(".")[1].replace("v", "")) +def find_file_version(published_file: Path) -> int: + """Returns the version number from a published file's name + + Args: + file (Path): Path to a publish file, naming convention is + asset_name.v{3-digit_version}.blend` + + Returns: + int: returns current version in filename as integer + """ + return int(published_file.name.split(".")[1].replace("v", "")) def get_next_published_file( current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY -): +) -> Path: + """Returns the path where the next published file version should be saved to + + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. + + Returns: + Path: Path where the next published file should be saved to, path doesn't exist yet + """ """""" last_publish = find_latest_publish(current_file, publish_type) base_name = current_file.name.split(".")[0] publish_dir = current_file.parent.joinpath(publish_type) @@ -126,7 +188,17 @@ def get_next_published_file( return publish_dir.joinpath(base_name + f".v" + new_version + ".blend") -def find_all_published(current_file, publish_type): +def find_all_published(current_file: Path, publish_type: str) -> list[Path]: + """Retuns a list of published files of a given type, + each publish type is seperated into it's own folder at the + root of the asset's directory + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. + + Returns: + list[Path]: list of published files of a given publish type + """ publish_dir = current_file.parent.joinpath(publish_type) if not publish_dir.exists(): return @@ -135,13 +207,34 @@ def find_all_published(current_file, publish_type): return published_files -def find_latest_publish(current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY): +def find_latest_publish( + current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY +) -> Path: + """Returns the path to the latest published file in a given folder + + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. + + Returns: + Path: Path to latest publish file of a given publish type + """ published_files = find_all_published(current_file, publish_type) if published_files: return published_files[-1] -def find_sync_target(current_file: Path): +def find_sync_target(current_file: Path) -> Path: + """Returns the latest published file to use as push/pull a.k.a sync target + this will either be the latest active publish, or the latest staged asset if + any asset is staged + + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + + Returns: + Path: Path to latest active or staged publish file + """ """""" latest_staged = find_latest_publish( current_file, publish_type=constants.STAGED_PUBLISH_KEY ) @@ -155,7 +248,19 @@ def import_data_from_lib( data_category: str, data_name: str, link: bool = False, -): +) -> bpy.data: + """Appends/Links data from an external file into the current file. + + Args: + libpath (Path): path to .blend file that contains library + data_category (str): bpy.types, like object or collection + data_name (str): name of datablock to link/append + link (bool, optional): Set to link library otherwise append. Defaults to False. + + Returns: + bpy.data: returns whichever data_category/type that was linked/appended + """ + noun = "Appended" if link: noun = "Linked" @@ -191,7 +296,8 @@ def import_data_from_lib( return eval(f"bpy.data.{data_category}['{data_name}']") -def get_task_layer_name_from_file(): +def get_task_layer_name_from_file() -> str: + """Returns task layer name found task's file name""" file_name = bpy.path.basename(bpy.context.blend_data.filepath) task_layer_name = file_name.split(".")[-2] if task_layer_name in constants.TASK_LAYER_KEYS: @@ -199,6 +305,7 @@ def get_task_layer_name_from_file(): def get_dict_tuple_item(dict: dict, key: str) -> tuple: + """For a dict of tuples, returns a dict item based on it's key""" for item in dict: if item[0] == key: return item -- 2.30.2 From acbb10e9403754903038644fe8dd7b689449488f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 17:21:47 -0400 Subject: [PATCH 122/429] Asset Pipe: Rename Task Layer Items to Task Layer Types --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 2 +- scripts-blender/addons/asset_pipeline_2/constants.py | 6 +++--- scripts-blender/addons/asset_pipeline_2/core.py | 2 +- scripts-blender/addons/asset_pipeline_2/props.py | 6 +++--- .../addons/asset_pipeline_2/transfer_data/transfer_ui.py | 2 +- scripts-blender/addons/asset_pipeline_2/ui.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 27e144cf..a3816c6d 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -94,7 +94,7 @@ class AssetTransferMapping: coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} local_tl_names = [ - item[1] for item in constants.TASK_LAYER_ITEMS if item[0] in self._local_tls + item[1] for item in constants.TASK_LAYER_TYPES if item[0] in self._local_tls ] for local_task_layer_col in self._local_col.children: diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index a2fc296c..aad801e6 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -1,14 +1,14 @@ # TODO Tie this into props and generate based on JSON file instead -TASK_LAYER_ITEMS = [ +TASK_LAYER_TYPES = [ ("NONE", "None", ""), ("MODEL", "Modeling", ""), ("RIG", "Rigging", ""), ("SHADE", "Shading", ""), ] -TASK_LAYER_KEYS = [item[0] for item in TASK_LAYER_ITEMS] -TASK_LAYER_NAMES = [item[1] for item in TASK_LAYER_ITEMS] +TASK_LAYER_KEYS = [item[0] for item in TASK_LAYER_TYPES] +TASK_LAYER_NAMES = [item[1] for item in TASK_LAYER_TYPES] # KEYS FOR TRANSFER DATA TYPE MATCH NAME OF ICON TRANSFER_DATA_TYPES = [ diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 04118f8a..f86f8b3b 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -54,7 +54,7 @@ def ownership_get( """ invalid_objs = [] task_layer_col_name = get_dict_tuple_item( - constants.TASK_LAYER_ITEMS, task_layer_name + constants.TASK_LAYER_TYPES, task_layer_name )[1] task_layer_col = local_col.children.get(task_layer_col_name) for obj in local_col.all_objects: diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 32a633e3..6d3f7c4e 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -35,7 +35,7 @@ from . import constants class ASSETOWNERSHIP(bpy.types.PropertyGroup): owner: bpy.props.EnumProperty( name="Transfer Data Owner", - items=constants.TASK_LAYER_ITEMS, + items=constants.TASK_LAYER_TYPES, ) type: bpy.props.EnumProperty( name="Transfer Data Type", @@ -49,7 +49,7 @@ class TEMP_ASSETOWNERSHIP(bpy.types.PropertyGroup): owner: bpy.props.EnumProperty( name="Transfer Data Owner", - items=constants.TASK_LAYER_ITEMS, + items=constants.TASK_LAYER_TYPES, ) type: bpy.props.EnumProperty( name="Transfer Data Type", @@ -78,7 +78,7 @@ def register(): bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=ASSET_STATUS) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", - items=constants.TASK_LAYER_ITEMS, + items=constants.TASK_LAYER_TYPES, ) bpy.types.Scene.asset_new = bpy.props.PointerProperty(type=NEW_ASSET) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index e816be24..97d78c40 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -8,7 +8,7 @@ def draw_transfer_data_type(layout, items): box = layout.box() box.label(text=name, icon=items[0].type) for item in items: - owner = core.get_dict_tuple_item(constants.TASK_LAYER_ITEMS, item.owner)[1] + owner = core.get_dict_tuple_item(constants.TASK_LAYER_TYPES, item.owner)[1] box.label(text=f"{item.name}: '{owner}'") diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index e8f80f3b..84f3343b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -54,7 +54,7 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): ownership = obj.transfer_data_ownership layout = layout.box() owner = core.get_dict_tuple_item( - constants.TASK_LAYER_ITEMS, obj.asset_id_owner + constants.TASK_LAYER_TYPES, obj.asset_id_owner )[1] layout.label(text=f"{obj.name}: '{owner}'", icon="OBJECT_DATA") transfer_ui.draw_transfer_data(ownership, layout) -- 2.30.2 From f89455bf0073c8cab2075a7293673c17e92d0437 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 17:32:47 -0400 Subject: [PATCH 123/429] Asset Pipe: Add docstring, return types and type hunts to asset_suffix --- .../addons/asset_pipeline_2/asset_suffix.py | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index 1b655281..aeb9b01c 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -26,27 +26,50 @@ from .util import get_storage_of_id DELIMITER = "." -def get_target_suffix(suffix: str): +def get_target_suffix(suffix: str) -> str: + """Get the corrisponding suffix for a given suffix + + Args: + suffix (str): Suffix for External or Local Datablock + + Returns: + str: Returns External Suffix if given Local suffix for vice-versa + """ if suffix.endswith(constants.EXTERNAL_SUFFIX): return constants.LOCAL_SUFFIX if suffix.endswith(constants.LOCAL_SUFFIX): return constants.EXTERNAL_SUFFIX -def get_target_name(name: str, occurrence=1) -> str: +def get_target_name(name: str) -> str: + """Get the corrisponding target name for a given datablock's suffix. + Suffixes are set by the add_suffix_to_hierarchy() function prior to + calling this function. + + Args: + name (str): Name of a given datablock including it's suffix + + Returns: + str: Returns datablock name with the opposite suffix + """ old = name.split(DELIMITER)[-1] new = get_target_suffix(old) - li = name.rsplit(old, occurrence) + li = name.rsplit(old, 1) return new.join(li) -def get_basename(name): +def get_basename(name: str) -> str: + """Returns the name of an asset without it's suffix""" return DELIMITER.join(name.split(DELIMITER)[:-1]) -def remove_suffix_from_hierarchy(collection: bpy.types.Collection): +def remove_suffix_from_hierarchy(collection: bpy.types.Collection) -> None: """Removes the suffix after a set delimiter from all datablocks - referenced by a collection, itself included""" + referenced by a collection, itself included + + Args: + collection (bpy.types.Collection): Collection that as been suffixed + """ ref_map = get_id_reference_map() datablocks = get_all_referenced_ids(collection, ref_map) @@ -61,9 +84,14 @@ def remove_suffix_from_hierarchy(collection: bpy.types.Collection): pass -def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str): +def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) -> None: """Add a suffix to the names of all datablocks referenced by a collection, - itself included.""" + itself included. + + Args: + collection (bpy.types.Collection): Collection that needs to be suffixed + suffix_base (str): Suffix to append to collection and items linked to collection + """ suffix = f"{DELIMITER}{suffix_base}" -- 2.30.2 From 0087a681d4efcfdc7b8daabdcf0a46b95609069d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 17:40:20 -0400 Subject: [PATCH 124/429] Asset Pipe: Add docstring, return types and type hunts to transfer_ui --- .../asset_pipeline_2/transfer_data/transfer_ui.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 97d78c40..d2182ac5 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -1,7 +1,11 @@ from .. import constants, core +import bpy -def draw_transfer_data_type(layout, items): +def draw_transfer_data_type( + layout: bpy.types.UILayout, items: bpy.types.CollectionProperty +) -> None: + """Draw UI Element for items of a transfer data type""" if items == []: return name = core.get_dict_tuple_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] @@ -12,7 +16,10 @@ def draw_transfer_data_type(layout, items): box.label(text=f"{item.name}: '{owner}'") -def draw_transfer_data(ownership, layout) -> None: +def draw_transfer_data( + ownership: bpy.types.CollectionProperty, layout: bpy.types.UILayout +) -> None: + """Draw UI List of Transfer Data""" vertex_groups = [] material_slots = [] modifiers = [] -- 2.30.2 From a82d680c487d43c6cc6ad0313551654cadeae3fe Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 17:53:49 -0400 Subject: [PATCH 125/429] Asset Pipe: Add docstring, return types and type hints to transfer_core --- .../transfer_data/transfer_core.py | 53 +++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index f3cacf95..5101cfd1 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -4,7 +4,15 @@ from . import transfer_functions from .. import constants -def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Object): +def copy_transfer_data_ownership( + transfer_data_item, target_obj: bpy.types.Object +) -> None: + """Copy transfer data item to object if non entry exists + + Args: + transfer_data_item (_type_): Item of bpy.types.CollectionProperty from source object + target_obj (bpy.types.Object): Object to add transfer data item to + """ ownership = target_obj.transfer_data_ownership transfer_items_names = [item.name for item in ownership] if transfer_data_item.name not in transfer_items_names: @@ -16,7 +24,17 @@ def update_transfer_data_ownership(transfer_data_item, target_obj: bpy.types.Obj ) -def apply_transfer_data(context, transfer_data_map): +def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: + """Apply all transfer data from transfer data map onto objects. + Copies any transfer data owned by local layer onto objects owned by external layers. + Applies transfer data from external layers onto objects owned by local layers + + Transfer_data_map is generated by class 'AssetTransferMapping' + + Args: + context (bpy.types.Context): context of .blend file + transfer_data_map: Map generated by class AssetTransferMapping + """ for name in transfer_data_map: transfer_data = transfer_data_map[name] item = transfer_data[0] @@ -38,18 +56,43 @@ def apply_transfer_data(context, transfer_data_map): if item.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") transfer_functions.transfer_material_slot(item, target_obj) - update_transfer_data_ownership( + copy_transfer_data_ownership( transfer_data_item=item, target_obj=target_obj, ) -def check_transfer_data_entry(ownership, key, td_type): +def check_transfer_data_entry( + ownership: bpy.types.CollectionProperty, key: str, td_type: str +) -> set: + """Verifies if transfer data entry exists + + Args: + ownership (bpy.types.CollectionProperty): Transfer Data of an object + key (str): Name of item that is being verified + td_type (str): Type of transfer data + + Returns: + set: Returns set of matches where name is found in ownership + """ existing_items = [item.name for item in ownership if item.type == td_type] return set([key]).intersection(set(existing_items)) -def transfer_data_add_entry(ownership, name, td_type, task_layer_name): +def transfer_data_add_entry( + ownership: bpy.types.CollectionProperty, + name: str, + td_type: str, + task_layer_name: str, +): + """Add entry to transfer data ownership + + Args: + ownership (bpy.types.CollectionProperty): Transfer Data of an object + name (str): Name of new transfer data item + td_type (str): Type of transfer data + task_layer_name (str): Name of current task layer + """ item = ownership.add() item.name = name item.owner = task_layer_name.upper() -- 2.30.2 From b73166e37998baf1d2b9da943269701ff460bfc7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 18:10:53 -0400 Subject: [PATCH 126/429] Asset Pipe: Add Docstrings to props --- .../addons/asset_pipeline_2/props.py | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 6d3f7c4e..bc7ff264 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -6,7 +6,9 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr """ -class NEW_ASSET(bpy.types.PropertyGroup): +class ASSET_NEW(bpy.types.PropertyGroup): + """Properties needed to create new asset file/folders""" + dir: bpy.props.StringProperty( name="Directory", description="Target Path for new asset files", @@ -15,7 +17,9 @@ class NEW_ASSET(bpy.types.PropertyGroup): name: bpy.props.StringProperty(name="Name", description="Name for new Asset") -class ASSET_STATUS(bpy.types.PropertyGroup): +class ASSET_FILE_STATUS(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", @@ -32,7 +36,9 @@ class ASSET_STATUS(bpy.types.PropertyGroup): from . import constants -class ASSETOWNERSHIP(bpy.types.PropertyGroup): +class ASSET_TRANSFER_DATA(bpy.types.PropertyGroup): + """Properties to track transferable data on an object""" + owner: bpy.props.EnumProperty( name="Transfer Data Owner", items=constants.TASK_LAYER_TYPES, @@ -43,9 +49,9 @@ class ASSETOWNERSHIP(bpy.types.PropertyGroup): ) -class TEMP_ASSETOWNERSHIP(bpy.types.PropertyGroup): +class ASSET_TRANSFER_DATA_TEMP(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 ASSETOWNERSHIP""" + with the same method as the existing ownership data from ASSET_TRANSFER_DATA""" owner: bpy.props.EnumProperty( name="Transfer Data Owner", @@ -59,10 +65,10 @@ class TEMP_ASSETOWNERSHIP(bpy.types.PropertyGroup): classes = ( - ASSETOWNERSHIP, - ASSET_STATUS, - TEMP_ASSETOWNERSHIP, - NEW_ASSET, + ASSET_TRANSFER_DATA, + ASSET_FILE_STATUS, + ASSET_TRANSFER_DATA_TEMP, + ASSET_NEW, ) @@ -70,17 +76,17 @@ def register(): for i in classes: bpy.utils.register_class(i) bpy.types.Object.transfer_data_ownership = bpy.props.CollectionProperty( - type=ASSETOWNERSHIP + type=ASSET_TRANSFER_DATA ) bpy.types.Scene.temp_transfer_data_ownership = bpy.props.CollectionProperty( - type=TEMP_ASSETOWNERSHIP + type=ASSET_TRANSFER_DATA_TEMP ) - bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=ASSET_STATUS) + bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=ASSET_FILE_STATUS) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", items=constants.TASK_LAYER_TYPES, ) - bpy.types.Scene.asset_new = bpy.props.PointerProperty(type=NEW_ASSET) + bpy.types.Scene.asset_new = bpy.props.PointerProperty(type=ASSET_NEW) def unregister(): -- 2.30.2 From fb797fa090e0dedcfecd56fa57848681bc14424f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 18:50:26 -0400 Subject: [PATCH 127/429] Asset Pipe: Add Constraints to Transfer Data Types --- .../addons/asset_pipeline_2/constants.py | 5 +- .../addons/asset_pipeline_2/core.py | 4 ++ .../transfer_data/transfer_core.py | 2 + .../transfer_data/transfer_functions.py | 56 +++++++++++++++++++ .../transfer_data/transfer_ui.py | 4 ++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index aad801e6..f2e377ed 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -15,6 +15,7 @@ TRANSFER_DATA_TYPES = [ ("NONE", "None", ""), ("GROUP_VERTEX", "Vertex Group", ""), ("MODIFIER", "Modifier", ""), + ("CONSTRAINT", "Constraint", ""), ("MATERIAL", "Material Slot", ""), ] @@ -22,7 +23,9 @@ TRANSFER_DATA_KEYS = [item[0] for item in TRANSFER_DATA_TYPES] VERTEX_GROUP_KEY = TRANSFER_DATA_KEYS[1] MODIFIER_KEY = TRANSFER_DATA_KEYS[2] -MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[3] +CONSTRAINT_KEY = TRANSFER_DATA_KEYS[3] +MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[4] + PUBLISH_TYPES = [ ( diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index f86f8b3b..88f89ab7 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -12,6 +12,7 @@ from . import constants def ownership_transfer_data_cleanup( obj: bpy.types.Object, task_layer_name: str ) -> None: + # TODO MOVE TRANSFER DATA SPECIFIC STUFF TO TRANSFER CORE """Remove Transfer Data ownership items if the corrisponding data is missing Args: @@ -26,6 +27,7 @@ def ownership_transfer_data_cleanup( transfer_functions.vertex_group_is_missing(item) or transfer_functions.modifiers_is_missing(item) or transfer_functions.material_slot_is_missing(item) + or transfer_functions.constraints_is_missing(item) ): to_remove.append(item.name) @@ -38,6 +40,7 @@ def ownership_get( task_layer_name: str, temp_transfer_data: bpy.types.CollectionProperty, ) -> list[bpy.types.Object]: + # TODO MOVE TRANSFER DATA SPECIFIC STUFF TO TRANSFER CORE """Find new transfer data owned by the local task layer. Marks items as owned by the local task layer if they are in the corrisponding task layer collection and have no owner. @@ -69,6 +72,7 @@ def ownership_get( transfer_functions.get_vertex_groups(obj, task_layer_name, temp_transfer_data) transfer_functions.get_material_slots(obj, task_layer_name, temp_transfer_data) transfer_functions.get_modifiers(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_constraints(obj, task_layer_name, temp_transfer_data) return invalid_objs diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 5101cfd1..ad92f654 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -53,6 +53,8 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: if item.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") transfer_functions.transfer_modifier(item, target_obj) + if item.type == constants.CONSTRAINT_KEY: + transfer_functions.transfer_constraint(item, target_obj) if item.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") transfer_functions.transfer_material_slot(item, target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 456fdcf9..f8f58dcf 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -137,6 +137,62 @@ def transfer_modifier(item, obj_target): ) +# CONSTRAINTS +def constraints_is_missing(item): + obj = item.id_data + if item.type == constants.CONSTRAINT_KEY and not obj.constraints.get(item["name"]): + return True + + +def get_constraints(obj, task_layer_name, new_transfer_data): + ownership = obj.transfer_data_ownership + for mod in obj.constraints: + matches = transfer_core.check_transfer_data_entry( + ownership, mod.name, constants.CONSTRAINT_KEY + ) + if len(matches) == 0: + item = new_transfer_data.add() + item.name = mod.name + item.owner = task_layer_name + item.type = constants.CONSTRAINT_KEY + item.obj = obj + + +def transfer_constraint(item, obj_target): + # remove old and sync existing modifiers + obj_source = item.id_data + if obj_source == obj_target: + return + old_mod = obj_target.constraints.get(item.name) + if old_mod: + obj_target.constraints.remove(old_mod) + + # transfer new modifiers + for i, constraint in enumerate(obj_source.constraints): + if constraint.name == item.name: + constraint_new = obj_target.constraints.new(constraint.type) + constraint_new.name = constraint.name + # sort new modifier at correct index (default to beginning of the stack) + idx = 0 + if i > 0: + name_prev = obj_source.constraints[i - 1].name + for target_mod_i, target_constraint in enumerate( + obj_target.constraints + ): + if target_constraint.name == name_prev: + idx = target_mod_i + 1 + bpy.ops.constraint.move_to_index( + {'object': obj_target}, constraint=constraint_new.name, index=idx + ) + constraint_target = obj_target.constraints.get(constraint.name) + props = [ + p.identifier for p in constraint.bl_rna.properties if not p.is_readonly + ] + for prop in props: + value = getattr(constraint, prop) + setattr(constraint_target, prop, value) + + # MATERIAL SLOT def material_slot_is_missing(item): obj = item.id_data diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index d2182ac5..8155077f 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -23,6 +23,7 @@ def draw_transfer_data( vertex_groups = [] material_slots = [] modifiers = [] + constraints = [] for item in ownership: if item.type == constants.VERTEX_GROUP_KEY: @@ -31,7 +32,10 @@ def draw_transfer_data( material_slots.append(item) if item.type == constants.MODIFIER_KEY: modifiers.append(item) + if item.type == constants.CONSTRAINT_KEY: + constraints.append(item) draw_transfer_data_type(layout, vertex_groups) draw_transfer_data_type(layout, modifiers) draw_transfer_data_type(layout, material_slots) + draw_transfer_data_type(layout, constraints) -- 2.30.2 From c2f0b4c6a773e95110f5bd65f596b9ebb88f7eb0 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 18:59:28 -0400 Subject: [PATCH 128/429] Asset Pipe: Centralize Transfer Function calls to Transfer Core file --- .../addons/asset_pipeline_2/core.py | 16 ++------- .../transfer_data/transfer_core.py | 33 ++++++++++++++++++- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 88f89ab7..7f8ef24b 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,5 +1,5 @@ import bpy -from .transfer_data import transfer_core, transfer_functions +from .transfer_data import transfer_core from . import asset_suffix from pathlib import Path @@ -12,7 +12,6 @@ from . import constants def ownership_transfer_data_cleanup( obj: bpy.types.Object, task_layer_name: str ) -> None: - # TODO MOVE TRANSFER DATA SPECIFIC STUFF TO TRANSFER CORE """Remove Transfer Data ownership items if the corrisponding data is missing Args: @@ -23,12 +22,7 @@ def ownership_transfer_data_cleanup( to_remove = [] for item in ownership: if constants.TASK_LAYER_KEYS[item["owner"]] == task_layer_name: - if ( - transfer_functions.vertex_group_is_missing(item) - or transfer_functions.modifiers_is_missing(item) - or transfer_functions.material_slot_is_missing(item) - or transfer_functions.constraints_is_missing(item) - ): + if transfer_core.transfer_data_is_missing(item): to_remove.append(item.name) for name in to_remove: @@ -40,7 +34,6 @@ def ownership_get( task_layer_name: str, temp_transfer_data: bpy.types.CollectionProperty, ) -> list[bpy.types.Object]: - # TODO MOVE TRANSFER DATA SPECIFIC STUFF TO TRANSFER CORE """Find new transfer data owned by the local task layer. Marks items as owned by the local task layer if they are in the corrisponding task layer collection and have no owner. @@ -69,10 +62,7 @@ def ownership_get( invalid_objs.append(obj) continue ownership_transfer_data_cleanup(obj, task_layer_name) - transfer_functions.get_vertex_groups(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_material_slots(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_modifiers(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_constraints(obj, task_layer_name, temp_transfer_data) + transfer_core.get_transfer_data(obj, task_layer_name, temp_transfer_data) return invalid_objs diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index ad92f654..0b8f92f9 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -10,7 +10,7 @@ def copy_transfer_data_ownership( """Copy transfer data item to object if non entry exists Args: - transfer_data_item (_type_): Item of bpy.types.CollectionProperty from source object + transfer_data_item: Item of bpy.types.CollectionProperty from source object target_obj (bpy.types.Object): Object to add transfer data item to """ ownership = target_obj.transfer_data_ownership @@ -24,6 +24,37 @@ def copy_transfer_data_ownership( ) +def transfer_data_is_missing(transfer_data_item) -> bool: + """Check if Transfer Data item is missing + + Args: + transfer_data_item: Item of class ASSET_TRANSFER_DATA + + Returns: + bool: bool if item is missing + """ + return bool( + transfer_functions.vertex_group_is_missing(transfer_data_item) + or transfer_functions.modifiers_is_missing(transfer_data_item) + or transfer_functions.material_slot_is_missing(transfer_data_item) + or transfer_functions.constraints_is_missing(transfer_data_item) + ) + + +def get_transfer_data(obj: bpy.types.Object, task_layer_name: str, temp_transfer_data): + """Collect Transfer Data Items on a given object + + Args: + obj (bpy.types.Object): Target object for transfer data + task_layer_name (str): Name of task layer + temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP + """ + transfer_functions.get_vertex_groups(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_material_slots(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_modifiers(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_constraints(obj, task_layer_name, temp_transfer_data) + + def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: """Apply all transfer data from transfer data map onto objects. Copies any transfer data owned by local layer onto objects owned by external layers. -- 2.30.2 From 42d888a7326d8df7d7a78d273461b82987069c6e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 21:12:31 -0400 Subject: [PATCH 129/429] Asset Pipe: Add Vertex Colors to Transfer Data Types --- .../addons/asset_pipeline_2/constants.py | 8 ++-- .../transfer_data/transfer_core.py | 7 +++ .../transfer_data/transfer_functions.py | 47 +++++++++++++++++++ .../transfer_data/transfer_ui.py | 4 ++ 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index f2e377ed..4461d6fa 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -14,6 +14,7 @@ TASK_LAYER_NAMES = [item[1] for item in TASK_LAYER_TYPES] TRANSFER_DATA_TYPES = [ ("NONE", "None", ""), ("GROUP_VERTEX", "Vertex Group", ""), + ("GROUP_VCOL", "Color Attribute", ""), ("MODIFIER", "Modifier", ""), ("CONSTRAINT", "Constraint", ""), ("MATERIAL", "Material Slot", ""), @@ -22,9 +23,10 @@ TRANSFER_DATA_TYPES = [ TRANSFER_DATA_KEYS = [item[0] for item in TRANSFER_DATA_TYPES] VERTEX_GROUP_KEY = TRANSFER_DATA_KEYS[1] -MODIFIER_KEY = TRANSFER_DATA_KEYS[2] -CONSTRAINT_KEY = TRANSFER_DATA_KEYS[3] -MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[4] +VERTEX_COLOR_KEY = TRANSFER_DATA_KEYS[2] +MODIFIER_KEY = TRANSFER_DATA_KEYS[3] +CONSTRAINT_KEY = TRANSFER_DATA_KEYS[4] +MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[5] PUBLISH_TYPES = [ diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 0b8f92f9..6a206178 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -53,6 +53,7 @@ def get_transfer_data(obj: bpy.types.Object, task_layer_name: str, temp_transfer transfer_functions.get_material_slots(obj, task_layer_name, temp_transfer_data) transfer_functions.get_modifiers(obj, task_layer_name, temp_transfer_data) transfer_functions.get_constraints(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_vertex_colors(obj, task_layer_name, temp_transfer_data) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: @@ -81,6 +82,12 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: obj_target=target_obj, obj_source=item.id_data, ) + if item.type == constants.VERTEX_COLOR_KEY: + transfer_functions.transfer_vertex_colors( + vertex_color_name=item.name, + obj_target=target_obj, + obj_source=item.id_data, + ) if item.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") transfer_functions.transfer_modifier(item, target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index f8f58dcf..86d334c3 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -59,6 +59,53 @@ def transfer_vertex_group( ) +def vertex_color_is_missing(item): + obj = item.id_data + if item.type == constants.VERTEX_COLOR_KEY and not obj.data.vertex_colors.get( + item["name"] + ): + return True + + +def get_vertex_colors(obj, task_layer_name, new_transfer_data): + ownership = obj.transfer_data_ownership + if not obj.type == "MESH": + return + for vertex_color in obj.data.vertex_colors: + # Only add new ownership item if vertex color doesn't have an owner + matches = transfer_core.check_transfer_data_entry( + ownership, vertex_color.name, constants.VERTEX_COLOR_KEY + ) + if len(matches) == 0: + item = new_transfer_data.add() + item.name = vertex_color.name + item.owner = task_layer_name + item.type = constants.VERTEX_COLOR_KEY + item.obj = obj + + +def transfer_vertex_colors( + vertex_color_name: str, + obj_target: bpy.types.Object, + obj_source: bpy.types.Object, +): + old_color = obj_target.data.vertex_colors.get(vertex_color_name) + if old_color: + obj_target.data.vertex_colors.remove(old_color) + transfer_color = obj_source.data.vertex_colors.get(vertex_color_name) + new_color = obj_target.data.vertex_colors.new( + name=transfer_color.name, do_init=False + ) + for loop in obj_target.data.loops: + new_color.data[loop.index].color = transfer_color.data[loop.index].color + # ABOVE FOR LOOP IS FOR TOPOLOGY THAT MATCHES + # BELOW COMMENTED OUT CODE IS FOR TOPOLOGY THAT DOESN'T MATCH + # else: + # for vcol_from in obj_source.data.vertex_colors: + # vcol_to = obj_target.data.vertex_colors.new(name=vcol_from.name, do_init=False) + # transfer_corner_data(obj_source, obj_target, vcol_from.data, vcol_to.data, data_suffix = 'color') + + # MODIFIERS def modifiers_is_missing(item): obj = item.id_data diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 8155077f..33ca421d 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -21,6 +21,7 @@ def draw_transfer_data( ) -> None: """Draw UI List of Transfer Data""" vertex_groups = [] + vertex_colors = [] material_slots = [] modifiers = [] constraints = [] @@ -28,6 +29,8 @@ def draw_transfer_data( for item in ownership: if item.type == constants.VERTEX_GROUP_KEY: vertex_groups.append(item) + if item.type == constants.VERTEX_COLOR_KEY: + vertex_colors.append(item) if item.type == constants.MATERIAL_SLOT_KEY: material_slots.append(item) if item.type == constants.MODIFIER_KEY: @@ -36,6 +39,7 @@ def draw_transfer_data( constraints.append(item) draw_transfer_data_type(layout, vertex_groups) + draw_transfer_data_type(layout, vertex_colors) draw_transfer_data_type(layout, modifiers) draw_transfer_data_type(layout, material_slots) draw_transfer_data_type(layout, constraints) -- 2.30.2 From 90352c59f28fea2a8832fb989fb163473d5820fe Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sun, 27 Aug 2023 22:12:56 -0400 Subject: [PATCH 130/429] Asset Pipe: Add UV Maps to Transfer Data Types --- .../addons/asset_pipeline_2/constants.py | 2 + .../transfer_data/transfer_core.py | 7 +++ .../transfer_data/transfer_functions.py | 49 +++++++++++++++++++ .../transfer_data/transfer_ui.py | 4 ++ 4 files changed, 62 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 4461d6fa..15a96395 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -18,6 +18,7 @@ TRANSFER_DATA_TYPES = [ ("MODIFIER", "Modifier", ""), ("CONSTRAINT", "Constraint", ""), ("MATERIAL", "Material Slot", ""), + ("GROUP_UVS", "UV Maps", ""), ] TRANSFER_DATA_KEYS = [item[0] for item in TRANSFER_DATA_TYPES] @@ -27,6 +28,7 @@ VERTEX_COLOR_KEY = TRANSFER_DATA_KEYS[2] MODIFIER_KEY = TRANSFER_DATA_KEYS[3] CONSTRAINT_KEY = TRANSFER_DATA_KEYS[4] MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[5] +UV_LAYERS_KEY = TRANSFER_DATA_KEYS[6] PUBLISH_TYPES = [ diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 6a206178..b8548249 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -38,6 +38,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: or transfer_functions.modifiers_is_missing(transfer_data_item) or transfer_functions.material_slot_is_missing(transfer_data_item) or transfer_functions.constraints_is_missing(transfer_data_item) + or transfer_functions.uv_layer_is_missing(transfer_data_item) ) @@ -54,6 +55,7 @@ def get_transfer_data(obj: bpy.types.Object, task_layer_name: str, temp_transfer transfer_functions.get_modifiers(obj, task_layer_name, temp_transfer_data) transfer_functions.get_constraints(obj, task_layer_name, temp_transfer_data) transfer_functions.get_vertex_colors(obj, task_layer_name, temp_transfer_data) + transfer_functions.get_uv_layers(obj, task_layer_name, temp_transfer_data) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: @@ -96,6 +98,11 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: if item.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") transfer_functions.transfer_material_slot(item, target_obj) + if item.type == constants.UV_LAYERS_KEY: + transfer_functions.transfer_uv_layer( + obj_target=target_obj, obj_source=item.id_data, uv_name=item.name + ) + copy_transfer_data_ownership( transfer_data_item=item, target_obj=target_obj, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 86d334c3..95189a89 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -59,6 +59,7 @@ def transfer_vertex_group( ) +# VERTEX COLORS def vertex_color_is_missing(item): obj = item.id_data if item.type == constants.VERTEX_COLOR_KEY and not obj.data.vertex_colors.get( @@ -106,6 +107,54 @@ def transfer_vertex_colors( # transfer_corner_data(obj_source, obj_target, vcol_from.data, vcol_to.data, data_suffix = 'color') +# UV LAYERS +def uv_layer_is_missing(item): + obj = item.id_data + if item.type == constants.UV_LAYERS_KEY and not obj.data.uv_layers.get( + item["name"] + ): + return True + + +def get_uv_layers(obj, task_layer_name, new_transfer_data): + ownership = obj.transfer_data_ownership + if not obj.type == "MESH": + return + for uv_layer in obj.data.uv_layers: + # Only add new ownership item if vertex color doesn't have an owner + matches = transfer_core.check_transfer_data_entry( + ownership, uv_layer.name, constants.UV_LAYERS_KEY + ) + if len(matches) == 0: + item = new_transfer_data.add() + item.name = uv_layer.name + item.owner = task_layer_name + item.type = constants.UV_LAYERS_KEY + item.obj = obj + + +def transfer_uv_layer(obj_source, obj_target, uv_name): + old_uv_layer = obj_target.data.uv_layers.get(uv_name) + if old_uv_layer: + obj_target.data.uv_layers.remove(old_uv_layer) + + transfer_uv = obj_source.data.uv_layers.get(uv_name) + new_uv = obj_target.data.uv_layers.new(name=uv_name, do_init=False) + for loop in obj_target.data.loops: + new_uv.data[loop.index].uv = transfer_uv.data[loop.index].uv + # BELOW CODE IS FOR NON MATCHING TOPOLOGY + # else: + # for uv_from in obj_source.data.uv_layers: + # uv_to = obj_target.data.uv_layers.new(name=uv_from.name, do_init=False) + # transfer_corner_data(obj_source, obj_target, uv_from.data, uv_to.data, data_suffix = 'uv') + + # Make sure correct layer is set to active + for uv_l in obj_source.data.uv_layers: + if uv_l.active_render: + obj_target.data.uv_layers[uv_l.name].active_render = True + break + + # MODIFIERS def modifiers_is_missing(item): obj = item.id_data diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 33ca421d..3484a860 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -25,6 +25,7 @@ def draw_transfer_data( material_slots = [] modifiers = [] constraints = [] + uv_layers = [] for item in ownership: if item.type == constants.VERTEX_GROUP_KEY: @@ -37,9 +38,12 @@ def draw_transfer_data( modifiers.append(item) if item.type == constants.CONSTRAINT_KEY: constraints.append(item) + if item.type == constants.UV_LAYERS_KEY: + modifiers.append(item) draw_transfer_data_type(layout, vertex_groups) draw_transfer_data_type(layout, vertex_colors) draw_transfer_data_type(layout, modifiers) draw_transfer_data_type(layout, material_slots) draw_transfer_data_type(layout, constraints) + draw_transfer_data_type(layout, uv_layers) -- 2.30.2 From 0fae738189db8b26770ec8265e12ba9549ea48c2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 29 Aug 2023 11:38:04 -0400 Subject: [PATCH 131/429] Asset Pipe: Replace item with transfer info --- .../addons/asset_pipeline_2/asset_mapping.py | 23 ++++++---- .../addons/asset_pipeline_2/constants.py | 8 ++-- .../addons/asset_pipeline_2/core.py | 18 ++++---- .../addons/asset_pipeline_2/ops.py | 26 +++++++---- .../transfer_data/transfer_core.py | 18 +++++--- .../transfer_data/transfer_functions.py | 24 +++++----- .../transfer_data/transfer_ui.py | 46 ++++++++++--------- scripts-blender/addons/asset_pipeline_2/ui.py | 4 +- 8 files changed, 93 insertions(+), 74 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index a3816c6d..a5483d15 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -94,7 +94,9 @@ class AssetTransferMapping: coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} local_tl_names = [ - item[1] for item in constants.TASK_LAYER_TYPES if item[0] in self._local_tls + tl_type[1] + for tl_type in constants.TASK_LAYER_TYPES + if tl_type[0] in self._local_tls ] for local_task_layer_col in self._local_col.children: @@ -126,18 +128,21 @@ class AssetTransferMapping: transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} for obj in self._external_col.all_objects: - for item in obj.transfer_data_ownership: - if item.owner not in self._local_tls and item.owner != "NONE": - name = item.name + '_' + obj.name + for transfer_info in obj.transfer_data_ownership: + if ( + transfer_info.owner not in self._local_tls + and transfer_info.owner != "NONE" + ): + name = transfer_info.name + '_' + obj.name target_obj_name = asset_suffix.get_target_name(obj.name) target_obj = self._local_col.all_objects.get(target_obj_name) - transfer_data_map[name] = (item, target_obj) + transfer_data_map[name] = (transfer_info, target_obj) for obj in self._local_col.all_objects: - for item in obj.transfer_data_ownership: - if item.owner in self._local_tls: - name = item.name + '_' + obj.name + for transfer_info in obj.transfer_data_ownership: + if transfer_info.owner in self._local_tls: + name = transfer_info.name + '_' + obj.name target_obj_name = asset_suffix.get_target_name(obj.name) target_obj = self._external_col.all_objects.get(target_obj_name) - transfer_data_map[name] = (item, target_obj) + transfer_data_map[name] = (transfer_info, target_obj) return transfer_data_map diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 15a96395..917cc703 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -7,8 +7,8 @@ TASK_LAYER_TYPES = [ ("SHADE", "Shading", ""), ] -TASK_LAYER_KEYS = [item[0] for item in TASK_LAYER_TYPES] -TASK_LAYER_NAMES = [item[1] for item in TASK_LAYER_TYPES] +TASK_LAYER_KEYS = [task_layer[0] for task_layer in TASK_LAYER_TYPES] +TASK_LAYER_NAMES = [task_layer[1] for task_layer in TASK_LAYER_TYPES] # KEYS FOR TRANSFER DATA TYPE MATCH NAME OF ICON TRANSFER_DATA_TYPES = [ @@ -21,7 +21,7 @@ TRANSFER_DATA_TYPES = [ ("GROUP_UVS", "UV Maps", ""), ] -TRANSFER_DATA_KEYS = [item[0] for item in TRANSFER_DATA_TYPES] +TRANSFER_DATA_KEYS = [transfer_data[0] for transfer_data in TRANSFER_DATA_TYPES] VERTEX_GROUP_KEY = TRANSFER_DATA_KEYS[1] VERTEX_COLOR_KEY = TRANSFER_DATA_KEYS[2] @@ -49,7 +49,7 @@ PUBLISH_TYPES = [ "Test the results that will be published in the review area, will not be used as Push/Pull target", ), ] -PUBLISH_KEYS = [item[0] for item in PUBLISH_TYPES] +PUBLISH_KEYS = [pub_type[0] for pub_type in PUBLISH_TYPES] ACTIVE_PUBLISH_KEY = PUBLISH_KEYS[0] STAGED_PUBLISH_KEY = PUBLISH_KEYS[1] diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 7f8ef24b..77045951 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -18,15 +18,15 @@ def ownership_transfer_data_cleanup( obj (bpy.types.Object): Object that contains the transfer data task_layer_name (str): Name of the current task layer that owns the data """ - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership to_remove = [] - for item in ownership: - if constants.TASK_LAYER_KEYS[item["owner"]] == task_layer_name: - if transfer_core.transfer_data_is_missing(item): - to_remove.append(item.name) + for transfer_info in transfer_data: + if constants.TASK_LAYER_KEYS[transfer_info["owner"]] == task_layer_name: + if transfer_core.transfer_data_is_missing(transfer_info): + to_remove.append(transfer_info.name) for name in to_remove: - ownership.remove(ownership.keys().index(name)) + transfer_data.remove(transfer_data.keys().index(name)) def ownership_get( @@ -75,10 +75,10 @@ def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: temp_transfer_data (bpy.types.CollectionProperty): Collection property containing newly found data and the object that contains this data. """ - for item in temp_transfer_data: - ownership = item.obj.transfer_data_ownership + for transfer_info in temp_transfer_data: + transfer_data = transfer_info.obj.transfer_data_ownership transfer_core.transfer_data_add_entry( - ownership, item.name, item.type, item.owner + transfer_data, transfer_info.name, transfer_info.type, transfer_info.owner ) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index f75943ef..f63fc2a5 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -87,7 +87,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bl_label = "Sync with Publish" bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" - _temp_ownership = None + _temp_transfer_data = None _invalid_objs = [] save: bpy.props.BoolProperty( @@ -107,8 +107,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): - self._temp_ownership = context.scene.temp_transfer_data_ownership - self._temp_ownership.clear() + self._temp_transfer_data = context.scene.temp_transfer_data_ownership + self._temp_transfer_data.clear() self._invalid_objs.clear() local_col = context.scene.asset_status.asset_collection @@ -123,7 +123,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self._invalid_objs = core.ownership_get( local_col, task_layer_name, - self._temp_ownership, + self._temp_transfer_data, ) # Default behaviour is to pull before pushing @@ -146,23 +146,27 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): for obj in self._invalid_objs: box.label(text=obj.name, icon="OBJECT_DATA") - if len(self._temp_ownership) == 0: + if len(self._temp_transfer_data) == 0: layout.label(text="No New Transfer Data found") else: layout.label(text="New Transfer Data will be Pushed to Publish") - objs = [item.obj for item in self._temp_ownership] + objs = [transfer_info.obj for transfer_info in self._temp_transfer_data] for obj in set(objs): - obj_ownership = [item for item in self._temp_ownership if item.obj == obj] + obj_ownership = [ + transfer_info + for transfer_info in self._temp_transfer_data + if transfer_info.obj == obj + ] box = layout.box() box.label(text=obj.name, icon="OBJECT_DATA") transfer_ui.draw_transfer_data(obj_ownership, box) def execute(self, context: bpy.types.Context): # Find current task Layer - ownership = context.scene.temp_transfer_data_ownership - core.ownership_set(ownership) + temp_transfer_data = context.scene.temp_transfer_data_ownership + core.ownership_set(temp_transfer_data) current_file = Path(bpy.data.filepath) task_layer_name = core.get_task_layer_name_from_file() if not task_layer_name: @@ -206,7 +210,9 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): continue local_tls = [ - item for item in constants.TASK_LAYER_KEYS if item != task_layer_name + task_layer + for task_layer in constants.TASK_LAYER_KEYS + if task_layer != task_layer_name ] error_msg = core.merge_task_layer( diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index b8548249..9974067b 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -13,11 +13,11 @@ def copy_transfer_data_ownership( transfer_data_item: Item of bpy.types.CollectionProperty from source object target_obj (bpy.types.Object): Object to add transfer data item to """ - ownership = target_obj.transfer_data_ownership - transfer_items_names = [item.name for item in ownership] + transfer_data = target_obj.transfer_data_ownership + transfer_items_names = [transfer_info.name for transfer_info in transfer_data] if transfer_data_item.name not in transfer_items_names: transfer_data_add_entry( - ownership, + transfer_data, transfer_data_item.name, transfer_data_item.type, transfer_data_item.owner, @@ -110,7 +110,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: def check_transfer_data_entry( - ownership: bpy.types.CollectionProperty, key: str, td_type: str + transfer_data: bpy.types.CollectionProperty, key: str, td_type: str ) -> set: """Verifies if transfer data entry exists @@ -122,12 +122,16 @@ def check_transfer_data_entry( Returns: set: Returns set of matches where name is found in ownership """ - existing_items = [item.name for item in ownership if item.type == td_type] + existing_items = [ + transfer_info.name + for transfer_info in transfer_data + if transfer_info.type == td_type + ] return set([key]).intersection(set(existing_items)) def transfer_data_add_entry( - ownership: bpy.types.CollectionProperty, + transfer_data: bpy.types.CollectionProperty, name: str, td_type: str, task_layer_name: str, @@ -140,7 +144,7 @@ def transfer_data_add_entry( td_type (str): Type of transfer data task_layer_name (str): Name of current task layer """ - item = ownership.add() + item = transfer_data.add() item.name = name item.owner = task_layer_name.upper() item.type = td_type diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 95189a89..01a26e13 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -17,11 +17,11 @@ def vertex_group_is_missing(item): def get_vertex_groups(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: # Only add new ownership item if vertex group doesn't have an owner matches = transfer_core.check_transfer_data_entry( - ownership, vertex_group.name, constants.VERTEX_GROUP_KEY + transfer_data, vertex_group.name, constants.VERTEX_GROUP_KEY ) if len(matches) == 0: item = new_transfer_data.add() @@ -69,13 +69,13 @@ def vertex_color_is_missing(item): def get_vertex_colors(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership if not obj.type == "MESH": return for vertex_color in obj.data.vertex_colors: # Only add new ownership item if vertex color doesn't have an owner matches = transfer_core.check_transfer_data_entry( - ownership, vertex_color.name, constants.VERTEX_COLOR_KEY + transfer_data, vertex_color.name, constants.VERTEX_COLOR_KEY ) if len(matches) == 0: item = new_transfer_data.add() @@ -117,13 +117,13 @@ def uv_layer_is_missing(item): def get_uv_layers(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership if not obj.type == "MESH": return for uv_layer in obj.data.uv_layers: # Only add new ownership item if vertex color doesn't have an owner matches = transfer_core.check_transfer_data_entry( - ownership, uv_layer.name, constants.UV_LAYERS_KEY + transfer_data, uv_layer.name, constants.UV_LAYERS_KEY ) if len(matches) == 0: item = new_transfer_data.add() @@ -163,10 +163,10 @@ def modifiers_is_missing(item): def get_modifiers(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership for mod in obj.modifiers: matches = transfer_core.check_transfer_data_entry( - ownership, mod.name, constants.MODIFIER_KEY + transfer_data, mod.name, constants.MODIFIER_KEY ) if len(matches) == 0: item = new_transfer_data.add() @@ -241,10 +241,10 @@ def constraints_is_missing(item): def get_constraints(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership for mod in obj.constraints: matches = transfer_core.check_transfer_data_entry( - ownership, mod.name, constants.CONSTRAINT_KEY + transfer_data, mod.name, constants.CONSTRAINT_KEY ) if len(matches) == 0: item = new_transfer_data.add() @@ -299,10 +299,10 @@ def material_slot_is_missing(item): def get_material_slots(obj, task_layer_name, new_transfer_data): - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership for slot in obj.material_slots: matches = transfer_core.check_transfer_data_entry( - ownership, slot.name, constants.MATERIAL_SLOT_KEY + transfer_data, slot.name, constants.MATERIAL_SLOT_KEY ) if len(matches) == 0: item = new_transfer_data.add() diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 3484a860..8f227dbb 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -3,21 +3,25 @@ import bpy def draw_transfer_data_type( - layout: bpy.types.UILayout, items: bpy.types.CollectionProperty + layout: bpy.types.UILayout, transfer_data: bpy.types.CollectionProperty ) -> None: """Draw UI Element for items of a transfer data type""" - if items == []: + if transfer_data == []: return - name = core.get_dict_tuple_item(constants.TRANSFER_DATA_TYPES, items[0].type)[1] + name = core.get_dict_tuple_item( + constants.TRANSFER_DATA_TYPES, transfer_data[0].type + )[1] box = layout.box() - box.label(text=name, icon=items[0].type) - for item in items: - owner = core.get_dict_tuple_item(constants.TASK_LAYER_TYPES, item.owner)[1] - box.label(text=f"{item.name}: '{owner}'") + box.label(text=name, icon=transfer_data[0].type) + for transfer_info in transfer_data: + owner = core.get_dict_tuple_item( + constants.TASK_LAYER_TYPES, transfer_info.owner + )[1] + box.label(text=f"{transfer_info.name}: '{owner}'") def draw_transfer_data( - ownership: bpy.types.CollectionProperty, layout: bpy.types.UILayout + transfer_data: bpy.types.CollectionProperty, layout: bpy.types.UILayout ) -> None: """Draw UI List of Transfer Data""" vertex_groups = [] @@ -27,19 +31,19 @@ def draw_transfer_data( constraints = [] uv_layers = [] - for item in ownership: - if item.type == constants.VERTEX_GROUP_KEY: - vertex_groups.append(item) - if item.type == constants.VERTEX_COLOR_KEY: - vertex_colors.append(item) - if item.type == constants.MATERIAL_SLOT_KEY: - material_slots.append(item) - if item.type == constants.MODIFIER_KEY: - modifiers.append(item) - if item.type == constants.CONSTRAINT_KEY: - constraints.append(item) - if item.type == constants.UV_LAYERS_KEY: - modifiers.append(item) + for transfer_info in transfer_data: + if transfer_info.type == constants.VERTEX_GROUP_KEY: + vertex_groups.append(transfer_info) + if transfer_info.type == constants.VERTEX_COLOR_KEY: + vertex_colors.append(transfer_info) + if transfer_info.type == constants.MATERIAL_SLOT_KEY: + material_slots.append(transfer_info) + if transfer_info.type == constants.MODIFIER_KEY: + modifiers.append(transfer_info) + if transfer_info.type == constants.CONSTRAINT_KEY: + constraints.append(transfer_info) + if transfer_info.type == constants.UV_LAYERS_KEY: + modifiers.append(transfer_info) draw_transfer_data_type(layout, vertex_groups) draw_transfer_data_type(layout, vertex_colors) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 84f3343b..70c5f0a9 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -51,13 +51,13 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): layout.label(text="Set an Active Object to Inspect") return obj = context.active_object - ownership = obj.transfer_data_ownership + transfer_data = obj.transfer_data_ownership layout = layout.box() owner = core.get_dict_tuple_item( constants.TASK_LAYER_TYPES, obj.asset_id_owner )[1] layout.label(text=f"{obj.name}: '{owner}'", icon="OBJECT_DATA") - transfer_ui.draw_transfer_data(ownership, layout) + transfer_ui.draw_transfer_data(transfer_data, layout) task_layer_name = core.get_task_layer_name_from_file() if task_layer_name not in constants.TASK_LAYER_KEYS: -- 2.30.2 From d9c380bb97aeccdd6067143555a4513a89d4ac8b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 29 Aug 2023 11:39:56 -0400 Subject: [PATCH 132/429] Asset Pipe: Property Group names as camel case --- .../addons/asset_pipeline_2/props.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index bc7ff264..3fbc7037 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -6,7 +6,7 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr """ -class ASSET_NEW(bpy.types.PropertyGroup): +class AssetNew(bpy.types.PropertyGroup): """Properties needed to create new asset file/folders""" dir: bpy.props.StringProperty( @@ -17,7 +17,7 @@ class ASSET_NEW(bpy.types.PropertyGroup): name: bpy.props.StringProperty(name="Name", description="Name for new Asset") -class ASSET_FILE_STATUS(bpy.types.PropertyGroup): +class AssetFileStatus(bpy.types.PropertyGroup): """Properties to manage the status of asset pipeline files""" is_asset_pipeline_file: bpy.props.BoolProperty( @@ -36,7 +36,7 @@ class ASSET_FILE_STATUS(bpy.types.PropertyGroup): from . import constants -class ASSET_TRANSFER_DATA(bpy.types.PropertyGroup): +class AssetTransferData(bpy.types.PropertyGroup): """Properties to track transferable data on an object""" owner: bpy.props.EnumProperty( @@ -49,7 +49,7 @@ class ASSET_TRANSFER_DATA(bpy.types.PropertyGroup): ) -class ASSET_TRANSFER_DATA_TEMP(bpy.types.PropertyGroup): +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""" @@ -65,10 +65,10 @@ class ASSET_TRANSFER_DATA_TEMP(bpy.types.PropertyGroup): classes = ( - ASSET_TRANSFER_DATA, - ASSET_FILE_STATUS, - ASSET_TRANSFER_DATA_TEMP, - ASSET_NEW, + AssetTransferData, + AssetFileStatus, + AssetTransferDataTemp, + AssetNew, ) @@ -76,17 +76,17 @@ def register(): for i in classes: bpy.utils.register_class(i) bpy.types.Object.transfer_data_ownership = bpy.props.CollectionProperty( - type=ASSET_TRANSFER_DATA + type=AssetTransferData ) bpy.types.Scene.temp_transfer_data_ownership = bpy.props.CollectionProperty( - type=ASSET_TRANSFER_DATA_TEMP + type=AssetTransferDataTemp ) - bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=ASSET_FILE_STATUS) + bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=AssetFileStatus) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", items=constants.TASK_LAYER_TYPES, ) - bpy.types.Scene.asset_new = bpy.props.PointerProperty(type=ASSET_NEW) + bpy.types.Scene.asset_new = bpy.props.PointerProperty(type=AssetNew) def unregister(): -- 2.30.2 From 08b07f565c2865a75ed92133d1beeb1639b033df Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 29 Aug 2023 11:42:21 -0400 Subject: [PATCH 133/429] Asset Pipe: Fix Plural/Singular mistakes in function names --- .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 6 +++--- .../asset_pipeline_2/transfer_data/transfer_functions.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 9974067b..1ee0edbc 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -35,9 +35,9 @@ def transfer_data_is_missing(transfer_data_item) -> bool: """ return bool( transfer_functions.vertex_group_is_missing(transfer_data_item) - or transfer_functions.modifiers_is_missing(transfer_data_item) + or transfer_functions.modifier_is_missing(transfer_data_item) or transfer_functions.material_slot_is_missing(transfer_data_item) - or transfer_functions.constraints_is_missing(transfer_data_item) + or transfer_functions.constraint_is_missing(transfer_data_item) or transfer_functions.uv_layer_is_missing(transfer_data_item) ) @@ -85,7 +85,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: obj_source=item.id_data, ) if item.type == constants.VERTEX_COLOR_KEY: - transfer_functions.transfer_vertex_colors( + transfer_functions.transfer_vertex_color( vertex_color_name=item.name, obj_target=target_obj, obj_source=item.id_data, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 01a26e13..76909924 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -85,7 +85,7 @@ def get_vertex_colors(obj, task_layer_name, new_transfer_data): item.obj = obj -def transfer_vertex_colors( +def transfer_vertex_color( vertex_color_name: str, obj_target: bpy.types.Object, obj_source: bpy.types.Object, @@ -156,7 +156,7 @@ def transfer_uv_layer(obj_source, obj_target, uv_name): # MODIFIERS -def modifiers_is_missing(item): +def modifier_is_missing(item): obj = item.id_data if item.type == constants.MODIFIER_KEY and not obj.modifiers.get(item["name"]): return True @@ -234,7 +234,7 @@ def transfer_modifier(item, obj_target): # CONSTRAINTS -def constraints_is_missing(item): +def constraint_is_missing(item): obj = item.id_data if item.type == constants.CONSTRAINT_KEY and not obj.constraints.get(item["name"]): return True -- 2.30.2 From 43949907587bf95df58164b9637ab0f20570440c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 29 Aug 2023 11:51:09 -0400 Subject: [PATCH 134/429] Asset Pipe: Add Warning about clearing undo stack --- scripts-blender/addons/asset_pipeline_2/ops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index f63fc2a5..3baa3a0b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -85,7 +85,8 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bl_idname = "assetpipe.sync_with_publish" bl_label = "Sync with Publish" - bl_description = """Push and or Pull data from Published file. Will prompt with a list of new data found before pushing""" + bl_description = """'Push'band or 'Pull' data from Published file. Will prompt with a list of new data found before pushing + During 'Push' current blender session will revert to the last saved file and clear the current undo history""" _temp_transfer_data = None _invalid_objs = [] -- 2.30.2 From d2b9f4378e3573921b3c694277e35fe24bfc5172 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 29 Aug 2023 14:07:54 -0400 Subject: [PATCH 135/429] Asset Pipe: Fix Removal of Old Transfer Data --- .../addons/asset_pipeline_2/core.py | 5 + .../transfer_data/transfer_core.py | 20 ++ .../transfer_data/transfer_functions.py | 186 +++++++++++------- 3 files changed, 144 insertions(+), 67 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 77045951..8f3dc309 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -134,7 +134,12 @@ def merge_task_layer( for source_obj in map.object_map: target_obj = map.object_map[source_obj] + if target_obj.asset_id_owner not in local_tls: + transfer_core.copy_transfer_data( + target_obj.transfer_data_ownership, source_obj.transfer_data_ownership + ) remap_user(source_obj, target_obj) + transfer_core.transfer_data_clean(target_obj) for col in map.collection_map: remap_user(col, map.collection_map[col]) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 1ee0edbc..118e4199 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -24,6 +24,15 @@ def copy_transfer_data_ownership( ) +def transfer_data_clean(obj): + # TODO add each type of transfer data here + transfer_functions.vertex_groups_clean(obj) + transfer_functions.vertex_colors_clean(obj) + transfer_functions.uv_layer_clean(obj) + transfer_functions.modifiers_clean(obj) + transfer_functions.constraints_clean(obj) + + def transfer_data_is_missing(transfer_data_item) -> bool: """Check if Transfer Data item is missing @@ -148,3 +157,14 @@ def transfer_data_add_entry( item.name = name item.owner = task_layer_name.upper() item.type = td_type + + +def copy_transfer_data(source_data, target_data): + source_data.clear() + for transfer_info in target_data: + transfer_data_add_entry( + transfer_data=source_data, + name=transfer_info.name, + task_layer_name=transfer_info.owner, + td_type=transfer_info.type, + ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 76909924..726bd9dd 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -7,11 +7,18 @@ from .. import asset_suffix, constants ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES +def vertex_groups_clean(obj): + transfer_data = obj.transfer_data_ownership + for vertex_group in obj.vertex_groups: + if not transfer_data.get(vertex_group.name): + obj.vertex_groups.remove(vertex_group) + + # VERTEX GROUPS -def vertex_group_is_missing(item): - obj = item.id_data - if item.type == constants.VERTEX_GROUP_KEY and not obj.vertex_groups.get( - item["name"] +def vertex_group_is_missing(transfer_info): + obj = transfer_info.id_data + if transfer_info.type == constants.VERTEX_GROUP_KEY and not obj.vertex_groups.get( + transfer_info["name"] ): return True @@ -19,16 +26,16 @@ def vertex_group_is_missing(item): def get_vertex_groups(obj, task_layer_name, new_transfer_data): transfer_data = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: - # Only add new ownership item if vertex group doesn't have an owner + # Only add new ownership transfer_info if vertex group doesn't have an owner matches = transfer_core.check_transfer_data_entry( transfer_data, vertex_group.name, constants.VERTEX_GROUP_KEY ) if len(matches) == 0: - item = new_transfer_data.add() - item.name = vertex_group.name - item.owner = task_layer_name - item.type = constants.VERTEX_GROUP_KEY - item.obj = obj + transfer_info = new_transfer_data.add() + transfer_info.name = vertex_group.name + transfer_info.owner = task_layer_name + transfer_info.type = constants.VERTEX_GROUP_KEY + transfer_info.obj = obj def transfer_vertex_group( @@ -60,10 +67,20 @@ def transfer_vertex_group( # VERTEX COLORS -def vertex_color_is_missing(item): - obj = item.id_data - if item.type == constants.VERTEX_COLOR_KEY and not obj.data.vertex_colors.get( - item["name"] +def vertex_colors_clean(obj): + if not obj.type == "MESH": + return + transfer_data = obj.transfer_data_ownership + for vertex_color in obj.data.vertex_colors: + if not transfer_data.get(vertex_color.name): + obj.vertex_colors.data.remove(vertex_color) + + +def vertex_color_is_missing(transfer_info): + obj = transfer_info.id_data + if ( + transfer_info.type == constants.VERTEX_COLOR_KEY + and not obj.data.vertex_colors.get(transfer_info["name"]) ): return True @@ -73,16 +90,16 @@ def get_vertex_colors(obj, task_layer_name, new_transfer_data): if not obj.type == "MESH": return for vertex_color in obj.data.vertex_colors: - # Only add new ownership item if vertex color doesn't have an owner + # Only add new ownership transfer_info if vertex color doesn't have an owner matches = transfer_core.check_transfer_data_entry( transfer_data, vertex_color.name, constants.VERTEX_COLOR_KEY ) if len(matches) == 0: - item = new_transfer_data.add() - item.name = vertex_color.name - item.owner = task_layer_name - item.type = constants.VERTEX_COLOR_KEY - item.obj = obj + transfer_info = new_transfer_data.add() + transfer_info.name = vertex_color.name + transfer_info.owner = task_layer_name + transfer_info.type = constants.VERTEX_COLOR_KEY + transfer_info.obj = obj def transfer_vertex_color( @@ -108,10 +125,19 @@ def transfer_vertex_color( # UV LAYERS -def uv_layer_is_missing(item): - obj = item.id_data - if item.type == constants.UV_LAYERS_KEY and not obj.data.uv_layers.get( - item["name"] +def uv_layer_clean(obj): + if not obj.type == "MESH": + return + transfer_data = obj.transfer_data_ownership + for uv_layer in obj.data.uv_layers: + if not transfer_data.get(uv_layer.name): + obj.data.uv_layers.remove(uv_layer) + + +def uv_layer_is_missing(transfer_info): + obj = transfer_info.id_data + if transfer_info.type == constants.UV_LAYERS_KEY and not obj.data.uv_layers.get( + transfer_info["name"] ): return True @@ -121,16 +147,16 @@ def get_uv_layers(obj, task_layer_name, new_transfer_data): if not obj.type == "MESH": return for uv_layer in obj.data.uv_layers: - # Only add new ownership item if vertex color doesn't have an owner + # Only add new ownership transfer_info if vertex color doesn't have an owner matches = transfer_core.check_transfer_data_entry( transfer_data, uv_layer.name, constants.UV_LAYERS_KEY ) if len(matches) == 0: - item = new_transfer_data.add() - item.name = uv_layer.name - item.owner = task_layer_name - item.type = constants.UV_LAYERS_KEY - item.obj = obj + transfer_info = new_transfer_data.add() + transfer_info.name = uv_layer.name + transfer_info.owner = task_layer_name + transfer_info.type = constants.UV_LAYERS_KEY + transfer_info.obj = obj def transfer_uv_layer(obj_source, obj_target, uv_name): @@ -156,9 +182,18 @@ def transfer_uv_layer(obj_source, obj_target, uv_name): # MODIFIERS -def modifier_is_missing(item): - obj = item.id_data - if item.type == constants.MODIFIER_KEY and not obj.modifiers.get(item["name"]): +def modifiers_clean(obj): + transfer_data = obj.transfer_data_ownership + for modifiers in obj.modifiers: + if not transfer_data.get(modifiers.name): + obj.modifiers.remove(modifiers) + + +def modifier_is_missing(transfer_info): + obj = transfer_info.id_data + if transfer_info.type == constants.MODIFIER_KEY and not obj.modifiers.get( + transfer_info["name"] + ): return True @@ -169,25 +204,25 @@ def get_modifiers(obj, task_layer_name, new_transfer_data): transfer_data, mod.name, constants.MODIFIER_KEY ) if len(matches) == 0: - item = new_transfer_data.add() - item.name = mod.name - item.owner = task_layer_name - item.type = constants.MODIFIER_KEY - item.obj = obj + transfer_info = new_transfer_data.add() + transfer_info.name = mod.name + transfer_info.owner = task_layer_name + transfer_info.type = constants.MODIFIER_KEY + transfer_info.obj = obj -def transfer_modifier(item, obj_target): +def transfer_modifier(transfer_info, obj_target): # remove old and sync existing modifiers - obj_source = item.id_data + obj_source = transfer_info.id_data if obj_source == obj_target: return - old_mod = obj_target.modifiers.get(item.name) + old_mod = obj_target.modifiers.get(transfer_info.name) if old_mod: obj_target.modifiers.remove(old_mod) # transfer new modifiers for i, mod in enumerate(obj_source.modifiers): - if mod.name == item.name: + if mod.name == transfer_info.name: mod_new = obj_target.modifiers.new(mod.name, mod.type) # sort new modifier at correct index (default to beginning of the stack) idx = 0 @@ -234,9 +269,18 @@ def transfer_modifier(item, obj_target): # CONSTRAINTS -def constraint_is_missing(item): - obj = item.id_data - if item.type == constants.CONSTRAINT_KEY and not obj.constraints.get(item["name"]): +def constraints_clean(obj): + transfer_data = obj.transfer_data_ownership + for constraint in obj.constraints: + if not transfer_data.get(constraint.name): + obj.constraints.remove(constraint) + + +def constraint_is_missing(transfer_info): + obj = transfer_info.id_data + if transfer_info.type == constants.CONSTRAINT_KEY and not obj.constraints.get( + transfer_info["name"] + ): return True @@ -247,25 +291,25 @@ def get_constraints(obj, task_layer_name, new_transfer_data): transfer_data, mod.name, constants.CONSTRAINT_KEY ) if len(matches) == 0: - item = new_transfer_data.add() - item.name = mod.name - item.owner = task_layer_name - item.type = constants.CONSTRAINT_KEY - item.obj = obj + transfer_info = new_transfer_data.add() + transfer_info.name = mod.name + transfer_info.owner = task_layer_name + transfer_info.type = constants.CONSTRAINT_KEY + transfer_info.obj = obj -def transfer_constraint(item, obj_target): +def transfer_constraint(transfer_info, obj_target): # remove old and sync existing modifiers - obj_source = item.id_data + obj_source = transfer_info.id_data if obj_source == obj_target: return - old_mod = obj_target.constraints.get(item.name) + old_mod = obj_target.constraints.get(transfer_info.name) if old_mod: obj_target.constraints.remove(old_mod) # transfer new modifiers for i, constraint in enumerate(obj_source.constraints): - if constraint.name == item.name: + if constraint.name == transfer_info.name: constraint_new = obj_target.constraints.new(constraint.type) constraint_new.name = constraint.name # sort new modifier at correct index (default to beginning of the stack) @@ -290,10 +334,18 @@ def transfer_constraint(item, obj_target): # MATERIAL SLOT -def material_slot_is_missing(item): - obj = item.id_data - if item.type == constants.MATERIAL_SLOT_KEY and not obj.material_slots.get( - item["name"] +def material_slot_clean(obj): + transfer_data = obj.transfer_data_ownership + for mat_slot in obj.material_slots: + if not transfer_data.get(mat_slot.name): + obj.material_slots.remove(mat_slot) + + +def material_slot_is_missing(transfer_info): + obj = transfer_info.id_data + if ( + transfer_info.type == constants.MATERIAL_SLOT_KEY + and not obj.material_slots.get(transfer_info["name"]) ): return True @@ -305,21 +357,21 @@ def get_material_slots(obj, task_layer_name, new_transfer_data): transfer_data, slot.name, constants.MATERIAL_SLOT_KEY ) if len(matches) == 0: - item = new_transfer_data.add() - item.name = slot.name - item.owner = task_layer_name - item.type = constants.MATERIAL_SLOT_KEY - item.obj = obj + transfer_info = new_transfer_data.add() + transfer_info.name = slot.name + transfer_info.owner = task_layer_name + transfer_info.type = constants.MATERIAL_SLOT_KEY + transfer_info.obj = obj -def transfer_material_slot(item, obj_target): - obj_source = item.id_data +def transfer_material_slot(transfer_info, obj_target): + obj_source = transfer_info.id_data if obj_source == obj_target: return # Delete existing material slot if exists for idx in range(len(obj_source.material_slots)): slot = obj_source.material_slots[idx] - if asset_suffix.get_basename(slot.material.name) == item.name: + if asset_suffix.get_basename(slot.material.name) == transfer_info.name: obj_target.active_material_index = idx bpy.ops.object.material_slot_remove({"object": obj_target}) @@ -328,7 +380,7 @@ def transfer_material_slot(item, obj_target): for idx in range(len(obj_source.material_slots)): if idx >= len(obj_target.material_slots): slot = obj_source.material_slots[idx] - if asset_suffix.get_basename(slot.material.name) == item.name: + if asset_suffix.get_basename(slot.material.name) == transfer_info.name: bpy.ops.object.material_slot_add({"object": obj_target}) obj_target.material_slots[idx].link = obj_source.material_slots[ idx -- 2.30.2 From 71ad7bf071ad92bd14b8520db1e5238ad50c9eea Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 29 Aug 2023 14:17:29 -0400 Subject: [PATCH 136/429] Asset Pipe: Fix Rename Item to Transfer_Info --- .../transfer_data/transfer_core.py | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 118e4199..dc419ca9 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -80,40 +80,42 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: """ for name in transfer_data_map: transfer_data = transfer_data_map[name] - item = transfer_data[0] + transfer_info = transfer_data[0] target_obj = transfer_data[1] if target_obj is None: - print(f"Failed to Transfer data for {item.id_data.name}") + print(f"Failed to Transfer data for {transfer_info.id_data.name}") continue - if item.type == constants.VERTEX_GROUP_KEY: + if transfer_info.type == constants.VERTEX_GROUP_KEY: print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") transfer_functions.transfer_vertex_group( context=context, - vertex_group_name=item.name, + vertex_group_name=transfer_info.name, obj_target=target_obj, - obj_source=item.id_data, + obj_source=transfer_info.id_data, ) - if item.type == constants.VERTEX_COLOR_KEY: + if transfer_info.type == constants.VERTEX_COLOR_KEY: transfer_functions.transfer_vertex_color( - vertex_color_name=item.name, + vertex_color_name=transfer_info.name, obj_target=target_obj, - obj_source=item.id_data, + obj_source=transfer_info.id_data, ) - if item.type == constants.MODIFIER_KEY: + if transfer_info.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") - transfer_functions.transfer_modifier(item, target_obj) - if item.type == constants.CONSTRAINT_KEY: - transfer_functions.transfer_constraint(item, target_obj) - if item.type == constants.MATERIAL_SLOT_KEY: + transfer_functions.transfer_modifier(transfer_info, target_obj) + if transfer_info.type == constants.CONSTRAINT_KEY: + transfer_functions.transfer_constraint(transfer_info, target_obj) + if transfer_info.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") - transfer_functions.transfer_material_slot(item, target_obj) - if item.type == constants.UV_LAYERS_KEY: + transfer_functions.transfer_material_slot(transfer_info, target_obj) + if transfer_info.type == constants.UV_LAYERS_KEY: transfer_functions.transfer_uv_layer( - obj_target=target_obj, obj_source=item.id_data, uv_name=item.name + obj_target=target_obj, + obj_source=transfer_info.id_data, + uv_name=transfer_info.name, ) copy_transfer_data_ownership( - transfer_data_item=item, + transfer_data_item=transfer_info, target_obj=target_obj, ) @@ -153,10 +155,10 @@ def transfer_data_add_entry( td_type (str): Type of transfer data task_layer_name (str): Name of current task layer """ - item = transfer_data.add() - item.name = name - item.owner = task_layer_name.upper() - item.type = td_type + transfer_info = transfer_data.add() + transfer_info.name = name + transfer_info.owner = task_layer_name.upper() + transfer_info.type = td_type def copy_transfer_data(source_data, target_data): -- 2.30.2 From c32c22e8c623ca4678049a8c9d0737b60ba99659 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 29 Aug 2023 14:20:02 -0400 Subject: [PATCH 137/429] Asset Pipe: Add TODO for bug --- scripts-blender/addons/asset_pipeline_2/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 8f3dc309..c19f28e6 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -134,6 +134,7 @@ def merge_task_layer( for source_obj in map.object_map: target_obj = map.object_map[source_obj] + # TODO Figure out why this doesn't work on Push only Pull currently if target_obj.asset_id_owner not in local_tls: transfer_core.copy_transfer_data( target_obj.transfer_data_ownership, source_obj.transfer_data_ownership -- 2.30.2 From f0d457b2599738dc01aed70079147ecd0ea3f775 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 30 Aug 2023 10:43:24 -0400 Subject: [PATCH 138/429] Asset Pipe: Update Test Files --- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index 7be01e2c..b2563617 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df7194bd64dbc5c16a075ac98076e84cb17f9e197fe228e99c1792815181beec -size 950888 +oid sha256:ef8fdd0183a84abcd97dff02a2bb8df4159b0905ed608103a6eb3082b224069f +size 925908 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend index 85d91522..8b3acc05 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:439f2b6692538b799c305dd2ca849c9ce8b937c0ca037d5b7fb2d8bf4c06fa56 -size 970156 +oid sha256:68ae45ff135b8e4ccc94c514348087752b044e5067eaff3f00c238ae11e0bb78 +size 967920 -- 2.30.2 From d5dedc499db093641c4ab420bf17a90b4d74714e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 30 Aug 2023 10:47:20 -0400 Subject: [PATCH 139/429] Asset Pipe: Update Files to Test Pull Error --- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend | 4 ++-- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index b2563617..5812bc81 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef8fdd0183a84abcd97dff02a2bb8df4159b0905ed608103a6eb3082b224069f -size 925908 +oid sha256:bf52d6f62fbaf2e2c0e138df299ef1fbd5921ff25f3b69caf9bf2a4f106b7f85 +size 925616 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend index 8b3acc05..5c4f6720 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68ae45ff135b8e4ccc94c514348087752b044e5067eaff3f00c238ae11e0bb78 -size 967920 +oid sha256:be04e6d1979f4c7aafb5c2e5b01d90ce3656e2c5d4dfcfcc2201465097641eb5 +size 955084 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index 71582e16..ba1a6046 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8931f453faaa71d14e42a41356a2fb8acaa337702e8c58f3dd82a9b5de2e2142 -size 937188 +oid sha256:abdf9267fa3905539a5e12efd1dad8d5376b960be2ed7844d9dd44cef889c58f +size 886388 -- 2.30.2 From 849537de2914a3348a1a2f59b46f56bf274f6748 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 30 Aug 2023 14:56:08 -0400 Subject: [PATCH 140/429] Asset Pipe: WIP Fix Bug with Removals --- .../addons/asset_pipeline_2/asset_mapping.py | 29 ++++++++++++++++++- .../addons/asset_pipeline_2/core.py | 7 ++--- .../transfer_data/transfer_core.py | 1 + .../transfer_data/transfer_functions.py | 16 +++++----- scripts-blender/addons/asset_pipeline_2/ui.py | 4 ++- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index a5483d15..ed7950a8 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -126,9 +126,14 @@ class AssetTransferMapping: def _gen_transfer_data_map(self): transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - + remove_transfer_data = [] for obj in self._external_col.all_objects: + # TODO Remove any tansfer_info that is on an external obj but owned by the local TL + # TODO this works on pull not push FIX! + # TODO Just do the opposite of this in the local collection for transfer_info in obj.transfer_data_ownership: + if transfer_info.owner in self._local_tls: + remove_transfer_data.append(transfer_info.name) if ( transfer_info.owner not in self._local_tls and transfer_info.owner != "NONE" @@ -138,11 +143,33 @@ class AssetTransferMapping: target_obj = self._local_col.all_objects.get(target_obj_name) transfer_data_map[name] = (transfer_info, target_obj) + for name in remove_transfer_data: + # TODO FIX THIS + try: + index = obj.transfer_data_ownership.keys().index(name) + obj.transfer_data_ownership.remove(index) + print(f"Remvoing {name} from {obj.name}") + except: + continue + + remove_transfer_data = [] for obj in self._local_col.all_objects: for transfer_info in obj.transfer_data_ownership: + # if transfer_info.owner not in self._local_tls: + # remove_transfer_data.append(transfer_info.name) if transfer_info.owner in self._local_tls: name = transfer_info.name + '_' + obj.name target_obj_name = asset_suffix.get_target_name(obj.name) target_obj = self._external_col.all_objects.get(target_obj_name) transfer_data_map[name] = (transfer_info, target_obj) + + # for name in remove_transfer_data: + # # TODO FIX THIS + # try: + # index = obj.transfer_data_ownership.keys().index(name) + # obj.transfer_data_ownership.remove(index) + # print(f"Remvoing {name} from {obj.name}") + # except: + # continue + return transfer_data_map diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index c19f28e6..eb7e4eb3 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -135,10 +135,9 @@ def merge_task_layer( for source_obj in map.object_map: target_obj = map.object_map[source_obj] # TODO Figure out why this doesn't work on Push only Pull currently - if target_obj.asset_id_owner not in local_tls: - transfer_core.copy_transfer_data( - target_obj.transfer_data_ownership, source_obj.transfer_data_ownership - ) + # transfer_core.copy_transfer_data( + # target_obj.transfer_data_ownership, source_obj.transfer_data_ownership + # ) remap_user(source_obj, target_obj) transfer_core.transfer_data_clean(target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index dc419ca9..6e6db58d 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -31,6 +31,7 @@ def transfer_data_clean(obj): transfer_functions.uv_layer_clean(obj) transfer_functions.modifiers_clean(obj) transfer_functions.constraints_clean(obj) + transfer_functions.material_slot_clean(obj) def transfer_data_is_missing(transfer_data_item) -> bool: diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 726bd9dd..47fc1aa4 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -10,7 +10,7 @@ from .. import asset_suffix, constants def vertex_groups_clean(obj): transfer_data = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: - if not transfer_data.get(vertex_group.name): + if not vertex_group.name in transfer_data.keys(): obj.vertex_groups.remove(vertex_group) @@ -72,7 +72,7 @@ def vertex_colors_clean(obj): return transfer_data = obj.transfer_data_ownership for vertex_color in obj.data.vertex_colors: - if not transfer_data.get(vertex_color.name): + if not asset_suffix.get_basename(vertex_color.name) in transfer_data.keys(): obj.vertex_colors.data.remove(vertex_color) @@ -130,7 +130,7 @@ def uv_layer_clean(obj): return transfer_data = obj.transfer_data_ownership for uv_layer in obj.data.uv_layers: - if not transfer_data.get(uv_layer.name): + if not asset_suffix.get_basename(uv_layer.name) in transfer_data.keys(): obj.data.uv_layers.remove(uv_layer) @@ -185,7 +185,7 @@ def transfer_uv_layer(obj_source, obj_target, uv_name): def modifiers_clean(obj): transfer_data = obj.transfer_data_ownership for modifiers in obj.modifiers: - if not transfer_data.get(modifiers.name): + if not modifiers.name in transfer_data.keys(): obj.modifiers.remove(modifiers) @@ -272,7 +272,7 @@ def transfer_modifier(transfer_info, obj_target): def constraints_clean(obj): transfer_data = obj.transfer_data_ownership for constraint in obj.constraints: - if not transfer_data.get(constraint.name): + if not constraint.name in transfer_data.keys(): obj.constraints.remove(constraint) @@ -337,8 +337,10 @@ def transfer_constraint(transfer_info, obj_target): def material_slot_clean(obj): transfer_data = obj.transfer_data_ownership for mat_slot in obj.material_slots: - if not transfer_data.get(mat_slot.name): - obj.material_slots.remove(mat_slot) + if not asset_suffix.get_basename(mat_slot.name) in transfer_data.keys(): + index = obj.material_slots.keys().index(mat_slot.name) + obj.active_material_index = index + bpy.ops.object.material_slot_remove({"object": obj}) def material_slot_is_missing(transfer_info): diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 70c5f0a9..80ed08b6 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -26,7 +26,9 @@ class ASSETPIPE_sync(bpy.types.Panel): layout.prop(status, "asset_collection", text="Asset") layout.label(text="Test UI") layout.operator("assetpipe.publish_new_version") - layout.operator("assetpipe.sync_with_publish", text="Update Ownership") + layout.operator( + "assetpipe.sync_with_publish", text="Update Ownership" + ).pull = False layout.operator( "assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP" ).push = True -- 2.30.2 From 186d114a1865841bc40f2968a640dba8587a2e08 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 31 Aug 2023 16:52:02 -0400 Subject: [PATCH 141/429] Asset Pipe: Refactor Asset Mapping for Transfer Data (Fixes Removal Bug) --- .../addons/asset_pipeline_2/asset_mapping.py | 84 +++++------ .../transfer_data/transfer_core.py | 81 +++++++---- .../transfer_data/transfer_functions.py | 134 ++++++++---------- 3 files changed, 153 insertions(+), 146 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index ed7950a8..a6cc3126 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -3,6 +3,7 @@ from typing import Dict, Set from . import asset_suffix from . import constants +from .transfer_data import transfer_core class AssetTransferMapping: @@ -126,50 +127,43 @@ class AssetTransferMapping: def _gen_transfer_data_map(self): transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - remove_transfer_data = [] - for obj in self._external_col.all_objects: - # TODO Remove any tansfer_info that is on an external obj but owned by the local TL - # TODO this works on pull not push FIX! - # TODO Just do the opposite of this in the local collection - for transfer_info in obj.transfer_data_ownership: - if transfer_info.owner in self._local_tls: - remove_transfer_data.append(transfer_info.name) - if ( - transfer_info.owner not in self._local_tls - and transfer_info.owner != "NONE" - ): - name = transfer_info.name + '_' + obj.name - target_obj_name = asset_suffix.get_target_name(obj.name) - target_obj = self._local_col.all_objects.get(target_obj_name) - transfer_data_map[name] = (transfer_info, target_obj) - - for name in remove_transfer_data: - # TODO FIX THIS - try: - index = obj.transfer_data_ownership.keys().index(name) - obj.transfer_data_ownership.remove(index) - print(f"Remvoing {name} from {obj.name}") - except: - continue - - remove_transfer_data = [] - for obj in self._local_col.all_objects: - for transfer_info in obj.transfer_data_ownership: - # if transfer_info.owner not in self._local_tls: - # remove_transfer_data.append(transfer_info.name) - if transfer_info.owner in self._local_tls: - name = transfer_info.name + '_' + obj.name - target_obj_name = asset_suffix.get_target_name(obj.name) - target_obj = self._external_col.all_objects.get(target_obj_name) - transfer_data_map[name] = (transfer_info, target_obj) - - # for name in remove_transfer_data: - # # TODO FIX THIS - # try: - # index = obj.transfer_data_ownership.keys().index(name) - # obj.transfer_data_ownership.remove(index) - # print(f"Remvoing {name} from {obj.name}") - # except: - # continue + temp_transfer_data = bpy.context.scene.temp_transfer_data_ownership + for source_obj in self.object_map: + target_obj = self.object_map[source_obj] + objs = [source_obj, target_obj] + for obj in objs: + if obj.name.endswith(constants.LOCAL_SUFFIX): + for transfer_info in obj.transfer_data_ownership: + if transfer_info.owner in self._local_tls: + temp_info = transfer_core.transfer_data_add_entry( + transfer_data=temp_transfer_data, + name=transfer_info.name, + td_type=transfer_info.type, + task_layer_name=transfer_info.owner, + ) + name = transfer_info.name + '_' + obj.name + transfer_data_map[name] = { + 'transfer_info': temp_info, + 'source_obj': obj, + 'target_obj': target_obj, + } + if obj.name.endswith(constants.EXTERNAL_SUFFIX): + for transfer_info in obj.transfer_data_ownership: + if ( + transfer_info.owner not in self._local_tls + and transfer_info.owner != "NONE" + ): + temp_info = transfer_core.transfer_data_add_entry( + transfer_data=temp_transfer_data, + name=transfer_info.name, + td_type=transfer_info.type, + task_layer_name=transfer_info.owner, + ) + name = transfer_info.name + '_' + obj.name + transfer_data_map[name] = { + 'transfer_info': temp_info, + 'source_obj': obj, + 'target_obj': target_obj, + } return transfer_data_map diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 6e6db58d..f01ca872 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -79,41 +79,61 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: context (bpy.types.Context): context of .blend file transfer_data_map: Map generated by class AssetTransferMapping """ + target_objs = [ + transfer_data_map[name].get("target_obj") for name in transfer_data_map + ] + for target_obj in set(target_objs): + target_obj.transfer_data_ownership.clear() + for name in transfer_data_map: transfer_data = transfer_data_map[name] - transfer_info = transfer_data[0] - target_obj = transfer_data[1] + transfer_info = transfer_data.get('transfer_info') + target_obj = transfer_data.get('target_obj') + source_obj = transfer_data.get('source_obj') if target_obj is None: print(f"Failed to Transfer data for {transfer_info.id_data.name}") continue - if transfer_info.type == constants.VERTEX_GROUP_KEY: - print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") - transfer_functions.transfer_vertex_group( - context=context, - vertex_group_name=transfer_info.name, - obj_target=target_obj, - obj_source=transfer_info.id_data, - ) - if transfer_info.type == constants.VERTEX_COLOR_KEY: - transfer_functions.transfer_vertex_color( - vertex_color_name=transfer_info.name, - obj_target=target_obj, - obj_source=transfer_info.id_data, - ) - if transfer_info.type == constants.MODIFIER_KEY: - print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") - transfer_functions.transfer_modifier(transfer_info, target_obj) - if transfer_info.type == constants.CONSTRAINT_KEY: - transfer_functions.transfer_constraint(transfer_info, target_obj) - if transfer_info.type == constants.MATERIAL_SLOT_KEY: - print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") - transfer_functions.transfer_material_slot(transfer_info, target_obj) - if transfer_info.type == constants.UV_LAYERS_KEY: - transfer_functions.transfer_uv_layer( - obj_target=target_obj, - obj_source=transfer_info.id_data, - uv_name=transfer_info.name, - ) + if source_obj != target_obj: + if transfer_info.type == constants.VERTEX_GROUP_KEY: + print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") + transfer_functions.transfer_vertex_group( + context=context, + vertex_group_name=transfer_info.name, + target_obj=target_obj, + source_obj=source_obj, + ) + if transfer_info.type == constants.VERTEX_COLOR_KEY: + transfer_functions.transfer_vertex_color( + vertex_color_name=transfer_info.name, + target_obj=target_obj, + source_obj=source_obj, + ) + if transfer_info.type == constants.MODIFIER_KEY: + print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") + transfer_functions.transfer_modifier( + transfer_info=transfer_info, + target_obj=target_obj, + source_obj=source_obj, + ) + if transfer_info.type == constants.CONSTRAINT_KEY: + transfer_functions.transfer_constraint( + transfer_info=transfer_info, + target_obj=target_obj, + source_obj=source_obj, + ) + if transfer_info.type == constants.MATERIAL_SLOT_KEY: + print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") + transfer_functions.transfer_material_slot( + transfer_info=transfer_info, + target_obj=target_obj, + source_obj=source_obj, + ) + if transfer_info.type == constants.UV_LAYERS_KEY: + transfer_functions.transfer_uv_layer( + target_obj=target_obj, + source_obj=source_obj, + uv_name=transfer_info.name, + ) copy_transfer_data_ownership( transfer_data_item=transfer_info, @@ -160,6 +180,7 @@ def transfer_data_add_entry( transfer_info.name = name transfer_info.owner = task_layer_name.upper() transfer_info.type = td_type + return transfer_info def copy_transfer_data(source_data, target_data): diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 47fc1aa4..416cd1ac 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -5,6 +5,7 @@ from .. import asset_suffix, constants ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES +# TODO Remove reliance on transfer_info for 'transfer' functions def vertex_groups_clean(obj): @@ -41,20 +42,20 @@ def get_vertex_groups(obj, task_layer_name, new_transfer_data): def transfer_vertex_group( context, vertex_group_name: str, - obj_target: bpy.types.Object, - obj_source: bpy.types.Object, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, ): - if obj_target == obj_source: + if target_obj == source_obj: return - if obj_target.vertex_groups.get(vertex_group_name): - obj_target.vertex_groups.remove(obj_target.vertex_groups.get(vertex_group_name)) + if target_obj.vertex_groups.get(vertex_group_name): + target_obj.vertex_groups.remove(target_obj.vertex_groups.get(vertex_group_name)) - obj_source.vertex_groups.active = obj_source.vertex_groups[vertex_group_name] + source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] override = context.copy() - override["selected_editable_objects"] = [obj_target, obj_source] - override["active_object"] = obj_source - override["object"] = obj_source + override["selected_editable_objects"] = [target_obj, source_obj] + override["active_object"] = source_obj + override["object"] = source_obj with context.temp_override(**override): bpy.ops.object.data_transfer( data_type="VGROUP_WEIGHTS", @@ -104,17 +105,17 @@ def get_vertex_colors(obj, task_layer_name, new_transfer_data): def transfer_vertex_color( vertex_color_name: str, - obj_target: bpy.types.Object, - obj_source: bpy.types.Object, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, ): - old_color = obj_target.data.vertex_colors.get(vertex_color_name) + old_color = target_obj.data.vertex_colors.get(vertex_color_name) if old_color: - obj_target.data.vertex_colors.remove(old_color) - transfer_color = obj_source.data.vertex_colors.get(vertex_color_name) - new_color = obj_target.data.vertex_colors.new( + target_obj.data.vertex_colors.remove(old_color) + transfer_color = source_obj.data.vertex_colors.get(vertex_color_name) + new_color = target_obj.data.vertex_colors.new( name=transfer_color.name, do_init=False ) - for loop in obj_target.data.loops: + for loop in target_obj.data.loops: new_color.data[loop.index].color = transfer_color.data[loop.index].color # ABOVE FOR LOOP IS FOR TOPOLOGY THAT MATCHES # BELOW COMMENTED OUT CODE IS FOR TOPOLOGY THAT DOESN'T MATCH @@ -159,14 +160,14 @@ def get_uv_layers(obj, task_layer_name, new_transfer_data): transfer_info.obj = obj -def transfer_uv_layer(obj_source, obj_target, uv_name): - old_uv_layer = obj_target.data.uv_layers.get(uv_name) +def transfer_uv_layer(source_obj, target_obj, uv_name): + old_uv_layer = target_obj.data.uv_layers.get(uv_name) if old_uv_layer: - obj_target.data.uv_layers.remove(old_uv_layer) + target_obj.data.uv_layers.remove(old_uv_layer) - transfer_uv = obj_source.data.uv_layers.get(uv_name) - new_uv = obj_target.data.uv_layers.new(name=uv_name, do_init=False) - for loop in obj_target.data.loops: + transfer_uv = source_obj.data.uv_layers.get(uv_name) + new_uv = target_obj.data.uv_layers.new(name=uv_name, do_init=False) + for loop in target_obj.data.loops: new_uv.data[loop.index].uv = transfer_uv.data[loop.index].uv # BELOW CODE IS FOR NON MATCHING TOPOLOGY # else: @@ -175,9 +176,9 @@ def transfer_uv_layer(obj_source, obj_target, uv_name): # transfer_corner_data(obj_source, obj_target, uv_from.data, uv_to.data, data_suffix = 'uv') # Make sure correct layer is set to active - for uv_l in obj_source.data.uv_layers: + for uv_l in source_obj.data.uv_layers: if uv_l.active_render: - obj_target.data.uv_layers[uv_l.name].active_render = True + target_obj.data.uv_layers[uv_l.name].active_render = True break @@ -211,43 +212,40 @@ def get_modifiers(obj, task_layer_name, new_transfer_data): transfer_info.obj = obj -def transfer_modifier(transfer_info, obj_target): +def transfer_modifier(transfer_info, target_obj, source_obj): # remove old and sync existing modifiers - obj_source = transfer_info.id_data - if obj_source == obj_target: - return - old_mod = obj_target.modifiers.get(transfer_info.name) + old_mod = target_obj.modifiers.get(transfer_info.name) if old_mod: - obj_target.modifiers.remove(old_mod) + target_obj.modifiers.remove(old_mod) # transfer new modifiers - for i, mod in enumerate(obj_source.modifiers): + for i, mod in enumerate(source_obj.modifiers): if mod.name == transfer_info.name: - mod_new = obj_target.modifiers.new(mod.name, mod.type) + mod_new = target_obj.modifiers.new(mod.name, mod.type) # sort new modifier at correct index (default to beginning of the stack) idx = 0 if i > 0: - name_prev = obj_source.modifiers[i - 1].name - for target_mod_i, target_mod in enumerate(obj_target.modifiers): + name_prev = source_obj.modifiers[i - 1].name + for target_mod_i, target_mod in enumerate(target_obj.modifiers): if target_mod.name == name_prev: idx = target_mod_i + 1 bpy.ops.object.modifier_move_to_index( - {'object': obj_target}, modifier=mod_new.name, index=idx + {'object': target_obj}, modifier=mod_new.name, index=idx ) - mod_target = obj_target.modifiers.get(mod.name) + mod_target = target_obj.modifiers.get(mod.name) props = [p.identifier for p in mod.bl_rna.properties if not p.is_readonly] for prop in props: value = getattr(mod, prop) setattr(mod_target, prop, value) # rebind modifiers (corr. smooth, surf. deform, mesh deform) - for mod in obj_target.modifiers: + for mod in target_obj.modifiers: if mod.type == 'SURFACE_DEFORM': if not mod.is_bound: continue for i in range(2): bpy.ops.object.surfacedeform_bind( - {"object": obj_target, "active_object": obj_target}, + {"object": target_obj, "active_object": target_obj}, modifier=mod.name, ) elif mod.type == 'MESH_DEFORM': @@ -255,7 +253,7 @@ def transfer_modifier(transfer_info, obj_target): continue for i in range(2): bpy.ops.object.meshdeform_bind( - {"object": obj_target, "active_object": obj_target}, + {"object": target_obj, "active_object": target_obj}, modifier=mod.name, ) elif mod.type == 'CORRECTIVE_SMOOTH': @@ -263,7 +261,7 @@ def transfer_modifier(transfer_info, obj_target): continue for i in range(2): bpy.ops.object.correctivesmooth_bind( - {"object": obj_target, "active_object": obj_target}, + {"object": target_obj, "active_object": target_obj}, modifier=mod.name, ) @@ -298,33 +296,30 @@ def get_constraints(obj, task_layer_name, new_transfer_data): transfer_info.obj = obj -def transfer_constraint(transfer_info, obj_target): +def transfer_constraint(transfer_info, target_obj, source_obj): # remove old and sync existing modifiers - obj_source = transfer_info.id_data - if obj_source == obj_target: - return - old_mod = obj_target.constraints.get(transfer_info.name) + old_mod = target_obj.constraints.get(transfer_info.name) if old_mod: - obj_target.constraints.remove(old_mod) + target_obj.constraints.remove(old_mod) # transfer new modifiers - for i, constraint in enumerate(obj_source.constraints): + for i, constraint in enumerate(source_obj.constraints): if constraint.name == transfer_info.name: - constraint_new = obj_target.constraints.new(constraint.type) + constraint_new = target_obj.constraints.new(constraint.type) constraint_new.name = constraint.name # sort new modifier at correct index (default to beginning of the stack) idx = 0 if i > 0: - name_prev = obj_source.constraints[i - 1].name + name_prev = source_obj.constraints[i - 1].name for target_mod_i, target_constraint in enumerate( - obj_target.constraints + target_obj.constraints ): if target_constraint.name == name_prev: idx = target_mod_i + 1 bpy.ops.constraint.move_to_index( - {'object': obj_target}, constraint=constraint_new.name, index=idx + {'object': target_obj}, constraint=constraint_new.name, index=idx ) - constraint_target = obj_target.constraints.get(constraint.name) + constraint_target = target_obj.constraints.get(constraint.name) props = [ p.identifier for p in constraint.bl_rna.properties if not p.is_readonly ] @@ -366,42 +361,39 @@ def get_material_slots(obj, task_layer_name, new_transfer_data): transfer_info.obj = obj -def transfer_material_slot(transfer_info, obj_target): - obj_source = transfer_info.id_data - if obj_source == obj_target: - return +def transfer_material_slot(transfer_info, target_obj, source_obj): # Delete existing material slot if exists - for idx in range(len(obj_source.material_slots)): - slot = obj_source.material_slots[idx] + for idx in range(len(source_obj.material_slots)): + slot = source_obj.material_slots[idx] if asset_suffix.get_basename(slot.material.name) == transfer_info.name: - obj_target.active_material_index = idx - bpy.ops.object.material_slot_remove({"object": obj_target}) + target_obj.active_material_index = idx + bpy.ops.object.material_slot_remove({"object": target_obj}) # Transfer material slots - for idx in range(len(obj_source.material_slots)): - if idx >= len(obj_target.material_slots): - slot = obj_source.material_slots[idx] + for idx in range(len(source_obj.material_slots)): + if idx >= len(target_obj.material_slots): + slot = source_obj.material_slots[idx] if asset_suffix.get_basename(slot.material.name) == transfer_info.name: - bpy.ops.object.material_slot_add({"object": obj_target}) - obj_target.material_slots[idx].link = obj_source.material_slots[ + bpy.ops.object.material_slot_add({"object": target_obj}) + target_obj.material_slots[idx].link = source_obj.material_slots[ idx ].link - obj_target.material_slots[idx].material = obj_source.material_slots[ + target_obj.material_slots[idx].material = source_obj.material_slots[ idx ].material # Transfer active material slot index - obj_target.active_material_index = obj_source.active_material_index + target_obj.active_material_index = source_obj.active_material_index # Transfer material slot assignments for curve - if obj_target.type == "CURVE": - for spl_to, spl_from in zip(obj_target.data.splines, obj_source.data.splines): + if target_obj.type == "CURVE": + for spl_to, spl_from in zip(target_obj.data.splines, source_obj.data.splines): spl_to.material_index = spl_from.material_index # Rest of the loop applies only to meshes. - if obj_target.type == "MESH": + if target_obj.type == "MESH": # Transfer material slot assignments for mesh - for pol_to, pol_from in zip(obj_target.data.polygons, obj_source.data.polygons): + for pol_to, pol_from in zip(target_obj.data.polygons, source_obj.data.polygons): pol_to.material_index = pol_from.material_index pol_to.use_smooth = pol_from.use_smooth -- 2.30.2 From 2ff79144a2f186c285b75f1ea16816a478194534 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 31 Aug 2023 18:00:23 -0400 Subject: [PATCH 142/429] Asset Pipe: Remove transfer_info from transfer functions --- .../transfer_data/transfer_core.py | 6 +++--- .../transfer_data/transfer_functions.py | 21 ++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index f01ca872..b7ce8c27 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -111,20 +111,20 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: if transfer_info.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") transfer_functions.transfer_modifier( - transfer_info=transfer_info, + modifier_name=transfer_info.name, target_obj=target_obj, source_obj=source_obj, ) if transfer_info.type == constants.CONSTRAINT_KEY: transfer_functions.transfer_constraint( - transfer_info=transfer_info, + constraint_name=transfer_info.name, target_obj=target_obj, source_obj=source_obj, ) if transfer_info.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") transfer_functions.transfer_material_slot( - transfer_info=transfer_info, + material_slot_name=transfer_info.name, target_obj=target_obj, source_obj=source_obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 416cd1ac..07e03b7a 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -5,9 +5,6 @@ from .. import asset_suffix, constants ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES -# TODO Remove reliance on transfer_info for 'transfer' functions - - def vertex_groups_clean(obj): transfer_data = obj.transfer_data_ownership for vertex_group in obj.vertex_groups: @@ -212,15 +209,15 @@ def get_modifiers(obj, task_layer_name, new_transfer_data): transfer_info.obj = obj -def transfer_modifier(transfer_info, target_obj, source_obj): +def transfer_modifier(modifier_name, target_obj, source_obj): # remove old and sync existing modifiers - old_mod = target_obj.modifiers.get(transfer_info.name) + old_mod = target_obj.modifiers.get(modifier_name) if old_mod: target_obj.modifiers.remove(old_mod) # transfer new modifiers for i, mod in enumerate(source_obj.modifiers): - if mod.name == transfer_info.name: + if mod.name == modifier_name: mod_new = target_obj.modifiers.new(mod.name, mod.type) # sort new modifier at correct index (default to beginning of the stack) idx = 0 @@ -296,15 +293,15 @@ def get_constraints(obj, task_layer_name, new_transfer_data): transfer_info.obj = obj -def transfer_constraint(transfer_info, target_obj, source_obj): +def transfer_constraint(constraint_name, target_obj, source_obj): # remove old and sync existing modifiers - old_mod = target_obj.constraints.get(transfer_info.name) + old_mod = target_obj.constraints.get(constraint_name) if old_mod: target_obj.constraints.remove(old_mod) # transfer new modifiers for i, constraint in enumerate(source_obj.constraints): - if constraint.name == transfer_info.name: + if constraint.name == constraint_name: constraint_new = target_obj.constraints.new(constraint.type) constraint_new.name = constraint.name # sort new modifier at correct index (default to beginning of the stack) @@ -361,11 +358,11 @@ def get_material_slots(obj, task_layer_name, new_transfer_data): transfer_info.obj = obj -def transfer_material_slot(transfer_info, target_obj, source_obj): +def transfer_material_slot(material_slot_name, target_obj, source_obj): # Delete existing material slot if exists for idx in range(len(source_obj.material_slots)): slot = source_obj.material_slots[idx] - if asset_suffix.get_basename(slot.material.name) == transfer_info.name: + if asset_suffix.get_basename(slot.material.name) == material_slot_name: target_obj.active_material_index = idx bpy.ops.object.material_slot_remove({"object": target_obj}) @@ -374,7 +371,7 @@ def transfer_material_slot(transfer_info, target_obj, source_obj): for idx in range(len(source_obj.material_slots)): if idx >= len(target_obj.material_slots): slot = source_obj.material_slots[idx] - if asset_suffix.get_basename(slot.material.name) == transfer_info.name: + if asset_suffix.get_basename(slot.material.name) == material_slot_name: bpy.ops.object.material_slot_add({"object": target_obj}) target_obj.material_slots[idx].link = source_obj.material_slots[ idx -- 2.30.2 From b339cad2103a14dfccc7245fe8b835f17f731a8d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 31 Aug 2023 18:18:40 -0400 Subject: [PATCH 143/429] Asset Pipe: Clean-up Asset Mapping for Transfer Data --- .../addons/asset_pipeline_2/asset_mapping.py | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index a6cc3126..8921f0e9 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -127,7 +127,6 @@ class AssetTransferMapping: def _gen_transfer_data_map(self): transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - temp_transfer_data = bpy.context.scene.temp_transfer_data_ownership for source_obj in self.object_map: target_obj = self.object_map[source_obj] objs = [source_obj, target_obj] @@ -135,18 +134,9 @@ class AssetTransferMapping: if obj.name.endswith(constants.LOCAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: if transfer_info.owner in self._local_tls: - temp_info = transfer_core.transfer_data_add_entry( - transfer_data=temp_transfer_data, - name=transfer_info.name, - td_type=transfer_info.type, - task_layer_name=transfer_info.owner, + self._add_transfer_data_item( + transfer_data_map, transfer_info, obj, target_obj ) - name = transfer_info.name + '_' + obj.name - transfer_data_map[name] = { - 'transfer_info': temp_info, - 'source_obj': obj, - 'target_obj': target_obj, - } if obj.name.endswith(constants.EXTERNAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: @@ -154,16 +144,26 @@ class AssetTransferMapping: transfer_info.owner not in self._local_tls and transfer_info.owner != "NONE" ): - temp_info = transfer_core.transfer_data_add_entry( - transfer_data=temp_transfer_data, - name=transfer_info.name, - td_type=transfer_info.type, - task_layer_name=transfer_info.owner, + self._add_transfer_data_item( + transfer_data_map, transfer_info, obj, target_obj ) - name = transfer_info.name + '_' + obj.name - transfer_data_map[name] = { - 'transfer_info': temp_info, - 'source_obj': obj, - 'target_obj': target_obj, - } + return transfer_data_map + + def _add_transfer_data_item( + self, transfer_data_map, transfer_info, source_obj, target_obj + ): + temp_transfer_data = bpy.context.scene.temp_transfer_data_ownership + + temp_info = transfer_core.transfer_data_add_entry( + transfer_data=temp_transfer_data, + name=transfer_info.name, + td_type=transfer_info.type, + task_layer_name=transfer_info.owner, + ) + name = transfer_info.name + '_' + source_obj.name + transfer_data_map[name] = { + 'transfer_info': temp_info, + 'source_obj': source_obj, + 'target_obj': target_obj, + } -- 2.30.2 From 1c2693725fb0eba24fb46f27fb7e731884bb000e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 31 Aug 2023 18:21:31 -0400 Subject: [PATCH 144/429] Asset Pipe: Clean-up Old TODOs and No-Op Code --- scripts-blender/addons/asset_pipeline_2/core.py | 4 ---- .../asset_pipeline_2/transfer_data/transfer_core.py | 12 ------------ 2 files changed, 16 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index eb7e4eb3..d4fa43dd 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -134,10 +134,6 @@ def merge_task_layer( for source_obj in map.object_map: target_obj = map.object_map[source_obj] - # TODO Figure out why this doesn't work on Push only Pull currently - # transfer_core.copy_transfer_data( - # target_obj.transfer_data_ownership, source_obj.transfer_data_ownership - # ) remap_user(source_obj, target_obj) transfer_core.transfer_data_clean(target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index b7ce8c27..34599623 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -25,7 +25,6 @@ def copy_transfer_data_ownership( def transfer_data_clean(obj): - # TODO add each type of transfer data here transfer_functions.vertex_groups_clean(obj) transfer_functions.vertex_colors_clean(obj) transfer_functions.uv_layer_clean(obj) @@ -181,14 +180,3 @@ def transfer_data_add_entry( transfer_info.owner = task_layer_name.upper() transfer_info.type = td_type return transfer_info - - -def copy_transfer_data(source_data, target_data): - source_data.clear() - for transfer_info in target_data: - transfer_data_add_entry( - transfer_data=source_data, - name=transfer_info.name, - task_layer_name=transfer_info.owner, - td_type=transfer_info.type, - ) -- 2.30.2 From d67df7a5f90a706236f04df3316b5212c84b51c1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 31 Aug 2023 19:07:54 -0400 Subject: [PATCH 145/429] Revert "Asset Pipe: Clean-up Asset Mapping for Transfer Data" This reverts commit b339cad2103a14dfccc7245fe8b835f17f731a8d. --- .../addons/asset_pipeline_2/asset_mapping.py | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 8921f0e9..a6cc3126 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -127,6 +127,7 @@ class AssetTransferMapping: def _gen_transfer_data_map(self): transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} + temp_transfer_data = bpy.context.scene.temp_transfer_data_ownership for source_obj in self.object_map: target_obj = self.object_map[source_obj] objs = [source_obj, target_obj] @@ -134,9 +135,18 @@ class AssetTransferMapping: if obj.name.endswith(constants.LOCAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: if transfer_info.owner in self._local_tls: - self._add_transfer_data_item( - transfer_data_map, transfer_info, obj, target_obj + temp_info = transfer_core.transfer_data_add_entry( + transfer_data=temp_transfer_data, + name=transfer_info.name, + td_type=transfer_info.type, + task_layer_name=transfer_info.owner, ) + name = transfer_info.name + '_' + obj.name + transfer_data_map[name] = { + 'transfer_info': temp_info, + 'source_obj': obj, + 'target_obj': target_obj, + } if obj.name.endswith(constants.EXTERNAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: @@ -144,26 +154,16 @@ class AssetTransferMapping: transfer_info.owner not in self._local_tls and transfer_info.owner != "NONE" ): - self._add_transfer_data_item( - transfer_data_map, transfer_info, obj, target_obj + temp_info = transfer_core.transfer_data_add_entry( + transfer_data=temp_transfer_data, + name=transfer_info.name, + td_type=transfer_info.type, + task_layer_name=transfer_info.owner, ) - + name = transfer_info.name + '_' + obj.name + transfer_data_map[name] = { + 'transfer_info': temp_info, + 'source_obj': obj, + 'target_obj': target_obj, + } return transfer_data_map - - def _add_transfer_data_item( - self, transfer_data_map, transfer_info, source_obj, target_obj - ): - temp_transfer_data = bpy.context.scene.temp_transfer_data_ownership - - temp_info = transfer_core.transfer_data_add_entry( - transfer_data=temp_transfer_data, - name=transfer_info.name, - td_type=transfer_info.type, - task_layer_name=transfer_info.owner, - ) - name = transfer_info.name + '_' + source_obj.name - transfer_data_map[name] = { - 'transfer_info': temp_info, - 'source_obj': source_obj, - 'target_obj': target_obj, - } -- 2.30.2 From 40b2cba673f7d1c87a8cc6caf0613ffc1a2d0d44 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 31 Aug 2023 19:18:57 -0400 Subject: [PATCH 146/429] Asset Pipe: Consolidate Scene Property Groups --- .../addons/asset_pipeline_2/asset_mapping.py | 2 +- .../addons/asset_pipeline_2/core.py | 2 +- .../addons/asset_pipeline_2/ops.py | 22 +++--- .../addons/asset_pipeline_2/props.py | 70 ++++++++----------- scripts-blender/addons/asset_pipeline_2/ui.py | 19 +++-- 5 files changed, 52 insertions(+), 63 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index a6cc3126..24146030 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -127,7 +127,7 @@ class AssetTransferMapping: def _gen_transfer_data_map(self): transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - temp_transfer_data = bpy.context.scene.temp_transfer_data_ownership + temp_transfer_data = bpy.context.scene.asset_pipeline.temp_transfer_data for source_obj in self.object_map: target_obj = self.object_map[source_obj] objs = [source_obj, target_obj] diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index d4fa43dd..974705a4 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -114,7 +114,7 @@ def merge_task_layer( local_tls: (list[str]): list of task layers that are local to the current file external_file (Path): external file to pull data into the current file from """ - local_col = context.scene.asset_status.asset_collection + local_col = context.scene.asset_pipeline.asset_collection if not local_col: return "Unable to find Asset Collection" col_base_name = local_col.name diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 3baa3a0b..d8a6eb27 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -17,20 +17,20 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): @classmethod def poll(cls, context: bpy.types.Context) -> bool: - new_asset = context.scene.asset_new - if new_asset.name == "" or new_asset.dir == "": + asset_pipe = context.scene.asset_pipeline + if asset_pipe.name == "" or asset_pipe.dir == "": cls.poll_message_set("Asset Name and Directory must be valid") return False - if os.path.exists(os.path.join(new_asset.dir, new_asset.name)): + if os.path.exists(os.path.join(asset_pipe.dir, asset_pipe.name)): cls.poll_message_set("Asset Folder already exists") return False return True def execute(self, context: bpy.types.Context): # New File is Createed so Props need to be saved - new_asset = context.scene.asset_new - self._name = new_asset.name - self._dir = new_asset.dir + asset_pipe = context.scene.asset_pipeline + self._name = asset_pipe.name + self._dir = asset_pipe.dir # Create Asset Folder at Directory asset_path = os.path.join(self._dir, self._name) @@ -53,7 +53,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): ) # Setup New File - asset_status = context.scene.asset_status + asset_status = context.scene.asset_pipeline asset_status.is_asset_pipeline_file = True bpy.data.collections.new(self._name) asset_col = bpy.data.collections.get(self._name) @@ -108,11 +108,11 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): - self._temp_transfer_data = context.scene.temp_transfer_data_ownership + self._temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data self._temp_transfer_data.clear() self._invalid_objs.clear() - local_col = context.scene.asset_status.asset_collection + local_col = context.scene.asset_pipeline.asset_collection if not local_col: self.report({'ERROR'}, "Top level collection could not be found") return {'CANCELLED'} @@ -166,7 +166,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): def execute(self, context: bpy.types.Context): # Find current task Layer - temp_transfer_data = context.scene.temp_transfer_data_ownership + temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data core.ownership_set(temp_transfer_data) current_file = Path(bpy.data.filepath) task_layer_name = core.get_task_layer_name_from_file() @@ -207,7 +207,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bpy.ops.wm.open_mainfile(filepath=file_path) # SKIP DEPRECIATED FILES - if context.scene.asset_status.is_depreciated: + if context.scene.asset_pipeline.is_depreciated: continue local_tls = [ diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 3fbc7037..d5061f9a 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -6,36 +6,6 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr """ -class AssetNew(bpy.types.PropertyGroup): - """Properties needed to create new asset file/folders""" - - 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") - - -class AssetFileStatus(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, - ) - asset_collection: bpy.props.PointerProperty(type=bpy.types.Collection) - - -from . import constants - - class AssetTransferData(bpy.types.PropertyGroup): """Properties to track transferable data on an object""" @@ -64,11 +34,37 @@ class AssetTransferDataTemp(bpy.types.PropertyGroup): obj: bpy.props.PointerProperty(type=bpy.types.Object) +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, + ) + asset_collection: bpy.props.PointerProperty(type=bpy.types.Collection) + + temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) + + ## NEW FILE + + 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") + + classes = ( AssetTransferData, - AssetFileStatus, AssetTransferDataTemp, - AssetNew, + AssetPipeline, ) @@ -78,22 +74,16 @@ def register(): bpy.types.Object.transfer_data_ownership = bpy.props.CollectionProperty( type=AssetTransferData ) - bpy.types.Scene.temp_transfer_data_ownership = bpy.props.CollectionProperty( - type=AssetTransferDataTemp - ) - bpy.types.Scene.asset_status = bpy.props.PointerProperty(type=AssetFileStatus) + bpy.types.Scene.asset_pipeline = bpy.props.PointerProperty(type=AssetPipeline) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", items=constants.TASK_LAYER_TYPES, ) - bpy.types.Scene.asset_new = bpy.props.PointerProperty(type=AssetNew) def unregister(): for i in classes: bpy.utils.unregister_class(i) del bpy.types.Object.transfer_data_ownership - del bpy.types.Scene.temp_transfer_data_ownership - del bpy.types.Scene.asset_status + del bpy.types.Scene.asset_pipeline del bpy.types.Object.asset_id_owner - del bpy.types.Scene.asset_new diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 80ed08b6..1dca9b2e 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -12,18 +12,17 @@ class ASSETPIPE_sync(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout - status = context.scene.asset_status - if not status.is_asset_pipeline_file: - new_asset = context.scene.asset_new - layout.prop(new_asset, "dir") - layout.prop(new_asset, "name") + asset_pipe = context.scene.asset_pipeline + if not asset_pipe.is_asset_pipeline_file: + layout.prop(asset_pipe, "dir") + layout.prop(asset_pipe, "name") layout.operator("assetpipe.create_new_asset") # layout.operator("") return layout.label( text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" ) - layout.prop(status, "asset_collection", text="Asset") + layout.prop(asset_pipe, "asset_collection", text="Asset") layout.label(text="Test UI") layout.operator("assetpipe.publish_new_version") layout.operator( @@ -45,8 +44,8 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout - status = context.scene.asset_status - if not status.is_asset_pipeline_file: + asset_pipe = context.scene.asset_pipeline + if not asset_pipe.is_asset_pipeline_file: layout.label(text="Open valid 'Asset Pipeline' file", icon="ERROR") return if not context.active_object: @@ -63,10 +62,10 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): task_layer_name = core.get_task_layer_name_from_file() if task_layer_name not in constants.TASK_LAYER_KEYS: - status = context.scene.asset_status + asset_pipe = context.scene.asset_pipeline box = layout.box() box.label(text="Published File Settings") - box.prop(status, "is_depreciated") + box.prop(asset_pipe, "is_depreciated") classes = ( -- 2.30.2 From 8cab8668745e9e2ac26ce2a838c78d28dc764c78 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 1 Sep 2023 17:40:23 -0400 Subject: [PATCH 147/429] Asset Pipe: Reset Temp Transfer Data during Mapping --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 24146030..cd562f7a 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -128,6 +128,7 @@ class AssetTransferMapping: def _gen_transfer_data_map(self): transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} temp_transfer_data = bpy.context.scene.asset_pipeline.temp_transfer_data + temp_transfer_data.clear() for source_obj in self.object_map: target_obj = self.object_map[source_obj] objs = [source_obj, target_obj] -- 2.30.2 From aadfd4dca3921b39350317f5117e015710837af9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 1 Sep 2023 17:40:38 -0400 Subject: [PATCH 148/429] Asset Pipe: Update Test Files --- .../addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 4 ++-- .../addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend | 4 ++-- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend index 35d17096..2312f52e 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c71ffac74d17bcb6ed532646bde5e969f3a728c67ae1d7c51f04795a77e3c556 -size 937556 +oid sha256:8cc9c9b612bc1a46c878e8efb11c383ca2eccde9640f19ea09eef0c266e54548 +size 938180 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend index 5812bc81..b85ae341 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf52d6f62fbaf2e2c0e138df299ef1fbd5921ff25f3b69caf9bf2a4f106b7f85 -size 925616 +oid sha256:0b829592dd611c6784ed14741f6202f91d01d527c4d0850a353de48e24ccc4aa +size 957840 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend index 5c4f6720..af4b77cf 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be04e6d1979f4c7aafb5c2e5b01d90ce3656e2c5d4dfcfcc2201465097641eb5 -size 955084 +oid sha256:4c7207ed1ba8f01eccba432da219f6fe5773a58daaa2f81b7a805178e3037d56 +size 935888 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend index ba1a6046..35c9fd70 100644 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abdf9267fa3905539a5e12efd1dad8d5376b960be2ed7844d9dd44cef889c58f -size 886388 +oid sha256:3bd253dfde076306586e34b23b6e7513ff5170f9998f6aeb0299b4dfd77c96a6 +size 913544 -- 2.30.2 From afcfaee924574767a9126a6f44ff27fc00a5b040 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 1 Sep 2023 18:34:24 -0400 Subject: [PATCH 149/429] Asset Pipe: Add Task Layer Name property to Scene Property Group --- scripts-blender/addons/asset_pipeline_2/ops.py | 3 +++ scripts-blender/addons/asset_pipeline_2/props.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index d8a6eb27..ba66d8a2 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -59,6 +59,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_col = bpy.data.collections.get(self._name) context.scene.collection.children.link(asset_col) asset_status.asset_collection = asset_col + asset_pipe = context.scene.asset_pipeline for task_layer_name in constants.TASK_LAYER_NAMES: if task_layer_name == "None": @@ -71,9 +72,11 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): continue name = self._name + "." + task_layer_key + ".blend" task_layer_file = os.path.join(asset_path, name) + asset_pipe.task_layer_name = task_layer_key bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) # Creata intial publish based on task layers + asset_pipe.task_layer_name = "NONE" publish_path = os.path.join(asset_path, constants.ACTIVE_PUBLISH_KEY) name = self._name + "." + "v001" + ".blend" publish_file = os.path.join(publish_path, name) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index d5061f9a..d54aa584 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -51,6 +51,9 @@ class AssetPipeline(bpy.types.PropertyGroup): temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) + task_layer_name: bpy.props.EnumProperty( + name="Task Layer Name", items=constants.TASK_LAYER_TYPES + ) ## NEW FILE dir: bpy.props.StringProperty( -- 2.30.2 From f6f47f0dc265996c31339f1da219f31f287ad4c1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 1 Sep 2023 18:44:16 -0400 Subject: [PATCH 150/429] Asset Pipe: Rename Improve Get Transfer Data and rename to init --- .../addons/asset_pipeline_2/core.py | 6 +- .../addons/asset_pipeline_2/ops.py | 6 +- .../addons/asset_pipeline_2/props.py | 9 ++ .../transfer_data/transfer_core.py | 17 +-- .../transfer_data/transfer_functions.py | 104 ++++++++++-------- 5 files changed, 84 insertions(+), 58 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 974705a4..5c3becd1 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -31,8 +31,7 @@ def ownership_transfer_data_cleanup( def ownership_get( local_col: bpy.types.Collection, - task_layer_name: str, - temp_transfer_data: bpy.types.CollectionProperty, + scene: bpy.types.Scene, ) -> list[bpy.types.Object]: """Find new transfer data owned by the local task layer. Marks items as owned by the local task layer if they are in the @@ -49,6 +48,7 @@ def ownership_get( in the merge process """ invalid_objs = [] + task_layer_name = scene.asset_pipeline.task_layer_name task_layer_col_name = get_dict_tuple_item( constants.TASK_LAYER_TYPES, task_layer_name )[1] @@ -62,7 +62,7 @@ def ownership_get( invalid_objs.append(obj) continue ownership_transfer_data_cleanup(obj, task_layer_name) - transfer_core.get_transfer_data(obj, task_layer_name, temp_transfer_data) + transfer_core.init_transfer_data(scene, obj) return invalid_objs diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index ba66d8a2..f36776f2 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -124,11 +124,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - self._invalid_objs = core.ownership_get( - local_col, - task_layer_name, - self._temp_transfer_data, - ) + self._invalid_objs = core.ownership_get(local_col, context.scene) # Default behaviour is to pull before pushing if self.push: diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index d54aa584..3afbcb27 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -54,6 +54,15 @@ class AssetPipeline(bpy.types.PropertyGroup): task_layer_name: bpy.props.EnumProperty( name="Task Layer Name", items=constants.TASK_LAYER_TYPES ) + + def add_temp_trasnfer_data(self, name, owner, type, obj): + new_transfer_data = self.temp_transfer_data + transfer_info = new_transfer_data.add() + transfer_info.name = name + transfer_info.owner = owner + transfer_info.type = type + transfer_info.obj = obj + ## NEW FILE dir: bpy.props.StringProperty( diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 34599623..ad988a17 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -51,7 +51,10 @@ def transfer_data_is_missing(transfer_data_item) -> bool: ) -def get_transfer_data(obj: bpy.types.Object, task_layer_name: str, temp_transfer_data): +def init_transfer_data( + scene: bpy.types.Scene, + obj: bpy.types.Object, +): """Collect Transfer Data Items on a given object Args: @@ -59,12 +62,12 @@ def get_transfer_data(obj: bpy.types.Object, task_layer_name: str, temp_transfer task_layer_name (str): Name of task layer temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP """ - transfer_functions.get_vertex_groups(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_material_slots(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_modifiers(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_constraints(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_vertex_colors(obj, task_layer_name, temp_transfer_data) - transfer_functions.get_uv_layers(obj, task_layer_name, temp_transfer_data) + transfer_functions.init_vertex_groups(scene, obj) + transfer_functions.init_material_slots(scene, obj) + transfer_functions.init_modifiers(scene, obj) + transfer_functions.init_constraints(scene, obj) + transfer_functions.init_vertex_colors(scene, obj) + transfer_functions.init_uv_layers(scene, obj) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 07e03b7a..7ebb1f89 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -21,19 +21,22 @@ def vertex_group_is_missing(transfer_info): return True -def get_vertex_groups(obj, task_layer_name, new_transfer_data): +def init_vertex_groups(scene, obj): transfer_data = obj.transfer_data_ownership + task_layer_name = scene.asset_pipeline.task_layer_name + td_type = constants.VERTEX_GROUP_KEY for vertex_group in obj.vertex_groups: # Only add new ownership transfer_info if vertex group doesn't have an owner matches = transfer_core.check_transfer_data_entry( - transfer_data, vertex_group.name, constants.VERTEX_GROUP_KEY + transfer_data, vertex_group.name, td_type ) if len(matches) == 0: - transfer_info = new_transfer_data.add() - transfer_info.name = vertex_group.name - transfer_info.owner = task_layer_name - transfer_info.type = constants.VERTEX_GROUP_KEY - transfer_info.obj = obj + scene.asset_pipeline.add_temp_trasnfer_data( + name=vertex_group.name, + owner=task_layer_name, + type=td_type, + obj=obj, + ) def transfer_vertex_group( @@ -83,21 +86,24 @@ def vertex_color_is_missing(transfer_info): return True -def get_vertex_colors(obj, task_layer_name, new_transfer_data): +def init_vertex_colors(scene, obj): transfer_data = obj.transfer_data_ownership + task_layer_name = scene.asset_pipeline.task_layer_name + td_type = constants.VERTEX_COLOR_KEY if not obj.type == "MESH": return for vertex_color in obj.data.vertex_colors: # Only add new ownership transfer_info if vertex color doesn't have an owner matches = transfer_core.check_transfer_data_entry( - transfer_data, vertex_color.name, constants.VERTEX_COLOR_KEY + transfer_data, vertex_color.name, td_type ) if len(matches) == 0: - transfer_info = new_transfer_data.add() - transfer_info.name = vertex_color.name - transfer_info.owner = task_layer_name - transfer_info.type = constants.VERTEX_COLOR_KEY - transfer_info.obj = obj + scene.asset_pipeline.add_temp_trasnfer_data( + name=vertex_color.name, + owner=task_layer_name, + type=td_type, + obj=obj, + ) def transfer_vertex_color( @@ -140,21 +146,24 @@ def uv_layer_is_missing(transfer_info): return True -def get_uv_layers(obj, task_layer_name, new_transfer_data): +def init_uv_layers(scene, obj): transfer_data = obj.transfer_data_ownership + task_layer_name = scene.asset_pipeline.task_layer_name + td_type = constants.UV_LAYERS_KEY if not obj.type == "MESH": return for uv_layer in obj.data.uv_layers: # Only add new ownership transfer_info if vertex color doesn't have an owner matches = transfer_core.check_transfer_data_entry( - transfer_data, uv_layer.name, constants.UV_LAYERS_KEY + transfer_data, uv_layer.name, td_type ) if len(matches) == 0: - transfer_info = new_transfer_data.add() - transfer_info.name = uv_layer.name - transfer_info.owner = task_layer_name - transfer_info.type = constants.UV_LAYERS_KEY - transfer_info.obj = obj + scene.asset_pipeline.add_temp_trasnfer_data( + name=uv_layer.name, + owner=task_layer_name, + type=td_type, + obj=obj, + ) def transfer_uv_layer(source_obj, target_obj, uv_name): @@ -195,18 +204,21 @@ def modifier_is_missing(transfer_info): return True -def get_modifiers(obj, task_layer_name, new_transfer_data): +def init_modifiers(scene, obj): + task_layer_name = scene.asset_pipeline.task_layer_name transfer_data = obj.transfer_data_ownership + td_type = constants.MODIFIER_KEY for mod in obj.modifiers: matches = transfer_core.check_transfer_data_entry( - transfer_data, mod.name, constants.MODIFIER_KEY + transfer_data, mod.name, td_type ) if len(matches) == 0: - transfer_info = new_transfer_data.add() - transfer_info.name = mod.name - transfer_info.owner = task_layer_name - transfer_info.type = constants.MODIFIER_KEY - transfer_info.obj = obj + scene.asset_pipeline.add_temp_trasnfer_data( + name=mod.name, + owner=task_layer_name, + type=td_type, + obj=obj, + ) def transfer_modifier(modifier_name, target_obj, source_obj): @@ -279,18 +291,21 @@ def constraint_is_missing(transfer_info): return True -def get_constraints(obj, task_layer_name, new_transfer_data): +def init_constraints(scene, obj): + task_layer_name = scene.asset_pipeline.task_layer_name transfer_data = obj.transfer_data_ownership - for mod in obj.constraints: + td_type = constants.CONSTRAINT_KEY + for constraint in obj.constraints: matches = transfer_core.check_transfer_data_entry( - transfer_data, mod.name, constants.CONSTRAINT_KEY + transfer_data, constraint.name, td_type ) if len(matches) == 0: - transfer_info = new_transfer_data.add() - transfer_info.name = mod.name - transfer_info.owner = task_layer_name - transfer_info.type = constants.CONSTRAINT_KEY - transfer_info.obj = obj + scene.asset_pipeline.add_temp_trasnfer_data( + name=constraint.name, + owner=task_layer_name, + type=td_type, + obj=obj, + ) def transfer_constraint(constraint_name, target_obj, source_obj): @@ -344,18 +359,21 @@ def material_slot_is_missing(transfer_info): return True -def get_material_slots(obj, task_layer_name, new_transfer_data): +def init_material_slots(scene, obj): + task_layer_name = scene.asset_pipeline.task_layer_name transfer_data = obj.transfer_data_ownership + td_type = constants.MATERIAL_SLOT_KEY for slot in obj.material_slots: matches = transfer_core.check_transfer_data_entry( - transfer_data, slot.name, constants.MATERIAL_SLOT_KEY + transfer_data, slot.name, td_type ) if len(matches) == 0: - transfer_info = new_transfer_data.add() - transfer_info.name = slot.name - transfer_info.owner = task_layer_name - transfer_info.type = constants.MATERIAL_SLOT_KEY - transfer_info.obj = obj + scene.asset_pipeline.add_temp_trasnfer_data( + name=slot.name, + owner=task_layer_name, + type=td_type, + obj=obj, + ) def transfer_material_slot(material_slot_name, target_obj, source_obj): -- 2.30.2 From 0340893b566f1ef532b33fe32a142310371ade6e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 1 Sep 2023 18:49:25 -0400 Subject: [PATCH 151/429] Asset Pipe: Clear Temp Transfer Data during init --- .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index ad988a17..f1325f13 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -62,6 +62,7 @@ def init_transfer_data( task_layer_name (str): Name of task layer temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP """ + scene.asset_pipeline.temp_transfer_data.clear() transfer_functions.init_vertex_groups(scene, obj) transfer_functions.init_material_slots(scene, obj) transfer_functions.init_modifiers(scene, obj) -- 2.30.2 From 776b55fd7e1a6030a9243f33e593f2c8eeda416b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 1 Sep 2023 18:54:06 -0400 Subject: [PATCH 152/429] Asset Pipe: Fix Bug 'Removed data re-appears if local item has no transfer_data' --- scripts-blender/addons/asset_pipeline_2/core.py | 5 +++++ .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 5c3becd1..8ea728b9 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -130,6 +130,11 @@ def merge_task_layer( map = AssetTransferMapping(local_col, external_col, local_tls) + # Remove all transfer data from target objects + for source_obj in map.object_map: + target_obj = map.object_map[source_obj] + target_obj.transfer_data_ownership.clear() + transfer_core.apply_transfer_data(context, map.transfer_data_map) for source_obj in map.object_map: diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index f1325f13..990fc317 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -82,11 +82,6 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: context (bpy.types.Context): context of .blend file transfer_data_map: Map generated by class AssetTransferMapping """ - target_objs = [ - transfer_data_map[name].get("target_obj") for name in transfer_data_map - ] - for target_obj in set(target_objs): - target_obj.transfer_data_ownership.clear() for name in transfer_data_map: transfer_data = transfer_data_map[name] -- 2.30.2 From de42c0a7d96839ba4284a8727ffee5e4195ca100 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 1 Sep 2023 20:55:26 -0400 Subject: [PATCH 153/429] Asset Pipe: Fix Clearing Temp Transfer Data --- scripts-blender/addons/asset_pipeline_2/core.py | 1 + .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 8ea728b9..37b93aa3 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -52,6 +52,7 @@ def ownership_get( task_layer_col_name = get_dict_tuple_item( constants.TASK_LAYER_TYPES, task_layer_name )[1] + scene.asset_pipeline.temp_transfer_data.clear() task_layer_col = local_col.children.get(task_layer_col_name) for obj in local_col.all_objects: # Mark Asset ID Owner for objects in the current task layers collection diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 990fc317..02ba8d2f 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -62,7 +62,6 @@ def init_transfer_data( task_layer_name (str): Name of task layer temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP """ - scene.asset_pipeline.temp_transfer_data.clear() transfer_functions.init_vertex_groups(scene, obj) transfer_functions.init_material_slots(scene, obj) transfer_functions.init_modifiers(scene, obj) -- 2.30.2 From 2397fb5220402accf98582126a7a8949c0a40c74 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 4 Sep 2023 12:08:04 -0400 Subject: [PATCH 154/429] Asset Pipe: Improve get_basename function --- scripts-blender/addons/asset_pipeline_2/asset_suffix.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index aeb9b01c..883906bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -60,7 +60,11 @@ def get_target_name(name: str) -> str: def get_basename(name: str) -> str: """Returns the name of an asset without it's suffix""" - return DELIMITER.join(name.split(DELIMITER)[:-1]) + if name.endswith(constants.LOCAL_SUFFIX) or name.endswith( + constants.EXTERNAL_SUFFIX + ): + return DELIMITER.join(name.split(DELIMITER)[:-1]) + return name def remove_suffix_from_hierarchy(collection: bpy.types.Collection) -> None: -- 2.30.2 From 8c57539d7140d332b81a349f647b2e24e4e2cc6d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 4 Sep 2023 12:09:53 -0400 Subject: [PATCH 155/429] Asset Pipe: Centralize Code for Cleaning Transfer Data --- .../transfer_data/transfer_core.py | 9 +++++- .../transfer_data/transfer_functions.py | 30 ++++++------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 02ba8d2f..94b09ce3 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -1,7 +1,7 @@ import bpy from . import transfer_functions -from .. import constants +from .. import constants, asset_suffix def copy_transfer_data_ownership( @@ -178,3 +178,10 @@ def transfer_data_add_entry( transfer_info.owner = task_layer_name.upper() transfer_info.type = td_type return transfer_info + + +def transfer_info_clean(obj, list): + transfer_data = obj.transfer_data_ownership + for item in list: + if not asset_suffix.get_basename(item.name) in transfer_data.keys(): + list.remove(item) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 7ebb1f89..cbefe6e7 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -5,14 +5,13 @@ from .. import asset_suffix, constants ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES -def vertex_groups_clean(obj): - transfer_data = obj.transfer_data_ownership - for vertex_group in obj.vertex_groups: - if not vertex_group.name in transfer_data.keys(): - obj.vertex_groups.remove(vertex_group) # VERTEX GROUPS +def vertex_groups_clean(obj): + transfer_core.transfer_info_clean(obj, obj.vertex_groups) + + def vertex_group_is_missing(transfer_info): obj = transfer_info.id_data if transfer_info.type == constants.VERTEX_GROUP_KEY and not obj.vertex_groups.get( @@ -71,10 +70,7 @@ def transfer_vertex_group( def vertex_colors_clean(obj): if not obj.type == "MESH": return - transfer_data = obj.transfer_data_ownership - for vertex_color in obj.data.vertex_colors: - if not asset_suffix.get_basename(vertex_color.name) in transfer_data.keys(): - obj.vertex_colors.data.remove(vertex_color) + transfer_core.transfer_info_clean(obj, obj.data.vertex_colors) def vertex_color_is_missing(transfer_info): @@ -132,10 +128,7 @@ def transfer_vertex_color( def uv_layer_clean(obj): if not obj.type == "MESH": return - transfer_data = obj.transfer_data_ownership - for uv_layer in obj.data.uv_layers: - if not asset_suffix.get_basename(uv_layer.name) in transfer_data.keys(): - obj.data.uv_layers.remove(uv_layer) + transfer_core.transfer_info_clean(obj, obj.data.uv_layers) def uv_layer_is_missing(transfer_info): @@ -190,10 +183,7 @@ def transfer_uv_layer(source_obj, target_obj, uv_name): # MODIFIERS def modifiers_clean(obj): - transfer_data = obj.transfer_data_ownership - for modifiers in obj.modifiers: - if not modifiers.name in transfer_data.keys(): - obj.modifiers.remove(modifiers) + transfer_core.transfer_info_clean(obj, obj.modifiers) def modifier_is_missing(transfer_info): @@ -277,10 +267,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): # CONSTRAINTS def constraints_clean(obj): - transfer_data = obj.transfer_data_ownership - for constraint in obj.constraints: - if not constraint.name in transfer_data.keys(): - obj.constraints.remove(constraint) + transfer_core.transfer_info_clean(obj, obj.constraints) def constraint_is_missing(transfer_info): @@ -342,6 +329,7 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT def material_slot_clean(obj): + # Material slots cannot use generic transfer_info_clean() function transfer_data = obj.transfer_data_ownership for mat_slot in obj.material_slots: if not asset_suffix.get_basename(mat_slot.name) in transfer_data.keys(): -- 2.30.2 From 84d81f8beaa353c7d682dcd580f3b35ab03ff020 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 4 Sep 2023 12:23:38 -0400 Subject: [PATCH 156/429] Asset Pipe: Centralize Code for Missing Transfer Data --- .../transfer_data/transfer_core.py | 5 ++ .../transfer_data/transfer_functions.py | 50 +++++++------------ 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 94b09ce3..67d1521c 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -185,3 +185,8 @@ def transfer_info_clean(obj, list): for item in list: if not asset_suffix.get_basename(item.name) in transfer_data.keys(): list.remove(item) + + +def transfer_info_is_missing(transfer_info, type_key, list): + if transfer_info.type == type_key and not list.get(transfer_info["name"]): + return True diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index cbefe6e7..0d21ed1b 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -13,11 +13,9 @@ def vertex_groups_clean(obj): def vertex_group_is_missing(transfer_info): - obj = transfer_info.id_data - if transfer_info.type == constants.VERTEX_GROUP_KEY and not obj.vertex_groups.get( - transfer_info["name"] - ): - return True + return transfer_core.transfer_info_is_missing( + transfer_info, constants.VERTEX_GROUP_KEY, transfer_info.id_data.vertex_groups + ) def init_vertex_groups(scene, obj): @@ -74,12 +72,9 @@ def vertex_colors_clean(obj): def vertex_color_is_missing(transfer_info): - obj = transfer_info.id_data - if ( - transfer_info.type == constants.VERTEX_COLOR_KEY - and not obj.data.vertex_colors.get(transfer_info["name"]) - ): - return True + return transfer_core.transfer_info_is_missing( + transfer_info, constants.VERTEX_COLOR_KEY, transfer_info.id_data.vertex_colors + ) def init_vertex_colors(scene, obj): @@ -132,11 +127,9 @@ def uv_layer_clean(obj): def uv_layer_is_missing(transfer_info): - obj = transfer_info.id_data - if transfer_info.type == constants.UV_LAYERS_KEY and not obj.data.uv_layers.get( - transfer_info["name"] - ): - return True + return transfer_core.transfer_info_is_missing( + transfer_info, constants.UV_LAYERS_KEY, transfer_info.id_data.data.uv_layers + ) def init_uv_layers(scene, obj): @@ -187,11 +180,9 @@ def modifiers_clean(obj): def modifier_is_missing(transfer_info): - obj = transfer_info.id_data - if transfer_info.type == constants.MODIFIER_KEY and not obj.modifiers.get( - transfer_info["name"] - ): - return True + return transfer_core.transfer_info_is_missing( + transfer_info, constants.MODIFIER_KEY, transfer_info.id_data.modifiers + ) def init_modifiers(scene, obj): @@ -271,11 +262,9 @@ def constraints_clean(obj): def constraint_is_missing(transfer_info): - obj = transfer_info.id_data - if transfer_info.type == constants.CONSTRAINT_KEY and not obj.constraints.get( - transfer_info["name"] - ): - return True + return transfer_core.transfer_info_is_missing( + transfer_info, constants.CONSTRAINT_KEY, transfer_info.id_data.constraints + ) def init_constraints(scene, obj): @@ -339,12 +328,9 @@ def material_slot_clean(obj): def material_slot_is_missing(transfer_info): - obj = transfer_info.id_data - if ( - transfer_info.type == constants.MATERIAL_SLOT_KEY - and not obj.material_slots.get(transfer_info["name"]) - ): - return True + return transfer_core.transfer_info_is_missing( + transfer_info, constants.MATERIAL_SLOT_KEY, transfer_info.id_data.material_slots + ) def init_material_slots(scene, obj): -- 2.30.2 From a05f5b20b6e882770c8d70ea787f2fc0edeb19b3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 4 Sep 2023 12:32:11 -0400 Subject: [PATCH 157/429] Asset Pipe: Centralize Code for init of Transfer Info --- .../transfer_data/transfer_core.py | 15 +++ .../transfer_data/transfer_functions.py | 103 +++--------------- 2 files changed, 31 insertions(+), 87 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 67d1521c..6e0c70cd 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -190,3 +190,18 @@ def transfer_info_clean(obj, list): def transfer_info_is_missing(transfer_info, type_key, list): if transfer_info.type == type_key and not list.get(transfer_info["name"]): return True + + +def transfer_info_init(scene, obj, list, type_key): + transfer_data = obj.transfer_data_ownership + task_layer_name = scene.asset_pipeline.task_layer_name + for item in list: + # Only add new ownership transfer_info if vertex group doesn't have an owner + matches = check_transfer_data_entry(transfer_data, item.name, type_key) + if len(matches) == 0: + scene.asset_pipeline.add_temp_trasnfer_data( + name=item.name, + owner=task_layer_name, + type=type_key, + obj=obj, + ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 0d21ed1b..fd5932da 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -19,21 +19,9 @@ def vertex_group_is_missing(transfer_info): def init_vertex_groups(scene, obj): - transfer_data = obj.transfer_data_ownership - task_layer_name = scene.asset_pipeline.task_layer_name - td_type = constants.VERTEX_GROUP_KEY - for vertex_group in obj.vertex_groups: - # Only add new ownership transfer_info if vertex group doesn't have an owner - matches = transfer_core.check_transfer_data_entry( - transfer_data, vertex_group.name, td_type - ) - if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( - name=vertex_group.name, - owner=task_layer_name, - type=td_type, - obj=obj, - ) + transfer_core.transfer_info_init( + scene, obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY + ) def transfer_vertex_group( @@ -78,23 +66,11 @@ def vertex_color_is_missing(transfer_info): def init_vertex_colors(scene, obj): - transfer_data = obj.transfer_data_ownership - task_layer_name = scene.asset_pipeline.task_layer_name - td_type = constants.VERTEX_COLOR_KEY if not obj.type == "MESH": return - for vertex_color in obj.data.vertex_colors: - # Only add new ownership transfer_info if vertex color doesn't have an owner - matches = transfer_core.check_transfer_data_entry( - transfer_data, vertex_color.name, td_type - ) - if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( - name=vertex_color.name, - owner=task_layer_name, - type=td_type, - obj=obj, - ) + transfer_core.transfer_info_init( + scene, obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY + ) def transfer_vertex_color( @@ -133,23 +109,11 @@ def uv_layer_is_missing(transfer_info): def init_uv_layers(scene, obj): - transfer_data = obj.transfer_data_ownership - task_layer_name = scene.asset_pipeline.task_layer_name - td_type = constants.UV_LAYERS_KEY if not obj.type == "MESH": return - for uv_layer in obj.data.uv_layers: - # Only add new ownership transfer_info if vertex color doesn't have an owner - matches = transfer_core.check_transfer_data_entry( - transfer_data, uv_layer.name, td_type - ) - if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( - name=uv_layer.name, - owner=task_layer_name, - type=td_type, - obj=obj, - ) + transfer_core.transfer_info_init( + scene, obj, obj.data.uv_layers, constants.UV_LAYERS_KEY + ) def transfer_uv_layer(source_obj, target_obj, uv_name): @@ -186,20 +150,7 @@ def modifier_is_missing(transfer_info): def init_modifiers(scene, obj): - task_layer_name = scene.asset_pipeline.task_layer_name - transfer_data = obj.transfer_data_ownership - td_type = constants.MODIFIER_KEY - for mod in obj.modifiers: - matches = transfer_core.check_transfer_data_entry( - transfer_data, mod.name, td_type - ) - if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( - name=mod.name, - owner=task_layer_name, - type=td_type, - obj=obj, - ) + transfer_core.transfer_info_init(scene, obj, obj.modifiers, constants.MODIFIER_KEY) def transfer_modifier(modifier_name, target_obj, source_obj): @@ -268,20 +219,9 @@ def constraint_is_missing(transfer_info): def init_constraints(scene, obj): - task_layer_name = scene.asset_pipeline.task_layer_name - transfer_data = obj.transfer_data_ownership - td_type = constants.CONSTRAINT_KEY - for constraint in obj.constraints: - matches = transfer_core.check_transfer_data_entry( - transfer_data, constraint.name, td_type - ) - if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( - name=constraint.name, - owner=task_layer_name, - type=td_type, - obj=obj, - ) + transfer_core.transfer_info_init( + scene, obj, obj.constraints, constants.CONSTRAINT_KEY + ) def transfer_constraint(constraint_name, target_obj, source_obj): @@ -334,20 +274,9 @@ def material_slot_is_missing(transfer_info): def init_material_slots(scene, obj): - task_layer_name = scene.asset_pipeline.task_layer_name - transfer_data = obj.transfer_data_ownership - td_type = constants.MATERIAL_SLOT_KEY - for slot in obj.material_slots: - matches = transfer_core.check_transfer_data_entry( - transfer_data, slot.name, td_type - ) - if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( - name=slot.name, - owner=task_layer_name, - type=td_type, - obj=obj, - ) + transfer_core.transfer_info_init( + scene, obj, obj.material_slots, constants.MATERIAL_SLOT_KEY + ) def transfer_material_slot(material_slot_name, target_obj, source_obj): -- 2.30.2 From 1641a433cf83a2b3c2a7390e9a9879d53964e22f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 5 Sep 2023 16:13:39 -0400 Subject: [PATCH 158/429] Asset Pipe: Add Shape Keys to Transfer Data --- .../addons/asset_pipeline_2/constants.py | 3 +- .../transfer_data/transfer_core.py | 9 ++ .../transfer_data/transfer_functions.py | 130 ++++++++++++++++++ .../transfer_data/transfer_ui.py | 6 +- 4 files changed, 146 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 917cc703..33198f43 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -19,6 +19,7 @@ TRANSFER_DATA_TYPES = [ ("CONSTRAINT", "Constraint", ""), ("MATERIAL", "Material Slot", ""), ("GROUP_UVS", "UV Maps", ""), + ("SHAPEKEY_DATA", "Shape Key", ""), ] TRANSFER_DATA_KEYS = [transfer_data[0] for transfer_data in TRANSFER_DATA_TYPES] @@ -29,7 +30,7 @@ MODIFIER_KEY = TRANSFER_DATA_KEYS[3] CONSTRAINT_KEY = TRANSFER_DATA_KEYS[4] MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[5] UV_LAYERS_KEY = TRANSFER_DATA_KEYS[6] - +SHAPE_KEY_KEY = TRANSFER_DATA_KEYS[7] PUBLISH_TYPES = [ ( diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 6e0c70cd..52efe1cb 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -31,6 +31,7 @@ def transfer_data_clean(obj): transfer_functions.modifiers_clean(obj) transfer_functions.constraints_clean(obj) transfer_functions.material_slot_clean(obj) + transfer_functions.shape_keys_clean(obj) def transfer_data_is_missing(transfer_data_item) -> bool: @@ -48,6 +49,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: or transfer_functions.material_slot_is_missing(transfer_data_item) or transfer_functions.constraint_is_missing(transfer_data_item) or transfer_functions.uv_layer_is_missing(transfer_data_item) + or transfer_functions.shape_key_is_missing(transfer_data_item) ) @@ -68,6 +70,7 @@ def init_transfer_data( transfer_functions.init_constraints(scene, obj) transfer_functions.init_vertex_colors(scene, obj) transfer_functions.init_uv_layers(scene, obj) + transfer_functions.init_shap_keys(scene, obj) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: @@ -131,6 +134,12 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: source_obj=source_obj, uv_name=transfer_info.name, ) + if transfer_info.type == constants.SHAPE_KEY_KEY: + transfer_functions.transfer_shape_key( + target_obj=target_obj, + source_obj=source_obj, + shape_key_name=transfer_info.name, + ) copy_transfer_data_ownership( transfer_data_item=transfer_info, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index fd5932da..ad65d998 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -2,6 +2,9 @@ import bpy from bpy import context from . import transfer_core from .. import asset_suffix, constants +import mathutils +import bmesh +import numpy as np ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES @@ -315,3 +318,130 @@ def transfer_material_slot(material_slot_name, target_obj, source_obj): for pol_to, pol_from in zip(target_obj.data.polygons, source_obj.data.polygons): pol_to.material_index = pol_from.material_index pol_to.use_smooth = pol_from.use_smooth + + +# SHAPE KEYS +def shape_key_closest_face_to_point(bm_source, p_target, bvh_tree=None): + if not bvh_tree: + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + (loc, norm, index, distance) = bvh_tree.find_nearest(p_target) + return bm_source.faces[index] + + +def shape_key_tris_per_face(bm_source): + tris_source = bm_source.calc_loop_triangles() + tris_dict = dict() + for face in bm_source.faces: + tris_face = [] + for i in range(len(tris_source))[::-1]: + if tris_source[i][0] in face.loops: + tris_face.append(tris_source.pop(i)) + tris_dict[face] = tris_face + return tris_dict + + +def shape_key_closest_tri_on_face(tris_dict, face, p): + points = [] + dist = [] + tris = [] + for tri in tris_dict[face]: + point = mathutils.geometry.closest_point_on_tri( + p, *[tri[i].vert.co for i in range(3)] + ) + tris.append(tri) + points.append(point) + dist.append((point - p).length) + min_idx = np.argmin(np.array(dist)) + point = points[min_idx] + tri = tris[min_idx] + return (tri, point) + + +def shape_keys_clean(obj): + if obj.type != "MESH" or obj.data.shape_keys is None: + return + transfer_data = obj.transfer_data_ownership + for shape_key in obj.data.shape_keys.key_blocks: + if not asset_suffix.get_basename(shape_key.name) in transfer_data.keys(): + obj.shape_key_remove(shape_key) + + +def shape_key_is_missing(transfer_info): + return transfer_core.transfer_info_is_missing( + transfer_info, + constants.SHAPE_KEY_KEY, + transfer_info.id_data.data.shape_keys.key_blocks, + ) + + +def init_shap_keys(scene, obj): + if obj.type != "MESH" or obj.data.shape_keys is None: + return + transfer_core.transfer_info_init( + scene, obj, obj.data.shape_keys.key_blocks, constants.SHAPE_KEY_KEY + ) + + +def transfer_shape_key( + shape_key_name: str, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, +): + # BASIS SHAPE KEY MUST BE PASSED FIRST OTHERWISE THIS WILL ERROR OUT + sk_source = source_obj.data.shape_keys.key_blocks.get(shape_key_name) + + # TODO Restore Shape Key Index Position after Transfer + + # If key is relative to another key that doesn't exist yet + if sk_source.relative_key != sk_source: + relative_key = target_obj.data.shape_keys.key_blocks.get( + sk_source.relative_key.name + ) + if not relative_key: + print( + f"Shape Key '{sk_source.name}' failed to find Relative Key '{sk_source.relative_key.name}' on Object '{target_obj.name}'" + ) + return + + # Remove existing shape keys that match name + if target_obj.data.shape_keys is not None: + old_sk = target_obj.data.shape_keys.key_blocks.get(shape_key_name) + if old_sk: + target_obj.shape_key_remove(old_sk) + + sk_target = target_obj.shape_key_add() + sk_target.name = sk_source.name + sk_target.vertex_group = sk_source.vertex_group + sk_target.relative_key = target_obj.data.shape_keys.key_blocks[ + sk_source.relative_key.name + ] + + bm_source = bmesh.new() + bm_source.from_mesh(source_obj.data) + bm_source.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + + tris_dict = shape_key_tris_per_face(bm_source) + + for i, vert in enumerate(target_obj.data.vertices): + p = vert.co + face = shape_key_closest_face_to_point(bm_source, p, bvh_tree) + + (tri, point) = shape_key_closest_tri_on_face(tris_dict, face, p) + if not tri: + continue + weights = mathutils.interpolate.poly_3d_calc( + [tri[i].vert.co for i in range(3)], point + ) + + vals_weighted = [ + weights[i] + * ( + sk_source.data[tri[i].vert.index].co + - source_obj.data.vertices[tri[i].vert.index].co + ) + for i in range(3) + ] + val = mathutils.Vector(sum(np.array(vals_weighted))) + sk_target.data[i].co = vert.co + val diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 8f227dbb..d8ef72dd 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -30,6 +30,7 @@ def draw_transfer_data( modifiers = [] constraints = [] uv_layers = [] + shape_keys = [] for transfer_info in transfer_data: if transfer_info.type == constants.VERTEX_GROUP_KEY: @@ -43,7 +44,9 @@ def draw_transfer_data( if transfer_info.type == constants.CONSTRAINT_KEY: constraints.append(transfer_info) if transfer_info.type == constants.UV_LAYERS_KEY: - modifiers.append(transfer_info) + uv_layers.append(transfer_info) + if transfer_info.type == constants.SHAPE_KEY_KEY: + shape_keys.append(transfer_info) draw_transfer_data_type(layout, vertex_groups) draw_transfer_data_type(layout, vertex_colors) @@ -51,3 +54,4 @@ def draw_transfer_data( draw_transfer_data_type(layout, material_slots) draw_transfer_data_type(layout, constraints) draw_transfer_data_type(layout, uv_layers) + draw_transfer_data_type(layout, shape_keys) -- 2.30.2 From 33cb36d8c77dac5f0f6c7b8dbb6e46c80c551b5e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 5 Sep 2023 16:53:59 -0400 Subject: [PATCH 159/429] Asset Pipe: WIP Re-Order Shape Keys after apply --- .../transfer_data/transfer_core.py | 1 + .../transfer_data/transfer_functions.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 52efe1cb..b6fde68a 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -136,6 +136,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: ) if transfer_info.type == constants.SHAPE_KEY_KEY: transfer_functions.transfer_shape_key( + context=context, target_obj=target_obj, source_obj=source_obj, shape_key_name=transfer_info.name, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index ad65d998..cc3ade76 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -321,6 +321,21 @@ def transfer_material_slot(material_slot_name, target_obj, source_obj): # SHAPE KEYS + + +def shape_key_set_active(obj, shape_key_name): + for index, shape_key in enumerate(obj.data.shape_keys.key_blocks): + if shape_key.name == shape_key_name: + obj.active_shape_key_index = index + + +def shape_key_active_move(context, obj, shape_key_name, top=True): + move_type = "TOP" if top else "BOTTOM" + shape_key_set_active(obj, shape_key_name) + with context.temp_override(active_object=obj, object=obj): + bpy.ops.object.shape_key_move(type=move_type) + + def shape_key_closest_face_to_point(bm_source, p_target, bvh_tree=None): if not bvh_tree: bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) @@ -383,12 +398,14 @@ def init_shap_keys(scene, obj): def transfer_shape_key( + context: bpy.types.Context, shape_key_name: str, target_obj: bpy.types.Object, source_obj: bpy.types.Object, ): # BASIS SHAPE KEY MUST BE PASSED FIRST OTHERWISE THIS WILL ERROR OUT sk_source = source_obj.data.shape_keys.key_blocks.get(shape_key_name) + print(f"Moving shape key: {shape_key_name}") # TODO Restore Shape Key Index Position after Transfer @@ -415,6 +432,7 @@ def transfer_shape_key( sk_target.relative_key = target_obj.data.shape_keys.key_blocks[ sk_source.relative_key.name ] + shape_key_active_move(context, target_obj, shape_key_name) bm_source = bmesh.new() bm_source.from_mesh(source_obj.data) -- 2.30.2 From bca86f197ff15ac71eef85118641928e500b942f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 5 Sep 2023 16:56:47 -0400 Subject: [PATCH 160/429] Asset Pipe: Add TODOs for debugging context override --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index cc3ade76..ea98eebe 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -40,6 +40,7 @@ def transfer_vertex_group( target_obj.vertex_groups.remove(target_obj.vertex_groups.get(vertex_group_name)) source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] + # TODO Debug crashing / use context.temp_override(object=obj) style override = context.copy() override["selected_editable_objects"] = [target_obj, source_obj] override["active_object"] = source_obj @@ -432,6 +433,8 @@ def transfer_shape_key( sk_target.relative_key = target_obj.data.shape_keys.key_blocks[ sk_source.relative_key.name ] + # TODO DEBUG COTNEXT OVERRIDE ISSUE HERE TO RE-ORDER ITEMS + # TODO Only move relative keys to the top shape_key_active_move(context, target_obj, shape_key_name) bm_source = bmesh.new() -- 2.30.2 From e6b49ff58f10ec40925de247ff050deda0d74654 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 6 Sep 2023 12:39:18 -0400 Subject: [PATCH 161/429] Asset Pipe: Re-Order Shape Keys during Shape Key Clean --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index ea98eebe..51f2882e 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -381,6 +381,10 @@ def shape_keys_clean(obj): if not asset_suffix.get_basename(shape_key.name) in transfer_data.keys(): obj.shape_key_remove(shape_key) + # Move Shape Keys relative to themselves to the top (usually basis key) + if shape_key.relative_key == shape_key: + shape_key_move(bpy.context, obj, shape_key.name) + def shape_key_is_missing(transfer_info): return transfer_core.transfer_info_is_missing( @@ -408,8 +412,6 @@ def transfer_shape_key( sk_source = source_obj.data.shape_keys.key_blocks.get(shape_key_name) print(f"Moving shape key: {shape_key_name}") - # TODO Restore Shape Key Index Position after Transfer - # If key is relative to another key that doesn't exist yet if sk_source.relative_key != sk_source: relative_key = target_obj.data.shape_keys.key_blocks.get( @@ -433,9 +435,6 @@ def transfer_shape_key( sk_target.relative_key = target_obj.data.shape_keys.key_blocks[ sk_source.relative_key.name ] - # TODO DEBUG COTNEXT OVERRIDE ISSUE HERE TO RE-ORDER ITEMS - # TODO Only move relative keys to the top - shape_key_active_move(context, target_obj, shape_key_name) bm_source = bmesh.new() bm_source.from_mesh(source_obj.data) -- 2.30.2 From 5bf4edbdca6e783790d2197e48a84f93399f820a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 6 Sep 2023 12:40:18 -0400 Subject: [PATCH 162/429] Asset Pipe: FIx Shape Key Context Override --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 51f2882e..1330b3f5 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -330,10 +330,10 @@ def shape_key_set_active(obj, shape_key_name): obj.active_shape_key_index = index -def shape_key_active_move(context, obj, shape_key_name, top=True): +def shape_key_move(context, obj, shape_key_name, top=True): move_type = "TOP" if top else "BOTTOM" shape_key_set_active(obj, shape_key_name) - with context.temp_override(active_object=obj, object=obj): + with context.temp_override(object=obj): bpy.ops.object.shape_key_move(type=move_type) -- 2.30.2 From 359867158885a7314f52d472301b72eb4e9fab99 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 6 Sep 2023 12:43:32 -0400 Subject: [PATCH 163/429] Asset Pipe: FIx Shape Key Re-Order --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 1330b3f5..7172d9f2 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -378,13 +378,13 @@ def shape_keys_clean(obj): return transfer_data = obj.transfer_data_ownership for shape_key in obj.data.shape_keys.key_blocks: - if not asset_suffix.get_basename(shape_key.name) in transfer_data.keys(): - obj.shape_key_remove(shape_key) - # Move Shape Keys relative to themselves to the top (usually basis key) if shape_key.relative_key == shape_key: shape_key_move(bpy.context, obj, shape_key.name) + if not asset_suffix.get_basename(shape_key.name) in transfer_data.keys(): + obj.shape_key_remove(shape_key) + def shape_key_is_missing(transfer_info): return transfer_core.transfer_info_is_missing( -- 2.30.2 From c235022a0dc41bb0143a9036d165a781bad78927 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 6 Sep 2023 16:56:38 -0400 Subject: [PATCH 164/429] Asset Pipe: Add init, clean & missing functions for Attribute Transfer Data --- .../addons/asset_pipeline_2/constants.py | 2 ++ .../transfer_data/transfer_core.py | 3 ++ .../transfer_data/transfer_functions.py | 32 +++++++++++++++++++ .../transfer_data/transfer_ui.py | 4 +++ 4 files changed, 41 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 33198f43..cd3bc593 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -20,6 +20,7 @@ TRANSFER_DATA_TYPES = [ ("MATERIAL", "Material Slot", ""), ("GROUP_UVS", "UV Maps", ""), ("SHAPEKEY_DATA", "Shape Key", ""), + ("EVENT_A", "Attribute", ""), ] TRANSFER_DATA_KEYS = [transfer_data[0] for transfer_data in TRANSFER_DATA_TYPES] @@ -31,6 +32,7 @@ CONSTRAINT_KEY = TRANSFER_DATA_KEYS[4] MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[5] UV_LAYERS_KEY = TRANSFER_DATA_KEYS[6] SHAPE_KEY_KEY = TRANSFER_DATA_KEYS[7] +ATTRIBUTE_KEY = TRANSFER_DATA_KEYS[8] PUBLISH_TYPES = [ ( diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index b6fde68a..331a6d61 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -32,6 +32,7 @@ def transfer_data_clean(obj): transfer_functions.constraints_clean(obj) transfer_functions.material_slot_clean(obj) transfer_functions.shape_keys_clean(obj) + transfer_functions.attribute_clean(obj) def transfer_data_is_missing(transfer_data_item) -> bool: @@ -50,6 +51,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: or transfer_functions.constraint_is_missing(transfer_data_item) or transfer_functions.uv_layer_is_missing(transfer_data_item) or transfer_functions.shape_key_is_missing(transfer_data_item) + or transfer_functions.attribute_is_missing(transfer_data_item) ) @@ -71,6 +73,7 @@ def init_transfer_data( transfer_functions.init_vertex_colors(scene, obj) transfer_functions.init_uv_layers(scene, obj) transfer_functions.init_shap_keys(scene, obj) + transfer_functions.init_attributes(scene, obj) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 7172d9f2..692f2413 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -465,3 +465,35 @@ def transfer_shape_key( ] val = mathutils.Vector(sum(np.array(vals_weighted))) sk_target.data[i].co = vert.co + val + + +# ATTRIBUTE +def attribute_clean(obj): + transfer_core.transfer_info_clean(obj, obj.data.attributes) + + +def attribute_is_missing(transfer_info): + return transfer_core.transfer_info_is_missing( + transfer_info, constants.ATTRIBUTE_KEY, transfer_info.id_data.data.attributes + ) + + +def init_attributes(scene, obj): + transfer_data = obj.transfer_data_ownership + task_layer_name = scene.asset_pipeline.task_layer_name + type_key = constants.ATTRIBUTE_KEY + list = obj.data.attributes + for item in list: + if not item.is_required: + continue + # Only add new ownership transfer_info if vertex group doesn't have an owner + matches = transfer_core.check_transfer_data_entry( + transfer_data, item.name, type_key + ) + if len(matches) == 0: + scene.asset_pipeline.add_temp_trasnfer_data( + name=item.name, + owner=task_layer_name, + type=type_key, + obj=obj, + ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index d8ef72dd..e4510fdd 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -31,6 +31,7 @@ def draw_transfer_data( constraints = [] uv_layers = [] shape_keys = [] + attributes = [] for transfer_info in transfer_data: if transfer_info.type == constants.VERTEX_GROUP_KEY: @@ -47,6 +48,8 @@ def draw_transfer_data( uv_layers.append(transfer_info) if transfer_info.type == constants.SHAPE_KEY_KEY: shape_keys.append(transfer_info) + if transfer_info.type == constants.ATTRIBUTE_KEY: + attributes.append(transfer_info) draw_transfer_data_type(layout, vertex_groups) draw_transfer_data_type(layout, vertex_colors) @@ -55,3 +58,4 @@ def draw_transfer_data( draw_transfer_data_type(layout, constraints) draw_transfer_data_type(layout, uv_layers) draw_transfer_data_type(layout, shape_keys) + draw_transfer_data_type(layout, attributes) -- 2.30.2 From 7fb7282c0551d40880f6f38b6d09b3ce924c81c1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 09:47:15 -0400 Subject: [PATCH 165/429] Asset Pipe: Fix Init Attributes --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 692f2413..9d919704 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -479,12 +479,14 @@ def attribute_is_missing(transfer_info): def init_attributes(scene, obj): + if obj.type != "MESH": + return transfer_data = obj.transfer_data_ownership task_layer_name = scene.asset_pipeline.task_layer_name type_key = constants.ATTRIBUTE_KEY list = obj.data.attributes for item in list: - if not item.is_required: + if item.is_required or item.is_internal: continue # Only add new ownership transfer_info if vertex group doesn't have an owner matches = transfer_core.check_transfer_data_entry( -- 2.30.2 From 79915cf19c31a61a575650844832a05ee1f1db92 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 11:21:31 -0400 Subject: [PATCH 166/429] Asset Pipe: Fix Override in Material Functions --- .../transfer_data/transfer_functions.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 9d919704..8b76a2b9 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -263,12 +263,14 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT def material_slot_clean(obj): # Material slots cannot use generic transfer_info_clean() function + context = bpy.context # TODO pass context here transfer_data = obj.transfer_data_ownership for mat_slot in obj.material_slots: if not asset_suffix.get_basename(mat_slot.name) in transfer_data.keys(): index = obj.material_slots.keys().index(mat_slot.name) obj.active_material_index = index - bpy.ops.object.material_slot_remove({"object": obj}) + with context.temp_override(object=obj): + bpy.ops.object.material_slot_remove() def material_slot_is_missing(transfer_info): @@ -285,11 +287,13 @@ def init_material_slots(scene, obj): def transfer_material_slot(material_slot_name, target_obj, source_obj): # Delete existing material slot if exists + context = bpy.context # TODO pass context here for idx in range(len(source_obj.material_slots)): slot = source_obj.material_slots[idx] if asset_suffix.get_basename(slot.material.name) == material_slot_name: target_obj.active_material_index = idx - bpy.ops.object.material_slot_remove({"object": target_obj}) + with context.temp_override(object=target_obj): + bpy.ops.object.material_slot_remove() # Transfer material slots @@ -297,7 +301,8 @@ def transfer_material_slot(material_slot_name, target_obj, source_obj): if idx >= len(target_obj.material_slots): slot = source_obj.material_slots[idx] if asset_suffix.get_basename(slot.material.name) == material_slot_name: - bpy.ops.object.material_slot_add({"object": target_obj}) + with context.temp_override(object=target_obj): + bpy.ops.object.material_slot_add() target_obj.material_slots[idx].link = source_obj.material_slots[ idx ].link -- 2.30.2 From fbd4c632c1952387957fdab0d8d1bc72d4833363 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 11:22:07 -0400 Subject: [PATCH 167/429] Asset Pipe: Fix Shape Key Missing --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 8b76a2b9..f10182c9 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -392,10 +392,13 @@ def shape_keys_clean(obj): def shape_key_is_missing(transfer_info): + obj = transfer_info.id_data + if obj.type != "MESH" or obj.data.shape_keys is None: + return return transfer_core.transfer_info_is_missing( transfer_info, constants.SHAPE_KEY_KEY, - transfer_info.id_data.data.shape_keys.key_blocks, + obj.data.shape_keys.key_blocks, ) -- 2.30.2 From 664dd9221e1e6d9e5df185d5fdd25ba8b36337a7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 12:26:20 -0400 Subject: [PATCH 168/429] Asset Pipe: Fix Override in Constraint Transfer --- .../transfer_data/transfer_functions.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index f10182c9..82a08e9f 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -229,6 +229,9 @@ def init_constraints(scene, obj): def transfer_constraint(constraint_name, target_obj, source_obj): + # TODO DEBUG WHY TARGETS DISAPPEAR + context = bpy.context # TODO PASS CONTEXT + # remove old and sync existing modifiers old_mod = target_obj.constraints.get(constraint_name) if old_mod: @@ -248,9 +251,10 @@ def transfer_constraint(constraint_name, target_obj, source_obj): ): if target_constraint.name == name_prev: idx = target_mod_i + 1 - bpy.ops.constraint.move_to_index( - {'object': target_obj}, constraint=constraint_new.name, index=idx - ) + with context.temp_override(object=target_obj): + bpy.ops.constraint.move_to_index( + constraint=constraint_new.name, index=idx + ) constraint_target = target_obj.constraints.get(constraint.name) props = [ p.identifier for p in constraint.bl_rna.properties if not p.is_readonly -- 2.30.2 From f24fcf4f66fec7e06c2b2a2d65c0070bacd463ab Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 12:26:48 -0400 Subject: [PATCH 169/429] Asset Pipe: Fix Override in Modifier Transfer --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 82a08e9f..7a542445 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -159,6 +159,7 @@ def init_modifiers(scene, obj): def transfer_modifier(modifier_name, target_obj, source_obj): # remove old and sync existing modifiers + context = bpy.context # TODO PASS CONTEXT old_mod = target_obj.modifiers.get(modifier_name) if old_mod: target_obj.modifiers.remove(old_mod) @@ -174,9 +175,8 @@ def transfer_modifier(modifier_name, target_obj, source_obj): for target_mod_i, target_mod in enumerate(target_obj.modifiers): if target_mod.name == name_prev: idx = target_mod_i + 1 - bpy.ops.object.modifier_move_to_index( - {'object': target_obj}, modifier=mod_new.name, index=idx - ) + with context.temp_override(object=target_obj): + bpy.ops.object.modifier_move_to_index(modifier=mod_new.name, index=idx) mod_target = target_obj.modifiers.get(mod.name) props = [p.identifier for p in mod.bl_rna.properties if not p.is_readonly] for prop in props: -- 2.30.2 From b0ca820fe53069174c7ef8f06aeb99c15baa3831 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 12:27:40 -0400 Subject: [PATCH 170/429] Asset Pipe: Fix init, clean & missing functions for Attribute Transfer Data --- .../transfer_data/transfer_functions.py | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 7a542445..5fd1844c 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -480,14 +480,33 @@ def transfer_shape_key( # ATTRIBUTE +def attributes_get_editable(attributes): + return [item for item in attributes if not (item.is_required or item.is_internal)] + + def attribute_clean(obj): - transfer_core.transfer_info_clean(obj, obj.data.attributes) + # TODO debug clean function + if obj.type != "MESH": + return + attributes = attributes_get_editable(obj.data.attributes) + transfer_data = obj.transfer_data_ownership + for item in attributes: + if not asset_suffix.get_basename(item.name) in transfer_data.keys(): + print(f"Cleaning attribute {item.name}") + obj.data.attributes.remove(item) def attribute_is_missing(transfer_info): - return transfer_core.transfer_info_is_missing( - transfer_info, constants.ATTRIBUTE_KEY, transfer_info.id_data.data.attributes - ) + obj = transfer_info.id_data + if obj.type != "MESH": + return + attributes = attributes_get_editable(obj.data.attributes) + attribute_names = [attribute.name for attribute in attributes] + if ( + transfer_info.type == constants.ATTRIBUTE_KEY + and not transfer_info["name"] in attribute_names + ): + return True def init_attributes(scene, obj): @@ -496,17 +515,16 @@ def init_attributes(scene, obj): transfer_data = obj.transfer_data_ownership task_layer_name = scene.asset_pipeline.task_layer_name type_key = constants.ATTRIBUTE_KEY - list = obj.data.attributes - for item in list: - if item.is_required or item.is_internal: + for atttribute in attributes_get_editable(obj.data.attributes): + if atttribute.is_required or atttribute.is_internal: continue # Only add new ownership transfer_info if vertex group doesn't have an owner matches = transfer_core.check_transfer_data_entry( - transfer_data, item.name, type_key + transfer_data, atttribute.name, type_key ) if len(matches) == 0: scene.asset_pipeline.add_temp_trasnfer_data( - name=item.name, + name=atttribute.name, owner=task_layer_name, type=type_key, obj=obj, -- 2.30.2 From ccdf09eb9e5791b1696df06078d5b795fd26eb47 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 12:28:17 -0400 Subject: [PATCH 171/429] Asset Pipe: Add Attribute to Transfer Data --- .../transfer_data/transfer_core.py | 6 ++++ .../transfer_data/transfer_functions.py | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 331a6d61..09be020f 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -144,6 +144,12 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: source_obj=source_obj, shape_key_name=transfer_info.name, ) + if transfer_info.type == constants.ATTRIBUTE_KEY: + transfer_functions.transfer_attribute( + target_obj=target_obj, + source_obj=source_obj, + attribute_name=transfer_info.name, + ) copy_transfer_data_ownership( transfer_data_item=transfer_info, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 5fd1844c..ec0e7d8d 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -529,3 +529,33 @@ def init_attributes(scene, obj): type=type_key, obj=obj, ) + + +def transfer_attribute( + attribute_name: str, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, +): + source_attributes = source_obj.data.attributes + target_attributes = target_obj.data.attributes + source_attribute = source_attributes.get(attribute_name) + + target_attribute = target_attributes.get(attribute_name) + if target_attribute: + target_attributes.remove(target_attribute) + + target_attribute = target_attributes.new( + name=attribute_name, + type=source_attribute.data_type, + domain=source_attribute.domain, + ) + + for source_data_item in source_attribute.data.items(): + index = source_data_item[0] + source_data = source_data_item[1] + keys = set(source_data.bl_rna.properties.keys()) - set( + bpy.types.Attribute.bl_rna.properties.keys() + ) + for key in list(keys): + target_data = target_attribute.data[index] + setattr(target_data, key, getattr(source_data, key)) -- 2.30.2 From 56fd20df088f24e69cee5029446fd36d2ffad634 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 7 Sep 2023 13:00:25 -0400 Subject: [PATCH 172/429] Asset Pipe: Fix Bug, Armature Constrain Targets Disappear --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index ec0e7d8d..95fb35df 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -229,7 +229,6 @@ def init_constraints(scene, obj): def transfer_constraint(constraint_name, target_obj, source_obj): - # TODO DEBUG WHY TARGETS DISAPPEAR context = bpy.context # TODO PASS CONTEXT # remove old and sync existing modifiers @@ -263,6 +262,13 @@ def transfer_constraint(constraint_name, target_obj, source_obj): value = getattr(constraint, prop) setattr(constraint_target, prop, value) + # HACK to cover edge case of armature constraints + if constraint.type == "ARMATURE": + for target_item in constraint.targets: + new_target = constraint_new.targets.new() + new_target.target = target_item.target + new_target.subtarget = target_item.subtarget + # MATERIAL SLOT def material_slot_clean(obj): -- 2.30.2 From f4d377f78f0bbac7c884847239e064feda1b2f4c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 8 Sep 2023 10:22:03 -0400 Subject: [PATCH 173/429] Asset Pipe: Fix Bug where Transfer Info Disappears --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 4 ++-- .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index cd562f7a..4ea37835 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -144,7 +144,7 @@ class AssetTransferMapping: ) name = transfer_info.name + '_' + obj.name transfer_data_map[name] = { - 'transfer_info': temp_info, + 'transfer_info': temp_info.name, # TODO avoid name collisions 'source_obj': obj, 'target_obj': target_obj, } @@ -163,7 +163,7 @@ class AssetTransferMapping: ) name = transfer_info.name + '_' + obj.name transfer_data_map[name] = { - 'transfer_info': temp_info, + 'transfer_info': temp_info.name, # TODO avoid name collisions 'source_obj': obj, 'target_obj': target_obj, } diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 09be020f..94ec4a12 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -89,8 +89,9 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: """ for name in transfer_data_map: + temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data transfer_data = transfer_data_map[name] - transfer_info = transfer_data.get('transfer_info') + transfer_info = temp_transfer_data.get(transfer_data.get('transfer_info')) target_obj = transfer_data.get('target_obj') source_obj = transfer_data.get('source_obj') if target_obj is None: -- 2.30.2 From 99ccbe27dbc33992edec64b6aa9dd5356de427b0 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 8 Sep 2023 11:28:10 -0400 Subject: [PATCH 174/429] Asset Pipe: Ignore Sharp_Face in attributes --- .../transfer_data/transfer_functions.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 95fb35df..3d27edb9 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -487,7 +487,17 @@ def transfer_shape_key( # ATTRIBUTE def attributes_get_editable(attributes): - return [item for item in attributes if not (item.is_required or item.is_internal)] + # TODO replace 'position' HACK with is_required once https://projects.blender.org/blender/blender/pulls/111468 is merged + return [ + item + for item in attributes + if not ( + item.is_internal + or item.name == 'position' + or item.name == 'material_index' + or item.name == 'sharp_face' + ) + ] def attribute_clean(obj): -- 2.30.2 From e6c38a02cd01a218599e000d3dca5bcc21a41f67 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 8 Sep 2023 11:28:31 -0400 Subject: [PATCH 175/429] Asset Pipe: Remove No-Op Code --- .../addons/asset_pipeline_2/transfer_data/transfer_functions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 3d27edb9..f677ddc2 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -532,8 +532,6 @@ def init_attributes(scene, obj): task_layer_name = scene.asset_pipeline.task_layer_name type_key = constants.ATTRIBUTE_KEY for atttribute in attributes_get_editable(obj.data.attributes): - if atttribute.is_required or atttribute.is_internal: - continue # Only add new ownership transfer_info if vertex group doesn't have an owner matches = transfer_core.check_transfer_data_entry( transfer_data, atttribute.name, type_key -- 2.30.2 From 195d85e166141876fd0e5bf0d2437bd74b85a6b1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 8 Sep 2023 11:29:53 -0400 Subject: [PATCH 176/429] Asset Pipe: Add Print Statement to Attribute Transfer --- .../addons/asset_pipeline_2/transfer_data/transfer_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index f677ddc2..bb8f74c3 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -563,7 +563,7 @@ def transfer_attribute( type=source_attribute.data_type, domain=source_attribute.domain, ) - + print(f"Transferring Attribute{attribute_name}") for source_data_item in source_attribute.data.items(): index = source_data_item[0] source_data = source_data_item[1] -- 2.30.2 From 09aa05fa0059bee5f1df21fd7f6a62ccccf73644 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 8 Sep 2023 11:30:18 -0400 Subject: [PATCH 177/429] Asset Pipe: Comment Out UV Transfer Data as it Conflicts with Attributes --- .../transfer_data/transfer_core.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 94ec4a12..0baffcfc 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -27,7 +27,7 @@ def copy_transfer_data_ownership( def transfer_data_clean(obj): transfer_functions.vertex_groups_clean(obj) transfer_functions.vertex_colors_clean(obj) - transfer_functions.uv_layer_clean(obj) + # transfer_functions.uv_layer_clean(obj) transfer_functions.modifiers_clean(obj) transfer_functions.constraints_clean(obj) transfer_functions.material_slot_clean(obj) @@ -49,7 +49,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: or transfer_functions.modifier_is_missing(transfer_data_item) or transfer_functions.material_slot_is_missing(transfer_data_item) or transfer_functions.constraint_is_missing(transfer_data_item) - or transfer_functions.uv_layer_is_missing(transfer_data_item) + # or transfer_functions.uv_layer_is_missing(transfer_data_item) or transfer_functions.shape_key_is_missing(transfer_data_item) or transfer_functions.attribute_is_missing(transfer_data_item) ) @@ -71,7 +71,7 @@ def init_transfer_data( transfer_functions.init_modifiers(scene, obj) transfer_functions.init_constraints(scene, obj) transfer_functions.init_vertex_colors(scene, obj) - transfer_functions.init_uv_layers(scene, obj) + # transfer_functions.init_uv_layers(scene, obj) transfer_functions.init_shap_keys(scene, obj) transfer_functions.init_attributes(scene, obj) @@ -132,12 +132,12 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: target_obj=target_obj, source_obj=source_obj, ) - if transfer_info.type == constants.UV_LAYERS_KEY: - transfer_functions.transfer_uv_layer( - target_obj=target_obj, - source_obj=source_obj, - uv_name=transfer_info.name, - ) + # if transfer_info.type == constants.UV_LAYERS_KEY: + # transfer_functions.transfer_uv_layer( + # target_obj=target_obj, + # source_obj=source_obj, + # uv_name=transfer_info.name, + # ) if transfer_info.type == constants.SHAPE_KEY_KEY: transfer_functions.transfer_shape_key( context=context, -- 2.30.2 From aaf834cd55880857ab6921b06007588d02b8b31d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 8 Sep 2023 12:02:24 -0400 Subject: [PATCH 178/429] Asset Pipe Improve Transfer Data Cleaning --- .../transfer_data/transfer_core.py | 14 ++++++-- .../transfer_data/transfer_functions.py | 32 ++++++++++++------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 0baffcfc..4939e967 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -200,10 +200,18 @@ def transfer_data_add_entry( return transfer_info -def transfer_info_clean(obj, list): - transfer_data = obj.transfer_data_ownership +def get_transfer_data_by_name(transfer_data, td_type): + return [ + transfer_info.name + for transfer_info in transfer_data + if transfer_info.type == td_type + ] + + +def transfer_info_clean(obj, list, td_type): + transfer_data_list = get_transfer_data_by_name(obj.transfer_data_ownership, td_type) for item in list: - if not asset_suffix.get_basename(item.name) in transfer_data.keys(): + if not asset_suffix.get_basename(item.name) in transfer_data_list: list.remove(item) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index bb8f74c3..ce8280c0 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -12,7 +12,9 @@ import numpy as np # VERTEX GROUPS def vertex_groups_clean(obj): - transfer_core.transfer_info_clean(obj, obj.vertex_groups) + transfer_core.transfer_info_clean( + obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY + ) def vertex_group_is_missing(transfer_info): @@ -60,7 +62,9 @@ def transfer_vertex_group( def vertex_colors_clean(obj): if not obj.type == "MESH": return - transfer_core.transfer_info_clean(obj, obj.data.vertex_colors) + transfer_core.transfer_info_clean( + obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY + ) def vertex_color_is_missing(transfer_info): @@ -103,7 +107,7 @@ def transfer_vertex_color( def uv_layer_clean(obj): if not obj.type == "MESH": return - transfer_core.transfer_info_clean(obj, obj.data.uv_layers) + transfer_core.transfer_info_clean(obj, obj.data.uv_layers, constants.UV_LAYERS_KEY) def uv_layer_is_missing(transfer_info): @@ -144,7 +148,7 @@ def transfer_uv_layer(source_obj, target_obj, uv_name): # MODIFIERS def modifiers_clean(obj): - transfer_core.transfer_info_clean(obj, obj.modifiers) + transfer_core.transfer_info_clean(obj, obj.modifiers, constants.MODIFIER_KEY) def modifier_is_missing(transfer_info): @@ -213,7 +217,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): # CONSTRAINTS def constraints_clean(obj): - transfer_core.transfer_info_clean(obj, obj.constraints) + transfer_core.transfer_info_clean(obj, obj.constraints, constants.CONSTRAINT_KEY) def constraint_is_missing(transfer_info): @@ -274,9 +278,11 @@ def transfer_constraint(constraint_name, target_obj, source_obj): def material_slot_clean(obj): # Material slots cannot use generic transfer_info_clean() function context = bpy.context # TODO pass context here - transfer_data = obj.transfer_data_ownership + transfer_data_list = transfer_core.get_transfer_data_by_name( + obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY + ) for mat_slot in obj.material_slots: - if not asset_suffix.get_basename(mat_slot.name) in transfer_data.keys(): + if not asset_suffix.get_basename(mat_slot.name) in transfer_data_list: index = obj.material_slots.keys().index(mat_slot.name) obj.active_material_index = index with context.temp_override(object=obj): @@ -391,13 +397,15 @@ def shape_key_closest_tri_on_face(tris_dict, face, p): def shape_keys_clean(obj): if obj.type != "MESH" or obj.data.shape_keys is None: return - transfer_data = obj.transfer_data_ownership + transfer_data_list = transfer_core.get_transfer_data_by_name( + obj.transfer_data_ownership, constants.SHAPE_KEY_KEY + ) for shape_key in obj.data.shape_keys.key_blocks: # Move Shape Keys relative to themselves to the top (usually basis key) if shape_key.relative_key == shape_key: shape_key_move(bpy.context, obj, shape_key.name) - if not asset_suffix.get_basename(shape_key.name) in transfer_data.keys(): + if not asset_suffix.get_basename(shape_key.name) in transfer_data_list: obj.shape_key_remove(shape_key) @@ -505,9 +513,11 @@ def attribute_clean(obj): if obj.type != "MESH": return attributes = attributes_get_editable(obj.data.attributes) - transfer_data = obj.transfer_data_ownership + transfer_data_list = transfer_core.get_transfer_data_by_name( + obj.transfer_data_ownership, constants.ATTRIBUTE_KEY + ) for item in attributes: - if not asset_suffix.get_basename(item.name) in transfer_data.keys(): + if not asset_suffix.get_basename(item.name) in transfer_data_list: print(f"Cleaning attribute {item.name}") obj.data.attributes.remove(item) -- 2.30.2 From 7f938d1a4d1ef837e569c9ed1ba8dfd38026d5dd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 11:13:33 -0400 Subject: [PATCH 179/429] Asset Pipe: Clear Old TODO --- .../addons/asset_pipeline_2/transfer_data/transfer_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index ce8280c0..c8246910 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -509,7 +509,6 @@ def attributes_get_editable(attributes): def attribute_clean(obj): - # TODO debug clean function if obj.type != "MESH": return attributes = attributes_get_editable(obj.data.attributes) -- 2.30.2 From 6b6c5d669671aa8a574cc949a34c2d5365fd531c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 11:21:26 -0400 Subject: [PATCH 180/429] Asset Pipe: Pass Context via Function --- .../addons/asset_pipeline_2/asset_mapping.py | 6 +++--- scripts-blender/addons/asset_pipeline_2/core.py | 6 ++++-- .../transfer_data/transfer_functions.py | 14 +++++++------- scripts-blender/addons/asset_pipeline_2/util.py | 13 +++++++++++++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 4ea37835..82a68b33 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -1,8 +1,7 @@ import bpy from typing import Dict, Set -from . import asset_suffix -from . import constants +from . import asset_suffix, constants, util from .transfer_data import transfer_core @@ -126,8 +125,9 @@ class AssetTransferMapping: return coll_map def _gen_transfer_data_map(self): + context = util.get_stored_context() transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - temp_transfer_data = bpy.context.scene.asset_pipeline.temp_transfer_data + temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data temp_transfer_data.clear() for source_obj in self.object_map: target_obj = self.object_map[source_obj] diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 37b93aa3..d496de77 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Dict from .asset_mapping import AssetTransferMapping -from . import constants +from . import constants, util def ownership_transfer_data_cleanup( @@ -115,6 +115,7 @@ def merge_task_layer( local_tls: (list[str]): list of task layers that are local to the current file external_file (Path): external file to pull data into the current file from """ + util.set_stored_context(context=context) local_col = context.scene.asset_pipeline.asset_collection if not local_col: return "Unable to find Asset Collection" @@ -299,7 +300,8 @@ def import_data_from_lib( def get_task_layer_name_from_file() -> str: """Returns task layer name found task's file name""" - file_name = bpy.path.basename(bpy.context.blend_data.filepath) + context = util.get_stored_context() + file_name = bpy.path.basename(context.blend_data.filepath) task_layer_name = file_name.split(".")[-2] if task_layer_name in constants.TASK_LAYER_KEYS: return task_layer_name diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index c8246910..015683e1 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,7 +1,7 @@ import bpy from bpy import context from . import transfer_core -from .. import asset_suffix, constants +from .. import asset_suffix, constants, util import mathutils import bmesh import numpy as np @@ -163,7 +163,7 @@ def init_modifiers(scene, obj): def transfer_modifier(modifier_name, target_obj, source_obj): # remove old and sync existing modifiers - context = bpy.context # TODO PASS CONTEXT + context = util.get_stored_context() old_mod = target_obj.modifiers.get(modifier_name) if old_mod: target_obj.modifiers.remove(old_mod) @@ -233,8 +233,7 @@ def init_constraints(scene, obj): def transfer_constraint(constraint_name, target_obj, source_obj): - context = bpy.context # TODO PASS CONTEXT - + context = util.get_stored_context() # remove old and sync existing modifiers old_mod = target_obj.constraints.get(constraint_name) if old_mod: @@ -277,7 +276,7 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT def material_slot_clean(obj): # Material slots cannot use generic transfer_info_clean() function - context = bpy.context # TODO pass context here + context = util.get_stored_context() transfer_data_list = transfer_core.get_transfer_data_by_name( obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY ) @@ -303,7 +302,7 @@ def init_material_slots(scene, obj): def transfer_material_slot(material_slot_name, target_obj, source_obj): # Delete existing material slot if exists - context = bpy.context # TODO pass context here + context = util.get_stored_context() for idx in range(len(source_obj.material_slots)): slot = source_obj.material_slots[idx] if asset_suffix.get_basename(slot.material.name) == material_slot_name: @@ -395,6 +394,7 @@ def shape_key_closest_tri_on_face(tris_dict, face, p): def shape_keys_clean(obj): + context = util.get_stored_context() if obj.type != "MESH" or obj.data.shape_keys is None: return transfer_data_list = transfer_core.get_transfer_data_by_name( @@ -403,7 +403,7 @@ def shape_keys_clean(obj): for shape_key in obj.data.shape_keys.key_blocks: # Move Shape Keys relative to themselves to the top (usually basis key) if shape_key.relative_key == shape_key: - shape_key_move(bpy.context, obj, shape_key.name) + shape_key_move(context, obj, shape_key.name) if not asset_suffix.get_basename(shape_key.name) in transfer_data_list: obj.shape_key_remove(shape_key) diff --git a/scripts-blender/addons/asset_pipeline_2/util.py b/scripts-blender/addons/asset_pipeline_2/util.py index 26f590d5..54d02c70 100644 --- a/scripts-blender/addons/asset_pipeline_2/util.py +++ b/scripts-blender/addons/asset_pipeline_2/util.py @@ -99,3 +99,16 @@ def traverse_collection_tree( yield collection for child in collection.children: yield from traverse_collection_tree(child) + + +stored_context = None + + +def set_stored_context(context): + global stored_context + stored_context = context + + +def get_stored_context(): + global stored_context + return stored_context -- 2.30.2 From 8afe6679fb1b882c41b33b18b9472e26f17c98f9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 11:22:33 -0400 Subject: [PATCH 181/429] Asset Pipe: Move Depreciated Setting to main UI Panel --- scripts-blender/addons/asset_pipeline_2/ui.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 1dca9b2e..143d98b2 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -35,6 +35,12 @@ class ASSETPIPE_sync(bpy.types.Panel): "assetpipe.sync_with_publish", text="Pull from Publish", icon="TRIA_DOWN" ).pull = True + if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE": + asset_pipe = context.scene.asset_pipeline + box = layout.box() + box.label(text="Published File Settings") + box.prop(asset_pipe, "is_depreciated") + class ASSETPIPE_ownership_inspector(bpy.types.Panel): bl_space_type = 'VIEW_3D' @@ -60,13 +66,6 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): layout.label(text=f"{obj.name}: '{owner}'", icon="OBJECT_DATA") transfer_ui.draw_transfer_data(transfer_data, layout) - task_layer_name = core.get_task_layer_name_from_file() - if task_layer_name not in constants.TASK_LAYER_KEYS: - asset_pipe = context.scene.asset_pipeline - box = layout.box() - box.label(text="Published File Settings") - box.prop(asset_pipe, "is_depreciated") - classes = ( ASSETPIPE_sync, -- 2.30.2 From 6ef34740183d721f309329b8c07fedfbf1e4b803 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 11:26:54 -0400 Subject: [PATCH 182/429] Asset Pipe: Replace Task Layer Name Function with Property --- scripts-blender/addons/asset_pipeline_2/core.py | 9 --------- scripts-blender/addons/asset_pipeline_2/ops.py | 8 ++++---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index d496de77..a3d6fdb8 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -298,15 +298,6 @@ def import_data_from_lib( return eval(f"bpy.data.{data_category}['{data_name}']") -def get_task_layer_name_from_file() -> str: - """Returns task layer name found task's file name""" - context = util.get_stored_context() - file_name = bpy.path.basename(context.blend_data.filepath) - task_layer_name = file_name.split(".")[-2] - if task_layer_name in constants.TASK_LAYER_KEYS: - return task_layer_name - - def get_dict_tuple_item(dict: dict, key: str) -> tuple: """For a dict of tuples, returns a dict item based on it's key""" for item in dict: diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index f36776f2..570ef55d 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -119,8 +119,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not local_col: self.report({'ERROR'}, "Top level collection could not be found") return {'CANCELLED'} - task_layer_name = core.get_task_layer_name_from_file() - if not task_layer_name: + task_layer_name = context.scene.asset_pipeline.task_layer_name + if task_layer_name == "NONE": self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} @@ -168,8 +168,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data core.ownership_set(temp_transfer_data) current_file = Path(bpy.data.filepath) - task_layer_name = core.get_task_layer_name_from_file() - if not task_layer_name: + task_layer_name = context.scene.asset_pipeline.task_layer_name + if task_layer_name == "NONE": self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} -- 2.30.2 From ae48b115bc3bf2b8059a12ec78706e0c7ad1d926 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 11:54:46 -0400 Subject: [PATCH 183/429] Asset Pipe: Comment Out Vertex Color Transfer --- .../transfer_data/transfer_core.py | 19 +++++++++++-------- .../transfer_data/transfer_ui.py | 16 ++++++++-------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 4939e967..1fc24ba2 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -26,7 +26,7 @@ def copy_transfer_data_ownership( def transfer_data_clean(obj): transfer_functions.vertex_groups_clean(obj) - transfer_functions.vertex_colors_clean(obj) + # transfer_functions.vertex_colors_clean(obj) # transfer_functions.uv_layer_clean(obj) transfer_functions.modifiers_clean(obj) transfer_functions.constraints_clean(obj) @@ -49,6 +49,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: or transfer_functions.modifier_is_missing(transfer_data_item) or transfer_functions.material_slot_is_missing(transfer_data_item) or transfer_functions.constraint_is_missing(transfer_data_item) + # or transfer_functions.vertex_color_is_missing(transfer_data_item) # or transfer_functions.uv_layer_is_missing(transfer_data_item) or transfer_functions.shape_key_is_missing(transfer_data_item) or transfer_functions.attribute_is_missing(transfer_data_item) @@ -70,7 +71,7 @@ def init_transfer_data( transfer_functions.init_material_slots(scene, obj) transfer_functions.init_modifiers(scene, obj) transfer_functions.init_constraints(scene, obj) - transfer_functions.init_vertex_colors(scene, obj) + # transfer_functions.init_vertex_colors(scene, obj) # transfer_functions.init_uv_layers(scene, obj) transfer_functions.init_shap_keys(scene, obj) transfer_functions.init_attributes(scene, obj) @@ -97,6 +98,8 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: if target_obj is None: print(f"Failed to Transfer data for {transfer_info.id_data.name}") continue + if transfer_info is None: + continue if source_obj != target_obj: if transfer_info.type == constants.VERTEX_GROUP_KEY: print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") @@ -106,12 +109,12 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: target_obj=target_obj, source_obj=source_obj, ) - if transfer_info.type == constants.VERTEX_COLOR_KEY: - transfer_functions.transfer_vertex_color( - vertex_color_name=transfer_info.name, - target_obj=target_obj, - source_obj=source_obj, - ) + # if transfer_info.type == constants.VERTEX_COLOR_KEY: + # transfer_functions.transfer_vertex_color( + # vertex_color_name=transfer_info.name, + # target_obj=target_obj, + # source_obj=source_obj, + # ) if transfer_info.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") transfer_functions.transfer_modifier( diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index e4510fdd..4442dd72 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -25,37 +25,37 @@ def draw_transfer_data( ) -> None: """Draw UI List of Transfer Data""" vertex_groups = [] - vertex_colors = [] + # vertex_colors = [] material_slots = [] modifiers = [] constraints = [] - uv_layers = [] + # uv_layers = [] shape_keys = [] attributes = [] for transfer_info in transfer_data: if transfer_info.type == constants.VERTEX_GROUP_KEY: vertex_groups.append(transfer_info) - if transfer_info.type == constants.VERTEX_COLOR_KEY: - vertex_colors.append(transfer_info) + # if transfer_info.type == constants.VERTEX_COLOR_KEY: + # vertex_colors.append(transfer_info) if transfer_info.type == constants.MATERIAL_SLOT_KEY: material_slots.append(transfer_info) if transfer_info.type == constants.MODIFIER_KEY: modifiers.append(transfer_info) if transfer_info.type == constants.CONSTRAINT_KEY: constraints.append(transfer_info) - if transfer_info.type == constants.UV_LAYERS_KEY: - uv_layers.append(transfer_info) + # if transfer_info.type == constants.UV_LAYERS_KEY: + # uv_layers.append(transfer_info) if transfer_info.type == constants.SHAPE_KEY_KEY: shape_keys.append(transfer_info) if transfer_info.type == constants.ATTRIBUTE_KEY: attributes.append(transfer_info) draw_transfer_data_type(layout, vertex_groups) - draw_transfer_data_type(layout, vertex_colors) + # draw_transfer_data_type(layout, vertex_colors) draw_transfer_data_type(layout, modifiers) draw_transfer_data_type(layout, material_slots) draw_transfer_data_type(layout, constraints) - draw_transfer_data_type(layout, uv_layers) + # draw_transfer_data_type(layout, uv_layers) draw_transfer_data_type(layout, shape_keys) draw_transfer_data_type(layout, attributes) -- 2.30.2 From 286c1d8188216891a5f203034d9a06677a6f69ef Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 12:52:23 -0400 Subject: [PATCH 184/429] Asset Pipe: Fix naming of function - Rename `get_transfer_data_by_name` to `get_transfer_data_as_names` --- .../addons/asset_pipeline_2/transfer_data/transfer_core.py | 6 ++++-- .../asset_pipeline_2/transfer_data/transfer_functions.py | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 1fc24ba2..4baebca0 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -203,7 +203,7 @@ def transfer_data_add_entry( return transfer_info -def get_transfer_data_by_name(transfer_data, td_type): +def get_transfer_data_as_names(transfer_data, td_type): return [ transfer_info.name for transfer_info in transfer_data @@ -212,7 +212,9 @@ def get_transfer_data_by_name(transfer_data, td_type): def transfer_info_clean(obj, list, td_type): - transfer_data_list = get_transfer_data_by_name(obj.transfer_data_ownership, td_type) + transfer_data_list = get_transfer_data_as_names( + obj.transfer_data_ownership, td_type + ) for item in list: if not asset_suffix.get_basename(item.name) in transfer_data_list: list.remove(item) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 015683e1..76aa7b6d 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -277,7 +277,7 @@ def transfer_constraint(constraint_name, target_obj, source_obj): def material_slot_clean(obj): # Material slots cannot use generic transfer_info_clean() function context = util.get_stored_context() - transfer_data_list = transfer_core.get_transfer_data_by_name( + transfer_data_list = transfer_core.get_transfer_data_as_names( obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY ) for mat_slot in obj.material_slots: @@ -397,7 +397,7 @@ def shape_keys_clean(obj): context = util.get_stored_context() if obj.type != "MESH" or obj.data.shape_keys is None: return - transfer_data_list = transfer_core.get_transfer_data_by_name( + transfer_data_list = transfer_core.get_transfer_data_as_names( obj.transfer_data_ownership, constants.SHAPE_KEY_KEY ) for shape_key in obj.data.shape_keys.key_blocks: @@ -512,7 +512,7 @@ def attribute_clean(obj): if obj.type != "MESH": return attributes = attributes_get_editable(obj.data.attributes) - transfer_data_list = transfer_core.get_transfer_data_by_name( + transfer_data_list = transfer_core.get_transfer_data_as_names( obj.transfer_data_ownership, constants.ATTRIBUTE_KEY ) for item in attributes: -- 2.30.2 From 12e029b4cfbed368c96b0cd46a9bc174ed749136 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 15:36:24 -0400 Subject: [PATCH 185/429] Asset Pipe: Re-write Vertex Group Transfer --- .../transfer_data/transfer_core.py | 1 - .../transfer_data/transfer_functions.py | 56 +++++++++++++------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 4baebca0..1591e3c5 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -104,7 +104,6 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: if transfer_info.type == constants.VERTEX_GROUP_KEY: print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") transfer_functions.transfer_vertex_group( - context=context, vertex_group_name=transfer_info.name, target_obj=target_obj, source_obj=source_obj, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 76aa7b6d..bb0935c8 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -30,32 +30,56 @@ def init_vertex_groups(scene, obj): def transfer_vertex_group( - context, vertex_group_name: str, target_obj: bpy.types.Object, source_obj: bpy.types.Object, ): + # Adapted from https://stackoverflow.com/questions/40969164/how-to-add-vertex-groups-from-one-object-to-another-without-replacing-them if target_obj == source_obj: return + source_data = source_obj.data + target_data = target_obj.data + + # sanity check: do source and target have the same amount of verts? + if len(source_data.vertices) != len(target_data.vertices): + print( + 'ERROR: objects {} and {} have different vertex counts.'.format( + source_obj.name, target_obj.name + ) + ) + return + + data = {} + for v in source_data.vertices: + vg = v.groups + vertex_index = v.index + if len(vg) > 0: + vgroup_collect = [] + for i in range(0, len(vg)): + vgroup_collect.append((vg[i].group, vg[i].weight)) + data[vertex_index] = vgroup_collect + + # check if group already exists... if target_obj.vertex_groups.get(vertex_group_name): target_obj.vertex_groups.remove(target_obj.vertex_groups.get(vertex_group_name)) - source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] - # TODO Debug crashing / use context.temp_override(object=obj) style - override = context.copy() - override["selected_editable_objects"] = [target_obj, source_obj] - override["active_object"] = source_obj - override["object"] = source_obj - with context.temp_override(**override): - bpy.ops.object.data_transfer( - data_type="VGROUP_WEIGHTS", - use_create=True, - vert_mapping='POLYINTERP_NEAREST', - layers_select_src="ACTIVE", - layers_select_dst="NAME", - mix_mode="REPLACE", - ) + # then add it + target_obj.vertex_groups.new(name=vertex_group_name) + + # write weights + for v in target_data.vertices: + for vi_source, vgroupIndex_weight in data.items(): + if v.index == vi_source: + for i in range(0, len(vgroupIndex_weight)): + group = target_obj.vertex_groups + for vgs in range(0, len(group)): + if group[vgs].name == vertex_group_name: + group[vgs].add( + (v.index,), + vgroupIndex_weight[i][1], + "REPLACE", + ) # VERTEX COLORS -- 2.30.2 From 75e93d8644b5c9240d36bb43b11ceee50d5f7387 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 16:01:19 -0400 Subject: [PATCH 186/429] Asset Pipe: Add Button to Show/Hide new Transfer Data --- scripts-blender/addons/asset_pipeline_2/ops.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 570ef55d..011a87b1 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -94,6 +94,12 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): _temp_transfer_data = None _invalid_objs = [] + expand: bpy.props.BoolProperty( + name="Show New Transfer Data", + default=False, + description="Show New Transfer Data", + ) + save: bpy.props.BoolProperty( name="Save Current File", default=True, @@ -150,9 +156,14 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): layout.label(text="No New Transfer Data found") else: layout.label(text="New Transfer Data will be Pushed to Publish") - + row = layout.row() + row.prop(self, "expand", text="", icon="COLLAPSEMENU", toggle=False) + row.label(text="Show New Transfer Data") objs = [transfer_info.obj for transfer_info in self._temp_transfer_data] + if not self.expand: + return + for obj in set(objs): obj_ownership = [ transfer_info -- 2.30.2 From b96102dc0de15de3df0703847723c3f21288d3ab Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 11 Sep 2023 16:02:28 -0400 Subject: [PATCH 187/429] Asset Pipe: Improve Asset Management Panel --- scripts-blender/addons/asset_pipeline_2/ui.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 143d98b2..2bbd0feb 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -24,16 +24,17 @@ class ASSETPIPE_sync(bpy.types.Panel): ) layout.prop(asset_pipe, "asset_collection", text="Asset") layout.label(text="Test UI") - layout.operator("assetpipe.publish_new_version") - layout.operator( - "assetpipe.sync_with_publish", text="Update Ownership" - ).pull = False + + # layout.operator( + # "assetpipe.sync_with_publish", text="Update Ownership" + # ).pull = False layout.operator( "assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP" ).push = True layout.operator( "assetpipe.sync_with_publish", text="Pull from Publish", icon="TRIA_DOWN" ).pull = True + layout.operator("assetpipe.publish_new_version", icon="PLUS") if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE": asset_pipe = context.scene.asset_pipeline -- 2.30.2 From a0efde88611d89ddd64582dd85be28a9ed89dde6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 12 Sep 2023 13:11:31 -0400 Subject: [PATCH 188/429] Revert "Asset Pipe: Re-write Vertex Group Transfer" This reverts commit 12e029b4cfbed368c96b0cd46a9bc174ed749136. --- .../transfer_data/transfer_core.py | 1 + .../transfer_data/transfer_functions.py | 56 ++++++------------- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 1591e3c5..4baebca0 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -104,6 +104,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: if transfer_info.type == constants.VERTEX_GROUP_KEY: print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") transfer_functions.transfer_vertex_group( + context=context, vertex_group_name=transfer_info.name, target_obj=target_obj, source_obj=source_obj, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index bb0935c8..76aa7b6d 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -30,56 +30,32 @@ def init_vertex_groups(scene, obj): def transfer_vertex_group( + context, vertex_group_name: str, target_obj: bpy.types.Object, source_obj: bpy.types.Object, ): - # Adapted from https://stackoverflow.com/questions/40969164/how-to-add-vertex-groups-from-one-object-to-another-without-replacing-them if target_obj == source_obj: return - source_data = source_obj.data - target_data = target_obj.data - - # sanity check: do source and target have the same amount of verts? - if len(source_data.vertices) != len(target_data.vertices): - print( - 'ERROR: objects {} and {} have different vertex counts.'.format( - source_obj.name, target_obj.name - ) - ) - return - - data = {} - for v in source_data.vertices: - vg = v.groups - vertex_index = v.index - if len(vg) > 0: - vgroup_collect = [] - for i in range(0, len(vg)): - vgroup_collect.append((vg[i].group, vg[i].weight)) - data[vertex_index] = vgroup_collect - - # check if group already exists... if target_obj.vertex_groups.get(vertex_group_name): target_obj.vertex_groups.remove(target_obj.vertex_groups.get(vertex_group_name)) - # then add it - target_obj.vertex_groups.new(name=vertex_group_name) - - # write weights - for v in target_data.vertices: - for vi_source, vgroupIndex_weight in data.items(): - if v.index == vi_source: - for i in range(0, len(vgroupIndex_weight)): - group = target_obj.vertex_groups - for vgs in range(0, len(group)): - if group[vgs].name == vertex_group_name: - group[vgs].add( - (v.index,), - vgroupIndex_weight[i][1], - "REPLACE", - ) + source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] + # TODO Debug crashing / use context.temp_override(object=obj) style + override = context.copy() + override["selected_editable_objects"] = [target_obj, source_obj] + override["active_object"] = source_obj + override["object"] = source_obj + with context.temp_override(**override): + bpy.ops.object.data_transfer( + data_type="VGROUP_WEIGHTS", + use_create=True, + vert_mapping='POLYINTERP_NEAREST', + layers_select_src="ACTIVE", + layers_select_dst="NAME", + mix_mode="REPLACE", + ) # VERTEX COLORS -- 2.30.2 From 54e79eb5c244e2dabdf94372e7aaf1ac680bb6ca Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 12 Sep 2023 15:24:47 -0400 Subject: [PATCH 189/429] Asset Pipe: Remove passing context via function --- .../addons/asset_pipeline_2/asset_mapping.py | 2 +- scripts-blender/addons/asset_pipeline_2/core.py | 1 - .../transfer_data/transfer_functions.py | 11 ++++++----- scripts-blender/addons/asset_pipeline_2/util.py | 13 ------------- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 82a68b33..522067aa 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -125,7 +125,7 @@ class AssetTransferMapping: return coll_map def _gen_transfer_data_map(self): - context = util.get_stored_context() + context = bpy.context transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data temp_transfer_data.clear() diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index a3d6fdb8..78850ac6 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -115,7 +115,6 @@ def merge_task_layer( local_tls: (list[str]): list of task layers that are local to the current file external_file (Path): external file to pull data into the current file from """ - util.set_stored_context(context=context) local_col = context.scene.asset_pipeline.asset_collection if not local_col: return "Unable to find Asset Collection" diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 76aa7b6d..0d494008 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -43,6 +43,7 @@ def transfer_vertex_group( source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] # TODO Debug crashing / use context.temp_override(object=obj) style + context = bpy.context override = context.copy() override["selected_editable_objects"] = [target_obj, source_obj] override["active_object"] = source_obj @@ -163,7 +164,7 @@ def init_modifiers(scene, obj): def transfer_modifier(modifier_name, target_obj, source_obj): # remove old and sync existing modifiers - context = util.get_stored_context() + context = bpy.context old_mod = target_obj.modifiers.get(modifier_name) if old_mod: target_obj.modifiers.remove(old_mod) @@ -233,7 +234,7 @@ def init_constraints(scene, obj): def transfer_constraint(constraint_name, target_obj, source_obj): - context = util.get_stored_context() + context = bpy.context # remove old and sync existing modifiers old_mod = target_obj.constraints.get(constraint_name) if old_mod: @@ -276,7 +277,7 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT def material_slot_clean(obj): # Material slots cannot use generic transfer_info_clean() function - context = util.get_stored_context() + context = bpy.context transfer_data_list = transfer_core.get_transfer_data_as_names( obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY ) @@ -302,7 +303,7 @@ def init_material_slots(scene, obj): def transfer_material_slot(material_slot_name, target_obj, source_obj): # Delete existing material slot if exists - context = util.get_stored_context() + context = bpy.context for idx in range(len(source_obj.material_slots)): slot = source_obj.material_slots[idx] if asset_suffix.get_basename(slot.material.name) == material_slot_name: @@ -394,7 +395,7 @@ def shape_key_closest_tri_on_face(tris_dict, face, p): def shape_keys_clean(obj): - context = util.get_stored_context() + context = bpy.context if obj.type != "MESH" or obj.data.shape_keys is None: return transfer_data_list = transfer_core.get_transfer_data_as_names( diff --git a/scripts-blender/addons/asset_pipeline_2/util.py b/scripts-blender/addons/asset_pipeline_2/util.py index 54d02c70..26f590d5 100644 --- a/scripts-blender/addons/asset_pipeline_2/util.py +++ b/scripts-blender/addons/asset_pipeline_2/util.py @@ -99,16 +99,3 @@ def traverse_collection_tree( yield collection for child in collection.children: yield from traverse_collection_tree(child) - - -stored_context = None - - -def set_stored_context(context): - global stored_context - stored_context = context - - -def get_stored_context(): - global stored_context - return stored_context -- 2.30.2 From d55b3859faa8f437ddd64f7f25a73b2bdfed0875 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 12 Sep 2023 15:25:35 -0400 Subject: [PATCH 190/429] Asset Pipe: Add Prints to Vertex Group Transfer --- .../transfer_data/transfer_functions.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 0d494008..434cb075 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -41,7 +41,11 @@ def transfer_vertex_group( if target_obj.vertex_groups.get(vertex_group_name): target_obj.vertex_groups.remove(target_obj.vertex_groups.get(vertex_group_name)) - source_obj.vertex_groups.active = source_obj.vertex_groups[vertex_group_name] + if not source_obj.vertex_groups.get(vertex_group_name): + print(f"ERROR Vertex Group {vertex_group_name} not found in {source_obj.name}") + return + + source_obj.vertex_groups.active = source_obj.vertex_groups.get(vertex_group_name) # TODO Debug crashing / use context.temp_override(object=obj) style context = bpy.context override = context.copy() @@ -57,6 +61,11 @@ def transfer_vertex_group( layers_select_dst="NAME", mix_mode="REPLACE", ) + if not target_obj.vertex_groups.get(vertex_group_name): + print( + f"FAILED to Transfer Vertex Group {vertex_group_name} to {target_obj.name}" + ) + return # VERTEX COLORS -- 2.30.2 From 5f717cf1f3bc35b2f9b5327745084284463ae3ae Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 12 Sep 2023 17:52:25 -0400 Subject: [PATCH 191/429] Asset Pipe: Fix Naming Collisions Bug in Temp Transfer Data --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 8 +++++--- .../asset_pipeline_2/transfer_data/transfer_core.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 522067aa..ca4e81fe 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -136,6 +136,7 @@ class AssetTransferMapping: if obj.name.endswith(constants.LOCAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: if transfer_info.owner in self._local_tls: + temp_info_index = len(temp_transfer_data) temp_info = transfer_core.transfer_data_add_entry( transfer_data=temp_transfer_data, name=transfer_info.name, @@ -144,26 +145,27 @@ class AssetTransferMapping: ) name = transfer_info.name + '_' + obj.name transfer_data_map[name] = { - 'transfer_info': temp_info.name, # TODO avoid name collisions + 'transfer_info_index': temp_info_index, 'source_obj': obj, 'target_obj': target_obj, } - if obj.name.endswith(constants.EXTERNAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: if ( transfer_info.owner not in self._local_tls and transfer_info.owner != "NONE" ): + temp_info_index = len(temp_transfer_data) temp_info = transfer_core.transfer_data_add_entry( transfer_data=temp_transfer_data, name=transfer_info.name, td_type=transfer_info.type, task_layer_name=transfer_info.owner, ) + name = transfer_info.name + '_' + obj.name transfer_data_map[name] = { - 'transfer_info': temp_info.name, # TODO avoid name collisions + 'transfer_info_index': temp_info_index, 'source_obj': obj, 'target_obj': target_obj, } diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 4baebca0..97806a38 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -92,7 +92,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: for name in transfer_data_map: temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data transfer_data = transfer_data_map[name] - transfer_info = temp_transfer_data.get(transfer_data.get('transfer_info')) + transfer_info = temp_transfer_data[transfer_data.get('transfer_info_index')] target_obj = transfer_data.get('target_obj') source_obj = transfer_data.get('source_obj') if target_obj is None: -- 2.30.2 From 5cfc0fcb01babbd167919030609ae3d2dd0d094a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 12 Sep 2023 18:21:40 -0400 Subject: [PATCH 192/429] Asset Pipe: Simplify Transfer Data Mapping --- .../addons/asset_pipeline_2/asset_mapping.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index ca4e81fe..d9cc93f7 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -124,6 +124,24 @@ class AssetTransferMapping: return coll_map + def _get_transfer_data_map_item(self, obj, target_obj, transfer_info): + temp_transfer_data = bpy.context.scene.asset_pipeline.temp_transfer_data + temp_info_index = len(temp_transfer_data) + temp_info = transfer_core.transfer_data_add_entry( + transfer_data=temp_transfer_data, + name=transfer_info.name, + td_type=transfer_info.type, + task_layer_name=transfer_info.owner, + ) + + map_item = { + 'transfer_info_index': temp_info_index, + 'source_obj': obj, + 'target_obj': target_obj, + } + name = transfer_info.name + '_' + obj.name + return name, map_item + def _gen_transfer_data_map(self): context = bpy.context transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} @@ -136,37 +154,19 @@ class AssetTransferMapping: if obj.name.endswith(constants.LOCAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: if transfer_info.owner in self._local_tls: - temp_info_index = len(temp_transfer_data) - temp_info = transfer_core.transfer_data_add_entry( - transfer_data=temp_transfer_data, - name=transfer_info.name, - td_type=transfer_info.type, - task_layer_name=transfer_info.owner, + name, map_item = self._get_transfer_data_map_item( + obj, target_obj, transfer_info ) - name = transfer_info.name + '_' + obj.name - transfer_data_map[name] = { - 'transfer_info_index': temp_info_index, - 'source_obj': obj, - 'target_obj': target_obj, - } + transfer_data_map[name] = map_item + if obj.name.endswith(constants.EXTERNAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: if ( transfer_info.owner not in self._local_tls and transfer_info.owner != "NONE" ): - temp_info_index = len(temp_transfer_data) - temp_info = transfer_core.transfer_data_add_entry( - transfer_data=temp_transfer_data, - name=transfer_info.name, - td_type=transfer_info.type, - task_layer_name=transfer_info.owner, + name, map_item = self._get_transfer_data_map_item( + obj, target_obj, transfer_info ) - - name = transfer_info.name + '_' + obj.name - transfer_data_map[name] = { - 'transfer_info_index': temp_info_index, - 'source_obj': obj, - 'target_obj': target_obj, - } + transfer_data_map[name] = map_item return transfer_data_map -- 2.30.2 From fcf62baa75cb4dd18ba1b453b76e4b880ef8906a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 11:11:24 -0400 Subject: [PATCH 193/429] Asset Pipe: Fix Sharp/Smooth Ownership Bug --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 434cb075..be533e42 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -348,7 +348,6 @@ def transfer_material_slot(material_slot_name, target_obj, source_obj): # Transfer material slot assignments for mesh for pol_to, pol_from in zip(target_obj.data.polygons, source_obj.data.polygons): pol_to.material_index = pol_from.material_index - pol_to.use_smooth = pol_from.use_smooth # SHAPE KEYS @@ -510,10 +509,7 @@ def attributes_get_editable(attributes): item for item in attributes if not ( - item.is_internal - or item.name == 'position' - or item.name == 'material_index' - or item.name == 'sharp_face' + item.is_internal or item.name == 'position' or item.name == 'material_index' ) ] -- 2.30.2 From 6d0d9d8325eee3d2794f04807e24537f29f5de5f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 13:27:54 -0400 Subject: [PATCH 194/429] Asset Pipe: Materials Owner owns All Materials on an Obj --- .../addons/asset_pipeline_2/constants.py | 2 + .../transfer_data/transfer_core.py | 7 +- .../transfer_data/transfer_functions.py | 89 ++++++++++--------- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index cd3bc593..642d3b03 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -34,6 +34,8 @@ UV_LAYERS_KEY = TRANSFER_DATA_KEYS[6] SHAPE_KEY_KEY = TRANSFER_DATA_KEYS[7] ATTRIBUTE_KEY = TRANSFER_DATA_KEYS[8] +MATERIAL_TRANSFER_INFO_NAME = "All Material Slots" + PUBLISH_TYPES = [ ( "publish", diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 97806a38..69a014b3 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -30,7 +30,7 @@ def transfer_data_clean(obj): # transfer_functions.uv_layer_clean(obj) transfer_functions.modifiers_clean(obj) transfer_functions.constraints_clean(obj) - transfer_functions.material_slot_clean(obj) + transfer_functions.material_slots_clean(obj) transfer_functions.shape_keys_clean(obj) transfer_functions.attribute_clean(obj) @@ -47,7 +47,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: return bool( transfer_functions.vertex_group_is_missing(transfer_data_item) or transfer_functions.modifier_is_missing(transfer_data_item) - or transfer_functions.material_slot_is_missing(transfer_data_item) + or transfer_functions.material_slots_is_missing(transfer_data_item) or transfer_functions.constraint_is_missing(transfer_data_item) # or transfer_functions.vertex_color_is_missing(transfer_data_item) # or transfer_functions.uv_layer_is_missing(transfer_data_item) @@ -130,8 +130,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: ) if transfer_info.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") - transfer_functions.transfer_material_slot( - material_slot_name=transfer_info.name, + transfer_functions.transfer_material_slots( target_obj=target_obj, source_obj=source_obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index be533e42..e03af701 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -284,56 +284,71 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT -def material_slot_clean(obj): +def material_slots_clean(obj): # Material slots cannot use generic transfer_info_clean() function context = bpy.context transfer_data_list = transfer_core.get_transfer_data_as_names( obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY ) + + # Clear Materials if No Transfer Data is Found + if transfer_data_list != []: + return + for mat_slot in obj.material_slots: - if not asset_suffix.get_basename(mat_slot.name) in transfer_data_list: - index = obj.material_slots.keys().index(mat_slot.name) - obj.active_material_index = index - with context.temp_override(object=obj): - bpy.ops.object.material_slot_remove() + index = obj.material_slots.keys().index(mat_slot.name) + obj.active_material_index = index + with context.temp_override(object=obj): + bpy.ops.object.material_slot_remove() -def material_slot_is_missing(transfer_info): - return transfer_core.transfer_info_is_missing( - transfer_info, constants.MATERIAL_SLOT_KEY, transfer_info.id_data.material_slots - ) +def material_slots_is_missing(transfer_info): + if ( + transfer_info.type == constants.MATERIAL_SLOT_KEY + and len(transfer_info.id_data.material_slots) == 0 + ): + return True def init_material_slots(scene, obj): - transfer_core.transfer_info_init( - scene, obj, obj.material_slots, constants.MATERIAL_SLOT_KEY - ) + task_layer_name = scene.asset_pipeline.task_layer_name + type_key = constants.MATERIAL_SLOT_KEY + name = constants.MATERIAL_TRANSFER_INFO_NAME + transfer_data = obj.transfer_data_ownership + + # Only Execute if Material Slots exist on object + if len(obj.material_slots) == 0: + return + matches = transfer_core.check_transfer_data_entry(transfer_data, name, type_key) + # Only add new ownership transfer_info if vertex group doesn't have an owner + if len(matches) == 0: + scene.asset_pipeline.add_temp_trasnfer_data( + name=name, + owner=task_layer_name, + type=type_key, + obj=obj, + ) -def transfer_material_slot(material_slot_name, target_obj, source_obj): +def transfer_material_slots(target_obj, source_obj): # Delete existing material slot if exists context = bpy.context - for idx in range(len(source_obj.material_slots)): - slot = source_obj.material_slots[idx] - if asset_suffix.get_basename(slot.material.name) == material_slot_name: - target_obj.active_material_index = idx - with context.temp_override(object=target_obj): - bpy.ops.object.material_slot_remove() + # Transfer material slot assignments. + # Delete all material slots of target object. + while len(target_obj.material_slots) > len(source_obj.material_slots): + target_obj.active_material_index = len(source_obj.material_slots) + with context.temp_override(object=target_obj): + bpy.ops.object.material_slot_remove() # Transfer material slots - for idx in range(len(source_obj.material_slots)): if idx >= len(target_obj.material_slots): - slot = source_obj.material_slots[idx] - if asset_suffix.get_basename(slot.material.name) == material_slot_name: - with context.temp_override(object=target_obj): - bpy.ops.object.material_slot_add() - target_obj.material_slots[idx].link = source_obj.material_slots[ - idx - ].link - target_obj.material_slots[idx].material = source_obj.material_slots[ - idx - ].material + with context.temp_override(object=target_obj): + bpy.ops.object.material_slot_add() + target_obj.material_slots[idx].link = source_obj.material_slots[idx].link + target_obj.material_slots[idx].material = source_obj.material_slots[ + idx + ].material # Transfer active material slot index target_obj.active_material_index = source_obj.active_material_index @@ -343,12 +358,6 @@ def transfer_material_slot(material_slot_name, target_obj, source_obj): for spl_to, spl_from in zip(target_obj.data.splines, source_obj.data.splines): spl_to.material_index = spl_from.material_index - # Rest of the loop applies only to meshes. - if target_obj.type == "MESH": - # Transfer material slot assignments for mesh - for pol_to, pol_from in zip(target_obj.data.polygons, source_obj.data.polygons): - pol_to.material_index = pol_from.material_index - # SHAPE KEYS @@ -506,11 +515,7 @@ def transfer_shape_key( def attributes_get_editable(attributes): # TODO replace 'position' HACK with is_required once https://projects.blender.org/blender/blender/pulls/111468 is merged return [ - item - for item in attributes - if not ( - item.is_internal or item.name == 'position' or item.name == 'material_index' - ) + item for item in attributes if not (item.is_internal or item.name == 'position') ] -- 2.30.2 From 794a1204139010f4a7f0542ff2a118bd1487ba79 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 14:14:08 -0400 Subject: [PATCH 195/429] Asset Pipe: Fix Invalid Objects UI Warning --- .../addons/asset_pipeline_2/core.py | 30 +++++++++++++++---- .../addons/asset_pipeline_2/ops.py | 3 +- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 78850ac6..f518db68 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -32,7 +32,7 @@ def ownership_transfer_data_cleanup( def ownership_get( local_col: bpy.types.Collection, scene: bpy.types.Scene, -) -> list[bpy.types.Object]: +) -> None: """Find new transfer data owned by the local task layer. Marks items as owned by the local task layer if they are in the corrisponding task layer collection and have no owner. @@ -47,7 +47,6 @@ def ownership_get( list[bpy.types.Object]: Returns a list of objects that have no owner and will not be included in the merge process """ - invalid_objs = [] task_layer_name = scene.asset_pipeline.task_layer_name task_layer_col_name = get_dict_tuple_item( constants.TASK_LAYER_TYPES, task_layer_name @@ -60,13 +59,10 @@ def ownership_get( obj.asset_id_owner = task_layer_name # Skip items that have no owner if obj.asset_id_owner == "NONE": - invalid_objs.append(obj) continue ownership_transfer_data_cleanup(obj, task_layer_name) transfer_core.init_transfer_data(scene, obj) - return invalid_objs - def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: """Add new transfer data items on each object found in the @@ -83,6 +79,30 @@ def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: ) +def get_invalid_objects( + local_col: bpy.types.Collection, + scene: bpy.types.Scene, +) -> list[bpy.types.Object]: + # TODO Add Docstring + # TODO Make common function to get task layer col via task layer name + task_layer_name = scene.asset_pipeline.task_layer_name + task_layer_col_name = get_dict_tuple_item( + constants.TASK_LAYER_TYPES, task_layer_name + )[1] + task_layer_col = local_col.children.get(task_layer_col_name) + + invalid_obj = [] + for obj in scene.objects: + if obj.asset_id_owner == "NONE": + invalid_obj.append(obj) + if ( + obj not in list(task_layer_col.all_objects) + and obj.asset_id_owner == task_layer_name + ): + invalid_obj.append(obj) + return invalid_obj + + def remap_user(source_datablock: bpy.data, target_datablock: bpy.data) -> None: """Remap datablock and append name to datablock that has been remapped diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 011a87b1..1e7d435c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -130,7 +130,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - self._invalid_objs = core.ownership_get(local_col, context.scene) + core.ownership_get(local_col, context.scene) + self._invalid_objs = core.get_invalid_objects(local_col, context.scene) # Default behaviour is to pull before pushing if self.push: -- 2.30.2 From c2a4dabe9858dc7c33ae95e4971ed48f1602d12b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 14:20:33 -0400 Subject: [PATCH 196/429] Asset Pipe: Add TODO for Invalid Objs --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 1e7d435c..65bf93d3 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -131,6 +131,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): return {'CANCELLED'} core.ownership_get(local_col, context.scene) + + # TODO Remove Invalid Objs Explicitly, some will be auto removed but not all self._invalid_objs = core.get_invalid_objects(local_col, context.scene) # Default behaviour is to pull before pushing -- 2.30.2 From 544f04bd23b704ef2faa10292b40f3cb5e5ffb99 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 14:45:31 -0400 Subject: [PATCH 197/429] Asset Pipe: Save Pre-Pull State in Temp Directory --- scripts-blender/addons/asset_pipeline_2/ops.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 65bf93d3..f2d5ad96 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -182,6 +182,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data core.ownership_set(temp_transfer_data) current_file = Path(bpy.data.filepath) + temp_dir = Path(bpy.app.tempdir).parent task_layer_name = context.scene.asset_pipeline.task_layer_name if task_layer_name == "NONE": self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") @@ -193,6 +194,10 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): return {'CANCELLED'} if self.pull: + temp_file = temp_dir.joinpath( + current_file.name.replace(".blend", "") + "_Asset_Pipe_Backup.blend" + ) + bpy.ops.wm.save_as_mainfile(filepath=temp_file.__str__(), copy=True) error_msg = core.merge_task_layer( context, local_tls=[task_layer_name], -- 2.30.2 From 0802febdda800bc13f9845849eef32a699852a82 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 15:34:28 -0400 Subject: [PATCH 198/429] Asset Pipe: Replace Task_Layer_Name with Task_Layer_Key --- scripts-blender/addons/asset_pipeline_2/core.py | 16 +++++----------- scripts-blender/addons/asset_pipeline_2/ops.py | 12 ++++++------ scripts-blender/addons/asset_pipeline_2/props.py | 1 + .../transfer_data/transfer_core.py | 4 ++-- .../transfer_data/transfer_functions.py | 8 ++++---- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index f518db68..dbacdfbb 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -47,20 +47,17 @@ def ownership_get( list[bpy.types.Object]: Returns a list of objects that have no owner and will not be included in the merge process """ - task_layer_name = scene.asset_pipeline.task_layer_name - task_layer_col_name = get_dict_tuple_item( - constants.TASK_LAYER_TYPES, task_layer_name - )[1] + task_layer_key = scene.asset_pipeline.task_layer_name scene.asset_pipeline.temp_transfer_data.clear() task_layer_col = local_col.children.get(task_layer_col_name) for obj in local_col.all_objects: # Mark Asset ID Owner for objects in the current task layers collection if obj.asset_id_owner == "NONE" and obj in list(task_layer_col.all_objects): - obj.asset_id_owner = task_layer_name + obj.asset_id_owner = task_layer_key # Skip items that have no owner if obj.asset_id_owner == "NONE": continue - ownership_transfer_data_cleanup(obj, task_layer_name) + ownership_transfer_data_cleanup(obj, task_layer_key) transfer_core.init_transfer_data(scene, obj) @@ -85,10 +82,7 @@ def get_invalid_objects( ) -> list[bpy.types.Object]: # TODO Add Docstring # TODO Make common function to get task layer col via task layer name - task_layer_name = scene.asset_pipeline.task_layer_name - task_layer_col_name = get_dict_tuple_item( - constants.TASK_LAYER_TYPES, task_layer_name - )[1] + task_layer_key = scene.asset_pipeline.task_layer_name task_layer_col = local_col.children.get(task_layer_col_name) invalid_obj = [] @@ -97,7 +91,7 @@ def get_invalid_objects( invalid_obj.append(obj) if ( obj not in list(task_layer_col.all_objects) - and obj.asset_id_owner == task_layer_name + and obj.asset_id_owner == task_layer_key ): invalid_obj.append(obj) return invalid_obj diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index f2d5ad96..77df6ab6 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -125,8 +125,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not local_col: self.report({'ERROR'}, "Top level collection could not be found") return {'CANCELLED'} - task_layer_name = context.scene.asset_pipeline.task_layer_name - if task_layer_name == "NONE": + task_layer_key = context.scene.asset_pipeline.task_layer_name + if task_layer_key == "NONE": self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} @@ -183,8 +183,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): core.ownership_set(temp_transfer_data) current_file = Path(bpy.data.filepath) temp_dir = Path(bpy.app.tempdir).parent - task_layer_name = context.scene.asset_pipeline.task_layer_name - if task_layer_name == "NONE": + task_layer_key = context.scene.asset_pipeline.task_layer_name + if task_layer_key == "NONE": self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} @@ -200,7 +200,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): bpy.ops.wm.save_as_mainfile(filepath=temp_file.__str__(), copy=True) error_msg = core.merge_task_layer( context, - local_tls=[task_layer_name], + local_tls=[task_layer_key], external_file=sync_target, ) @@ -231,7 +231,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): local_tls = [ task_layer for task_layer in constants.TASK_LAYER_KEYS - if task_layer != task_layer_name + if task_layer != task_layer_key ] error_msg = core.merge_task_layer( diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 3afbcb27..8087b5b8 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -51,6 +51,7 @@ class AssetPipeline(bpy.types.PropertyGroup): temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) + # TODO Rename to Current_Task_Layer task_layer_name: bpy.props.EnumProperty( name="Task Layer Name", items=constants.TASK_LAYER_TYPES ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 69a014b3..355e555e 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -226,14 +226,14 @@ def transfer_info_is_missing(transfer_info, type_key, list): def transfer_info_init(scene, obj, list, type_key): transfer_data = obj.transfer_data_ownership - task_layer_name = scene.asset_pipeline.task_layer_name + task_layer_key = scene.asset_pipeline.task_layer_name for item in list: # Only add new ownership transfer_info if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, item.name, type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_trasnfer_data( name=item.name, - owner=task_layer_name, + owner=task_layer_key, type=type_key, obj=obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index e03af701..7733cba6 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -311,7 +311,7 @@ def material_slots_is_missing(transfer_info): def init_material_slots(scene, obj): - task_layer_name = scene.asset_pipeline.task_layer_name + task_layer_key = scene.asset_pipeline.task_layer_name type_key = constants.MATERIAL_SLOT_KEY name = constants.MATERIAL_TRANSFER_INFO_NAME transfer_data = obj.transfer_data_ownership @@ -324,7 +324,7 @@ def init_material_slots(scene, obj): if len(matches) == 0: scene.asset_pipeline.add_temp_trasnfer_data( name=name, - owner=task_layer_name, + owner=task_layer_key, type=type_key, obj=obj, ) @@ -549,7 +549,7 @@ def init_attributes(scene, obj): if obj.type != "MESH": return transfer_data = obj.transfer_data_ownership - task_layer_name = scene.asset_pipeline.task_layer_name + task_layer_key = scene.asset_pipeline.task_layer_name type_key = constants.ATTRIBUTE_KEY for atttribute in attributes_get_editable(obj.data.attributes): # Only add new ownership transfer_info if vertex group doesn't have an owner @@ -559,7 +559,7 @@ def init_attributes(scene, obj): if len(matches) == 0: scene.asset_pipeline.add_temp_trasnfer_data( name=atttribute.name, - owner=task_layer_name, + owner=task_layer_key, type=type_key, obj=obj, ) -- 2.30.2 From 4de84eba9fee0fa2b16228b1883ca769f443a878 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 15:36:18 -0400 Subject: [PATCH 199/429] Asset Pipe: Add Core Function to Get Task Layer Collection Name --- scripts-blender/addons/asset_pipeline_2/core.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index dbacdfbb..01f18b31 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -48,6 +48,7 @@ def ownership_get( in the merge process """ task_layer_key = scene.asset_pipeline.task_layer_name + task_layer_col_name = get_task_layer_col_name(task_layer_key) scene.asset_pipeline.temp_transfer_data.clear() task_layer_col = local_col.children.get(task_layer_col_name) for obj in local_col.all_objects: @@ -83,6 +84,7 @@ def get_invalid_objects( # TODO Add Docstring # TODO Make common function to get task layer col via task layer name task_layer_key = scene.asset_pipeline.task_layer_name + task_layer_col_name = get_task_layer_col_name(task_layer_key) task_layer_col = local_col.children.get(task_layer_col_name) invalid_obj = [] @@ -97,6 +99,14 @@ def get_invalid_objects( return invalid_obj +def get_task_layer_col_name(task_layer_key): + # TODO Docstring and return types + asset_pipe = bpy.context.scene.asset_pipeline + task_layer_name = get_dict_tuple_item(constants.TASK_LAYER_TYPES, task_layer_key)[1] + prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" + return prefix + task_layer_name + + def remap_user(source_datablock: bpy.data, target_datablock: bpy.data) -> None: """Remap datablock and append name to datablock that has been remapped -- 2.30.2 From 06bf5de4549791fc8a5b8debd5b73baa3eedfef4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 15:51:57 -0400 Subject: [PATCH 200/429] Asset Pipe: Add Asset Prefix to Collections & Objects --- .../addons/asset_pipeline_2/asset_mapping.py | 5 +++-- .../addons/asset_pipeline_2/core.py | 15 ++++++++++---- .../addons/asset_pipeline_2/ops.py | 20 +++++++++++-------- .../addons/asset_pipeline_2/props.py | 4 ++++ scripts-blender/addons/asset_pipeline_2/ui.py | 1 + 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index d9cc93f7..25a22ba4 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -1,7 +1,7 @@ import bpy from typing import Dict, Set -from . import asset_suffix, constants, util +from . import asset_suffix, constants, util, core from .transfer_data import transfer_core @@ -94,11 +94,12 @@ class AssetTransferMapping: coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} local_tl_names = [ - tl_type[1] + core.get_name_with_asset_prefix(tl_type[1]) for tl_type in constants.TASK_LAYER_TYPES if tl_type[0] in self._local_tls ] + # TODO Include PREFIX checker in this stupidity for local_task_layer_col in self._local_col.children: if ( asset_suffix.get_basename(local_task_layer_col.name) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 01f18b31..84d9d43e 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -47,14 +47,16 @@ def ownership_get( list[bpy.types.Object]: Returns a list of objects that have no owner and will not be included in the merge process """ - task_layer_key = scene.asset_pipeline.task_layer_name + asset_pipe = scene.asset_pipeline + asset_pipe.temp_transfer_data.clear() + task_layer_key = asset_pipe.task_layer_name task_layer_col_name = get_task_layer_col_name(task_layer_key) - scene.asset_pipeline.temp_transfer_data.clear() task_layer_col = local_col.children.get(task_layer_col_name) for obj in local_col.all_objects: # Mark Asset ID Owner for objects in the current task layers collection if obj.asset_id_owner == "NONE" and obj in list(task_layer_col.all_objects): obj.asset_id_owner = task_layer_key + obj.name = get_name_with_asset_prefix(obj.name) # Skip items that have no owner if obj.asset_id_owner == "NONE": continue @@ -101,10 +103,15 @@ def get_invalid_objects( def get_task_layer_col_name(task_layer_key): # TODO Docstring and return types - asset_pipe = bpy.context.scene.asset_pipeline task_layer_name = get_dict_tuple_item(constants.TASK_LAYER_TYPES, task_layer_key)[1] + return get_name_with_asset_prefix(task_layer_name) + + +def get_name_with_asset_prefix(name): + # TODO Docstring and return types + asset_pipe = bpy.context.scene.asset_pipeline prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" - return prefix + task_layer_name + return prefix + name def remap_user(source_datablock: bpy.data, target_datablock: bpy.data) -> None: diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 77df6ab6..57b448c0 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -14,6 +14,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): _name = None _dir = None + _prefix = None @classmethod def poll(cls, context: bpy.types.Context) -> bool: @@ -31,6 +32,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe = context.scene.asset_pipeline self._name = asset_pipe.name self._dir = asset_pipe.dir + self._prefix = asset_pipe.prefix # Create Asset Folder at Directory asset_path = os.path.join(self._dir, self._name) @@ -53,19 +55,21 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): ) # Setup New File - asset_status = context.scene.asset_pipeline - asset_status.is_asset_pipeline_file = True + asset_pipe = context.scene.asset_pipeline + asset_pipe.is_asset_pipeline_file = True bpy.data.collections.new(self._name) asset_col = bpy.data.collections.get(self._name) context.scene.collection.children.link(asset_col) - asset_status.asset_collection = asset_col - asset_pipe = context.scene.asset_pipeline + asset_pipe.asset_collection = asset_col + asset_pipe.name = self._name + asset_pipe.prefix = self._prefix - for task_layer_name in constants.TASK_LAYER_NAMES: - if task_layer_name == "None": + for task_layer_key in constants.TASK_LAYER_KEYS: + if task_layer_key == "NONE": continue - bpy.data.collections.new(task_layer_name) - asset_col.children.link(bpy.data.collections.get(task_layer_name)) + col_name = core.get_task_layer_col_name(task_layer_key) + bpy.data.collections.new(col_name) + asset_col.children.link(bpy.data.collections.get(col_name)) for task_layer_key in reversed(constants.TASK_LAYER_KEYS): if task_layer_key == "NONE": diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 8087b5b8..c6ff124b 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -73,6 +73,10 @@ class AssetPipeline(bpy.types.PropertyGroup): ) name: bpy.props.StringProperty(name="Name", description="Name for new Asset") + prefix: bpy.props.StringProperty( + name="Prefix", description="Prefix for new Asset", default="" + ) + classes = ( AssetTransferData, diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 2bbd0feb..b216e2bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -16,6 +16,7 @@ class ASSETPIPE_sync(bpy.types.Panel): if not asset_pipe.is_asset_pipeline_file: layout.prop(asset_pipe, "dir") layout.prop(asset_pipe, "name") + layout.prop(asset_pipe, "prefix") layout.operator("assetpipe.create_new_asset") # layout.operator("") return -- 2.30.2 From 0eecb20aa4a8acf77cd8abc499a2ece227863bd7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 15:53:37 -0400 Subject: [PATCH 201/429] Asset Pipe: Add TODO for Prefixes --- scripts-blender/addons/asset_pipeline_2/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 84d9d43e..00839264 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -109,6 +109,7 @@ def get_task_layer_col_name(task_layer_key): def get_name_with_asset_prefix(name): # TODO Docstring and return types + # TODO Add check if prefix already exists, don't add two prefixes asset_pipe = bpy.context.scene.asset_pipeline prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" return prefix + name -- 2.30.2 From f761e0c4b86bd767cbe9e30e9edd4e886622d1c2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 16:26:17 -0400 Subject: [PATCH 202/429] Asset Pipe: Move Icons to seperate list --- .../addons/asset_pipeline_2/constants.py | 20 +++++++++++++++---- .../transfer_data/transfer_ui.py | 5 ++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 642d3b03..c82c4d29 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -14,13 +14,13 @@ TASK_LAYER_NAMES = [task_layer[1] for task_layer in TASK_LAYER_TYPES] TRANSFER_DATA_TYPES = [ ("NONE", "None", ""), ("GROUP_VERTEX", "Vertex Group", ""), - ("GROUP_VCOL", "Color Attribute", ""), + ("COLOR_ATTRIBUTE", "Color Attribute", ""), ("MODIFIER", "Modifier", ""), ("CONSTRAINT", "Constraint", ""), ("MATERIAL", "Material Slot", ""), - ("GROUP_UVS", "UV Maps", ""), - ("SHAPEKEY_DATA", "Shape Key", ""), - ("EVENT_A", "Attribute", ""), + ("UV_MAP", "UV Maps", ""), + ("SHAPE_KEY", "Shape Key", ""), + ("ATTRIBUTE", "Attribute", ""), ] TRANSFER_DATA_KEYS = [transfer_data[0] for transfer_data in TRANSFER_DATA_TYPES] @@ -34,6 +34,18 @@ UV_LAYERS_KEY = TRANSFER_DATA_KEYS[6] SHAPE_KEY_KEY = TRANSFER_DATA_KEYS[7] ATTRIBUTE_KEY = TRANSFER_DATA_KEYS[8] +TRANSFER_DATA_ICONS = [ + ("None", "NONE", ""), + (VERTEX_GROUP_KEY, "GROUP_VERTEX", ""), + (VERTEX_COLOR_KEY, "GROUP_VCOL", ""), + (MODIFIER_KEY, "MODIFIER", ""), + (CONSTRAINT_KEY, "CONSTRAINT", ""), + (MATERIAL_SLOT_KEY, "MATERIAL", ""), + (UV_LAYERS_KEY, "GROUP_UVS", ""), + (SHAPE_KEY_KEY, "SHAPEKEY_DATA", ""), + (ATTRIBUTE_KEY, "EVENT_A", ""), +] + MATERIAL_TRANSFER_INFO_NAME = "All Material Slots" PUBLISH_TYPES = [ diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 4442dd72..32b047bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -12,7 +12,10 @@ def draw_transfer_data_type( constants.TRANSFER_DATA_TYPES, transfer_data[0].type )[1] box = layout.box() - box.label(text=name, icon=transfer_data[0].type) + icon = core.get_dict_tuple_item( + constants.TRANSFER_DATA_ICONS, transfer_data[0].type + )[1] + box.label(text=name, icon=icon) for transfer_info in transfer_data: owner = core.get_dict_tuple_item( constants.TASK_LAYER_TYPES, transfer_info.owner -- 2.30.2 From 3baa0f14398b4d76f25516dd6c9f726cd15ce34c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 18:55:30 -0400 Subject: [PATCH 203/429] Asset Pipe: Cancel Merge Process if Transfer Data Conflicts Found --- .../addons/asset_pipeline_2/asset_mapping.py | 38 ++++++++++++++++--- .../addons/asset_pipeline_2/core.py | 5 +++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 25a22ba4..b1f2bb2a 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -143,6 +143,24 @@ class AssetTransferMapping: name = transfer_info.name + '_' + obj.name return name, map_item + def _check_transfer_data_conflict(self, obj, transfer_info): + other_obj = bpy.data.objects.get(asset_suffix.get_target_name(obj.name)) + check_transfer_info = None + if not other_obj: + return + for other_transfer_info in other_obj.transfer_data_ownership: + if ( + other_transfer_info.type == transfer_info.type + and other_transfer_info.name == transfer_info.name + ): + check_transfer_info = other_transfer_info + if check_transfer_info is None: + return + if check_transfer_info.owner != transfer_info.owner: + self.conflict_trasnfer_data.append(transfer_info) + print("CONFLICT FOUND") + return True + def _gen_transfer_data_map(self): context = bpy.context transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} @@ -155,10 +173,14 @@ class AssetTransferMapping: if obj.name.endswith(constants.LOCAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: if transfer_info.owner in self._local_tls: - name, map_item = self._get_transfer_data_map_item( - obj, target_obj, transfer_info + conflict = self._check_transfer_data_conflict( + obj, transfer_info ) - transfer_data_map[name] = map_item + if not conflict: + name, map_item = self._get_transfer_data_map_item( + obj, target_obj, transfer_info + ) + transfer_data_map[name] = map_item if obj.name.endswith(constants.EXTERNAL_SUFFIX): for transfer_info in obj.transfer_data_ownership: @@ -166,8 +188,12 @@ class AssetTransferMapping: transfer_info.owner not in self._local_tls and transfer_info.owner != "NONE" ): - name, map_item = self._get_transfer_data_map_item( - obj, target_obj, transfer_info + conflict = self._check_transfer_data_conflict( + obj, transfer_info ) - transfer_data_map[name] = map_item + if not conflict: + name, map_item = self._get_transfer_data_map_item( + obj, target_obj, transfer_info + ) + transfer_data_map[name] = map_item return transfer_data_map diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 00839264..0cf22d56 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -163,6 +163,11 @@ def merge_task_layer( map = AssetTransferMapping(local_col, external_col, local_tls) + if len(map.conflict_trasnfer_data) != 0: + error_msg = '' + for conflict in map.conflict_trasnfer_data: + error_msg += f"Transfer Data conflict found for '{conflict.name}' on obj '{conflict.id_data.name}'\n" + return error_msg # Remove all transfer data from target objects for source_obj in map.object_map: target_obj = map.object_map[source_obj] -- 2.30.2 From 0f2ce7fd10c11eb4a852c14d4084c38949097ae3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 13 Sep 2023 18:55:55 -0400 Subject: [PATCH 204/429] Asset Pipe: Cancel Merge Process if Object Conflicts Found --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 7 +++++++ scripts-blender/addons/asset_pipeline_2/core.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index b1f2bb2a..ea66ff20 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -35,6 +35,9 @@ class AssetTransferMapping: self._no_match_source_colls: Set[bpy.types.Object] = set() self._no_match_target_colls: Set[bpy.types.Object] = set() + self.conflict_objects = [] + self.conflict_trasnfer_data = [] + self.generate_mapping() def generate_mapping(self) -> None: @@ -67,12 +70,16 @@ class AssetTransferMapping: if local_obj.asset_id_owner in self._local_tls: external_obj = self._get_external_object(local_obj) if external_obj: + if external_obj.asset_id_owner != local_obj.asset_id_owner: + self.conflict_objects.append(local_obj) object_map[external_obj] = local_obj # IF ITEM IS NOT OWNED BY LOCAL TASK LAYERS else: external_obj = self._get_external_object(local_obj) if external_obj: + if external_obj.asset_id_owner != local_obj.asset_id_owner: + self.conflict_objects.append(local_obj) object_map[local_obj] = external_obj else: # REMOVE OBJ NOT OWNED BY LOCAL TASK LAYER THAT HAS NO MATCH diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 0cf22d56..f5bb684a 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -168,6 +168,13 @@ def merge_task_layer( for conflict in map.conflict_trasnfer_data: error_msg += f"Transfer Data conflict found for '{conflict.name}' on obj '{conflict.id_data.name}'\n" return error_msg + + if len(map.conflict_objects) != 0: + error_msg = '' + for conflict_obj in map.conflict_objects: + error_msg += f"Ownership conflic found for '{conflict_obj.name}'\n" + return error_msg + # Remove all transfer data from target objects for source_obj in map.object_map: target_obj = map.object_map[source_obj] -- 2.30.2 From 188cb82d47673f8a7918433b76eeb9630ee2e48f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 09:08:51 -0400 Subject: [PATCH 205/429] Asset Pipe: Fix Type in UI Label --- scripts-blender/addons/asset_pipeline_2/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index b216e2bc..10e80575 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -8,7 +8,7 @@ class ASSETPIPE_sync(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Asset Pipe 2' - bl_label = "Asset Managment" + bl_label = "Asset Management" def draw(self, context: bpy.types.Context) -> None: layout = self.layout -- 2.30.2 From 613d32b08b75d133f3e36dcef181ea25fe19940b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 09:20:23 -0400 Subject: [PATCH 206/429] Asset Pipe: Add Warning to UI if File has no Path --- scripts-blender/addons/asset_pipeline_2/ui.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 10e80575..766fd421 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -2,6 +2,7 @@ import bpy from . import core, constants from .transfer_data import transfer_ui +from pathlib import Path class ASSETPIPE_sync(bpy.types.Panel): @@ -20,9 +21,11 @@ class ASSETPIPE_sync(bpy.types.Panel): layout.operator("assetpipe.create_new_asset") # layout.operator("") return - layout.label( - text=f"Active Task Layer: {context.collection.name.split('.')[-1]}" - ) + + if not Path(bpy.data.filepath).exists: + layout.label(text="File is not saved", icon="ERROR") + return + layout.prop(asset_pipe, "asset_collection", text="Asset") layout.label(text="Test UI") -- 2.30.2 From 0d41e657e245461f9824c08c0917be890702c67d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 11:18:30 -0400 Subject: [PATCH 207/429] Asset Pipe: Restore Temp File if conflict is found on Pull --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 57b448c0..b65e21be 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -209,6 +209,8 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): ) if error_msg: + bpy.ops.wm.open_mainfile(filepath=temp_file.__str__()) + bpy.ops.wm.save_as_mainfile(filepath=current_file.__str__()) self.report({'ERROR'}, error_msg) return {'CANCELLED'} -- 2.30.2 From 61dc30b086e46bc5eb2bdc02ce2373d221aeaa9f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 11:18:53 -0400 Subject: [PATCH 208/429] Asset Pipe: Fix Typo in error message --- scripts-blender/addons/asset_pipeline_2/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index f5bb684a..3ab254a6 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -172,7 +172,7 @@ def merge_task_layer( if len(map.conflict_objects) != 0: error_msg = '' for conflict_obj in map.conflict_objects: - error_msg += f"Ownership conflic found for '{conflict_obj.name}'\n" + error_msg += f"Ownership conflict found for '{conflict_obj.name}'\n" return error_msg # Remove all transfer data from target objects -- 2.30.2 From c7ff9bb277678460b95f53c6ddca58b68becd851 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 12:05:08 -0400 Subject: [PATCH 209/429] Asset Pipe: Check if Prefix already exists when getting name with prefix --- scripts-blender/addons/asset_pipeline_2/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 3ab254a6..3b8e7d40 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -107,9 +107,10 @@ def get_task_layer_col_name(task_layer_key): return get_name_with_asset_prefix(task_layer_name) -def get_name_with_asset_prefix(name): +def get_name_with_asset_prefix(name: str): # TODO Docstring and return types - # TODO Add check if prefix already exists, don't add two prefixes + if name.startswith(asset_pipe.prefix + "."): + return name asset_pipe = bpy.context.scene.asset_pipeline prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" return prefix + name -- 2.30.2 From 9bffe617f51d4092da5afe1df3a23bc07c7e8aed Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 12:05:49 -0400 Subject: [PATCH 210/429] Asset Pipe: Clear Old TODO --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index ea66ff20..ffd4a533 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -106,7 +106,6 @@ class AssetTransferMapping: if tl_type[0] in self._local_tls ] - # TODO Include PREFIX checker in this stupidity for local_task_layer_col in self._local_col.children: if ( asset_suffix.get_basename(local_task_layer_col.name) -- 2.30.2 From b489cb9db8673489b4c6ad87364efc6041f3fdae Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 12:06:35 -0400 Subject: [PATCH 211/429] Asset Pipe: Clear Old TODOs --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 1 - scripts-blender/addons/asset_pipeline_2/core.py | 1 - 2 files changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index ea66ff20..ffd4a533 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -106,7 +106,6 @@ class AssetTransferMapping: if tl_type[0] in self._local_tls ] - # TODO Include PREFIX checker in this stupidity for local_task_layer_col in self._local_col.children: if ( asset_suffix.get_basename(local_task_layer_col.name) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 3b8e7d40..87ec8b6d 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -84,7 +84,6 @@ def get_invalid_objects( scene: bpy.types.Scene, ) -> list[bpy.types.Object]: # TODO Add Docstring - # TODO Make common function to get task layer col via task layer name task_layer_key = scene.asset_pipeline.task_layer_name task_layer_col_name = get_task_layer_col_name(task_layer_key) task_layer_col = local_col.children.get(task_layer_col_name) -- 2.30.2 From 27d7256cb7875677b3840b33abee07e52c0a8976 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 13:41:10 -0400 Subject: [PATCH 212/429] Asset Pipe: Fix Get_name_with_asset_prefix() --- scripts-blender/addons/asset_pipeline_2/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 87ec8b6d..758367e0 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -108,9 +108,9 @@ def get_task_layer_col_name(task_layer_key): def get_name_with_asset_prefix(name: str): # TODO Docstring and return types + asset_pipe = bpy.context.scene.asset_pipeline if name.startswith(asset_pipe.prefix + "."): return name - asset_pipe = bpy.context.scene.asset_pipeline prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" return prefix + name -- 2.30.2 From 28f51c2c1d31005b10790c5dcbf4f36bb7f1b3e1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 13:53:39 -0400 Subject: [PATCH 213/429] Asset Pipe: Include Material Index in Material Transfer Code --- .../addons/asset_pipeline_2/constants.py | 3 ++ .../transfer_data/transfer_functions.py | 33 ++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index c82c4d29..be4fb8b3 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -72,3 +72,6 @@ STAGED_PUBLISH_KEY = PUBLISH_KEYS[1] LOCAL_SUFFIX = "LOCAL" EXTERNAL_SUFFIX = "EXTERNAL" + + +MATERIAL_ATTRIBUTE_NAME = "material_index" diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 7733cba6..d74b9a75 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -47,6 +47,7 @@ def transfer_vertex_group( source_obj.vertex_groups.active = source_obj.vertex_groups.get(vertex_group_name) # TODO Debug crashing / use context.temp_override(object=obj) style + # https://projects.blender.org/blender/blender/issues/112299 context = bpy.context override = context.copy() override["selected_editable_objects"] = [target_obj, source_obj] @@ -295,11 +296,7 @@ def material_slots_clean(obj): if transfer_data_list != []: return - for mat_slot in obj.material_slots: - index = obj.material_slots.keys().index(mat_slot.name) - obj.active_material_index = index - with context.temp_override(object=obj): - bpy.ops.object.material_slot_remove() + obj.data.materials.clear() def material_slots_is_missing(transfer_info): @@ -330,25 +327,14 @@ def init_material_slots(scene, obj): ) -def transfer_material_slots(target_obj, source_obj): - # Delete existing material slot if exists - context = bpy.context - # Transfer material slot assignments. +def transfer_material_slots(target_obj: bpy.types.Object, source_obj): # Delete all material slots of target object. - while len(target_obj.material_slots) > len(source_obj.material_slots): - target_obj.active_material_index = len(source_obj.material_slots) - with context.temp_override(object=target_obj): - bpy.ops.object.material_slot_remove() + target_obj.data.materials.clear() # Transfer material slots for idx in range(len(source_obj.material_slots)): - if idx >= len(target_obj.material_slots): - with context.temp_override(object=target_obj): - bpy.ops.object.material_slot_add() + target_obj.data.materials.append(source_obj.material_slots[idx].material) target_obj.material_slots[idx].link = source_obj.material_slots[idx].link - target_obj.material_slots[idx].material = source_obj.material_slots[ - idx - ].material # Transfer active material slot index target_obj.active_material_index = source_obj.active_material_index @@ -358,6 +344,9 @@ def transfer_material_slots(target_obj, source_obj): for spl_to, spl_from in zip(target_obj.data.splines, source_obj.data.splines): spl_to.material_index = spl_from.material_index + if source_obj.data.attributes.get(constants.MATERIAL_ATTRIBUTE_NAME): + transfer_attribute(constants.MATERIAL_ATTRIBUTE_NAME, target_obj, source_obj) + # SHAPE KEYS @@ -515,7 +504,11 @@ def transfer_shape_key( def attributes_get_editable(attributes): # TODO replace 'position' HACK with is_required once https://projects.blender.org/blender/blender/pulls/111468 is merged return [ - item for item in attributes if not (item.is_internal or item.name == 'position') + item + for item in attributes + if not ( + item.is_internal or item.name == 'position' or item.name == 'material_index' + ) ] -- 2.30.2 From 59e13e0c28fb06db76c87c8467bde519c80eed72 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 14 Sep 2023 14:50:32 -0400 Subject: [PATCH 214/429] Asset Pipe: Test Commit --- scripts-blender/addons/asset_pipeline_2/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index be4fb8b3..4ce00a12 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -1,5 +1,5 @@ # TODO Tie this into props and generate based on JSON file instead - +# TODO Met to change this TASK_LAYER_TYPES = [ ("NONE", "None", ""), ("MODEL", "Modeling", ""), -- 2.30.2 From a8bdac98f587b368fbb4f263ee3c35680872deef Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Thu, 14 Sep 2023 21:53:23 +0200 Subject: [PATCH 215/429] Cleanup: Use dicts properly for constants This commit gets rid of get_dict_tuple_item(), which was designed to allow using tuple-lists that get fed into EnumProperty.items, as if they were a dict. Instead, we create separate versions of the data that's specifically transformed for the needs of EnumProperty.items, and leave the actual constants as intuitive dicts. --- .../addons/asset_pipeline_2/asset_mapping.py | 6 +- .../addons/asset_pipeline_2/constants.py | 84 ++++++++++--------- .../addons/asset_pipeline_2/core.py | 11 +-- .../addons/asset_pipeline_2/ops.py | 8 +- .../addons/asset_pipeline_2/props.py | 12 +-- .../transfer_data/transfer_ui.py | 13 +-- scripts-blender/addons/asset_pipeline_2/ui.py | 6 +- 7 files changed, 63 insertions(+), 77 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index ffd4a533..329fc405 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -101,9 +101,9 @@ class AssetTransferMapping: coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} local_tl_names = [ - core.get_name_with_asset_prefix(tl_type[1]) - for tl_type in constants.TASK_LAYER_TYPES - if tl_type[0] in self._local_tls + core.get_name_with_asset_prefix(tl_ui_name) + for tl_key, tl_ui_name in constants.TASK_LAYER_TYPES.items() + if tl_key in self._local_tls ] for local_task_layer_col in self._local_col.children: diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 4ce00a12..aa2084c4 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -1,49 +1,51 @@ # TODO Tie this into props and generate based on JSON file instead -# TODO Met to change this -TASK_LAYER_TYPES = [ - ("NONE", "None", ""), - ("MODEL", "Modeling", ""), - ("RIG", "Rigging", ""), - ("SHADE", "Shading", ""), + +# Information about configured task layers. +# Task Layers are not much more than a name in this dict; +# There is no behaviour that is specific to a particular task layer. +# You could even choose to name your task layers after artists in your team. +# {Task Layer Key: Collection/UI name} +TASK_LAYER_TYPES = { + "NONE": "None", + "MODEL": "Modeling", + "RIG": "Rigging", + "SHADE": "Shading", +} + +# Convert it to the format that EnumProperty.items wants: +# List of 3-tuples, re-use name as description at 3rd element. +TASK_LAYER_TYPES_ENUM_ITEMS = [ + (key, value, value) for key, value in TASK_LAYER_TYPES.items() ] -TASK_LAYER_KEYS = [task_layer[0] for task_layer in TASK_LAYER_TYPES] -TASK_LAYER_NAMES = [task_layer[1] for task_layer in TASK_LAYER_TYPES] +VERTEX_GROUP_KEY = "GROUP_VERTEX" +VERTEX_COLOR_KEY = "COLOR_ATTRIBUTE" +MODIFIER_KEY = "MODIFIER" +CONSTRAINT_KEY = "CONSTRAINT" +MATERIAL_SLOT_KEY = "MATERIAL" +UV_LAYERS_KEY = "UV_MAP" +SHAPE_KEY_KEY = "SHAPE_KEY" +ATTRIBUTE_KEY = "ATTRIBUTE" -# KEYS FOR TRANSFER DATA TYPE MATCH NAME OF ICON -TRANSFER_DATA_TYPES = [ - ("NONE", "None", ""), - ("GROUP_VERTEX", "Vertex Group", ""), - ("COLOR_ATTRIBUTE", "Color Attribute", ""), - ("MODIFIER", "Modifier", ""), - ("CONSTRAINT", "Constraint", ""), - ("MATERIAL", "Material Slot", ""), - ("UV_MAP", "UV Maps", ""), - ("SHAPE_KEY", "Shape Key", ""), - ("ATTRIBUTE", "Attribute", ""), -] +# Information about supported transferable data. +# {Key string : ("UI Name", 'ICON')} +TRANSFER_DATA_TYPES = { + "NONE": ("None", "BLANK1"), + VERTEX_GROUP_KEY: ("Vertex Group", 'GROUP_VERTEX'), + VERTEX_COLOR_KEY: ("Color Attribute", 'GROUP_VCOL'), + MODIFIER_KEY: ("Modifier", 'MODIFIER'), + CONSTRAINT_KEY: ("Constraint", 'CONSTRAINT'), + MATERIAL_SLOT_KEY: ("Material Slot", 'MATERIAL'), + UV_LAYERS_KEY: ("UV Maps", 'GROUP_UVS'), + SHAPE_KEY_KEY: ("Shape Key", 'SHAPEKEY_DATA'), + ATTRIBUTE_KEY: ("Attribute", 'EVENT_A'), +} -TRANSFER_DATA_KEYS = [transfer_data[0] for transfer_data in TRANSFER_DATA_TYPES] - -VERTEX_GROUP_KEY = TRANSFER_DATA_KEYS[1] -VERTEX_COLOR_KEY = TRANSFER_DATA_KEYS[2] -MODIFIER_KEY = TRANSFER_DATA_KEYS[3] -CONSTRAINT_KEY = TRANSFER_DATA_KEYS[4] -MATERIAL_SLOT_KEY = TRANSFER_DATA_KEYS[5] -UV_LAYERS_KEY = TRANSFER_DATA_KEYS[6] -SHAPE_KEY_KEY = TRANSFER_DATA_KEYS[7] -ATTRIBUTE_KEY = TRANSFER_DATA_KEYS[8] - -TRANSFER_DATA_ICONS = [ - ("None", "NONE", ""), - (VERTEX_GROUP_KEY, "GROUP_VERTEX", ""), - (VERTEX_COLOR_KEY, "GROUP_VCOL", ""), - (MODIFIER_KEY, "MODIFIER", ""), - (CONSTRAINT_KEY, "CONSTRAINT", ""), - (MATERIAL_SLOT_KEY, "MATERIAL", ""), - (UV_LAYERS_KEY, "GROUP_UVS", ""), - (SHAPE_KEY_KEY, "SHAPEKEY_DATA", ""), - (ATTRIBUTE_KEY, "EVENT_A", ""), +# Convert it to the format that EnumProperty.items wants: +# List of 5-tuples; Re-use name as description at 3rd element, add index at 5th. +TRANSFER_DATA_TYPES_ENUM_ITEMS = [ + (tup[0], tup[1][0], tup[1][0], tup[1][1], i) + for i, tup in enumerate(TRANSFER_DATA_TYPES.items()) ] MATERIAL_TRANSFER_INFO_NAME = "All Material Slots" diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 758367e0..dd5e15b3 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -21,7 +21,7 @@ def ownership_transfer_data_cleanup( transfer_data = obj.transfer_data_ownership to_remove = [] for transfer_info in transfer_data: - if constants.TASK_LAYER_KEYS[transfer_info["owner"]] == task_layer_name: + if transfer_info.owner == task_layer_name: if transfer_core.transfer_data_is_missing(transfer_info): to_remove.append(transfer_info.name) @@ -102,7 +102,7 @@ def get_invalid_objects( def get_task_layer_col_name(task_layer_key): # TODO Docstring and return types - task_layer_name = get_dict_tuple_item(constants.TASK_LAYER_TYPES, task_layer_key)[1] + task_layer_name = constants.TASK_LAYER_TYPES[task_layer_key] return get_name_with_asset_prefix(task_layer_name) @@ -339,10 +339,3 @@ def import_data_from_lib( ) return eval(f"bpy.data.{data_category}['{data_name}']") - - -def get_dict_tuple_item(dict: dict, key: str) -> tuple: - """For a dict of tuples, returns a dict item based on it's key""" - for item in dict: - if item[0] == key: - return item diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index b65e21be..88fb1c7b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -64,14 +64,14 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.name = self._name asset_pipe.prefix = self._prefix - for task_layer_key in constants.TASK_LAYER_KEYS: + for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE": continue col_name = core.get_task_layer_col_name(task_layer_key) bpy.data.collections.new(col_name) asset_col.children.link(bpy.data.collections.get(col_name)) - for task_layer_key in reversed(constants.TASK_LAYER_KEYS): + for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE": continue name = self._name + "." + task_layer_key + ".blend" @@ -79,7 +79,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.task_layer_name = task_layer_key bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) - # Creata intial publish based on task layers + # Create intial publish based on task layers. asset_pipe.task_layer_name = "NONE" publish_path = os.path.join(asset_path, constants.ACTIVE_PUBLISH_KEY) name = self._name + "." + "v001" + ".blend" @@ -236,7 +236,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): local_tls = [ task_layer - for task_layer in constants.TASK_LAYER_KEYS + for task_layer in constants.TASK_LAYER_TYPES.keys() if task_layer != task_layer_key ] diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index c6ff124b..9f3015cd 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -11,11 +11,11 @@ class AssetTransferData(bpy.types.PropertyGroup): owner: bpy.props.EnumProperty( name="Transfer Data Owner", - items=constants.TASK_LAYER_TYPES, + items=constants.TASK_LAYER_TYPES_ENUM_ITEMS, ) type: bpy.props.EnumProperty( name="Transfer Data Type", - items=constants.TRANSFER_DATA_TYPES, + items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) @@ -25,11 +25,11 @@ class AssetTransferDataTemp(bpy.types.PropertyGroup): owner: bpy.props.EnumProperty( name="Transfer Data Owner", - items=constants.TASK_LAYER_TYPES, + items=constants.TASK_LAYER_TYPES_ENUM_ITEMS, ) type: bpy.props.EnumProperty( name="Transfer Data Type", - items=constants.TRANSFER_DATA_TYPES, + items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) obj: bpy.props.PointerProperty(type=bpy.types.Object) @@ -53,7 +53,7 @@ class AssetPipeline(bpy.types.PropertyGroup): # TODO Rename to Current_Task_Layer task_layer_name: bpy.props.EnumProperty( - name="Task Layer Name", items=constants.TASK_LAYER_TYPES + name="Task Layer Name", items=constants.TASK_LAYER_TYPES_ENUM_ITEMS ) def add_temp_trasnfer_data(self, name, owner, type, obj): @@ -94,7 +94,7 @@ def register(): bpy.types.Scene.asset_pipeline = bpy.props.PointerProperty(type=AssetPipeline) bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", - items=constants.TASK_LAYER_TYPES, + items=constants.TASK_LAYER_TYPES_ENUM_ITEMS, ) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 32b047bc..9167d06f 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -8,19 +8,12 @@ def draw_transfer_data_type( """Draw UI Element for items of a transfer data type""" if transfer_data == []: return - name = core.get_dict_tuple_item( - constants.TRANSFER_DATA_TYPES, transfer_data[0].type - )[1] + name, icon = constants.TRANSFER_DATA_TYPES[transfer_data[0].type] box = layout.box() - icon = core.get_dict_tuple_item( - constants.TRANSFER_DATA_ICONS, transfer_data[0].type - )[1] box.label(text=name, icon=icon) for transfer_info in transfer_data: - owner = core.get_dict_tuple_item( - constants.TASK_LAYER_TYPES, transfer_info.owner - )[1] - box.label(text=f"{transfer_info.name}: '{owner}'") + owner_tl_ui_name = constants.TASK_LAYER_TYPES[transfer_info.owner] + box.label(text=f"{transfer_info.name}: '{owner_tl_ui_name}'") def draw_transfer_data( diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 766fd421..ec291c96 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -65,10 +65,8 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): obj = context.active_object transfer_data = obj.transfer_data_ownership layout = layout.box() - owner = core.get_dict_tuple_item( - constants.TASK_LAYER_TYPES, obj.asset_id_owner - )[1] - layout.label(text=f"{obj.name}: '{owner}'", icon="OBJECT_DATA") + owner_tl_ui_name = constants.TASK_LAYER_TYPES[obj.asset_id_owner] + layout.label(text=f"{obj.name}: '{owner_tl_ui_name}'", icon="OBJECT_DATA") transfer_ui.draw_transfer_data(transfer_data, layout) -- 2.30.2 From 93fd3313939e7ca7e9a5c917c153c1f4eb898aaf Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Thu, 14 Sep 2023 21:55:31 +0200 Subject: [PATCH 216/429] Not all object types support materials.clear() --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index d74b9a75..abef6078 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -296,7 +296,8 @@ def material_slots_clean(obj): if transfer_data_list != []: return - obj.data.materials.clear() + if obj.data and hasattr(obj.data, 'materials'): + obj.data.materials.clear() def material_slots_is_missing(transfer_info): -- 2.30.2 From f2cf53cb6bd53c4b0a18e9f7b06aa961010a4c59 Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Thu, 14 Sep 2023 22:20:31 +0200 Subject: [PATCH 217/429] Make starting file on asset creation explicit --- scripts-blender/addons/asset_pipeline_2/constants.py | 11 +++++++---- scripts-blender/addons/asset_pipeline_2/ops.py | 8 +++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index aa2084c4..87d2a933 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -1,7 +1,6 @@ # TODO Tie this into props and generate based on JSON file instead -# Information about configured task layers. -# Task Layers are not much more than a name in this dict; +# Information about the list of task layers. # There is no behaviour that is specific to a particular task layer. # You could even choose to name your task layers after artists in your team. # {Task Layer Key: Collection/UI name} @@ -9,15 +8,19 @@ TASK_LAYER_TYPES = { "NONE": "None", "MODEL": "Modeling", "RIG": "Rigging", - "SHADE": "Shading", + "SHADE": "Shading" } +# When creating a new asset, start in this task layer's file. +STARTING_FILE = 'MODEL' + # Convert it to the format that EnumProperty.items wants: # List of 3-tuples, re-use name as description at 3rd element. TASK_LAYER_TYPES_ENUM_ITEMS = [ (key, value, value) for key, value in TASK_LAYER_TYPES.items() ] +NONE_KEY = "NONE" VERTEX_GROUP_KEY = "GROUP_VERTEX" VERTEX_COLOR_KEY = "COLOR_ATTRIBUTE" MODIFIER_KEY = "MODIFIER" @@ -30,7 +33,7 @@ ATTRIBUTE_KEY = "ATTRIBUTE" # Information about supported transferable data. # {Key string : ("UI Name", 'ICON')} TRANSFER_DATA_TYPES = { - "NONE": ("None", "BLANK1"), + NONE_KEY: ("None", "BLANK1"), VERTEX_GROUP_KEY: ("Vertex Group", 'GROUP_VERTEX'), VERTEX_COLOR_KEY: ("Color Attribute", 'GROUP_VCOL'), MODIFIER_KEY: ("Modifier", 'MODIFIER'), diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 88fb1c7b..482f9759 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -64,6 +64,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.name = self._name asset_pipe.prefix = self._prefix + # Create the collections for each task layer. for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE": continue @@ -71,12 +72,16 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): bpy.data.collections.new(col_name) asset_col.children.link(bpy.data.collections.get(col_name)) + starting_file = "" + # Create the file for each task layer. for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE": continue name = self._name + "." + task_layer_key + ".blend" task_layer_file = os.path.join(asset_path, name) asset_pipe.task_layer_name = task_layer_key + if task_layer_key == constants.STARTING_FILE: + starting_file = task_layer_file bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) # Create intial publish based on task layers. @@ -85,7 +90,8 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): name = self._name + "." + "v001" + ".blend" publish_file = os.path.join(publish_path, name) bpy.ops.wm.save_as_mainfile(filepath=publish_file, copy=True) - bpy.ops.wm.open_mainfile(filepath=task_layer_file) + if starting_file: + bpy.ops.wm.open_mainfile(filepath=starting_file) return {'FINISHED'} -- 2.30.2 From e9f7278744c8fdfacf207ee902272afea3e399a0 Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Fri, 15 Sep 2023 18:54:43 +0200 Subject: [PATCH 218/429] Asset Pipe: Shape Key Transfer improvements - If there are no shape keys at all, is_missing should return True. - Raise Exception if shape key order is wrong. - This avoids need for (bpy.ops) shape_key_move. - Don't remove a key if it exists, it's gonna get overwritten anyway. - Add missing properties: slider_min/max, mute The fact that Basis is owned by anybody is a bit weird, but not catastrophic for now... Just means you can screw up other task layers if they have shape keys and you pull Basis out from under them, lol. Just don't do that. --- .../transfer_data/transfer_core.py | 2 +- .../transfer_data/transfer_functions.py | 86 ++++++++++--------- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 355e555e..5497c196 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -73,7 +73,7 @@ def init_transfer_data( transfer_functions.init_constraints(scene, obj) # transfer_functions.init_vertex_colors(scene, obj) # transfer_functions.init_uv_layers(scene, obj) - transfer_functions.init_shap_keys(scene, obj) + transfer_functions.init_shape_keys(scene, obj) transfer_functions.init_attributes(scene, obj) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index abef6078..33bc273e 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -358,13 +358,6 @@ def shape_key_set_active(obj, shape_key_name): obj.active_shape_key_index = index -def shape_key_move(context, obj, shape_key_name, top=True): - move_type = "TOP" if top else "BOTTOM" - shape_key_set_active(obj, shape_key_name) - with context.temp_override(object=obj): - bpy.ops.object.shape_key_move(type=move_type) - - def shape_key_closest_face_to_point(bm_source, p_target, bvh_tree=None): if not bvh_tree: bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) @@ -409,18 +402,18 @@ def shape_keys_clean(obj): obj.transfer_data_ownership, constants.SHAPE_KEY_KEY ) for shape_key in obj.data.shape_keys.key_blocks: - # Move Shape Keys relative to themselves to the top (usually basis key) - if shape_key.relative_key == shape_key: - shape_key_move(context, obj, shape_key.name) - if not asset_suffix.get_basename(shape_key.name) in transfer_data_list: obj.shape_key_remove(shape_key) def shape_key_is_missing(transfer_info): - obj = transfer_info.id_data - if obj.type != "MESH" or obj.data.shape_keys is None: + if not transfer_info.type == constants.SHAPE_KEY_KEY: return + obj = transfer_info.id_data + if obj.type != 'MESH': + return + if not obj.data.shape_keys: + return True return transfer_core.transfer_info_is_missing( transfer_info, constants.SHAPE_KEY_KEY, @@ -428,9 +421,18 @@ def shape_key_is_missing(transfer_info): ) -def init_shap_keys(scene, obj): +def init_shape_keys(scene, obj): if obj.type != "MESH" or obj.data.shape_keys is None: return + + # Check that the order is legal. + # Key Blocks must be ordered after the key they are Relative To. + for i, kb in enumerate(obj.data.shape_keys.key_blocks): + if kb.relative_key: + base_shape_idx = obj.data.shape_keys.key_blocks.find(kb.relative_key.name) + if base_shape_idx > i: + raise Exception(f'Shape Key "{kb.name}" must be ordered after its base shape "{kb.relative_key.name}" on object "{obj.name}".') + transfer_core.transfer_info_init( scene, obj, obj.data.shape_keys.key_blocks, constants.SHAPE_KEY_KEY ) @@ -442,42 +444,48 @@ def transfer_shape_key( target_obj: bpy.types.Object, source_obj: bpy.types.Object, ): - # BASIS SHAPE KEY MUST BE PASSED FIRST OTHERWISE THIS WILL ERROR OUT + if not source_obj.data.shape_keys: + return sk_source = source_obj.data.shape_keys.key_blocks.get(shape_key_name) - print(f"Moving shape key: {shape_key_name}") + assert sk_source - # If key is relative to another key that doesn't exist yet - if sk_source.relative_key != sk_source: - relative_key = target_obj.data.shape_keys.key_blocks.get( - sk_source.relative_key.name - ) - if not relative_key: - print( - f"Shape Key '{sk_source.name}' failed to find Relative Key '{sk_source.relative_key.name}' on Object '{target_obj.name}'" - ) - return + sk_target = None + if not target_obj.data.shape_keys: + sk_target = target_obj.shape_key_add() + if not sk_target: + sk_target = target_obj.data.shape_keys.key_blocks.get(shape_key_name) + if not sk_target: + sk_target = target_obj.shape_key_add() - # Remove existing shape keys that match name - if target_obj.data.shape_keys is not None: - old_sk = target_obj.data.shape_keys.key_blocks.get(shape_key_name) - if old_sk: - target_obj.shape_key_remove(old_sk) - - sk_target = target_obj.shape_key_add() sk_target.name = sk_source.name sk_target.vertex_group = sk_source.vertex_group - sk_target.relative_key = target_obj.data.shape_keys.key_blocks[ - sk_source.relative_key.name - ] + if sk_source.relative_key != sk_source: + relative_key = None + if target_obj.data.shape_keys: + relative_key = target_obj.data.shape_keys.key_blocks.get( + sk_source.relative_key.name + ) + if relative_key: + sk_target.relative_key = relative_key + else: + # If the base shape of one of our shapes was removed by another task layer, + # the result will probably be pretty bad, but it's not a catastrophic failure. + # Proceed with a warning. + print( + f'Warning: Base shape "{sk_source.relative_key.name}" of Key "{sk_source.name}" was removed from "{target_obj.name}"' + ) + + sk_target.slider_min = sk_source.slider_min + sk_target.slider_max = sk_source.slider_max + sk_target.value = sk_source.value + sk_target.mute = sk_source.mute bm_source = bmesh.new() bm_source.from_mesh(source_obj.data) bm_source.faces.ensure_lookup_table() bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - tris_dict = shape_key_tris_per_face(bm_source) - for i, vert in enumerate(target_obj.data.vertices): p = vert.co face = shape_key_closest_face_to_point(bm_source, p, bvh_tree) @@ -577,7 +585,7 @@ def transfer_attribute( type=source_attribute.data_type, domain=source_attribute.domain, ) - print(f"Transferring Attribute{attribute_name}") + # print(f"Transfering Attribute {attribute_name}") for source_data_item in source_attribute.data.items(): index = source_data_item[0] source_data = source_data_item[1] -- 2.30.2 From 9e21359066ca2e3d58ba4b04a8463029820069fd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 15 Sep 2023 17:52:28 -0400 Subject: [PATCH 219/429] Aasset Pipe: Add Ensure Visability contextmanager --- .../addons/asset_pipeline_2/visability.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 scripts-blender/addons/asset_pipeline_2/visability.py diff --git a/scripts-blender/addons/asset_pipeline_2/visability.py b/scripts-blender/addons/asset_pipeline_2/visability.py new file mode 100644 index 00000000..8154c6cc --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/visability.py @@ -0,0 +1,46 @@ +import bpy +import contextlib + +from typing import Optional + + +def get_visibility_driver(obj) -> Optional[bpy.types.FCurve]: + obj = bpy.data.objects.get(obj.name) + assert obj, "Object was renamed while its visibility was being ensured?" + if hasattr(obj, "animation_data") and obj.animation_data: + return obj.animation_data.drivers.find("hide_viewport") + + +@contextlib.contextmanager +def override_obj_visability(obj): + hide = obj.hide_get() # eye icon + hide_viewport = obj.hide_viewport # hide viewport + select = obj.hide_select # selectable + + driver = get_visibility_driver(obj) + if driver: + driver_mute = driver.mute + + try: + obj.hide_set(False) + obj.hide_viewport = False + obj.hide_select = False + if driver: + driver.mute = True + + assigned_to_scene_root = False + if obj.name not in bpy.context.scene.collection.objects: + assigned_to_scene_root = True + bpy.context.scene.collection.objects.link(obj) # TODO Pass Current Scene + + yield + + finally: + obj.hide_set(hide) + obj.hide_viewport = hide_viewport + obj.hide_select = select + if driver: + driver.mute = driver_mute + + if assigned_to_scene_root and obj.name in bpy.context.scene.collection.objects: + bpy.context.scene.collection.objects.unlink(obj) # TODO Pass Current Scene -- 2.30.2 From 2a6345571c72890323122d2476ed9c5bbc9db9ca Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 15 Sep 2023 19:14:14 -0400 Subject: [PATCH 220/429] Asset Pipe: Add Parent to Transfer Functions --- .../addons/asset_pipeline_2/constants.py | 5 +- .../transfer_data/transfer_core.py | 8 +++ .../transfer_data/transfer_functions.py | 50 ++++++++++++++++++- .../transfer_data/transfer_ui.py | 4 ++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 87d2a933..1fce599d 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -8,7 +8,7 @@ TASK_LAYER_TYPES = { "NONE": "None", "MODEL": "Modeling", "RIG": "Rigging", - "SHADE": "Shading" + "SHADE": "Shading", } # When creating a new asset, start in this task layer's file. @@ -29,6 +29,7 @@ MATERIAL_SLOT_KEY = "MATERIAL" UV_LAYERS_KEY = "UV_MAP" SHAPE_KEY_KEY = "SHAPE_KEY" ATTRIBUTE_KEY = "ATTRIBUTE" +PARENT_KEY = "PARENT" # Information about supported transferable data. # {Key string : ("UI Name", 'ICON')} @@ -42,6 +43,7 @@ TRANSFER_DATA_TYPES = { UV_LAYERS_KEY: ("UV Maps", 'GROUP_UVS'), SHAPE_KEY_KEY: ("Shape Key", 'SHAPEKEY_DATA'), ATTRIBUTE_KEY: ("Attribute", 'EVENT_A'), + PARENT_KEY: ("Parent", 'FILE_PARENT'), } # Convert it to the format that EnumProperty.items wants: @@ -52,6 +54,7 @@ TRANSFER_DATA_TYPES_ENUM_ITEMS = [ ] MATERIAL_TRANSFER_INFO_NAME = "All Material Slots" +PARENT_TRANSFER_INFO_NAME = "Parent Relationship" PUBLISH_TYPES = [ ( diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 5497c196..e454fad4 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -33,6 +33,7 @@ def transfer_data_clean(obj): transfer_functions.material_slots_clean(obj) transfer_functions.shape_keys_clean(obj) transfer_functions.attribute_clean(obj) + transfer_functions.parent_clean(obj) def transfer_data_is_missing(transfer_data_item) -> bool: @@ -53,6 +54,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: # or transfer_functions.uv_layer_is_missing(transfer_data_item) or transfer_functions.shape_key_is_missing(transfer_data_item) or transfer_functions.attribute_is_missing(transfer_data_item) + or transfer_functions.parent_is_missing(transfer_data_item) ) @@ -75,6 +77,7 @@ def init_transfer_data( # transfer_functions.init_uv_layers(scene, obj) transfer_functions.init_shape_keys(scene, obj) transfer_functions.init_attributes(scene, obj) + transfer_functions.init_parent(scene, obj) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: @@ -153,6 +156,11 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: source_obj=source_obj, attribute_name=transfer_info.name, ) + if transfer_info.type == constants.PARENT_KEY: + transfer_functions.transfer_parent( + target_obj=target_obj, + source_obj=source_obj, + ) copy_transfer_data_ownership( transfer_data_item=transfer_info, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 33bc273e..f7d8f93a 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -431,7 +431,9 @@ def init_shape_keys(scene, obj): if kb.relative_key: base_shape_idx = obj.data.shape_keys.key_blocks.find(kb.relative_key.name) if base_shape_idx > i: - raise Exception(f'Shape Key "{kb.name}" must be ordered after its base shape "{kb.relative_key.name}" on object "{obj.name}".') + raise Exception( + f'Shape Key "{kb.name}" must be ordered after its base shape "{kb.relative_key.name}" on object "{obj.name}".' + ) transfer_core.transfer_info_init( scene, obj, obj.data.shape_keys.key_blocks, constants.SHAPE_KEY_KEY @@ -595,3 +597,49 @@ def transfer_attribute( for key in list(keys): target_data = target_attribute.data[index] setattr(target_data, key, getattr(source_data, key)) + + +def parent_clean(obj): + transfer_data_list = transfer_core.get_transfer_data_as_names( + obj.transfer_data_ownership, constants.PARENT_KEY + ) + + if transfer_data_list != []: + return + + obj.parent = None + print("Cleaning Parent Relationship") + + +def parent_is_missing(transfer_info): + if ( + transfer_info.type == constants.PARENT_KEY + and transfer_info.id_data.parent == None + ): + return True + + +def init_parent(scene, obj): + task_layer_key = scene.asset_pipeline.task_layer_name + type_key = constants.PARENT_KEY + name = constants.PARENT_TRANSFER_INFO_NAME + transfer_data = obj.transfer_data_ownership + + # Only Execute if Material Slots exist on object + if obj.parent == None: + return + matches = transfer_core.check_transfer_data_entry(transfer_data, name, type_key) + # Only add new ownership transfer_info if vertex group doesn't have an owner + if len(matches) == 0: + scene.asset_pipeline.add_temp_trasnfer_data( + name=name, + owner=task_layer_key, + type=type_key, + obj=obj, + ) + + +def transfer_parent(target_obj, source_obj): + target_obj.parent = source_obj.parent + # TODO Test if this works in all cases + target_obj.matrix_parent_inverse = source_obj.parent.matrix_world.inverted() diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 9167d06f..6283aca0 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -28,6 +28,7 @@ def draw_transfer_data( # uv_layers = [] shape_keys = [] attributes = [] + parent = [] for transfer_info in transfer_data: if transfer_info.type == constants.VERTEX_GROUP_KEY: @@ -46,6 +47,8 @@ def draw_transfer_data( shape_keys.append(transfer_info) if transfer_info.type == constants.ATTRIBUTE_KEY: attributes.append(transfer_info) + if transfer_info.type == constants.PARENT_KEY: + parent.append(transfer_info) draw_transfer_data_type(layout, vertex_groups) # draw_transfer_data_type(layout, vertex_colors) @@ -55,3 +58,4 @@ def draw_transfer_data( # draw_transfer_data_type(layout, uv_layers) draw_transfer_data_type(layout, shape_keys) draw_transfer_data_type(layout, attributes) + draw_transfer_data_type(layout, parent) -- 2.30.2 From ccfb664868f7e8a054d8c852a3e1ec000c916bb4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 19 Sep 2023 09:07:18 -0400 Subject: [PATCH 221/429] Asset Pipe: Fix Typo --- .../addons/asset_pipeline_2/{visability.py => visibility.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts-blender/addons/asset_pipeline_2/{visability.py => visibility.py} (100%) diff --git a/scripts-blender/addons/asset_pipeline_2/visability.py b/scripts-blender/addons/asset_pipeline_2/visibility.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/visability.py rename to scripts-blender/addons/asset_pipeline_2/visibility.py -- 2.30.2 From c2b84695e4a14a602f8704d273ed1d7f004aecb1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 20 Sep 2023 16:16:02 -0400 Subject: [PATCH 222/429] Asset Pipe: Add Driver Copy to Modifiers --- .../addons/asset_pipeline_2/core.py | 53 +++++++++++++++++++ .../transfer_data/transfer_functions.py | 3 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index dd5e15b3..548c5523 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -8,6 +8,8 @@ from .asset_mapping import AssetTransferMapping from . import constants, util +from rigify.utils.misc import copy_attributes + def ownership_transfer_data_cleanup( obj: bpy.types.Object, task_layer_name: str @@ -339,3 +341,54 @@ def import_data_from_lib( ) return eval(f"bpy.data.{data_category}['{data_name}']") + + +## DRIVERS + + +def copy_driver(from_fcurve, obj, data_path=None, index=None): + if not data_path: + data_path = from_fcurve.data_path + + new_fc = None + if index: + new_fc = obj.driver_add(data_path, index) + else: + new_fc = obj.driver_add(data_path) + + copy_attributes(from_fcurve, new_fc) + copy_attributes(from_fcurve.driver, new_fc.driver) + + # Remove default modifiers, variables, etc. + for m in new_fc.modifiers: + new_fc.modifiers.remove(m) + for v in new_fc.driver.variables: + new_fc.driver.variables.remove(v) + + # Copy modifiers + for m1 in from_fcurve.modifiers: + m2 = new_fc.modifiers.new(type=m1.type) + copy_attributes(m1, m2) + + # Copy variables + for v1 in from_fcurve.driver.variables: + v2 = new_fc.driver.variables.new() + copy_attributes(v1, v2) + for i in range(len(v1.targets)): + copy_attributes(v1.targets[i], v2.targets[i]) + + return new_fc + + +def find_drivers(obj, target_type, target_name): + drivers = [] + for driver in obj.animation_data.drivers: + if f'{target_type}["{target_name}"]' in driver.data_path: + drivers.append(driver) + return drivers + + +def copy_all_drivers(source_obj, target_obj, driver_target_type, driver_target_name): + fcurves = find_drivers(source_obj, driver_target_type, driver_target_name) + for fcurve in fcurves: + copy_driver(from_fcurve=fcurve, obj=target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index f7d8f93a..caa0f746 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,7 +1,7 @@ import bpy from bpy import context from . import transfer_core -from .. import asset_suffix, constants, util +from .. import asset_suffix, constants, util, core import mathutils import bmesh import numpy as np @@ -224,6 +224,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): {"object": target_obj, "active_object": target_obj}, modifier=mod.name, ) + core.copy_all_drivers(source_obj, target_obj, 'modifiers', modifier_name) # CONSTRAINTS -- 2.30.2 From 4578dc67df5c9795008fd13aa3d6b53c647ef5b4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 20 Sep 2023 17:30:14 -0400 Subject: [PATCH 223/429] Asset Pipe: Make Driver Copy more Generic --- .../addons/asset_pipeline_2/core.py | 22 +++++++------------ .../transfer_data/transfer_functions.py | 6 ++++- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 548c5523..dafacb49 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -346,15 +346,15 @@ def import_data_from_lib( ## DRIVERS -def copy_driver(from_fcurve, obj, data_path=None, index=None): +def copy_driver(from_fcurve, target, data_path=None, index=None): if not data_path: data_path = from_fcurve.data_path new_fc = None if index: - new_fc = obj.driver_add(data_path, index) + new_fc = target.driver_add(data_path, index) else: - new_fc = obj.driver_add(data_path) + new_fc = target.driver_add(data_path) copy_attributes(from_fcurve, new_fc) copy_attributes(from_fcurve.driver, new_fc.driver) @@ -380,15 +380,9 @@ def copy_driver(from_fcurve, obj, data_path=None, index=None): return new_fc -def find_drivers(obj, target_type, target_name): - drivers = [] - for driver in obj.animation_data.drivers: +def find_drivers(drivers, target_type, target_name): + found_drivers = [] + for driver in drivers: if f'{target_type}["{target_name}"]' in driver.data_path: - drivers.append(driver) - return drivers - - -def copy_all_drivers(source_obj, target_obj, driver_target_type, driver_target_name): - fcurves = find_drivers(source_obj, driver_target_type, driver_target_name) - for fcurve in fcurves: - copy_driver(from_fcurve=fcurve, obj=target_obj) + found_drivers.append(driver) + return found_drivers diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index caa0f746..597661a9 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -224,7 +224,11 @@ def transfer_modifier(modifier_name, target_obj, source_obj): {"object": target_obj, "active_object": target_obj}, modifier=mod.name, ) - core.copy_all_drivers(source_obj, target_obj, 'modifiers', modifier_name) + fcurves = core.find_drivers( + source_obj.animation_data.drivers, 'modifiers', modifier_name + ) + for fcurve in fcurves: + core.copy_driver(from_fcurve=fcurve, target=target_obj) # CONSTRAINTS -- 2.30.2 From 8b56e31b772aaf4b8aa4153134658337df2f22fe Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 20 Sep 2023 17:31:02 -0400 Subject: [PATCH 224/429] Asset Pipe: Copy Shape Key Drivers --- .../asset_pipeline_2/transfer_data/transfer_functions.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 597661a9..335acbe2 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -514,6 +514,13 @@ def transfer_shape_key( ] val = mathutils.Vector(sum(np.array(vals_weighted))) sk_target.data[i].co = vert.co + val + fcurves = core.find_drivers( + source_obj.data.shape_keys.animation_data.drivers, + 'key_blocks', + shape_key_name, + ) + for fcurve in fcurves: + core.copy_driver(from_fcurve=fcurve, target=target_obj.data.shape_keys) # ATTRIBUTE -- 2.30.2 From 22fe13714876be2b43081a0c21ed20cb8879b8d2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 20 Sep 2023 20:45:46 -0400 Subject: [PATCH 225/429] Asset Pipe: Copy Constraint Drivers --- .../transfer_data/transfer_functions.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 335acbe2..4554965b 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -224,6 +224,8 @@ def transfer_modifier(modifier_name, target_obj, source_obj): {"object": target_obj, "active_object": target_obj}, modifier=mod.name, ) + if source_obj.animation_data is None: + return fcurves = core.find_drivers( source_obj.animation_data.drivers, 'modifiers', modifier_name ) @@ -288,6 +290,15 @@ def transfer_constraint(constraint_name, target_obj, source_obj): new_target.target = target_item.target new_target.subtarget = target_item.subtarget + if source_obj.animation_data is None: + return + fcurves = core.find_drivers( + source_obj.animation_data.drivers, 'constraints', constraint_name + ) + + for fcurve in fcurves: + core.copy_driver(from_fcurve=fcurve, target=target_obj) + # MATERIAL SLOT def material_slots_clean(obj): -- 2.30.2 From 283c3083bf3dd303ed9d5eb7ec1b9388495c3df6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 20 Sep 2023 21:06:22 -0400 Subject: [PATCH 226/429] Asset Pipe: Fix Name Conflict Bug --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 329fc405..3216da34 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -146,7 +146,9 @@ class AssetTransferMapping: 'source_obj': obj, 'target_obj': target_obj, } - name = transfer_info.name + '_' + obj.name + # Names of each map item need to be unique + # below name avoids name conflicts between different types + name = transfer_info.name + '_' + transfer_info.type + obj.name return name, map_item def _check_transfer_data_conflict(self, obj, transfer_info): -- 2.30.2 From 10ebc16227079f4c9dc59b9e789fc04b02c2322a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 20 Sep 2023 21:07:03 -0400 Subject: [PATCH 227/429] Asset Pipe: Improve Copy Transfer Data Ownership --- .../asset_pipeline_2/transfer_data/transfer_core.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index e454fad4..07c57920 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -14,8 +14,12 @@ def copy_transfer_data_ownership( target_obj (bpy.types.Object): Object to add transfer data item to """ transfer_data = target_obj.transfer_data_ownership - transfer_items_names = [transfer_info.name for transfer_info in transfer_data] - if transfer_data_item.name not in transfer_items_names: + matches = check_transfer_data_entry( + transfer_data, + transfer_data_item.name, + transfer_data_item.type, + ) + if len(matches) == 0: transfer_data_add_entry( transfer_data, transfer_data_item.name, -- 2.30.2 From c7dd2a87dbde2026449ca5bed105a73c00c8aaa8 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 11:57:52 -0400 Subject: [PATCH 228/429] Asset Pipe: Add Remapping for Other IDs - Remap Node Groups - Remap Images --- .../addons/asset_pipeline_2/asset_mapping.py | 20 +++++++++++++++ .../addons/asset_pipeline_2/core.py | 25 +++++++++++++++++++ .../addons/asset_pipeline_2/ops.py | 8 ++++++ .../addons/asset_pipeline_2/props.py | 4 +-- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 3216da34..3fdc1de3 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -44,6 +44,7 @@ class AssetTransferMapping: self.object_map = self._gen_object_map() self.collection_map = self._gen_collection_map() self.transfer_data_map = self._gen_transfer_data_map() + self.other_id_map = self._gen_other_id_map() def _get_external_object(self, local_obj): external_obj_name = asset_suffix.get_target_name( @@ -205,3 +206,22 @@ class AssetTransferMapping: ) transfer_data_map[name] = map_item return transfer_data_map + + def _gen_other_id_map(self): + other_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} + for local_id in core.get_other_ids(self._local_col): + external_id_name = asset_suffix.get_target_name(local_id.name) + id_storage = util.get_storage_of_id(local_id) + external_id = id_storage.get(external_id_name) + # TODO Check for conflicts + if ( + local_id.asset_id_owner in self._local_tls + and local_id.asset_id_owner != "NONE" + ): + if external_id: + other_id_map[external_id] = local_id + else: + if external_id: + other_id_map[local_id] = external_id + + return other_id_map diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index dafacb49..ce87071b 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -9,6 +9,7 @@ from .asset_mapping import AssetTransferMapping from . import constants, util from rigify.utils.misc import copy_attributes +from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids def ownership_transfer_data_cleanup( @@ -192,6 +193,9 @@ def merge_task_layer( for col in map.collection_map: remap_user(col, map.collection_map[col]) + for id in map.other_id_map: + remap_user(id, map.other_id_map[id]) + bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) @@ -386,3 +390,24 @@ def find_drivers(drivers, target_type, target_name): if f'{target_type}["{target_name}"]' in driver.data_path: found_drivers.append(driver) return found_drivers + + +def get_other_ids(collection): # TODO find better name + ref_map = get_id_reference_map() + all_ids_of_coll = get_all_referenced_ids(collection, ref_map) + return [ + id + for id in all_ids_of_coll + if type(id) == bpy.types.NodeGroup or type(id) == bpy.types.Image + ] + + +def init_other_ids(scene): + other_ids = [] + asset_pipe = scene.asset_pipeline + local_col = asset_pipe.asset_collection + for id in get_other_ids(local_col): + if id.asset_id_owner == 'NONE': + id.asset_id_owner = asset_pipe.task_layer_name + other_ids.append(id) + return other_ids diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 482f9759..36f70fe0 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -103,6 +103,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): _temp_transfer_data = None _invalid_objs = [] + _other_ids = [] expand: bpy.props.BoolProperty( name="Show New Transfer Data", @@ -144,6 +145,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): # TODO Remove Invalid Objs Explicitly, some will be auto removed but not all self._invalid_objs = core.get_invalid_objects(local_col, context.scene) + self._other_ids = core.init_other_ids(context.scene) # Default behaviour is to pull before pushing if self.push: @@ -165,6 +167,12 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): for obj in self._invalid_objs: box.label(text=obj.name, icon="OBJECT_DATA") + if len(self._other_ids) != 0: + box = layout.box() + box.label(text="New 'Other IDs' found") + for id in self._other_ids: + box.label(text=id.name) + if len(self._temp_transfer_data) == 0: layout.label(text="No New Transfer Data found") else: diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 9f3015cd..8189a264 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -92,7 +92,7 @@ def register(): type=AssetTransferData ) bpy.types.Scene.asset_pipeline = bpy.props.PointerProperty(type=AssetPipeline) - bpy.types.Object.asset_id_owner = bpy.props.EnumProperty( + bpy.types.ID.asset_id_owner = bpy.props.EnumProperty( name="ID Owner", items=constants.TASK_LAYER_TYPES_ENUM_ITEMS, ) @@ -103,4 +103,4 @@ def unregister(): bpy.utils.unregister_class(i) del bpy.types.Object.transfer_data_ownership del bpy.types.Scene.asset_pipeline - del bpy.types.Object.asset_id_owner + del bpy.types.ID.asset_id_owner -- 2.30.2 From 87d4d3718495b77020396e0721b22c959960ed3a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 12:53:39 -0400 Subject: [PATCH 229/429] Asset Pipe: Clean-up All Imports --- .../addons/asset_pipeline_2/asset_mapping.py | 31 ++++++------ .../addons/asset_pipeline_2/asset_suffix.py | 4 +- .../addons/asset_pipeline_2/core.py | 50 +++++++++---------- .../addons/asset_pipeline_2/ops.py | 42 +++++++++------- .../transfer_data/transfer_functions.py | 24 +++++---- .../transfer_data/transfer_ui.py | 2 +- scripts-blender/addons/asset_pipeline_2/ui.py | 6 +-- 7 files changed, 83 insertions(+), 76 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 3fdc1de3..6c38d44f 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -1,8 +1,10 @@ import bpy from typing import Dict, Set - -from . import asset_suffix, constants, util, core -from .transfer_data import transfer_core +from . import core # TODO DEBUG WHY THIS DOESN'T WORK +from .asset_suffix import get_target_name, get_basename, get_name_with_asset_prefix +from .util import get_storage_of_id +from .transfer_data.transfer_core import transfer_data_add_entry +from . import constants class AssetTransferMapping: @@ -47,7 +49,7 @@ class AssetTransferMapping: self.other_id_map = self._gen_other_id_map() def _get_external_object(self, local_obj): - external_obj_name = asset_suffix.get_target_name( + external_obj_name = get_target_name( local_obj.name, ) external_obj = self._external_col.all_objects.get(external_obj_name) @@ -89,7 +91,7 @@ class AssetTransferMapping: # Find new objects to add to local_col for external_obj in self._external_col.all_objects: local_col_objs = self._local_col.all_objects - obj = local_col_objs.get(asset_suffix.get_target_name(external_obj.name)) + obj = local_col_objs.get(get_target_name(external_obj.name)) if not obj and external_obj.asset_id_owner not in self._local_tls: self.external_obj_to_add.add(external_obj) return object_map @@ -102,20 +104,15 @@ class AssetTransferMapping: coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} local_tl_names = [ - core.get_name_with_asset_prefix(tl_ui_name) + get_name_with_asset_prefix(tl_ui_name) for tl_key, tl_ui_name in constants.TASK_LAYER_TYPES.items() if tl_key in self._local_tls ] for local_task_layer_col in self._local_col.children: - if ( - asset_suffix.get_basename(local_task_layer_col.name) - not in local_tl_names - ): + if get_basename(local_task_layer_col.name) not in local_tl_names: # Replace source object suffix with target suffix to get target object. - external_col_name = asset_suffix.get_target_name( - local_task_layer_col.name - ) + external_col_name = get_target_name(local_task_layer_col.name) external_col = bpy.data.collections.get(external_col_name) if external_col: coll_map[local_task_layer_col] = external_col @@ -135,7 +132,7 @@ class AssetTransferMapping: def _get_transfer_data_map_item(self, obj, target_obj, transfer_info): temp_transfer_data = bpy.context.scene.asset_pipeline.temp_transfer_data temp_info_index = len(temp_transfer_data) - temp_info = transfer_core.transfer_data_add_entry( + temp_info = transfer_data_add_entry( transfer_data=temp_transfer_data, name=transfer_info.name, td_type=transfer_info.type, @@ -153,7 +150,7 @@ class AssetTransferMapping: return name, map_item def _check_transfer_data_conflict(self, obj, transfer_info): - other_obj = bpy.data.objects.get(asset_suffix.get_target_name(obj.name)) + other_obj = bpy.data.objects.get(get_target_name(obj.name)) check_transfer_info = None if not other_obj: return @@ -210,8 +207,8 @@ class AssetTransferMapping: def _gen_other_id_map(self): other_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} for local_id in core.get_other_ids(self._local_col): - external_id_name = asset_suffix.get_target_name(local_id.name) - id_storage = util.get_storage_of_id(local_id) + external_id_name = get_target_name(local_id.name) + id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) # TODO Check for conflicts if ( diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py index 883906bc..83f960a1 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_suffix.py @@ -20,8 +20,8 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids -from . import constants from .util import get_storage_of_id +from . import constants DELIMITER = "." @@ -64,7 +64,7 @@ def get_basename(name: str) -> str: constants.EXTERNAL_SUFFIX ): return DELIMITER.join(name.split(DELIMITER)[:-1]) - return name + return name def remove_suffix_from_hierarchy(collection: bpy.types.Collection) -> None: diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index ce87071b..0947883f 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -1,15 +1,24 @@ import bpy -from .transfer_data import transfer_core +from .asset_mapping import AssetTransferMapping +from .transfer_data.transfer_core import ( + init_transfer_data, + transfer_data_is_missing, + transfer_data_add_entry, + apply_transfer_data, + transfer_data_clean, +) -from . import asset_suffix +from .asset_suffix import ( + add_suffix_to_hierarchy, + remove_suffix_from_hierarchy, + get_name_with_asset_prefix, +) + + +from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from pathlib import Path from typing import Dict -from .asset_mapping import AssetTransferMapping - -from . import constants, util - -from rigify.utils.misc import copy_attributes -from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids +from . import constants def ownership_transfer_data_cleanup( @@ -25,7 +34,7 @@ def ownership_transfer_data_cleanup( to_remove = [] for transfer_info in transfer_data: if transfer_info.owner == task_layer_name: - if transfer_core.transfer_data_is_missing(transfer_info): + if transfer_data_is_missing(transfer_info): to_remove.append(transfer_info.name) for name in to_remove: @@ -64,7 +73,7 @@ def ownership_get( if obj.asset_id_owner == "NONE": continue ownership_transfer_data_cleanup(obj, task_layer_key) - transfer_core.init_transfer_data(scene, obj) + init_transfer_data(scene, obj) def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: @@ -77,7 +86,7 @@ def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: """ for transfer_info in temp_transfer_data: transfer_data = transfer_info.obj.transfer_data_ownership - transfer_core.transfer_data_add_entry( + transfer_data_add_entry( transfer_data, transfer_info.name, transfer_info.type, transfer_info.owner ) @@ -109,15 +118,6 @@ def get_task_layer_col_name(task_layer_key): return get_name_with_asset_prefix(task_layer_name) -def get_name_with_asset_prefix(name: str): - # TODO Docstring and return types - asset_pipe = bpy.context.scene.asset_pipeline - if name.startswith(asset_pipe.prefix + "."): - return name - prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" - return prefix + name - - def remap_user(source_datablock: bpy.data, target_datablock: bpy.data) -> None: """Remap datablock and append name to datablock that has been remapped @@ -156,10 +156,10 @@ def merge_task_layer( col_base_name = local_col.name local_suffix = constants.LOCAL_SUFFIX external_suffix = constants.EXTERNAL_SUFFIX - asset_suffix.add_suffix_to_hierarchy(local_col, local_suffix) + add_suffix_to_hierarchy(local_col, local_suffix) appended_col = import_data_from_lib(external_file, "collections", col_base_name) - asset_suffix.add_suffix_to_hierarchy(appended_col, external_suffix) + add_suffix_to_hierarchy(appended_col, external_suffix) local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] @@ -183,12 +183,12 @@ def merge_task_layer( target_obj = map.object_map[source_obj] target_obj.transfer_data_ownership.clear() - transfer_core.apply_transfer_data(context, map.transfer_data_map) + apply_transfer_data(context, map.transfer_data_map) for source_obj in map.object_map: target_obj = map.object_map[source_obj] remap_user(source_obj, target_obj) - transfer_core.transfer_data_clean(target_obj) + transfer_data_clean(target_obj) for col in map.collection_map: remap_user(col, map.collection_map[col]) @@ -199,7 +199,7 @@ def merge_task_layer( bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) - asset_suffix.remove_suffix_from_hierarchy(local_col) + remove_suffix_from_hierarchy(local_col) def find_file_version(published_file: Path) -> int: diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 36f70fe0..0f29d421 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -1,10 +1,20 @@ import bpy -from . import core -from pathlib import Path -from . import constants -from .transfer_data import transfer_ui import os +from pathlib import Path +from .core import ( + get_task_layer_col_name, + ownership_get, + ownership_set, + get_invalid_objects, + init_other_ids, + find_sync_target, + merge_task_layer, + find_all_published, + get_next_published_file, +) +from .transfer_data.transfer_ui import draw_transfer_data +from . import constants class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): @@ -68,7 +78,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE": continue - col_name = core.get_task_layer_col_name(task_layer_key) + col_name = get_task_layer_col_name(task_layer_key) bpy.data.collections.new(col_name) asset_col.children.link(bpy.data.collections.get(col_name)) @@ -141,11 +151,11 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - core.ownership_get(local_col, context.scene) + ownership_get(local_col, context.scene) # TODO Remove Invalid Objs Explicitly, some will be auto removed but not all - self._invalid_objs = core.get_invalid_objects(local_col, context.scene) - self._other_ids = core.init_other_ids(context.scene) + self._invalid_objs = get_invalid_objects(local_col, context.scene) + self._other_ids = init_other_ids(context.scene) # Default behaviour is to pull before pushing if self.push: @@ -193,12 +203,12 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): ] box = layout.box() box.label(text=obj.name, icon="OBJECT_DATA") - transfer_ui.draw_transfer_data(obj_ownership, box) + draw_transfer_data(obj_ownership, box) def execute(self, context: bpy.types.Context): # Find current task Layer temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data - core.ownership_set(temp_transfer_data) + ownership_set(temp_transfer_data) current_file = Path(bpy.data.filepath) temp_dir = Path(bpy.app.tempdir).parent task_layer_key = context.scene.asset_pipeline.task_layer_name @@ -206,7 +216,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") return {'CANCELLED'} - sync_target = core.find_sync_target(current_file) + sync_target = find_sync_target(current_file) if not sync_target.exists(): self.report({'ERROR'}, "Sync Target could not be determined") return {'CANCELLED'} @@ -216,7 +226,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): current_file.name.replace(".blend", "") + "_Asset_Pipe_Backup.blend" ) bpy.ops.wm.save_as_mainfile(filepath=temp_file.__str__(), copy=True) - error_msg = core.merge_task_layer( + error_msg = merge_task_layer( context, local_tls=[task_layer_key], external_file=sync_target, @@ -234,9 +244,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if not self.push: return {'FINISHED'} - push_targets = core.find_all_published( - current_file, constants.ACTIVE_PUBLISH_KEY - ) + push_targets = find_all_published(current_file, constants.ACTIVE_PUBLISH_KEY) if sync_target not in push_targets: push_targets.append(sync_target) @@ -254,7 +262,7 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): if task_layer != task_layer_key ] - error_msg = core.merge_task_layer( + error_msg = merge_task_layer( context, local_tls=local_tls, external_file=current_file, @@ -294,7 +302,7 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): ) return {'CANCELLED'} current_file = Path(bpy.data.filepath) - new_file_path = core.get_next_published_file(current_file, self.publish_types) + new_file_path = get_next_published_file(current_file, self.publish_types) bpy.ops.wm.save_as_mainfile(filepath=new_file_path.__str__(), copy=True) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index 4554965b..f87b4c87 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,7 +1,9 @@ import bpy from bpy import context -from . import transfer_core -from .. import asset_suffix, constants, util, core +from ..asset_suffix import get_basename +from ..core import find_drivers, copy_driver +from . import transfer_core # TODO FIX +from .. import constants import mathutils import bmesh import numpy as np @@ -226,11 +228,11 @@ def transfer_modifier(modifier_name, target_obj, source_obj): ) if source_obj.animation_data is None: return - fcurves = core.find_drivers( + fcurves = find_drivers( source_obj.animation_data.drivers, 'modifiers', modifier_name ) for fcurve in fcurves: - core.copy_driver(from_fcurve=fcurve, target=target_obj) + copy_driver(from_fcurve=fcurve, target=target_obj) # CONSTRAINTS @@ -292,17 +294,17 @@ def transfer_constraint(constraint_name, target_obj, source_obj): if source_obj.animation_data is None: return - fcurves = core.find_drivers( + fcurves = find_drivers( source_obj.animation_data.drivers, 'constraints', constraint_name ) for fcurve in fcurves: - core.copy_driver(from_fcurve=fcurve, target=target_obj) + copy_driver(from_fcurve=fcurve, target=target_obj) # MATERIAL SLOT def material_slots_clean(obj): - # Material slots cannot use generic transfer_info_clean() function + # Material slots cannot use generic transfer_core.transfer_info_clean() function context = bpy.context transfer_data_list = transfer_core.get_transfer_data_as_names( obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY @@ -418,7 +420,7 @@ def shape_keys_clean(obj): obj.transfer_data_ownership, constants.SHAPE_KEY_KEY ) for shape_key in obj.data.shape_keys.key_blocks: - if not asset_suffix.get_basename(shape_key.name) in transfer_data_list: + if not get_basename(shape_key.name) in transfer_data_list: obj.shape_key_remove(shape_key) @@ -525,13 +527,13 @@ def transfer_shape_key( ] val = mathutils.Vector(sum(np.array(vals_weighted))) sk_target.data[i].co = vert.co + val - fcurves = core.find_drivers( + fcurves = find_drivers( source_obj.data.shape_keys.animation_data.drivers, 'key_blocks', shape_key_name, ) for fcurve in fcurves: - core.copy_driver(from_fcurve=fcurve, target=target_obj.data.shape_keys) + copy_driver(from_fcurve=fcurve, target=target_obj.data.shape_keys) # ATTRIBUTE @@ -554,7 +556,7 @@ def attribute_clean(obj): obj.transfer_data_ownership, constants.ATTRIBUTE_KEY ) for item in attributes: - if not asset_suffix.get_basename(item.name) in transfer_data_list: + if not get_basename(item.name) in transfer_data_list: print(f"Cleaning attribute {item.name}") obj.data.attributes.remove(item) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py index 6283aca0..9bc89e40 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py @@ -1,5 +1,5 @@ -from .. import constants, core import bpy +from .. import constants def draw_transfer_data_type( diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index ec291c96..60e82b2f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -1,8 +1,8 @@ import bpy -from . import core, constants -from .transfer_data import transfer_ui from pathlib import Path +from .transfer_data.transfer_ui import draw_transfer_data +from . import constants class ASSETPIPE_sync(bpy.types.Panel): @@ -67,7 +67,7 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): layout = layout.box() owner_tl_ui_name = constants.TASK_LAYER_TYPES[obj.asset_id_owner] layout.label(text=f"{obj.name}: '{owner_tl_ui_name}'", icon="OBJECT_DATA") - transfer_ui.draw_transfer_data(transfer_data, layout) + draw_transfer_data(transfer_data, layout) classes = ( -- 2.30.2 From da6826578b543513eb95c7e9ab80240e38de0f50 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 12:54:06 -0400 Subject: [PATCH 230/429] Asset Pipe: Move Driver Functions to New File --- .../addons/asset_pipeline_2/core.py | 42 ------------------ .../addons/asset_pipeline_2/drivers.py | 44 +++++++++++++++++++ .../transfer_data/transfer_functions.py | 2 +- 3 files changed, 45 insertions(+), 43 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/drivers.py diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 0947883f..cfbba408 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -350,48 +350,6 @@ def import_data_from_lib( ## DRIVERS -def copy_driver(from_fcurve, target, data_path=None, index=None): - if not data_path: - data_path = from_fcurve.data_path - - new_fc = None - if index: - new_fc = target.driver_add(data_path, index) - else: - new_fc = target.driver_add(data_path) - - copy_attributes(from_fcurve, new_fc) - copy_attributes(from_fcurve.driver, new_fc.driver) - - # Remove default modifiers, variables, etc. - for m in new_fc.modifiers: - new_fc.modifiers.remove(m) - for v in new_fc.driver.variables: - new_fc.driver.variables.remove(v) - - # Copy modifiers - for m1 in from_fcurve.modifiers: - m2 = new_fc.modifiers.new(type=m1.type) - copy_attributes(m1, m2) - - # Copy variables - for v1 in from_fcurve.driver.variables: - v2 = new_fc.driver.variables.new() - copy_attributes(v1, v2) - for i in range(len(v1.targets)): - copy_attributes(v1.targets[i], v2.targets[i]) - - return new_fc - - -def find_drivers(drivers, target_type, target_name): - found_drivers = [] - for driver in drivers: - if f'{target_type}["{target_name}"]' in driver.data_path: - found_drivers.append(driver) - return found_drivers - - def get_other_ids(collection): # TODO find better name ref_map = get_id_reference_map() all_ids_of_coll = get_all_referenced_ids(collection, ref_map) diff --git a/scripts-blender/addons/asset_pipeline_2/drivers.py b/scripts-blender/addons/asset_pipeline_2/drivers.py new file mode 100644 index 00000000..0bb4c3bb --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/drivers.py @@ -0,0 +1,44 @@ +import bpy +from rigify.utils.misc import copy_attributes + + +def copy_driver(from_fcurve, target, data_path=None, index=None): + if not data_path: + data_path = from_fcurve.data_path + + new_fc = None + if index: + new_fc = target.driver_add(data_path, index) + else: + new_fc = target.driver_add(data_path) + + copy_attributes(from_fcurve, new_fc) + copy_attributes(from_fcurve.driver, new_fc.driver) + + # Remove default modifiers, variables, etc. + for m in new_fc.modifiers: + new_fc.modifiers.remove(m) + for v in new_fc.driver.variables: + new_fc.driver.variables.remove(v) + + # Copy modifiers + for m1 in from_fcurve.modifiers: + m2 = new_fc.modifiers.new(type=m1.type) + copy_attributes(m1, m2) + + # Copy variables + for v1 in from_fcurve.driver.variables: + v2 = new_fc.driver.variables.new() + copy_attributes(v1, v2) + for i in range(len(v1.targets)): + copy_attributes(v1.targets[i], v2.targets[i]) + + return new_fc + + +def find_drivers(drivers, target_type, target_name): + found_drivers = [] + for driver in drivers: + if f'{target_type}["{target_name}"]' in driver.data_path: + found_drivers.append(driver) + return found_drivers diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index f87b4c87..f474e2bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,7 +1,7 @@ import bpy from bpy import context from ..asset_suffix import get_basename -from ..core import find_drivers, copy_driver +from ..drivers import find_drivers, copy_driver from . import transfer_core # TODO FIX from .. import constants import mathutils -- 2.30.2 From b57f486e604d4e9693052d65fc6437ec6918ddae Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 12:57:39 -0400 Subject: [PATCH 231/429] Asset Pipe: Rename Asset_Suffix to Asset_Naming --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 2 +- .../{asset_suffix.py => asset_naming.py} | 9 +++++++++ scripts-blender/addons/asset_pipeline_2/core.py | 2 +- .../asset_pipeline_2/transfer_data/transfer_core.py | 4 ++-- .../asset_pipeline_2/transfer_data/transfer_functions.py | 2 +- 5 files changed, 14 insertions(+), 5 deletions(-) rename scripts-blender/addons/asset_pipeline_2/{asset_suffix.py => asset_naming.py} (89%) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 6c38d44f..0f78b568 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -1,7 +1,7 @@ import bpy from typing import Dict, Set from . import core # TODO DEBUG WHY THIS DOESN'T WORK -from .asset_suffix import get_target_name, get_basename, get_name_with_asset_prefix +from .asset_naming import get_target_name, get_basename, get_name_with_asset_prefix from .util import get_storage_of_id from .transfer_data.transfer_core import transfer_data_add_entry from . import constants diff --git a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py b/scripts-blender/addons/asset_pipeline_2/asset_naming.py similarity index 89% rename from scripts-blender/addons/asset_pipeline_2/asset_suffix.py rename to scripts-blender/addons/asset_pipeline_2/asset_naming.py index 83f960a1..a2fa9c6a 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_suffix.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_naming.py @@ -113,3 +113,12 @@ def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) db.name += suffix except: pass + + +def get_name_with_asset_prefix(name: str): + # TODO Docstring and return types + asset_pipe = bpy.context.scene.asset_pipeline + if name.startswith(asset_pipe.prefix + "."): + return name + prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" + return prefix + name diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index cfbba408..b73c5729 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -8,7 +8,7 @@ from .transfer_data.transfer_core import ( transfer_data_clean, ) -from .asset_suffix import ( +from .asset_naming import ( add_suffix_to_hierarchy, remove_suffix_from_hierarchy, get_name_with_asset_prefix, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 07c57920..3a268e6a 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -1,7 +1,7 @@ import bpy from . import transfer_functions -from .. import constants, asset_suffix +from .. import asset_naming, constants def copy_transfer_data_ownership( @@ -227,7 +227,7 @@ def transfer_info_clean(obj, list, td_type): obj.transfer_data_ownership, td_type ) for item in list: - if not asset_suffix.get_basename(item.name) in transfer_data_list: + if not asset_naming.get_basename(item.name) in transfer_data_list: list.remove(item) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index f474e2bc..e249c461 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,6 +1,6 @@ import bpy from bpy import context -from ..asset_suffix import get_basename +from ..asset_naming import get_basename from ..drivers import find_drivers, copy_driver from . import transfer_core # TODO FIX from .. import constants -- 2.30.2 From 0ec6bd8e79530175be52dcec05548801f48e273a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 13:02:24 -0400 Subject: [PATCH 232/429] Asset Pipe: Move Publish Functions to New File --- .../addons/asset_pipeline_2/core.py | 95 ------------------- .../addons/asset_pipeline_2/ops.py | 8 +- .../addons/asset_pipeline_2/publish.py | 94 ++++++++++++++++++ 3 files changed, 99 insertions(+), 98 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/publish.py diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index b73c5729..18c9ffd4 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -202,98 +202,6 @@ def merge_task_layer( remove_suffix_from_hierarchy(local_col) -def find_file_version(published_file: Path) -> int: - """Returns the version number from a published file's name - - Args: - file (Path): Path to a publish file, naming convention is - asset_name.v{3-digit_version}.blend` - - Returns: - int: returns current version in filename as integer - """ - return int(published_file.name.split(".")[1].replace("v", "")) - - -def get_next_published_file( - current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY -) -> Path: - """Returns the path where the next published file version should be saved to - - Args: - current_file (Path): Current file, which must be a task file at root of asset directory - publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. - - Returns: - Path: Path where the next published file should be saved to, path doesn't exist yet - """ """""" - last_publish = find_latest_publish(current_file, publish_type) - base_name = current_file.name.split(".")[0] - publish_dir = current_file.parent.joinpath(publish_type) - if not last_publish: - new_version_number = 1 - - else: - new_version_number = find_file_version(last_publish) + 1 - new_version = "{0:0=3d}".format(new_version_number) - return publish_dir.joinpath(base_name + f".v" + new_version + ".blend") - - -def find_all_published(current_file: Path, publish_type: str) -> list[Path]: - """Retuns a list of published files of a given type, - each publish type is seperated into it's own folder at the - root of the asset's directory - Args: - current_file (Path): Current file, which must be a task file at root of asset directory - publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. - - Returns: - list[Path]: list of published files of a given publish type - """ - publish_dir = current_file.parent.joinpath(publish_type) - if not publish_dir.exists(): - return - published_files = list(publish_dir.glob('*.blend')) - published_files.sort(key=find_file_version) - return published_files - - -def find_latest_publish( - current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY -) -> Path: - """Returns the path to the latest published file in a given folder - - Args: - current_file (Path): Current file, which must be a task file at root of asset directory - publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. - - Returns: - Path: Path to latest publish file of a given publish type - """ - published_files = find_all_published(current_file, publish_type) - if published_files: - return published_files[-1] - - -def find_sync_target(current_file: Path) -> Path: - """Returns the latest published file to use as push/pull a.k.a sync target - this will either be the latest active publish, or the latest staged asset if - any asset is staged - - Args: - current_file (Path): Current file, which must be a task file at root of asset directory - - Returns: - Path: Path to latest active or staged publish file - """ """""" - latest_staged = find_latest_publish( - current_file, publish_type=constants.STAGED_PUBLISH_KEY - ) - if latest_staged: - return latest_staged - return find_latest_publish(current_file, publish_type=constants.ACTIVE_PUBLISH_KEY) - - def import_data_from_lib( libpath: Path, data_category: str, @@ -347,9 +255,6 @@ def import_data_from_lib( return eval(f"bpy.data.{data_category}['{data_name}']") -## DRIVERS - - def get_other_ids(collection): # TODO find better name ref_map = get_id_reference_map() all_ids_of_coll = get_all_referenced_ids(collection, ref_map) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 0f29d421..76465069 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,16 +2,18 @@ import bpy import os from pathlib import Path +from .publish import ( + find_sync_target, + find_all_published, + get_next_published_file, +) from .core import ( get_task_layer_col_name, ownership_get, ownership_set, get_invalid_objects, init_other_ids, - find_sync_target, merge_task_layer, - find_all_published, - get_next_published_file, ) from .transfer_data.transfer_ui import draw_transfer_data from . import constants diff --git a/scripts-blender/addons/asset_pipeline_2/publish.py b/scripts-blender/addons/asset_pipeline_2/publish.py new file mode 100644 index 00000000..87459f2a --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/publish.py @@ -0,0 +1,94 @@ +from pathlib import Path +from . import constants + + +def find_file_version(published_file: Path) -> int: + """Returns the version number from a published file's name + + Args: + file (Path): Path to a publish file, naming convention is + asset_name.v{3-digit_version}.blend` + + Returns: + int: returns current version in filename as integer + """ + return int(published_file.name.split(".")[1].replace("v", "")) + + +def get_next_published_file( + current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY +) -> Path: + """Returns the path where the next published file version should be saved to + + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. + + Returns: + Path: Path where the next published file should be saved to, path doesn't exist yet + """ """""" + last_publish = find_latest_publish(current_file, publish_type) + base_name = current_file.name.split(".")[0] + publish_dir = current_file.parent.joinpath(publish_type) + if not last_publish: + new_version_number = 1 + + else: + new_version_number = find_file_version(last_publish) + 1 + new_version = "{0:0=3d}".format(new_version_number) + return publish_dir.joinpath(base_name + f".v" + new_version + ".blend") + + +def find_all_published(current_file: Path, publish_type: str) -> list[Path]: + """Retuns a list of published files of a given type, + each publish type is seperated into it's own folder at the + root of the asset's directory + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. + + Returns: + list[Path]: list of published files of a given publish type + """ + publish_dir = current_file.parent.joinpath(publish_type) + if not publish_dir.exists(): + return + published_files = list(publish_dir.glob('*.blend')) + published_files.sort(key=find_file_version) + return published_files + + +def find_latest_publish( + current_file: Path, publish_type=constants.ACTIVE_PUBLISH_KEY +) -> Path: + """Returns the path to the latest published file in a given folder + + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + publish_type (_type_, optional): Publish type, 'publish', 'staged', 'review'. Defaults to 'publish'. + + Returns: + Path: Path to latest publish file of a given publish type + """ + published_files = find_all_published(current_file, publish_type) + if published_files: + return published_files[-1] + + +def find_sync_target(current_file: Path) -> Path: + """Returns the latest published file to use as push/pull a.k.a sync target + this will either be the latest active publish, or the latest staged asset if + any asset is staged + + Args: + current_file (Path): Current file, which must be a task file at root of asset directory + + Returns: + Path: Path to latest active or staged publish file + """ """""" + latest_staged = find_latest_publish( + current_file, publish_type=constants.STAGED_PUBLISH_KEY + ) + if latest_staged: + return latest_staged + return find_latest_publish(current_file, publish_type=constants.ACTIVE_PUBLISH_KEY) -- 2.30.2 From cee11107ae86b0ce75e13223b39a5d490bd27ff9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 13:07:26 -0400 Subject: [PATCH 233/429] Asset Pipe: Move Other Ids to New File --- .../addons/asset_pipeline_2/asset_mapping.py | 3 ++- .../addons/asset_pipeline_2/core.py | 22 ------------------ .../addons/asset_pipeline_2/ops.py | 2 +- .../addons/asset_pipeline_2/other_ids.py | 23 +++++++++++++++++++ 4 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/other_ids.py diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 0f78b568..4dfee806 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -4,6 +4,7 @@ from . import core # TODO DEBUG WHY THIS DOESN'T WORK from .asset_naming import get_target_name, get_basename, get_name_with_asset_prefix from .util import get_storage_of_id from .transfer_data.transfer_core import transfer_data_add_entry +from .other_ids import get_other_ids from . import constants @@ -206,7 +207,7 @@ class AssetTransferMapping: def _gen_other_id_map(self): other_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} - for local_id in core.get_other_ids(self._local_col): + for local_id in get_other_ids(self._local_col): external_id_name = get_target_name(local_id.name) id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index 18c9ffd4..f683fa30 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -15,7 +15,6 @@ from .asset_naming import ( ) -from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from pathlib import Path from typing import Dict from . import constants @@ -253,24 +252,3 @@ def import_data_from_lib( ) return eval(f"bpy.data.{data_category}['{data_name}']") - - -def get_other_ids(collection): # TODO find better name - ref_map = get_id_reference_map() - all_ids_of_coll = get_all_referenced_ids(collection, ref_map) - return [ - id - for id in all_ids_of_coll - if type(id) == bpy.types.NodeGroup or type(id) == bpy.types.Image - ] - - -def init_other_ids(scene): - other_ids = [] - asset_pipe = scene.asset_pipeline - local_col = asset_pipe.asset_collection - for id in get_other_ids(local_col): - if id.asset_id_owner == 'NONE': - id.asset_id_owner = asset_pipe.task_layer_name - other_ids.append(id) - return other_ids diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 76465069..3eaa756b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -7,12 +7,12 @@ from .publish import ( find_all_published, get_next_published_file, ) +from .other_ids import init_other_ids from .core import ( get_task_layer_col_name, ownership_get, ownership_set, get_invalid_objects, - init_other_ids, merge_task_layer, ) from .transfer_data.transfer_ui import draw_transfer_data diff --git a/scripts-blender/addons/asset_pipeline_2/other_ids.py b/scripts-blender/addons/asset_pipeline_2/other_ids.py new file mode 100644 index 00000000..9ae3eb5d --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/other_ids.py @@ -0,0 +1,23 @@ +import bpy +from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids + + +def get_other_ids(collection): # TODO find better name + ref_map = get_id_reference_map() + all_ids_of_coll = get_all_referenced_ids(collection, ref_map) + return [ + id + for id in all_ids_of_coll + if type(id) == bpy.types.NodeGroup or type(id) == bpy.types.Image + ] + + +def init_other_ids(scene): + other_ids = [] + asset_pipe = scene.asset_pipeline + local_col = asset_pipe.asset_collection + for id in get_other_ids(local_col): + if id.asset_id_owner == 'NONE': + id.asset_id_owner = asset_pipe.task_layer_name + other_ids.append(id) + return other_ids -- 2.30.2 From 67fefb8df320b7fc4d81f81231d11e082820b9ba Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 13:11:21 -0400 Subject: [PATCH 234/429] Asset Pipe: Rename Asset_Naming.py to just Naming.py --- scripts-blender/addons/asset_pipeline_2/asset_mapping.py | 2 +- scripts-blender/addons/asset_pipeline_2/core.py | 9 ++------- .../asset_pipeline_2/{asset_naming.py => naming.py} | 6 ++++++ scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- .../asset_pipeline_2/transfer_data/transfer_core.py | 4 ++-- .../asset_pipeline_2/transfer_data/transfer_functions.py | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) rename scripts-blender/addons/asset_pipeline_2/{asset_naming.py => naming.py} (92%) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py index 4dfee806..2e3ac89d 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/asset_mapping.py @@ -1,7 +1,7 @@ import bpy from typing import Dict, Set from . import core # TODO DEBUG WHY THIS DOESN'T WORK -from .asset_naming import get_target_name, get_basename, get_name_with_asset_prefix +from .naming import get_target_name, get_basename, get_name_with_asset_prefix from .util import get_storage_of_id from .transfer_data.transfer_core import transfer_data_add_entry from .other_ids import get_other_ids diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/core.py index f683fa30..828ed990 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/core.py @@ -8,10 +8,11 @@ from .transfer_data.transfer_core import ( transfer_data_clean, ) -from .asset_naming import ( +from .naming import ( add_suffix_to_hierarchy, remove_suffix_from_hierarchy, get_name_with_asset_prefix, + get_task_layer_col_name, ) @@ -111,12 +112,6 @@ def get_invalid_objects( return invalid_obj -def get_task_layer_col_name(task_layer_key): - # TODO Docstring and return types - task_layer_name = constants.TASK_LAYER_TYPES[task_layer_key] - return get_name_with_asset_prefix(task_layer_name) - - def remap_user(source_datablock: bpy.data, target_datablock: bpy.data) -> None: """Remap datablock and append name to datablock that has been remapped diff --git a/scripts-blender/addons/asset_pipeline_2/asset_naming.py b/scripts-blender/addons/asset_pipeline_2/naming.py similarity index 92% rename from scripts-blender/addons/asset_pipeline_2/asset_naming.py rename to scripts-blender/addons/asset_pipeline_2/naming.py index a2fa9c6a..e15074c1 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_naming.py +++ b/scripts-blender/addons/asset_pipeline_2/naming.py @@ -122,3 +122,9 @@ def get_name_with_asset_prefix(name: str): return name prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" return prefix + name + + +def get_task_layer_col_name(task_layer_key): + # TODO Docstring and return types + task_layer_name = constants.TASK_LAYER_TYPES[task_layer_key] + return get_name_with_asset_prefix(task_layer_name) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 3eaa756b..8886a2d7 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -7,9 +7,9 @@ from .publish import ( find_all_published, get_next_published_file, ) +from naming import get_task_layer_col_name from .other_ids import init_other_ids from .core import ( - get_task_layer_col_name, ownership_get, ownership_set, get_invalid_objects, diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py index 3a268e6a..b515612a 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py @@ -1,7 +1,7 @@ import bpy from . import transfer_functions -from .. import asset_naming, constants +from .. import constants, naming def copy_transfer_data_ownership( @@ -227,7 +227,7 @@ def transfer_info_clean(obj, list, td_type): obj.transfer_data_ownership, td_type ) for item in list: - if not asset_naming.get_basename(item.name) in transfer_data_list: + if not naming.get_basename(item.name) in transfer_data_list: list.remove(item) diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py index e249c461..4d283a91 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py @@ -1,6 +1,6 @@ import bpy from bpy import context -from ..asset_naming import get_basename +from ..naming import get_basename from ..drivers import find_drivers, copy_driver from . import transfer_core # TODO FIX from .. import constants -- 2.30.2 From 164124ff274bc3b5ba19a43e45886938f4881aa3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 13:21:28 -0400 Subject: [PATCH 235/429] Asset Pipe: Move files related to merge process into their own folder --- .../asset_pipeline_2/{ => merge}/asset_mapping.py | 2 +- .../addons/asset_pipeline_2/{ => merge}/core.py | 2 +- .../addons/asset_pipeline_2/{ => merge}/drivers.py | 0 .../addons/asset_pipeline_2/{ => merge}/naming.py | 2 +- .../addons/asset_pipeline_2/{ => merge}/other_ids.py | 0 .../addons/asset_pipeline_2/{ => merge}/publish.py | 2 +- .../{ => merge}/transfer_data/transfer_core.py | 4 +++- .../{ => merge}/transfer_data/transfer_functions.py | 2 +- .../{ => merge}/transfer_data/transfer_ui.py | 2 +- .../addons/asset_pipeline_2/{ => merge}/util.py | 0 .../addons/asset_pipeline_2/{ => merge}/visibility.py | 0 scripts-blender/addons/asset_pipeline_2/ops.py | 10 +++++----- scripts-blender/addons/asset_pipeline_2/ui.py | 2 +- 13 files changed, 15 insertions(+), 13 deletions(-) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/asset_mapping.py (97%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/core.py (99%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/drivers.py (100%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/naming.py (96%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/other_ids.py (100%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/publish.py (96%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/transfer_data/transfer_core.py (96%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/transfer_data/transfer_functions.py (99%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/transfer_data/transfer_ui.py (96%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/util.py (100%) rename scripts-blender/addons/asset_pipeline_2/{ => merge}/visibility.py (100%) diff --git a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py similarity index 97% rename from scripts-blender/addons/asset_pipeline_2/asset_mapping.py rename to scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 2e3ac89d..3b884977 100644 --- a/scripts-blender/addons/asset_pipeline_2/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -5,7 +5,7 @@ from .naming import get_target_name, get_basename, get_name_with_asset_prefix from .util import get_storage_of_id from .transfer_data.transfer_core import transfer_data_add_entry from .other_ids import get_other_ids -from . import constants +from .. import constants class AssetTransferMapping: diff --git a/scripts-blender/addons/asset_pipeline_2/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py similarity index 99% rename from scripts-blender/addons/asset_pipeline_2/core.py rename to scripts-blender/addons/asset_pipeline_2/merge/core.py index 828ed990..e502da62 100644 --- a/scripts-blender/addons/asset_pipeline_2/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -18,7 +18,7 @@ from .naming import ( from pathlib import Path from typing import Dict -from . import constants +from .. import constants def ownership_transfer_data_cleanup( diff --git a/scripts-blender/addons/asset_pipeline_2/drivers.py b/scripts-blender/addons/asset_pipeline_2/merge/drivers.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/drivers.py rename to scripts-blender/addons/asset_pipeline_2/merge/drivers.py diff --git a/scripts-blender/addons/asset_pipeline_2/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py similarity index 96% rename from scripts-blender/addons/asset_pipeline_2/naming.py rename to scripts-blender/addons/asset_pipeline_2/merge/naming.py index e15074c1..e952cc33 100644 --- a/scripts-blender/addons/asset_pipeline_2/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -21,7 +21,7 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from .util import get_storage_of_id -from . import constants +from .. import constants DELIMITER = "." diff --git a/scripts-blender/addons/asset_pipeline_2/other_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/other_ids.py rename to scripts-blender/addons/asset_pipeline_2/merge/other_ids.py diff --git a/scripts-blender/addons/asset_pipeline_2/publish.py b/scripts-blender/addons/asset_pipeline_2/merge/publish.py similarity index 96% rename from scripts-blender/addons/asset_pipeline_2/publish.py rename to scripts-blender/addons/asset_pipeline_2/merge/publish.py index 87459f2a..4cfabffc 100644 --- a/scripts-blender/addons/asset_pipeline_2/publish.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/publish.py @@ -1,5 +1,5 @@ from pathlib import Path -from . import constants +from .. import constants def find_file_version(published_file: Path) -> int: diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py similarity index 96% rename from scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py rename to scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index b515612a..09388b52 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -1,7 +1,9 @@ import bpy + +from .. import naming from . import transfer_functions -from .. import constants, naming +from ... import constants def copy_transfer_data_ownership( diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py similarity index 99% rename from scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py rename to scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 4d283a91..0fa5f371 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -3,7 +3,7 @@ from bpy import context from ..naming import get_basename from ..drivers import find_drivers, copy_driver from . import transfer_core # TODO FIX -from .. import constants +from ... import constants import mathutils import bmesh import numpy as np diff --git a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py similarity index 96% rename from scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py rename to scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 9bc89e40..d9006751 100644 --- a/scripts-blender/addons/asset_pipeline_2/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -1,5 +1,5 @@ import bpy -from .. import constants +from ... import constants def draw_transfer_data_type( diff --git a/scripts-blender/addons/asset_pipeline_2/util.py b/scripts-blender/addons/asset_pipeline_2/merge/util.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/util.py rename to scripts-blender/addons/asset_pipeline_2/merge/util.py diff --git a/scripts-blender/addons/asset_pipeline_2/visibility.py b/scripts-blender/addons/asset_pipeline_2/merge/visibility.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/visibility.py rename to scripts-blender/addons/asset_pipeline_2/merge/visibility.py diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 8886a2d7..afe34c46 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,20 +2,20 @@ import bpy import os from pathlib import Path -from .publish import ( +from .merge.publish import ( find_sync_target, find_all_published, get_next_published_file, ) -from naming import get_task_layer_col_name -from .other_ids import init_other_ids -from .core import ( +from .merge.naming import get_task_layer_col_name +from .merge.other_ids import init_other_ids +from .merge.core import ( ownership_get, ownership_set, get_invalid_objects, merge_task_layer, ) -from .transfer_data.transfer_ui import draw_transfer_data +from .merge.transfer_data.transfer_ui import draw_transfer_data from . import constants diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 60e82b2f..ede9ba16 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -1,7 +1,7 @@ import bpy from pathlib import Path -from .transfer_data.transfer_ui import draw_transfer_data +from .merge.transfer_data.transfer_ui import draw_transfer_data from . import constants -- 2.30.2 From dafc4b37ae66cf7aebe09ca7384f663beb240f7e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 13:22:09 -0400 Subject: [PATCH 236/429] Asset Pipe: Remove Test Files from Repo --- .../addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend | 3 --- .../addons/asset_pipeline_2/chr_test/chr_test.RIG.blend | 3 --- .../addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend | 3 --- .../asset_pipeline_2/chr_test/publish/chr_test.v001.blend | 3 --- 4 files changed, 12 deletions(-) delete mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend delete mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend delete mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend delete mode 100644 scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend deleted file mode 100644 index 2312f52e..00000000 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.MODEL.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8cc9c9b612bc1a46c878e8efb11c383ca2eccde9640f19ea09eef0c266e54548 -size 938180 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend deleted file mode 100644 index b85ae341..00000000 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.RIG.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b829592dd611c6784ed14741f6202f91d01d527c4d0850a353de48e24ccc4aa -size 957840 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend deleted file mode 100644 index af4b77cf..00000000 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/chr_test.SHADE.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c7207ed1ba8f01eccba432da219f6fe5773a58daaa2f81b7a805178e3037d56 -size 935888 diff --git a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend b/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend deleted file mode 100644 index 35c9fd70..00000000 --- a/scripts-blender/addons/asset_pipeline_2/chr_test/publish/chr_test.v001.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3bd253dfde076306586e34b23b6e7513ff5170f9998f6aeb0299b4dfd77c96a6 -size 913544 -- 2.30.2 From d1debd5c4ad9a9ce88ab1d28f134840051b5ec5c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 13:40:48 -0400 Subject: [PATCH 237/429] Asset Pipe: Move Transfer util Functions to seperate File --- .../asset_pipeline_2/merge/asset_mapping.py | 2 +- .../addons/asset_pipeline_2/merge/core.py | 2 +- .../merge/transfer_data/transfer_core.py | 85 ++----------------- .../merge/transfer_data/transfer_functions.py | 72 +++++++--------- .../merge/transfer_data/transfer_util.py | 81 ++++++++++++++++++ 5 files changed, 120 insertions(+), 122 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 3b884977..b53cb1bd 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -3,7 +3,7 @@ from typing import Dict, Set from . import core # TODO DEBUG WHY THIS DOESN'T WORK from .naming import get_target_name, get_basename, get_name_with_asset_prefix from .util import get_storage_of_id -from .transfer_data.transfer_core import transfer_data_add_entry +from .transfer_data.transfer_util import transfer_data_add_entry from .other_ids import get_other_ids from .. import constants diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index e502da62..6f3ddeb2 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -3,10 +3,10 @@ from .asset_mapping import AssetTransferMapping from .transfer_data.transfer_core import ( init_transfer_data, transfer_data_is_missing, - transfer_data_add_entry, apply_transfer_data, transfer_data_clean, ) +from .transfer_data.transfer_util import transfer_data_add_entry from .naming import ( add_suffix_to_hierarchy, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 09388b52..616fd059 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -1,10 +1,14 @@ import bpy -from .. import naming from . import transfer_functions from ... import constants +from .transfer_util import ( + transfer_data_add_entry, + check_transfer_data_entry, +) + def copy_transfer_data_ownership( transfer_data_item, target_obj: bpy.types.Object @@ -172,82 +176,3 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: transfer_data_item=transfer_info, target_obj=target_obj, ) - - -def check_transfer_data_entry( - transfer_data: bpy.types.CollectionProperty, key: str, td_type: str -) -> set: - """Verifies if transfer data entry exists - - Args: - ownership (bpy.types.CollectionProperty): Transfer Data of an object - key (str): Name of item that is being verified - td_type (str): Type of transfer data - - Returns: - set: Returns set of matches where name is found in ownership - """ - existing_items = [ - transfer_info.name - for transfer_info in transfer_data - if transfer_info.type == td_type - ] - return set([key]).intersection(set(existing_items)) - - -def transfer_data_add_entry( - transfer_data: bpy.types.CollectionProperty, - name: str, - td_type: str, - task_layer_name: str, -): - """Add entry to transfer data ownership - - Args: - ownership (bpy.types.CollectionProperty): Transfer Data of an object - name (str): Name of new transfer data item - td_type (str): Type of transfer data - task_layer_name (str): Name of current task layer - """ - transfer_info = transfer_data.add() - transfer_info.name = name - transfer_info.owner = task_layer_name.upper() - transfer_info.type = td_type - return transfer_info - - -def get_transfer_data_as_names(transfer_data, td_type): - return [ - transfer_info.name - for transfer_info in transfer_data - if transfer_info.type == td_type - ] - - -def transfer_info_clean(obj, list, td_type): - transfer_data_list = get_transfer_data_as_names( - obj.transfer_data_ownership, td_type - ) - for item in list: - if not naming.get_basename(item.name) in transfer_data_list: - list.remove(item) - - -def transfer_info_is_missing(transfer_info, type_key, list): - if transfer_info.type == type_key and not list.get(transfer_info["name"]): - return True - - -def transfer_info_init(scene, obj, list, type_key): - transfer_data = obj.transfer_data_ownership - task_layer_key = scene.asset_pipeline.task_layer_name - for item in list: - # Only add new ownership transfer_info if vertex group doesn't have an owner - matches = check_transfer_data_entry(transfer_data, item.name, type_key) - if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( - name=item.name, - owner=task_layer_key, - type=type_key, - obj=obj, - ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 0fa5f371..7690fdfa 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -2,7 +2,13 @@ import bpy from bpy import context from ..naming import get_basename from ..drivers import find_drivers, copy_driver -from . import transfer_core # TODO FIX +from .transfer_util import ( + transfer_info_clean, + transfer_info_is_missing, + transfer_info_init, + get_transfer_data_as_names, # TODO Replce with check entry + check_transfer_data_entry, +) from ... import constants import mathutils import bmesh @@ -14,21 +20,17 @@ import numpy as np # VERTEX GROUPS def vertex_groups_clean(obj): - transfer_core.transfer_info_clean( - obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY - ) + transfer_info_clean(obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY) def vertex_group_is_missing(transfer_info): - return transfer_core.transfer_info_is_missing( + return transfer_info_is_missing( transfer_info, constants.VERTEX_GROUP_KEY, transfer_info.id_data.vertex_groups ) def init_vertex_groups(scene, obj): - transfer_core.transfer_info_init( - scene, obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY - ) + transfer_info_init(scene, obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY) def transfer_vertex_group( @@ -75,13 +77,11 @@ def transfer_vertex_group( def vertex_colors_clean(obj): if not obj.type == "MESH": return - transfer_core.transfer_info_clean( - obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY - ) + transfer_info_clean(obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY) def vertex_color_is_missing(transfer_info): - return transfer_core.transfer_info_is_missing( + return transfer_info_is_missing( transfer_info, constants.VERTEX_COLOR_KEY, transfer_info.id_data.vertex_colors ) @@ -89,9 +89,7 @@ def vertex_color_is_missing(transfer_info): def init_vertex_colors(scene, obj): if not obj.type == "MESH": return - transfer_core.transfer_info_init( - scene, obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY - ) + transfer_info_init(scene, obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY) def transfer_vertex_color( @@ -120,11 +118,11 @@ def transfer_vertex_color( def uv_layer_clean(obj): if not obj.type == "MESH": return - transfer_core.transfer_info_clean(obj, obj.data.uv_layers, constants.UV_LAYERS_KEY) + transfer_info_clean(obj, obj.data.uv_layers, constants.UV_LAYERS_KEY) def uv_layer_is_missing(transfer_info): - return transfer_core.transfer_info_is_missing( + return transfer_info_is_missing( transfer_info, constants.UV_LAYERS_KEY, transfer_info.id_data.data.uv_layers ) @@ -132,9 +130,7 @@ def uv_layer_is_missing(transfer_info): def init_uv_layers(scene, obj): if not obj.type == "MESH": return - transfer_core.transfer_info_init( - scene, obj, obj.data.uv_layers, constants.UV_LAYERS_KEY - ) + transfer_info_init(scene, obj, obj.data.uv_layers, constants.UV_LAYERS_KEY) def transfer_uv_layer(source_obj, target_obj, uv_name): @@ -161,17 +157,17 @@ def transfer_uv_layer(source_obj, target_obj, uv_name): # MODIFIERS def modifiers_clean(obj): - transfer_core.transfer_info_clean(obj, obj.modifiers, constants.MODIFIER_KEY) + transfer_info_clean(obj, obj.modifiers, constants.MODIFIER_KEY) def modifier_is_missing(transfer_info): - return transfer_core.transfer_info_is_missing( + return transfer_info_is_missing( transfer_info, constants.MODIFIER_KEY, transfer_info.id_data.modifiers ) def init_modifiers(scene, obj): - transfer_core.transfer_info_init(scene, obj, obj.modifiers, constants.MODIFIER_KEY) + transfer_info_init(scene, obj, obj.modifiers, constants.MODIFIER_KEY) def transfer_modifier(modifier_name, target_obj, source_obj): @@ -237,19 +233,17 @@ def transfer_modifier(modifier_name, target_obj, source_obj): # CONSTRAINTS def constraints_clean(obj): - transfer_core.transfer_info_clean(obj, obj.constraints, constants.CONSTRAINT_KEY) + transfer_info_clean(obj, obj.constraints, constants.CONSTRAINT_KEY) def constraint_is_missing(transfer_info): - return transfer_core.transfer_info_is_missing( + return transfer_info_is_missing( transfer_info, constants.CONSTRAINT_KEY, transfer_info.id_data.constraints ) def init_constraints(scene, obj): - transfer_core.transfer_info_init( - scene, obj, obj.constraints, constants.CONSTRAINT_KEY - ) + transfer_info_init(scene, obj, obj.constraints, constants.CONSTRAINT_KEY) def transfer_constraint(constraint_name, target_obj, source_obj): @@ -304,9 +298,9 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT def material_slots_clean(obj): - # Material slots cannot use generic transfer_core.transfer_info_clean() function + # Material slots cannot use generic transfer_info_clean() function context = bpy.context - transfer_data_list = transfer_core.get_transfer_data_as_names( + transfer_data_list = get_transfer_data_as_names( obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY ) @@ -335,7 +329,7 @@ def init_material_slots(scene, obj): # Only Execute if Material Slots exist on object if len(obj.material_slots) == 0: return - matches = transfer_core.check_transfer_data_entry(transfer_data, name, type_key) + matches = check_transfer_data_entry(transfer_data, name, type_key) # Only add new ownership transfer_info if vertex group doesn't have an owner if len(matches) == 0: scene.asset_pipeline.add_temp_trasnfer_data( @@ -416,7 +410,7 @@ def shape_keys_clean(obj): context = bpy.context if obj.type != "MESH" or obj.data.shape_keys is None: return - transfer_data_list = transfer_core.get_transfer_data_as_names( + transfer_data_list = get_transfer_data_as_names( obj.transfer_data_ownership, constants.SHAPE_KEY_KEY ) for shape_key in obj.data.shape_keys.key_blocks: @@ -432,7 +426,7 @@ def shape_key_is_missing(transfer_info): return if not obj.data.shape_keys: return True - return transfer_core.transfer_info_is_missing( + return transfer_info_is_missing( transfer_info, constants.SHAPE_KEY_KEY, obj.data.shape_keys.key_blocks, @@ -453,7 +447,7 @@ def init_shape_keys(scene, obj): f'Shape Key "{kb.name}" must be ordered after its base shape "{kb.relative_key.name}" on object "{obj.name}".' ) - transfer_core.transfer_info_init( + transfer_info_init( scene, obj, obj.data.shape_keys.key_blocks, constants.SHAPE_KEY_KEY ) @@ -552,7 +546,7 @@ def attribute_clean(obj): if obj.type != "MESH": return attributes = attributes_get_editable(obj.data.attributes) - transfer_data_list = transfer_core.get_transfer_data_as_names( + transfer_data_list = get_transfer_data_as_names( obj.transfer_data_ownership, constants.ATTRIBUTE_KEY ) for item in attributes: @@ -582,9 +576,7 @@ def init_attributes(scene, obj): type_key = constants.ATTRIBUTE_KEY for atttribute in attributes_get_editable(obj.data.attributes): # Only add new ownership transfer_info if vertex group doesn't have an owner - matches = transfer_core.check_transfer_data_entry( - transfer_data, atttribute.name, type_key - ) + matches = check_transfer_data_entry(transfer_data, atttribute.name, type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_trasnfer_data( name=atttribute.name, @@ -625,7 +617,7 @@ def transfer_attribute( def parent_clean(obj): - transfer_data_list = transfer_core.get_transfer_data_as_names( + transfer_data_list = get_transfer_data_as_names( obj.transfer_data_ownership, constants.PARENT_KEY ) @@ -653,7 +645,7 @@ def init_parent(scene, obj): # Only Execute if Material Slots exist on object if obj.parent == None: return - matches = transfer_core.check_transfer_data_entry(transfer_data, name, type_key) + matches = check_transfer_data_entry(transfer_data, name, type_key) # Only add new ownership transfer_info if vertex group doesn't have an owner if len(matches) == 0: scene.asset_pipeline.add_temp_trasnfer_data( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py new file mode 100644 index 00000000..c0c35bec --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -0,0 +1,81 @@ +import bpy +from ..naming import get_basename + + +def check_transfer_data_entry( + transfer_data: bpy.types.CollectionProperty, key: str, td_type: str +) -> set: + """Verifies if transfer data entry exists + + Args: + ownership (bpy.types.CollectionProperty): Transfer Data of an object + key (str): Name of item that is being verified + td_type (str): Type of transfer data + + Returns: + set: Returns set of matches where name is found in ownership + """ + existing_items = [ + transfer_info.name + for transfer_info in transfer_data + if transfer_info.type == td_type + ] + return set([key]).intersection(set(existing_items)) + + +def transfer_data_add_entry( + transfer_data: bpy.types.CollectionProperty, + name: str, + td_type: str, + task_layer_name: str, +): + """Add entry to transfer data ownership + + Args: + ownership (bpy.types.CollectionProperty): Transfer Data of an object + name (str): Name of new transfer data item + td_type (str): Type of transfer data + task_layer_name (str): Name of current task layer + """ + transfer_info = transfer_data.add() + transfer_info.name = name + transfer_info.owner = task_layer_name.upper() + transfer_info.type = td_type + return transfer_info + + +def get_transfer_data_as_names(transfer_data, td_type): + return [ + transfer_info.name + for transfer_info in transfer_data + if transfer_info.type == td_type + ] + + +def transfer_info_clean(obj, list, td_type): + transfer_data_list = get_transfer_data_as_names( + obj.transfer_data_ownership, td_type + ) + for item in list: + if not get_basename(item.name) in transfer_data_list: + list.remove(item) + + +def transfer_info_is_missing(transfer_info, type_key, list): + if transfer_info.type == type_key and not list.get(transfer_info["name"]): + return True + + +def transfer_info_init(scene, obj, list, type_key): + transfer_data = obj.transfer_data_ownership + task_layer_key = scene.asset_pipeline.task_layer_name + for item in list: + # Only add new ownership transfer_info if vertex group doesn't have an owner + matches = check_transfer_data_entry(transfer_data, item.name, type_key) + if len(matches) == 0: + scene.asset_pipeline.add_temp_trasnfer_data( + name=item.name, + owner=task_layer_key, + type=type_key, + obj=obj, + ) -- 2.30.2 From 2d6fcd1e031e23822fabbc525b3903986895eccd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 14:13:14 -0400 Subject: [PATCH 238/429] Asset Pipe: Fix Typo --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 1 - .../merge/transfer_data/transfer_functions.py | 6 +++--- .../asset_pipeline_2/merge/transfer_data/transfer_util.py | 2 +- scripts-blender/addons/asset_pipeline_2/props.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index b53cb1bd..a06cd07f 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -1,6 +1,5 @@ import bpy from typing import Dict, Set -from . import core # TODO DEBUG WHY THIS DOESN'T WORK from .naming import get_target_name, get_basename, get_name_with_asset_prefix from .util import get_storage_of_id from .transfer_data.transfer_util import transfer_data_add_entry diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 7690fdfa..e75b10b2 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -332,7 +332,7 @@ def init_material_slots(scene, obj): matches = check_transfer_data_entry(transfer_data, name, type_key) # Only add new ownership transfer_info if vertex group doesn't have an owner if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( + scene.asset_pipeline.add_temp_transfer_data( name=name, owner=task_layer_key, type=type_key, @@ -578,7 +578,7 @@ def init_attributes(scene, obj): # Only add new ownership transfer_info if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, atttribute.name, type_key) if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( + scene.asset_pipeline.add_temp_transfer_data( name=atttribute.name, owner=task_layer_key, type=type_key, @@ -648,7 +648,7 @@ def init_parent(scene, obj): matches = check_transfer_data_entry(transfer_data, name, type_key) # Only add new ownership transfer_info if vertex group doesn't have an owner if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( + scene.asset_pipeline.add_temp_transfer_data( name=name, owner=task_layer_key, type=type_key, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index c0c35bec..d42113f5 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -73,7 +73,7 @@ def transfer_info_init(scene, obj, list, type_key): # Only add new ownership transfer_info if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, item.name, type_key) if len(matches) == 0: - scene.asset_pipeline.add_temp_trasnfer_data( + scene.asset_pipeline.add_temp_transfer_data( name=item.name, owner=task_layer_key, type=type_key, diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 8189a264..365aff4b 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -56,7 +56,7 @@ class AssetPipeline(bpy.types.PropertyGroup): name="Task Layer Name", items=constants.TASK_LAYER_TYPES_ENUM_ITEMS ) - def add_temp_trasnfer_data(self, name, owner, type, obj): + def add_temp_transfer_data(self, name, owner, type, obj): new_transfer_data = self.temp_transfer_data transfer_info = new_transfer_data.add() transfer_info.name = name -- 2.30.2 From 129fbcc0a9b8122da429228dd32f55e15d5c9bfc Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 15:51:47 -0400 Subject: [PATCH 239/429] Asset Pipe: Update Documentation --- .../asset_pipeline_2/merge/asset_mapping.py | 2 +- .../addons/asset_pipeline_2/merge/core.py | 12 +- .../addons/asset_pipeline_2/merge/drivers.py | 30 ++++- .../addons/asset_pipeline_2/merge/naming.py | 26 ++++- .../asset_pipeline_2/merge/other_ids.py | 23 +++- .../merge/transfer_data/transfer_functions.py | 106 +++++++++++++----- .../merge/transfer_data/transfer_util.py | 74 +++++++++--- 7 files changed, 217 insertions(+), 56 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index a06cd07f..87051351 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -135,7 +135,7 @@ class AssetTransferMapping: temp_info = transfer_data_add_entry( transfer_data=temp_transfer_data, name=transfer_info.name, - td_type=transfer_info.type, + td_type_key=transfer_info.type, task_layer_name=transfer_info.owner, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 6f3ddeb2..7a4ceadc 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -95,7 +95,17 @@ def get_invalid_objects( local_col: bpy.types.Collection, scene: bpy.types.Scene, ) -> list[bpy.types.Object]: - # TODO Add Docstring + """Returns a list of objects not used in the merge processing, + which are considered invalid. The objects will be excluded from + the merge process. + + Args: + local_col (bpy.types.Collection): The top level asset collection that is local to the file + scene (bpy.types.Scene): Scene that contains a the file's asset + + Returns: + list[bpy.types.Object]: List of Invalid Objects + """ task_layer_key = scene.asset_pipeline.task_layer_name task_layer_col_name = get_task_layer_col_name(task_layer_key) task_layer_col = local_col.children.get(task_layer_col_name) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/drivers.py b/scripts-blender/addons/asset_pipeline_2/merge/drivers.py index 0bb4c3bb..f8655417 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/drivers.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/drivers.py @@ -2,7 +2,21 @@ import bpy from rigify.utils.misc import copy_attributes -def copy_driver(from_fcurve, target, data_path=None, index=None): +def copy_driver( + from_fcurve: bpy.types.FCurve, target: bpy.types.ID, data_path=None, index=None +) -> bpy.types.FCurve: + """Copy an existing FCurve containing a driver to a new ID, by creating a copy + of the existing driver on the target ID. + + Args: + from_fcurve (bpy.types.FCurve): FCurve containing a driver + target (bpy.types.ID): ID that can have drivers added to it + data_path (_type_, optional): Data Path of existing driver. Defaults to None. + index (_type_, optional): array index of the property drive. Defaults to None. + + Returns: + bpy.types.FCurve: Fcurve containing copy of driver on target ID + """ if not data_path: data_path = from_fcurve.data_path @@ -36,7 +50,19 @@ def copy_driver(from_fcurve, target, data_path=None, index=None): return new_fc -def find_drivers(drivers, target_type, target_name): +def find_drivers( + drivers: list[bpy.types.FCurve], target_type: str, target_name: str +) -> list[bpy.types.FCurve]: + """_summary_ + + Args: + drivers (list[bpy.types.FCurve]): List or Collection Property containing Fcurves with drivers + target_type (str): Name of data type found in driver data path, e.g. "modifiers" + target_name (str): Name of data found in driver path, e.g. modifier's name + + Returns: + list[bpy.types.FCurve]: List of FCurves containing drivers that match type & name + """ found_drivers = [] for driver in drivers: if f'{target_type}["{target_name}"]' in driver.data_path: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index e952cc33..8a00743c 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -115,8 +115,17 @@ def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) pass -def get_name_with_asset_prefix(name: str): - # TODO Docstring and return types +def get_name_with_asset_prefix(name: str) -> str: + """Returns a string with the prefix if it is not already set. + Users can specify a prefix to live on all objects during the + asset creation process. This prefix is stored in the scene. + + Args: + name (str): Name to add prefix to + + Returns: + str: Returns name with prefix + """ asset_pipe = bpy.context.scene.asset_pipeline if name.startswith(asset_pipe.prefix + "."): return name @@ -124,7 +133,16 @@ def get_name_with_asset_prefix(name: str): return prefix + name -def get_task_layer_col_name(task_layer_key): - # TODO Docstring and return types +def get_task_layer_col_name(task_layer_key) -> str: + """Returns the name of a givem task layer colection via + the task layer key. Task Layer Collection names are a combination + of a prefix if any and the task_layer_name. + + Args: + task_layer_key (_type_): Key of a given task layer + + Returns: + str: Task Layer Collection name including prefix if exists + """ task_layer_name = constants.TASK_LAYER_TYPES[task_layer_key] return get_name_with_asset_prefix(task_layer_name) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py index 9ae3eb5d..533398eb 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py @@ -1,8 +1,18 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids +# TODO find better name for 'other_ids' -def get_other_ids(collection): # TODO find better name + +def get_other_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: + """Returns a list of any ID that is not covered by the merge process + + Args: + collection (bpy.types.Collection): Collection that contains data that references 'other_ids' + + Returns: + list[bpy.types.ID]: List of 'other_ids' + """ ref_map = get_id_reference_map() all_ids_of_coll = get_all_referenced_ids(collection, ref_map) return [ @@ -12,7 +22,16 @@ def get_other_ids(collection): # TODO find better name ] -def init_other_ids(scene): +def init_other_ids(scene: bpy.types.Scene) -> list[bpy.types.ID]: + """Intilizes any ID not covered by the transfer process as an 'other_id' + and marks all 'other_ids' without an owner to the current task layer + + Args: + scene (bpy.types.Scene): Scene that contains a the file's asset + + Returns: + list[bpy.types.ID]: A list of new 'other_ids' owned by the file's task layer + """ other_ids = [] asset_pipe = scene.asset_pipeline local_col = asset_pipe.asset_collection diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index e75b10b2..60ca18e7 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -20,17 +20,26 @@ import numpy as np # VERTEX GROUPS def vertex_groups_clean(obj): - transfer_info_clean(obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY) + transfer_info_clean( + obj=obj, data_list=obj.vertex_groups, td_type_key=constants.VERTEX_GROUP_KEY + ) def vertex_group_is_missing(transfer_info): return transfer_info_is_missing( - transfer_info, constants.VERTEX_GROUP_KEY, transfer_info.id_data.vertex_groups + transfer_info=transfer_info, + td_type_key=constants.VERTEX_GROUP_KEY, + data_list=transfer_info.id_data.vertex_groups, ) def init_vertex_groups(scene, obj): - transfer_info_init(scene, obj, obj.vertex_groups, constants.VERTEX_GROUP_KEY) + transfer_info_init( + scene=scene, + obj=obj, + data_list=obj.vertex_groups, + td_type_key=constants.VERTEX_GROUP_KEY, + ) def transfer_vertex_group( @@ -77,19 +86,30 @@ def transfer_vertex_group( def vertex_colors_clean(obj): if not obj.type == "MESH": return - transfer_info_clean(obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY) + transfer_info_clean( + obj=obj, + data_list=obj.data.vertex_colors, + td_type_key=constants.VERTEX_COLOR_KEY, + ) def vertex_color_is_missing(transfer_info): return transfer_info_is_missing( - transfer_info, constants.VERTEX_COLOR_KEY, transfer_info.id_data.vertex_colors + transfer_info=transfer_info, + td_type_key=constants.VERTEX_COLOR_KEY, + data_list=transfer_info.id_data.vertex_colors, ) def init_vertex_colors(scene, obj): if not obj.type == "MESH": return - transfer_info_init(scene, obj, obj.data.vertex_colors, constants.VERTEX_COLOR_KEY) + transfer_info_init( + scene=scene, + obj=obj, + data_list=obj.data.vertex_colors, + td_type_key=constants.VERTEX_COLOR_KEY, + ) def transfer_vertex_color( @@ -118,19 +138,28 @@ def transfer_vertex_color( def uv_layer_clean(obj): if not obj.type == "MESH": return - transfer_info_clean(obj, obj.data.uv_layers, constants.UV_LAYERS_KEY) + transfer_info_clean( + obj=obj, data_list=obj.data.uv_layers, td_type_key=constants.UV_LAYERS_KEY + ) def uv_layer_is_missing(transfer_info): return transfer_info_is_missing( - transfer_info, constants.UV_LAYERS_KEY, transfer_info.id_data.data.uv_layers + transfer_info=transfer_info, + td_type_key=constants.UV_LAYERS_KEY, + data_list=transfer_info.id_data.data.uv_layers, ) def init_uv_layers(scene, obj): if not obj.type == "MESH": return - transfer_info_init(scene, obj, obj.data.uv_layers, constants.UV_LAYERS_KEY) + transfer_info_init( + scene=scene, + obj=obj, + data_list=obj.data.uv_layers, + td_type_key=constants.UV_LAYERS_KEY, + ) def transfer_uv_layer(source_obj, target_obj, uv_name): @@ -157,17 +186,26 @@ def transfer_uv_layer(source_obj, target_obj, uv_name): # MODIFIERS def modifiers_clean(obj): - transfer_info_clean(obj, obj.modifiers, constants.MODIFIER_KEY) + transfer_info_clean( + obj=obj, data_list=obj.modifiers, td_type_key=constants.MODIFIER_KEY + ) def modifier_is_missing(transfer_info): return transfer_info_is_missing( - transfer_info, constants.MODIFIER_KEY, transfer_info.id_data.modifiers + transfer_info=transfer_info, + td_type_key=constants.MODIFIER_KEY, + data_list=transfer_info.id_data.modifiers, ) def init_modifiers(scene, obj): - transfer_info_init(scene, obj, obj.modifiers, constants.MODIFIER_KEY) + transfer_info_init( + scene=scene, + obj=obj, + data_list=obj.modifiers, + td_type_key=constants.MODIFIER_KEY, + ) def transfer_modifier(modifier_name, target_obj, source_obj): @@ -233,17 +271,26 @@ def transfer_modifier(modifier_name, target_obj, source_obj): # CONSTRAINTS def constraints_clean(obj): - transfer_info_clean(obj, obj.constraints, constants.CONSTRAINT_KEY) + transfer_info_clean( + obj=obj, data_list=obj.constraints, td_type_key=constants.CONSTRAINT_KEY + ) def constraint_is_missing(transfer_info): return transfer_info_is_missing( - transfer_info, constants.CONSTRAINT_KEY, transfer_info.id_data.constraints + transfer_info=transfer_info, + td_type_key=constants.CONSTRAINT_KEY, + data_list=transfer_info.id_data.constraints, ) def init_constraints(scene, obj): - transfer_info_init(scene, obj, obj.constraints, constants.CONSTRAINT_KEY) + transfer_info_init( + scene=scene, + obj=obj, + data_list=obj.constraints, + td_type_key=constants.CONSTRAINT_KEY, + ) def transfer_constraint(constraint_name, target_obj, source_obj): @@ -322,20 +369,20 @@ def material_slots_is_missing(transfer_info): def init_material_slots(scene, obj): task_layer_key = scene.asset_pipeline.task_layer_name - type_key = constants.MATERIAL_SLOT_KEY + td_type_key = constants.MATERIAL_SLOT_KEY name = constants.MATERIAL_TRANSFER_INFO_NAME transfer_data = obj.transfer_data_ownership # Only Execute if Material Slots exist on object if len(obj.material_slots) == 0: return - matches = check_transfer_data_entry(transfer_data, name, type_key) + matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_info if vertex group doesn't have an owner if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=name, owner=task_layer_key, - type=type_key, + type=td_type_key, obj=obj, ) @@ -427,9 +474,9 @@ def shape_key_is_missing(transfer_info): if not obj.data.shape_keys: return True return transfer_info_is_missing( - transfer_info, - constants.SHAPE_KEY_KEY, - obj.data.shape_keys.key_blocks, + transfer_info=transfer_info, + td_type_key=constants.SHAPE_KEY_KEY, + data_list=obj.data.shape_keys.key_blocks, ) @@ -448,7 +495,10 @@ def init_shape_keys(scene, obj): ) transfer_info_init( - scene, obj, obj.data.shape_keys.key_blocks, constants.SHAPE_KEY_KEY + scene=scene, + obj=obj, + data_list=obj.data.shape_keys.key_blocks, + td_type_key=constants.SHAPE_KEY_KEY, ) @@ -573,15 +623,15 @@ def init_attributes(scene, obj): return transfer_data = obj.transfer_data_ownership task_layer_key = scene.asset_pipeline.task_layer_name - type_key = constants.ATTRIBUTE_KEY + td_type_key = constants.ATTRIBUTE_KEY for atttribute in attributes_get_editable(obj.data.attributes): # Only add new ownership transfer_info if vertex group doesn't have an owner - matches = check_transfer_data_entry(transfer_data, atttribute.name, type_key) + matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=atttribute.name, owner=task_layer_key, - type=type_key, + type=td_type_key, obj=obj, ) @@ -638,20 +688,20 @@ def parent_is_missing(transfer_info): def init_parent(scene, obj): task_layer_key = scene.asset_pipeline.task_layer_name - type_key = constants.PARENT_KEY + td_type_key = constants.PARENT_KEY name = constants.PARENT_TRANSFER_INFO_NAME transfer_data = obj.transfer_data_ownership # Only Execute if Material Slots exist on object if obj.parent == None: return - matches = check_transfer_data_entry(transfer_data, name, type_key) + matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_info if vertex group doesn't have an owner if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=name, owner=task_layer_key, - type=type_key, + type=td_type_key, obj=obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index d42113f5..d9468dd1 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -3,14 +3,14 @@ from ..naming import get_basename def check_transfer_data_entry( - transfer_data: bpy.types.CollectionProperty, key: str, td_type: str + transfer_data: bpy.types.CollectionProperty, key: str, td_type_key: str ) -> set: """Verifies if transfer data entry exists Args: ownership (bpy.types.CollectionProperty): Transfer Data of an object key (str): Name of item that is being verified - td_type (str): Type of transfer data + td_type_key (str): Type of transfer data Returns: set: Returns set of matches where name is found in ownership @@ -18,7 +18,7 @@ def check_transfer_data_entry( existing_items = [ transfer_info.name for transfer_info in transfer_data - if transfer_info.type == td_type + if transfer_info.type == td_type_key ] return set([key]).intersection(set(existing_items)) @@ -26,7 +26,7 @@ def check_transfer_data_entry( def transfer_data_add_entry( transfer_data: bpy.types.CollectionProperty, name: str, - td_type: str, + td_type_key: str, task_layer_name: str, ): """Add entry to transfer data ownership @@ -34,48 +34,86 @@ def transfer_data_add_entry( Args: ownership (bpy.types.CollectionProperty): Transfer Data of an object name (str): Name of new transfer data item - td_type (str): Type of transfer data + td_type_key (str): Type of transfer data task_layer_name (str): Name of current task layer """ transfer_info = transfer_data.add() transfer_info.name = name transfer_info.owner = task_layer_name.upper() - transfer_info.type = td_type + transfer_info.type = td_type_key return transfer_info -def get_transfer_data_as_names(transfer_data, td_type): +def get_transfer_data_as_names(transfer_data, td_type_key): return [ transfer_info.name for transfer_info in transfer_data - if transfer_info.type == td_type + if transfer_info.type == td_type_key ] -def transfer_info_clean(obj, list, td_type): +# TODO Test if Clean and Missing are redudent functions +def transfer_info_clean( + obj: bpy.types.Object, data_list: bpy.types.CollectionProperty, td_type_key: str +): + """Remove transfer data entries if the corrisponding data doesn't exist + Args: + obj (bpy.types.Object): Object containing transfer data + data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers + td_type_key (str): Key for the transfer data type + """ transfer_data_list = get_transfer_data_as_names( - obj.transfer_data_ownership, td_type + obj.transfer_data_ownership, td_type_key ) - for item in list: + for item in data_list: if not get_basename(item.name) in transfer_data_list: - list.remove(item) + data_list.remove(item) -def transfer_info_is_missing(transfer_info, type_key, list): - if transfer_info.type == type_key and not list.get(transfer_info["name"]): +def transfer_info_is_missing( + transfer_info, data_list: bpy.types.CollectionProperty, td_type_key: str +) -> bool: + """Returns true if a transfer_data_item does not exist + + Args: + transfer_info (_type_): Item of Transfer Data + data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers + td_type_key (str): Key for the transfer data type + Returns: + bool: Returns True if transfer_info is missing + """ + if transfer_info.type == td_type_key and not data_list.get(transfer_info["name"]): return True -def transfer_info_init(scene, obj, list, type_key): +"""Intilize transfer data to a temporary collection property, used + to draw a display of new transfer data to the user before merge process. +""" + + +def transfer_info_init( + scene: bpy.types.Scene, + obj: bpy.types.Object, + data_list: bpy.types.CollectionProperty, + td_type_key: str, +): + """_summary_ + + Args: + scene (bpy.types.Scene): Scene that contains a the file's asset + obj (bpy.types.Object): Object containing possible transfer data + data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers + td_type_key (str): Key for the transfer data type + """ transfer_data = obj.transfer_data_ownership task_layer_key = scene.asset_pipeline.task_layer_name - for item in list: + for item in data_list: # Only add new ownership transfer_info if vertex group doesn't have an owner - matches = check_transfer_data_entry(transfer_data, item.name, type_key) + matches = check_transfer_data_entry(transfer_data, item.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=item.name, owner=task_layer_key, - type=type_key, + type=td_type_key, obj=obj, ) -- 2.30.2 From 942427a374724520932a6eb801977b3132bb908b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 16:06:31 -0400 Subject: [PATCH 240/429] Asset Pipe: Remove Reduant Function get_transfer_data_as_names() --- .../merge/transfer_data/transfer_functions.py | 48 +++++++++++-------- .../merge/transfer_data/transfer_util.py | 18 +++---- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 60ca18e7..9a5f27bf 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -6,7 +6,6 @@ from .transfer_util import ( transfer_info_clean, transfer_info_is_missing, transfer_info_init, - get_transfer_data_as_names, # TODO Replce with check entry check_transfer_data_entry, ) from ... import constants @@ -346,13 +345,15 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT def material_slots_clean(obj): # Material slots cannot use generic transfer_info_clean() function - context = bpy.context - transfer_data_list = get_transfer_data_as_names( - obj.transfer_data_ownership, constants.MATERIAL_SLOT_KEY + + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + constants.MATERIAL_TRANSFER_INFO_NAME, + constants.MATERIAL_SLOT_KEY, ) # Clear Materials if No Transfer Data is Found - if transfer_data_list != []: + if len(matches) != 0: return if obj.data and hasattr(obj.data, 'materials'): @@ -454,14 +455,16 @@ def shape_key_closest_tri_on_face(tris_dict, face, p): def shape_keys_clean(obj): - context = bpy.context if obj.type != "MESH" or obj.data.shape_keys is None: return - transfer_data_list = get_transfer_data_as_names( - obj.transfer_data_ownership, constants.SHAPE_KEY_KEY - ) + for shape_key in obj.data.shape_keys.key_blocks: - if not get_basename(shape_key.name) in transfer_data_list: + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + get_basename(shape_key.name), + constants.SHAPE_KEY_KEY, + ) + if len(matches) == 0: obj.shape_key_remove(shape_key) @@ -596,13 +599,16 @@ def attribute_clean(obj): if obj.type != "MESH": return attributes = attributes_get_editable(obj.data.attributes) - transfer_data_list = get_transfer_data_as_names( - obj.transfer_data_ownership, constants.ATTRIBUTE_KEY - ) - for item in attributes: - if not get_basename(item.name) in transfer_data_list: - print(f"Cleaning attribute {item.name}") - obj.data.attributes.remove(item) + + for attribute in attributes: + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + get_basename(attribute.name), + constants.SHAPE_KEY_KEY, + ) + if len(matches) == 0: + print(f"Cleaning attribute {attribute.name}") + obj.data.attributes.remove(attribute) def attribute_is_missing(transfer_info): @@ -667,11 +673,13 @@ def transfer_attribute( def parent_clean(obj): - transfer_data_list = get_transfer_data_as_names( - obj.transfer_data_ownership, constants.PARENT_KEY + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + get_basename(constants.PARENT_TRANSFER_INFO_NAME), + constants.SHAPE_KEY_KEY, ) - if transfer_data_list != []: + if len(matches) != 0: return obj.parent = None diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index d9468dd1..f72d8e1e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -44,14 +44,6 @@ def transfer_data_add_entry( return transfer_info -def get_transfer_data_as_names(transfer_data, td_type_key): - return [ - transfer_info.name - for transfer_info in transfer_data - if transfer_info.type == td_type_key - ] - - # TODO Test if Clean and Missing are redudent functions def transfer_info_clean( obj: bpy.types.Object, data_list: bpy.types.CollectionProperty, td_type_key: str @@ -62,11 +54,13 @@ def transfer_info_clean( data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers td_type_key (str): Key for the transfer data type """ - transfer_data_list = get_transfer_data_as_names( - obj.transfer_data_ownership, td_type_key - ) for item in data_list: - if not get_basename(item.name) in transfer_data_list: + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + get_basename(item.name), + td_type_key, + ) + if len(matches) == 0: data_list.remove(item) -- 2.30.2 From 275fa99821e06979c1a96b57c8ccca5421f466a6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 16:13:31 -0400 Subject: [PATCH 241/429] Asset Pipe: Fix Bug in Parent Clean --- .../asset_pipeline_2/merge/transfer_data/transfer_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 9a5f27bf..6d647590 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -676,7 +676,7 @@ def parent_clean(obj): matches = check_transfer_data_entry( obj.transfer_data_ownership, get_basename(constants.PARENT_TRANSFER_INFO_NAME), - constants.SHAPE_KEY_KEY, + constants.PARENT_KEY, ) if len(matches) != 0: -- 2.30.2 From 554580e0b8e21f4ab771d1e7a402564214673688 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 16:15:08 -0400 Subject: [PATCH 242/429] Asset Pipe: Fix Bug in Attribute Clean --- .../asset_pipeline_2/merge/transfer_data/transfer_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 6d647590..9a3db8a6 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -604,7 +604,7 @@ def attribute_clean(obj): matches = check_transfer_data_entry( obj.transfer_data_ownership, get_basename(attribute.name), - constants.SHAPE_KEY_KEY, + constants.ATTRIBUTE_KEY, ) if len(matches) == 0: print(f"Cleaning attribute {attribute.name}") -- 2.30.2 From 26bdbcb3af7c8737ffd5fc27d053d7f1219e0fc2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 16:27:00 -0400 Subject: [PATCH 243/429] Asset Pipe: Rename Transfer_Info to Transfer_Data_Item --- .../addons/asset_pipeline_2/constants.py | 4 +- .../asset_pipeline_2/merge/asset_mapping.py | 52 ++++---- .../addons/asset_pipeline_2/merge/core.py | 17 +-- .../merge/transfer_data/transfer_core.py | 42 ++++--- .../merge/transfer_data/transfer_functions.py | 114 +++++++++--------- .../merge/transfer_data/transfer_ui.py | 44 +++---- .../merge/transfer_data/transfer_util.py | 34 +++--- .../addons/asset_pipeline_2/ops.py | 10 +- .../addons/asset_pipeline_2/props.py | 10 +- 9 files changed, 168 insertions(+), 159 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 1fce599d..5b048e47 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -53,8 +53,8 @@ TRANSFER_DATA_TYPES_ENUM_ITEMS = [ for i, tup in enumerate(TRANSFER_DATA_TYPES.items()) ] -MATERIAL_TRANSFER_INFO_NAME = "All Material Slots" -PARENT_TRANSFER_INFO_NAME = "Parent Relationship" +MATERIAL_TRANSFER_DATA_ITEM_NAME = "All Material Slots" +PARENT_TRANSFER_DATA_ITEM_NAME = "Parent Relationship" PUBLISH_TYPES = [ ( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 87051351..07942006 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -129,41 +129,41 @@ class AssetTransferMapping: return coll_map - def _get_transfer_data_map_item(self, obj, target_obj, transfer_info): + def _get_transfer_data_map_item(self, obj, target_obj, transfer_data_item): temp_transfer_data = bpy.context.scene.asset_pipeline.temp_transfer_data - temp_info_index = len(temp_transfer_data) - temp_info = transfer_data_add_entry( + temp_transfer_data_item_index = len(temp_transfer_data) + temp_transfer_data_item = transfer_data_add_entry( transfer_data=temp_transfer_data, - name=transfer_info.name, - td_type_key=transfer_info.type, - task_layer_name=transfer_info.owner, + name=transfer_data_item.name, + td_type_key=transfer_data_item.type, + task_layer_name=transfer_data_item.owner, ) map_item = { - 'transfer_info_index': temp_info_index, + 'transfer_data_item_index': temp_transfer_data_item_index, 'source_obj': obj, 'target_obj': target_obj, } # Names of each map item need to be unique # below name avoids name conflicts between different types - name = transfer_info.name + '_' + transfer_info.type + obj.name + name = transfer_data_item.name + '_' + transfer_data_item.type + obj.name return name, map_item - def _check_transfer_data_conflict(self, obj, transfer_info): + def _check_transfer_data_conflict(self, obj, transfer_data_item): other_obj = bpy.data.objects.get(get_target_name(obj.name)) - check_transfer_info = None + check_transfer_data_item = None if not other_obj: return - for other_transfer_info in other_obj.transfer_data_ownership: + for other_transfer_data_item in other_obj.transfer_data_ownership: if ( - other_transfer_info.type == transfer_info.type - and other_transfer_info.name == transfer_info.name + other_transfer_data_item.type == transfer_data_item.type + and other_transfer_data_item.name == transfer_data_item.name ): - check_transfer_info = other_transfer_info - if check_transfer_info is None: + check_transfer_data_item = other_transfer_data_item + if check_transfer_data_item is None: return - if check_transfer_info.owner != transfer_info.owner: - self.conflict_trasnfer_data.append(transfer_info) + if check_transfer_data_item.owner != transfer_data_item.owner: + self.conflict_trasnfer_data.append(transfer_data_item) print("CONFLICT FOUND") return True @@ -177,29 +177,29 @@ class AssetTransferMapping: objs = [source_obj, target_obj] for obj in objs: if obj.name.endswith(constants.LOCAL_SUFFIX): - for transfer_info in obj.transfer_data_ownership: - if transfer_info.owner in self._local_tls: + for transfer_data_item in obj.transfer_data_ownership: + if transfer_data_item.owner in self._local_tls: conflict = self._check_transfer_data_conflict( - obj, transfer_info + obj, transfer_data_item ) if not conflict: name, map_item = self._get_transfer_data_map_item( - obj, target_obj, transfer_info + obj, target_obj, transfer_data_item ) transfer_data_map[name] = map_item if obj.name.endswith(constants.EXTERNAL_SUFFIX): - for transfer_info in obj.transfer_data_ownership: + for transfer_data_item in obj.transfer_data_ownership: if ( - transfer_info.owner not in self._local_tls - and transfer_info.owner != "NONE" + transfer_data_item.owner not in self._local_tls + and transfer_data_item.owner != "NONE" ): conflict = self._check_transfer_data_conflict( - obj, transfer_info + obj, transfer_data_item ) if not conflict: name, map_item = self._get_transfer_data_map_item( - obj, target_obj, transfer_info + obj, target_obj, transfer_data_item ) transfer_data_map[name] = map_item return transfer_data_map diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 7a4ceadc..ce8e4334 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -32,10 +32,10 @@ def ownership_transfer_data_cleanup( """ transfer_data = obj.transfer_data_ownership to_remove = [] - for transfer_info in transfer_data: - if transfer_info.owner == task_layer_name: - if transfer_data_is_missing(transfer_info): - to_remove.append(transfer_info.name) + for transfer_data_item in transfer_data: + if transfer_data_item.owner == task_layer_name: + if transfer_data_is_missing(transfer_data_item): + to_remove.append(transfer_data_item.name) for name in to_remove: transfer_data.remove(transfer_data.keys().index(name)) @@ -84,10 +84,13 @@ def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: temp_transfer_data (bpy.types.CollectionProperty): Collection property containing newly found data and the object that contains this data. """ - for transfer_info in temp_transfer_data: - transfer_data = transfer_info.obj.transfer_data_ownership + for transfer_data_item in temp_transfer_data: + transfer_data = transfer_data_item.obj.transfer_data_ownership transfer_data_add_entry( - transfer_data, transfer_info.name, transfer_info.type, transfer_info.owner + transfer_data, + transfer_data_item.name, + transfer_data_item.type, + transfer_data_item.owner, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 616fd059..48220383 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -105,74 +105,76 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: for name in transfer_data_map: temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data transfer_data = transfer_data_map[name] - transfer_info = temp_transfer_data[transfer_data.get('transfer_info_index')] + transfer_data_item = temp_transfer_data[ + transfer_data.get('transfer_data_item_index') + ] target_obj = transfer_data.get('target_obj') source_obj = transfer_data.get('source_obj') if target_obj is None: - print(f"Failed to Transfer data for {transfer_info.id_data.name}") + print(f"Failed to Transfer data for {transfer_data_item.id_data.name}") continue - if transfer_info is None: + if transfer_data_item is None: continue if source_obj != target_obj: - if transfer_info.type == constants.VERTEX_GROUP_KEY: + if transfer_data_item.type == constants.VERTEX_GROUP_KEY: print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") transfer_functions.transfer_vertex_group( context=context, - vertex_group_name=transfer_info.name, + vertex_group_name=transfer_data_item.name, target_obj=target_obj, source_obj=source_obj, ) - # if transfer_info.type == constants.VERTEX_COLOR_KEY: + # if transfer_data_item.type == constants.VERTEX_COLOR_KEY: # transfer_functions.transfer_vertex_color( - # vertex_color_name=transfer_info.name, + # vertex_color_name=transfer_data_item.name, # target_obj=target_obj, # source_obj=source_obj, # ) - if transfer_info.type == constants.MODIFIER_KEY: + if transfer_data_item.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") transfer_functions.transfer_modifier( - modifier_name=transfer_info.name, + modifier_name=transfer_data_item.name, target_obj=target_obj, source_obj=source_obj, ) - if transfer_info.type == constants.CONSTRAINT_KEY: + if transfer_data_item.type == constants.CONSTRAINT_KEY: transfer_functions.transfer_constraint( - constraint_name=transfer_info.name, + constraint_name=transfer_data_item.name, target_obj=target_obj, source_obj=source_obj, ) - if transfer_info.type == constants.MATERIAL_SLOT_KEY: + if transfer_data_item.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") transfer_functions.transfer_material_slots( target_obj=target_obj, source_obj=source_obj, ) - # if transfer_info.type == constants.UV_LAYERS_KEY: + # if transfer_data_item.type == constants.UV_LAYERS_KEY: # transfer_functions.transfer_uv_layer( # target_obj=target_obj, # source_obj=source_obj, - # uv_name=transfer_info.name, + # uv_name=transfer_data_item.name, # ) - if transfer_info.type == constants.SHAPE_KEY_KEY: + if transfer_data_item.type == constants.SHAPE_KEY_KEY: transfer_functions.transfer_shape_key( context=context, target_obj=target_obj, source_obj=source_obj, - shape_key_name=transfer_info.name, + shape_key_name=transfer_data_item.name, ) - if transfer_info.type == constants.ATTRIBUTE_KEY: + if transfer_data_item.type == constants.ATTRIBUTE_KEY: transfer_functions.transfer_attribute( target_obj=target_obj, source_obj=source_obj, - attribute_name=transfer_info.name, + attribute_name=transfer_data_item.name, ) - if transfer_info.type == constants.PARENT_KEY: + if transfer_data_item.type == constants.PARENT_KEY: transfer_functions.transfer_parent( target_obj=target_obj, source_obj=source_obj, ) copy_transfer_data_ownership( - transfer_data_item=transfer_info, + transfer_data_item=transfer_data_item, target_obj=target_obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 9a3db8a6..c0ec8247 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -3,9 +3,9 @@ from bpy import context from ..naming import get_basename from ..drivers import find_drivers, copy_driver from .transfer_util import ( - transfer_info_clean, - transfer_info_is_missing, - transfer_info_init, + transfer_data_clean, + transfer_data_item_is_missing, + transfer_data_item_init, check_transfer_data_entry, ) from ... import constants @@ -19,21 +19,21 @@ import numpy as np # VERTEX GROUPS def vertex_groups_clean(obj): - transfer_info_clean( + transfer_data_clean( obj=obj, data_list=obj.vertex_groups, td_type_key=constants.VERTEX_GROUP_KEY ) -def vertex_group_is_missing(transfer_info): - return transfer_info_is_missing( - transfer_info=transfer_info, +def vertex_group_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, td_type_key=constants.VERTEX_GROUP_KEY, - data_list=transfer_info.id_data.vertex_groups, + data_list=transfer_data_item.id_data.vertex_groups, ) def init_vertex_groups(scene, obj): - transfer_info_init( + transfer_data_item_init( scene=scene, obj=obj, data_list=obj.vertex_groups, @@ -85,25 +85,25 @@ def transfer_vertex_group( def vertex_colors_clean(obj): if not obj.type == "MESH": return - transfer_info_clean( + transfer_data_clean( obj=obj, data_list=obj.data.vertex_colors, td_type_key=constants.VERTEX_COLOR_KEY, ) -def vertex_color_is_missing(transfer_info): - return transfer_info_is_missing( - transfer_info=transfer_info, +def vertex_color_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, td_type_key=constants.VERTEX_COLOR_KEY, - data_list=transfer_info.id_data.vertex_colors, + data_list=transfer_data_item.id_data.vertex_colors, ) def init_vertex_colors(scene, obj): if not obj.type == "MESH": return - transfer_info_init( + transfer_data_item_init( scene=scene, obj=obj, data_list=obj.data.vertex_colors, @@ -137,23 +137,23 @@ def transfer_vertex_color( def uv_layer_clean(obj): if not obj.type == "MESH": return - transfer_info_clean( + transfer_data_clean( obj=obj, data_list=obj.data.uv_layers, td_type_key=constants.UV_LAYERS_KEY ) -def uv_layer_is_missing(transfer_info): - return transfer_info_is_missing( - transfer_info=transfer_info, +def uv_layer_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, td_type_key=constants.UV_LAYERS_KEY, - data_list=transfer_info.id_data.data.uv_layers, + data_list=transfer_data_item.id_data.data.uv_layers, ) def init_uv_layers(scene, obj): if not obj.type == "MESH": return - transfer_info_init( + transfer_data_item_init( scene=scene, obj=obj, data_list=obj.data.uv_layers, @@ -185,21 +185,21 @@ def transfer_uv_layer(source_obj, target_obj, uv_name): # MODIFIERS def modifiers_clean(obj): - transfer_info_clean( + transfer_data_clean( obj=obj, data_list=obj.modifiers, td_type_key=constants.MODIFIER_KEY ) -def modifier_is_missing(transfer_info): - return transfer_info_is_missing( - transfer_info=transfer_info, +def modifier_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, td_type_key=constants.MODIFIER_KEY, - data_list=transfer_info.id_data.modifiers, + data_list=transfer_data_item.id_data.modifiers, ) def init_modifiers(scene, obj): - transfer_info_init( + transfer_data_item_init( scene=scene, obj=obj, data_list=obj.modifiers, @@ -270,21 +270,21 @@ def transfer_modifier(modifier_name, target_obj, source_obj): # CONSTRAINTS def constraints_clean(obj): - transfer_info_clean( + transfer_data_clean( obj=obj, data_list=obj.constraints, td_type_key=constants.CONSTRAINT_KEY ) -def constraint_is_missing(transfer_info): - return transfer_info_is_missing( - transfer_info=transfer_info, +def constraint_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, td_type_key=constants.CONSTRAINT_KEY, - data_list=transfer_info.id_data.constraints, + data_list=transfer_data_item.id_data.constraints, ) def init_constraints(scene, obj): - transfer_info_init( + transfer_data_item_init( scene=scene, obj=obj, data_list=obj.constraints, @@ -344,11 +344,11 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # MATERIAL SLOT def material_slots_clean(obj): - # Material slots cannot use generic transfer_info_clean() function + # Material slots cannot use generic transfer_data_clean() function matches = check_transfer_data_entry( obj.transfer_data_ownership, - constants.MATERIAL_TRANSFER_INFO_NAME, + constants.MATERIAL_TRANSFER_DATA_ITEM_NAME, constants.MATERIAL_SLOT_KEY, ) @@ -360,10 +360,10 @@ def material_slots_clean(obj): obj.data.materials.clear() -def material_slots_is_missing(transfer_info): +def material_slots_is_missing(transfer_data_item): if ( - transfer_info.type == constants.MATERIAL_SLOT_KEY - and len(transfer_info.id_data.material_slots) == 0 + transfer_data_item.type == constants.MATERIAL_SLOT_KEY + and len(transfer_data_item.id_data.material_slots) == 0 ): return True @@ -371,14 +371,14 @@ def material_slots_is_missing(transfer_info): def init_material_slots(scene, obj): task_layer_key = scene.asset_pipeline.task_layer_name td_type_key = constants.MATERIAL_SLOT_KEY - name = constants.MATERIAL_TRANSFER_INFO_NAME + name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership # Only Execute if Material Slots exist on object if len(obj.material_slots) == 0: return matches = check_transfer_data_entry(transfer_data, name, td_type_key) - # Only add new ownership transfer_info if vertex group doesn't have an owner + # Only add new ownership transfer_data_item if vertex group doesn't have an owner if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=name, @@ -468,16 +468,16 @@ def shape_keys_clean(obj): obj.shape_key_remove(shape_key) -def shape_key_is_missing(transfer_info): - if not transfer_info.type == constants.SHAPE_KEY_KEY: +def shape_key_is_missing(transfer_data_item): + if not transfer_data_item.type == constants.SHAPE_KEY_KEY: return - obj = transfer_info.id_data + obj = transfer_data_item.id_data if obj.type != 'MESH': return if not obj.data.shape_keys: return True - return transfer_info_is_missing( - transfer_info=transfer_info, + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, td_type_key=constants.SHAPE_KEY_KEY, data_list=obj.data.shape_keys.key_blocks, ) @@ -497,7 +497,7 @@ def init_shape_keys(scene, obj): f'Shape Key "{kb.name}" must be ordered after its base shape "{kb.relative_key.name}" on object "{obj.name}".' ) - transfer_info_init( + transfer_data_item_init( scene=scene, obj=obj, data_list=obj.data.shape_keys.key_blocks, @@ -611,15 +611,15 @@ def attribute_clean(obj): obj.data.attributes.remove(attribute) -def attribute_is_missing(transfer_info): - obj = transfer_info.id_data +def attribute_is_missing(transfer_data_item): + obj = transfer_data_item.id_data if obj.type != "MESH": return attributes = attributes_get_editable(obj.data.attributes) attribute_names = [attribute.name for attribute in attributes] if ( - transfer_info.type == constants.ATTRIBUTE_KEY - and not transfer_info["name"] in attribute_names + transfer_data_item.type == constants.ATTRIBUTE_KEY + and not transfer_data_item["name"] in attribute_names ): return True @@ -631,7 +631,7 @@ def init_attributes(scene, obj): task_layer_key = scene.asset_pipeline.task_layer_name td_type_key = constants.ATTRIBUTE_KEY for atttribute in attributes_get_editable(obj.data.attributes): - # Only add new ownership transfer_info if vertex group doesn't have an owner + # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( @@ -675,7 +675,7 @@ def transfer_attribute( def parent_clean(obj): matches = check_transfer_data_entry( obj.transfer_data_ownership, - get_basename(constants.PARENT_TRANSFER_INFO_NAME), + get_basename(constants.PARENT_TRANSFER_DATA_ITEM_NAME), constants.PARENT_KEY, ) @@ -686,10 +686,10 @@ def parent_clean(obj): print("Cleaning Parent Relationship") -def parent_is_missing(transfer_info): +def parent_is_missing(transfer_data_item): if ( - transfer_info.type == constants.PARENT_KEY - and transfer_info.id_data.parent == None + transfer_data_item.type == constants.PARENT_KEY + and transfer_data_item.id_data.parent == None ): return True @@ -697,14 +697,14 @@ def parent_is_missing(transfer_info): def init_parent(scene, obj): task_layer_key = scene.asset_pipeline.task_layer_name td_type_key = constants.PARENT_KEY - name = constants.PARENT_TRANSFER_INFO_NAME + name = constants.PARENT_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership # Only Execute if Material Slots exist on object if obj.parent == None: return matches = check_transfer_data_entry(transfer_data, name, td_type_key) - # Only add new ownership transfer_info if vertex group doesn't have an owner + # Only add new ownership transfer_data_item if vertex group doesn't have an owner if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=name, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index d9006751..9528323d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -11,9 +11,9 @@ def draw_transfer_data_type( name, icon = constants.TRANSFER_DATA_TYPES[transfer_data[0].type] box = layout.box() box.label(text=name, icon=icon) - for transfer_info in transfer_data: - owner_tl_ui_name = constants.TASK_LAYER_TYPES[transfer_info.owner] - box.label(text=f"{transfer_info.name}: '{owner_tl_ui_name}'") + for transfer_data_item in transfer_data: + owner_tl_ui_name = constants.TASK_LAYER_TYPES[transfer_data_item.owner] + box.label(text=f"{transfer_data_item.name}: '{owner_tl_ui_name}'") def draw_transfer_data( @@ -30,25 +30,25 @@ def draw_transfer_data( attributes = [] parent = [] - for transfer_info in transfer_data: - if transfer_info.type == constants.VERTEX_GROUP_KEY: - vertex_groups.append(transfer_info) - # if transfer_info.type == constants.VERTEX_COLOR_KEY: - # vertex_colors.append(transfer_info) - if transfer_info.type == constants.MATERIAL_SLOT_KEY: - material_slots.append(transfer_info) - if transfer_info.type == constants.MODIFIER_KEY: - modifiers.append(transfer_info) - if transfer_info.type == constants.CONSTRAINT_KEY: - constraints.append(transfer_info) - # if transfer_info.type == constants.UV_LAYERS_KEY: - # uv_layers.append(transfer_info) - if transfer_info.type == constants.SHAPE_KEY_KEY: - shape_keys.append(transfer_info) - if transfer_info.type == constants.ATTRIBUTE_KEY: - attributes.append(transfer_info) - if transfer_info.type == constants.PARENT_KEY: - parent.append(transfer_info) + for transfer_data_item in transfer_data: + if transfer_data_item.type == constants.VERTEX_GROUP_KEY: + vertex_groups.append(transfer_data_item) + # if transfer_data_item.type == constants.VERTEX_COLOR_KEY: + # vertex_colors.append(transfer_data_item) + if transfer_data_item.type == constants.MATERIAL_SLOT_KEY: + material_slots.append(transfer_data_item) + if transfer_data_item.type == constants.MODIFIER_KEY: + modifiers.append(transfer_data_item) + if transfer_data_item.type == constants.CONSTRAINT_KEY: + constraints.append(transfer_data_item) + # if transfer_data_item.type == constants.UV_LAYERS_KEY: + # uv_layers.append(transfer_data_item) + if transfer_data_item.type == constants.SHAPE_KEY_KEY: + shape_keys.append(transfer_data_item) + if transfer_data_item.type == constants.ATTRIBUTE_KEY: + attributes.append(transfer_data_item) + if transfer_data_item.type == constants.PARENT_KEY: + parent.append(transfer_data_item) draw_transfer_data_type(layout, vertex_groups) # draw_transfer_data_type(layout, vertex_colors) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index f72d8e1e..f03a3ee8 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -16,9 +16,9 @@ def check_transfer_data_entry( set: Returns set of matches where name is found in ownership """ existing_items = [ - transfer_info.name - for transfer_info in transfer_data - if transfer_info.type == td_type_key + transfer_data_item.name + for transfer_data_item in transfer_data + if transfer_data_item.type == td_type_key ] return set([key]).intersection(set(existing_items)) @@ -37,15 +37,15 @@ def transfer_data_add_entry( td_type_key (str): Type of transfer data task_layer_name (str): Name of current task layer """ - transfer_info = transfer_data.add() - transfer_info.name = name - transfer_info.owner = task_layer_name.upper() - transfer_info.type = td_type_key - return transfer_info + transfer_data_item = transfer_data.add() + transfer_data_item.name = name + transfer_data_item.owner = task_layer_name.upper() + transfer_data_item.type = td_type_key + return transfer_data_item # TODO Test if Clean and Missing are redudent functions -def transfer_info_clean( +def transfer_data_clean( obj: bpy.types.Object, data_list: bpy.types.CollectionProperty, td_type_key: str ): """Remove transfer data entries if the corrisponding data doesn't exist @@ -64,19 +64,21 @@ def transfer_info_clean( data_list.remove(item) -def transfer_info_is_missing( - transfer_info, data_list: bpy.types.CollectionProperty, td_type_key: str +def transfer_data_item_is_missing( + transfer_data_item, data_list: bpy.types.CollectionProperty, td_type_key: str ) -> bool: """Returns true if a transfer_data_item does not exist Args: - transfer_info (_type_): Item of Transfer Data + transfer_data_item (_type_): Item of Transfer Data data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers td_type_key (str): Key for the transfer data type Returns: - bool: Returns True if transfer_info is missing + bool: Returns True if transfer_data_item is missing """ - if transfer_info.type == td_type_key and not data_list.get(transfer_info["name"]): + if transfer_data_item.type == td_type_key and not data_list.get( + transfer_data_item["name"] + ): return True @@ -85,7 +87,7 @@ def transfer_info_is_missing( """ -def transfer_info_init( +def transfer_data_item_init( scene: bpy.types.Scene, obj: bpy.types.Object, data_list: bpy.types.CollectionProperty, @@ -102,7 +104,7 @@ def transfer_info_init( transfer_data = obj.transfer_data_ownership task_layer_key = scene.asset_pipeline.task_layer_name for item in data_list: - # Only add new ownership transfer_info if vertex group doesn't have an owner + # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, item.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index afe34c46..58e092ab 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -192,16 +192,18 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): row = layout.row() row.prop(self, "expand", text="", icon="COLLAPSEMENU", toggle=False) row.label(text="Show New Transfer Data") - objs = [transfer_info.obj for transfer_info in self._temp_transfer_data] + objs = [ + transfer_data_item.obj for transfer_data_item in self._temp_transfer_data + ] if not self.expand: return for obj in set(objs): obj_ownership = [ - transfer_info - for transfer_info in self._temp_transfer_data - if transfer_info.obj == obj + transfer_data_item + for transfer_data_item in self._temp_transfer_data + if transfer_data_item.obj == obj ] box = layout.box() box.label(text=obj.name, icon="OBJECT_DATA") diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 365aff4b..c6daae1c 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -58,11 +58,11 @@ class AssetPipeline(bpy.types.PropertyGroup): def add_temp_transfer_data(self, name, owner, type, obj): new_transfer_data = self.temp_transfer_data - transfer_info = new_transfer_data.add() - transfer_info.name = name - transfer_info.owner = owner - transfer_info.type = type - transfer_info.obj = obj + 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 = obj ## NEW FILE -- 2.30.2 From adffd8563b6855e66757af4a0ca52c11d2571b2c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 16:48:07 -0400 Subject: [PATCH 244/429] Asset Pipe: Remove old TODO --- .../asset_pipeline_2/merge/transfer_data/transfer_functions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index c0ec8247..e9c3738a 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -58,8 +58,6 @@ def transfer_vertex_group( return source_obj.vertex_groups.active = source_obj.vertex_groups.get(vertex_group_name) - # TODO Debug crashing / use context.temp_override(object=obj) style - # https://projects.blender.org/blender/blender/issues/112299 context = bpy.context override = context.copy() override["selected_editable_objects"] = [target_obj, source_obj] -- 2.30.2 From d8cb7eceba175b0095e2c317b350318acf1e4c6b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 21 Sep 2023 16:49:05 -0400 Subject: [PATCH 245/429] Asset Pipe: Clean Up Obj Mapping --- .../asset_pipeline_2/merge/asset_mapping.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 07942006..75515423 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -25,6 +25,7 @@ class AssetTransferMapping: external_coll: bpy.types.Collection, local_tls: Set[str], ): + # TODO Check if any of the below properties can be removed self._local_col = local_coll self._external_col = external_coll self._local_tls = local_tls @@ -59,6 +60,10 @@ class AssetTransferMapping: return return external_obj + def _check_obj_conflict(self, external_obj, local_obj): + if external_obj.asset_id_owner != local_obj.asset_id_owner: + self.conflict_objects.append(local_obj) + def _gen_object_map(self) -> Dict[bpy.types.Object, bpy.types.Object]: """ Tries to link all objects in source collection to an object in @@ -69,24 +74,18 @@ class AssetTransferMapping: # Skip items with no owner if local_obj.asset_id_owner == "NONE": continue + external_obj = self._get_external_object(local_obj) + if not external_obj: + continue # IF ITEM IS OWNED BY LOCAL TASK LAYERS if local_obj.asset_id_owner in self._local_tls: - external_obj = self._get_external_object(local_obj) - if external_obj: - if external_obj.asset_id_owner != local_obj.asset_id_owner: - self.conflict_objects.append(local_obj) - object_map[external_obj] = local_obj + self._check_obj_conflict(external_obj, local_obj) + object_map[external_obj] = local_obj # IF ITEM IS NOT OWNED BY LOCAL TASK LAYERS else: - external_obj = self._get_external_object(local_obj) - if external_obj: - if external_obj.asset_id_owner != local_obj.asset_id_owner: - self.conflict_objects.append(local_obj) - object_map[local_obj] = external_obj - else: - # REMOVE OBJ NOT OWNED BY LOCAL TASK LAYER THAT HAS NO MATCH - self.local_obj_to_remove.add(local_obj) + self._check_obj_conflict(external_obj, local_obj) + object_map[local_obj] = external_obj # Find new objects to add to local_col for external_obj in self._external_col.all_objects: @@ -168,6 +167,7 @@ class AssetTransferMapping: return True def _gen_transfer_data_map(self): + # TODO Clean up this mess context = bpy.context transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data -- 2.30.2 From 9cc346d8c2340f95dc05a140e32da6979b4b4001 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 09:43:47 -0400 Subject: [PATCH 246/429] Asset Pipe: Fix bug in `find_drivers` --- .../addons/asset_pipeline_2/merge/drivers.py | 5 ++++- .../merge/transfer_data/transfer_functions.py | 10 +++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/drivers.py b/scripts-blender/addons/asset_pipeline_2/merge/drivers.py index f8655417..505f665d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/drivers.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/drivers.py @@ -51,7 +51,7 @@ def copy_driver( def find_drivers( - drivers: list[bpy.types.FCurve], target_type: str, target_name: str + id: bpy.types.ID, target_type: str, target_name: str ) -> list[bpy.types.FCurve]: """_summary_ @@ -64,6 +64,9 @@ def find_drivers( list[bpy.types.FCurve]: List of FCurves containing drivers that match type & name """ found_drivers = [] + if id.animation_data is None or id.animation_data.drivers is None: + return found_drivers + drivers = id.animation_data.drivers for driver in drivers: if f'{target_type}["{target_name}"]' in driver.data_path: found_drivers.append(driver) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index e9c3738a..92bcf096 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -259,9 +259,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): ) if source_obj.animation_data is None: return - fcurves = find_drivers( - source_obj.animation_data.drivers, 'modifiers', modifier_name - ) + fcurves = find_drivers(source_obj, 'modifiers', modifier_name) for fcurve in fcurves: copy_driver(from_fcurve=fcurve, target=target_obj) @@ -332,9 +330,7 @@ def transfer_constraint(constraint_name, target_obj, source_obj): if source_obj.animation_data is None: return - fcurves = find_drivers( - source_obj.animation_data.drivers, 'constraints', constraint_name - ) + fcurves = find_drivers(source_obj, 'constraints', constraint_name) for fcurve in fcurves: copy_driver(from_fcurve=fcurve, target=target_obj) @@ -573,7 +569,7 @@ def transfer_shape_key( val = mathutils.Vector(sum(np.array(vals_weighted))) sk_target.data[i].co = vert.co + val fcurves = find_drivers( - source_obj.data.shape_keys.animation_data.drivers, + source_obj.data.shape_keys, 'key_blocks', shape_key_name, ) -- 2.30.2 From 11a4deecbc78efc4a56fc8996cb5a66d96b16e87 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 09:46:59 -0400 Subject: [PATCH 247/429] Asset Pipe: Remove No-op Code --- .../merge/transfer_data/transfer_functions.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 92bcf096..d42daca1 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -257,8 +257,6 @@ def transfer_modifier(modifier_name, target_obj, source_obj): {"object": target_obj, "active_object": target_obj}, modifier=mod.name, ) - if source_obj.animation_data is None: - return fcurves = find_drivers(source_obj, 'modifiers', modifier_name) for fcurve in fcurves: copy_driver(from_fcurve=fcurve, target=target_obj) @@ -328,8 +326,6 @@ def transfer_constraint(constraint_name, target_obj, source_obj): new_target.target = target_item.target new_target.subtarget = target_item.subtarget - if source_obj.animation_data is None: - return fcurves = find_drivers(source_obj, 'constraints', constraint_name) for fcurve in fcurves: -- 2.30.2 From b357c6dcf608d1caca931d9ce2b8cddcc693da3b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 09:55:29 -0400 Subject: [PATCH 248/429] Asset Pipe: Check if Shape Keys is None before copying Driver --- .../merge/transfer_data/transfer_functions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index d42daca1..15cdc9cb 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -564,6 +564,10 @@ def transfer_shape_key( ] val = mathutils.Vector(sum(np.array(vals_weighted))) sk_target.data[i].co = vert.co + val + + if source_obj.data.shape_keys is None: + return + fcurves = find_drivers( source_obj.data.shape_keys, 'key_blocks', -- 2.30.2 From b79b5965dd2fdf9ca7097b40e5d0a91e762f3e85 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 10:26:11 -0400 Subject: [PATCH 249/429] Asset Pipe: Fix typo in `get_other_ids()` --- scripts-blender/addons/asset_pipeline_2/merge/other_ids.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py index 533398eb..572ac0ab 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py @@ -18,7 +18,7 @@ def get_other_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: return [ id for id in all_ids_of_coll - if type(id) == bpy.types.NodeGroup or type(id) == bpy.types.Image + if type(id) == bpy.types.GeometryNodeTree or type(id) == bpy.types.Image ] -- 2.30.2 From 874069ff2df97c2213bce48dc153610c8ead662a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 11:13:13 -0400 Subject: [PATCH 250/429] Asset Pipe: Improve `get_other_ids()` --- scripts-blender/addons/asset_pipeline_2/merge/other_ids.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py index 572ac0ab..ede2d098 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py @@ -18,7 +18,7 @@ def get_other_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: return [ id for id in all_ids_of_coll - if type(id) == bpy.types.GeometryNodeTree or type(id) == bpy.types.Image + if isinstance(id, bpy.types.NodeTree) or isinstance(id, bpy.types.Image) ] -- 2.30.2 From 81c0249b916db476390472d253b9df4974dcd3fe Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 12:02:38 -0400 Subject: [PATCH 251/429] Asset Pipe: Improve Constraint Transfer - Only move constraint if index doesn't match --- .../merge/transfer_data/transfer_functions.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 15cdc9cb..6ef42759 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -307,10 +307,13 @@ def transfer_constraint(constraint_name, target_obj, source_obj): ): if target_constraint.name == name_prev: idx = target_mod_i + 1 - with context.temp_override(object=target_obj): - bpy.ops.constraint.move_to_index( - constraint=constraint_new.name, index=idx - ) + + # TODO use toggle visibility here + if idx != i: + with context.temp_override(object=target_obj): + bpy.ops.constraint.move_to_index( + constraint=constraint_new.name, index=idx + ) constraint_target = target_obj.constraints.get(constraint.name) props = [ p.identifier for p in constraint.bl_rna.properties if not p.is_readonly -- 2.30.2 From 311c6649a6c7f1067c0e7e33971393171453a67d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 14:56:09 -0400 Subject: [PATCH 252/429] Asset Pipe: Split Up Sync Process into Seprate Operators --- .../addons/asset_pipeline_2/ops.py | 261 ++++++++---------- .../addons/asset_pipeline_2/sync.py | 156 +++++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 12 +- 3 files changed, 268 insertions(+), 161 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/sync.py diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 58e092ab..dcb5e95c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -3,20 +3,21 @@ import bpy import os from pathlib import Path from .merge.publish import ( - find_sync_target, - find_all_published, get_next_published_file, ) from .merge.naming import get_task_layer_col_name -from .merge.other_ids import init_other_ids -from .merge.core import ( - ownership_get, - ownership_set, - get_invalid_objects, - merge_task_layer, -) + from .merge.transfer_data.transfer_ui import draw_transfer_data from . import constants +from .sync import ( + sync_poll, + sync_invoke, + sync_draw, + sync_execute_update_ownership, + sync_execute_prepare_sync, + sync_execute_pull, + sync_execute_push, +) class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): @@ -107,11 +108,10 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): return {'FINISHED'} -class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): - bl_idname = "assetpipe.sync_with_publish" - bl_label = "Sync with Publish" - bl_description = """'Push'band or 'Pull' data from Published file. Will prompt with a list of new data found before pushing - During 'Push' current blender session will revert to the last saved file and clear the current undo history""" +class ASSETPIPE_OT_update_ownership(bpy.types.Operator): + bl_idname = "assetpipe.update_ownership" + bl_label = "Update Onwership" + bl_description = """""" # TODO Add description _temp_transfer_data = None _invalid_objs = [] @@ -123,161 +123,114 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator): description="Show New Transfer Data", ) - save: bpy.props.BoolProperty( - name="Save Current File", - default=True, - description="Save the Current File (after pulling if enabled) before Pushing to Publish", - ) - - pull: bpy.props.BoolProperty( - name="Pull before Pushing", - default=False, - description="Pull in any new data from the Published file before Pushing", - ) - - push: bpy.props.BoolProperty( - name="Push", default=False, description="Push any new data to Published file" - ) - def invoke(self, context: bpy.types.Context, event: bpy.types.Event): - self._temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data - self._temp_transfer_data.clear() - self._invalid_objs.clear() - - local_col = context.scene.asset_pipeline.asset_collection - if not local_col: - self.report({'ERROR'}, "Top level collection could not be found") - return {'CANCELLED'} - task_layer_key = context.scene.asset_pipeline.task_layer_name - if task_layer_key == "NONE": - self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") - return {'CANCELLED'} - - ownership_get(local_col, context.scene) - - # TODO Remove Invalid Objs Explicitly, some will be auto removed but not all - self._invalid_objs = get_invalid_objects(local_col, context.scene) - self._other_ids = init_other_ids(context.scene) - - # Default behaviour is to pull before pushing - if self.push: - self.pull = True + sync_invoke(self, context) return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context: bpy.types.Context): - layout = self.layout + sync_draw(self, context) - row = layout.row() - if self.push: - row.prop(self, "pull") - row.prop(self, "save") + def execute(self, context: bpy.types.Context): + sync_execute_update_ownership(self, context) + return {'FINISHED'} - if len(self._invalid_objs) != 0: - box = layout.box() - box.alert = True - box.label(text="Sync will clear Invalid Objects:", icon="ERROR") - for obj in self._invalid_objs: - box.label(text=obj.name, icon="OBJECT_DATA") - if len(self._other_ids) != 0: - box = layout.box() - box.label(text="New 'Other IDs' found") - for id in self._other_ids: - box.label(text=id.name) +class ASSETPIPE_OT_sync_pull(bpy.types.Operator): + bl_idname = "assetpipe.sync_pull" + bl_label = "Pull from Publish" + bl_description = """""" # TODO Add description - if len(self._temp_transfer_data) == 0: - layout.label(text="No New Transfer Data found") - else: - layout.label(text="New Transfer Data will be Pushed to Publish") - row = layout.row() - row.prop(self, "expand", text="", icon="COLLAPSEMENU", toggle=False) - row.label(text="Show New Transfer Data") - objs = [ - transfer_data_item.obj for transfer_data_item in self._temp_transfer_data - ] + _temp_transfer_data = None + _invalid_objs = [] + _other_ids = [] + _temp_dir: Path = None + _current_file: Path = None + _task_layer_key: str = "" + _sync_target: Path = None - if not self.expand: - return + expand: bpy.props.BoolProperty( + name="Show New Transfer Data", + default=False, + description="Show New Transfer Data", + ) - for obj in set(objs): - obj_ownership = [ - transfer_data_item - for transfer_data_item in self._temp_transfer_data - if transfer_data_item.obj == obj - ] - box = layout.box() - box.label(text=obj.name, icon="OBJECT_DATA") - draw_transfer_data(obj_ownership, box) + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + if any([img.is_dirty for img in bpy.data.images]): + cls.poll_message_set("Please save unsaved Images") + return False + if bpy.data.is_dirty: + cls.poll_message_set("Please save current .blend file") + return False + return True + + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + sync_invoke(self, context) + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + sync_draw(self, context) def execute(self, context: bpy.types.Context): # Find current task Layer - temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data - ownership_set(temp_transfer_data) - current_file = Path(bpy.data.filepath) - temp_dir = Path(bpy.app.tempdir).parent - task_layer_key = context.scene.asset_pipeline.task_layer_name - if task_layer_key == "NONE": - self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") - return {'CANCELLED'} + sync_execute_update_ownership(self, context) + sync_execute_prepare_sync(self, context) + sync_execute_pull(self, context) + return {'FINISHED'} - sync_target = find_sync_target(current_file) - if not sync_target.exists(): - self.report({'ERROR'}, "Sync Target could not be determined") - return {'CANCELLED'} + +class ASSETPIPE_OT_sync_push(bpy.types.Operator): + bl_idname = "assetpipe.sync_push" + bl_label = "Push from Publish" + bl_description = """""" # TODO Add description + + _temp_transfer_data = None + _invalid_objs = [] + _other_ids = [] + _temp_dir: Path = None + _current_file: Path = None + _task_layer_key: str = "" + _sync_target: Path = None + + expand: bpy.props.BoolProperty( + name="Show New Transfer Data", + default=False, + description="Show New Transfer Data", + ) + pull: bpy.props.BoolProperty( + name="Pull before Pushing", + default=True, + description="Pull in any new data from the Published file before Pushing", + ) + + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + if any([img.is_dirty for img in bpy.data.images]): + cls.poll_message_set("Please save unsaved Images") + return False + if bpy.data.is_dirty: + cls.poll_message_set("Please save current .blend file") + return False + return True + + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + sync_invoke(self, context) + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + self.layout.prop(self, "pull") + sync_draw(self, context) + + def execute(self, context: bpy.types.Context): + # Find current task Layer + sync_execute_update_ownership(self, context) + sync_execute_prepare_sync(self, context) if self.pull: - temp_file = temp_dir.joinpath( - current_file.name.replace(".blend", "") + "_Asset_Pipe_Backup.blend" - ) - bpy.ops.wm.save_as_mainfile(filepath=temp_file.__str__(), copy=True) - error_msg = merge_task_layer( - context, - local_tls=[task_layer_key], - external_file=sync_target, - ) + sync_execute_pull(self, context) + bpy.ops.wm.save_mainfile(filepath=self._current_file.__str__()) - if error_msg: - bpy.ops.wm.open_mainfile(filepath=temp_file.__str__()) - bpy.ops.wm.save_as_mainfile(filepath=current_file.__str__()) - self.report({'ERROR'}, error_msg) - return {'CANCELLED'} - - if self.save: - bpy.ops.wm.save_as_mainfile(filepath=current_file.__str__()) - - if not self.push: - return {'FINISHED'} - - push_targets = find_all_published(current_file, constants.ACTIVE_PUBLISH_KEY) - if sync_target not in push_targets: - push_targets.append(sync_target) - - for file in push_targets: - file_path = file.__str__() - bpy.ops.wm.open_mainfile(filepath=file_path) - - # SKIP DEPRECIATED FILES - if context.scene.asset_pipeline.is_depreciated: - continue - - local_tls = [ - task_layer - for task_layer in constants.TASK_LAYER_TYPES.keys() - if task_layer != task_layer_key - ] - - error_msg = merge_task_layer( - context, - local_tls=local_tls, - external_file=current_file, - ) - if error_msg: - bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) - self.report({'ERROR'}, error_msg) - return {'CANCELLED'} - - bpy.ops.wm.save_as_mainfile(filepath=file_path) - bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) + sync_execute_push(self, context) return {'FINISHED'} @@ -312,7 +265,9 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): classes = ( - ASSETPIPE_OT_sync_with_publish, + ASSETPIPE_OT_update_ownership, + ASSETPIPE_OT_sync_push, + ASSETPIPE_OT_sync_pull, ASSETPIPE_OT_publish_new_version, ASSETPIPE_OT_create_new_asset, ) diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py new file mode 100644 index 00000000..2dd5e4fd --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -0,0 +1,156 @@ +import bpy +from pathlib import Path +from .merge.publish import ( + find_sync_target, + find_all_published, +) +from .merge.other_ids import init_other_ids +from .merge.core import ( + ownership_get, + ownership_set, + get_invalid_objects, + merge_task_layer, +) +from .merge.transfer_data.transfer_ui import draw_transfer_data +from . import constants + + +def sync_poll(cls, context): + if any([img.is_dirty for img in bpy.data.images]): + cls.poll_message_set("Please save unsaved Images") + return False + if bpy.data.is_dirty: + cls.poll_message_set("Please save current .blend file") + return False + return True + + +def sync_invoke(self, context): + self._temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data + self._temp_transfer_data.clear() + self._invalid_objs.clear() + + local_col = context.scene.asset_pipeline.asset_collection + if not local_col: + self.report({'ERROR'}, "Top level collection could not be found") + return {'CANCELLED'} + task_layer_key = context.scene.asset_pipeline.task_layer_name + if task_layer_key == "NONE": + self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + return {'CANCELLED'} + + ownership_get(local_col, context.scene) + + # TODO Remove Invalid Objs Explicitly, some will be auto removed but not all + self._invalid_objs = get_invalid_objects(local_col, context.scene) + self._other_ids = init_other_ids(context.scene) + + +def sync_draw(self, context): + layout = self.layout + row = layout.row() + + if len(self._invalid_objs) != 0: + box = layout.box() + box.alert = True + box.label(text="Sync will clear Invalid Objects:", icon="ERROR") + for obj in self._invalid_objs: + box.label(text=obj.name, icon="OBJECT_DATA") + + if len(self._other_ids) != 0: + box = layout.box() + box.label(text="New 'Other IDs' found") + for id in self._other_ids: + box.label(text=id.name) + + if len(self._temp_transfer_data) == 0: + layout.label(text="No New Transfer Data found") + else: + layout.label(text="New Transfer Data will be Pushed to Publish") + row = layout.row() + row.prop(self, "expand", text="", icon="COLLAPSEMENU", toggle=False) + row.label(text="Show New Transfer Data") + objs = [transfer_data_item.obj for transfer_data_item in self._temp_transfer_data] + + if not self.expand: + return + + for obj in set(objs): + obj_ownership = [ + transfer_data_item + for transfer_data_item in self._temp_transfer_data + if transfer_data_item.obj == obj + ] + box = layout.box() + box.label(text=obj.name, icon="OBJECT_DATA") + draw_transfer_data(obj_ownership, box) + + +def sync_execute_update_ownership(self, context): + temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data + ownership_set(temp_transfer_data) + + +def sync_execute_prepare_sync(self, context): + self._current_file = Path(bpy.data.filepath) + self._temp_dir = Path(bpy.app.tempdir).parent + self._task_layer_key = context.scene.asset_pipeline.task_layer_name + if self._task_layer_key == "NONE": + self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + return {'CANCELLED'} + + self._sync_target = find_sync_target(self._current_file) + if not self._sync_target.exists(): + self.report({'ERROR'}, "Sync Target could not be determined") + return {'CANCELLED'} + + +def sync_execute_pull(self, context): + temp_file = self._temp_dir.joinpath( + self._current_file.name.replace(".blend", "") + "_Asset_Pipe_Backup.blend" + ) + bpy.ops.wm.save_as_mainfile(filepath=temp_file.__str__(), copy=True) + error_msg = merge_task_layer( + context, + local_tls=[self._task_layer_key], + external_file=self._sync_target, + ) + + if error_msg: + bpy.ops.wm.open_mainfile(filepath=temp_file.__str__()) + bpy.ops.wm.save_as_mainfile(filepath=self._current_file.__str__()) + self.report({'ERROR'}, error_msg) + return {'CANCELLED'} + + +def sync_execute_push(self, context): + push_targets = find_all_published(self._current_file, constants.ACTIVE_PUBLISH_KEY) + if self._sync_target not in push_targets: + push_targets.append(self._sync_target) + + for file in push_targets: + file_path = file.__str__() + bpy.ops.wm.open_mainfile(filepath=file_path) + + # SKIP DEPRECIATED FILES + if context.scene.asset_pipeline.is_depreciated: + continue + + local_tls = [ + task_layer + for task_layer in constants.TASK_LAYER_TYPES.keys() + if task_layer != self._task_layer_key + ] + + error_msg = merge_task_layer( + context, + local_tls=local_tls, + external_file=self._current_file, + ) + if error_msg: + bpy.ops.wm.open_mainfile(filepath=self._current_file.__str__()) + self.report({'ERROR'}, error_msg) + return {'CANCELLED'} + + bpy.ops.wm.save_as_mainfile(filepath=file_path) + bpy.ops.wm.open_mainfile(filepath=self._current_file.__str__()) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index ede9ba16..779317df 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -29,15 +29,11 @@ class ASSETPIPE_sync(bpy.types.Panel): layout.prop(asset_pipe, "asset_collection", text="Asset") layout.label(text="Test UI") - # layout.operator( - # "assetpipe.sync_with_publish", text="Update Ownership" - # ).pull = False + layout.operator("assetpipe.update_ownership", text="Update Ownership") + layout.operator("assetpipe.sync_push", text="Push to Publish", icon="TRIA_UP") layout.operator( - "assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP" - ).push = True - layout.operator( - "assetpipe.sync_with_publish", text="Pull from Publish", icon="TRIA_DOWN" - ).pull = True + "assetpipe.sync_pull", text="Pull from Publish", icon="TRIA_DOWN" + ) layout.operator("assetpipe.publish_new_version", icon="PLUS") if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE": -- 2.30.2 From cbd94d1c85828006025586756f5e136d8a65b63b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 15:32:18 -0400 Subject: [PATCH 253/429] Asset Pipe: Update Description of Operators --- scripts-blender/addons/asset_pipeline_2/ops.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index dcb5e95c..b65347e4 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -111,7 +111,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_idname = "assetpipe.update_ownership" bl_label = "Update Onwership" - bl_description = """""" # TODO Add description + bl_description = """Update the Ownership of Objects and Transfer Data""" _temp_transfer_data = None _invalid_objs = [] @@ -138,7 +138,7 @@ class ASSETPIPE_OT_update_ownership(bpy.types.Operator): class ASSETPIPE_OT_sync_pull(bpy.types.Operator): bl_idname = "assetpipe.sync_pull" bl_label = "Pull from Publish" - bl_description = """""" # TODO Add description + bl_description = """Pull Task Layers from the published sync target""" _temp_transfer_data = None _invalid_objs = [] @@ -182,7 +182,7 @@ class ASSETPIPE_OT_sync_pull(bpy.types.Operator): class ASSETPIPE_OT_sync_push(bpy.types.Operator): bl_idname = "assetpipe.sync_push" bl_label = "Push from Publish" - bl_description = """""" # TODO Add description + bl_description = """Push the current Task Layer to the published sync target""" _temp_transfer_data = None _invalid_objs = [] -- 2.30.2 From 9970d299248bfffc8cdd1be988da8fd4325a1aad Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 15:40:51 -0400 Subject: [PATCH 254/429] Asset Pipe: Fix Docstrings for transfer_data_clean & transfer_data_item_is_missing - Confirm that there is a difference between these functions - Clear TODO - Update docstrings to clarify difference --- .../asset_pipeline_2/merge/transfer_data/transfer_util.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index f03a3ee8..39daeb46 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -44,11 +44,10 @@ def transfer_data_add_entry( return transfer_data_item -# TODO Test if Clean and Missing are redudent functions def transfer_data_clean( obj: bpy.types.Object, data_list: bpy.types.CollectionProperty, td_type_key: str ): - """Remove transfer data entries if the corrisponding data doesn't exist + """Removes data if a transfer_data_item doesn't exist but the data does exist Args: obj (bpy.types.Object): Object containing transfer data data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers @@ -67,7 +66,7 @@ def transfer_data_clean( def transfer_data_item_is_missing( transfer_data_item, data_list: bpy.types.CollectionProperty, td_type_key: str ) -> bool: - """Returns true if a transfer_data_item does not exist + """Returns true if a transfer_data_item exists the data doesn't exist Args: transfer_data_item (_type_): Item of Transfer Data -- 2.30.2 From 365009fbe5e1ea47b7630812f143a8d2d2e2f951 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 15:44:15 -0400 Subject: [PATCH 255/429] Asset Pipe: Rename 'Other_Ids' to 'Shared_Ids' --- .../asset_pipeline_2/merge/asset_mapping.py | 4 ++-- .../merge/{other_ids.py => shared_ids.py} | 24 +++++++++---------- .../addons/asset_pipeline_2/sync.py | 8 +++---- 3 files changed, 17 insertions(+), 19 deletions(-) rename scripts-blender/addons/asset_pipeline_2/merge/{other_ids.py => shared_ids.py} (59%) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 75515423..72fce187 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -3,7 +3,7 @@ from typing import Dict, Set from .naming import get_target_name, get_basename, get_name_with_asset_prefix from .util import get_storage_of_id from .transfer_data.transfer_util import transfer_data_add_entry -from .other_ids import get_other_ids +from .shared_ids import get_shared_ids from .. import constants @@ -206,7 +206,7 @@ class AssetTransferMapping: def _gen_other_id_map(self): other_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} - for local_id in get_other_ids(self._local_col): + for local_id in get_shared_ids(self._local_col): external_id_name = get_target_name(local_id.name) id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py similarity index 59% rename from scripts-blender/addons/asset_pipeline_2/merge/other_ids.py rename to scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py index ede2d098..81c36841 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/other_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py @@ -1,17 +1,15 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids -# TODO find better name for 'other_ids' - -def get_other_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: +def get_shared_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: """Returns a list of any ID that is not covered by the merge process Args: - collection (bpy.types.Collection): Collection that contains data that references 'other_ids' + collection (bpy.types.Collection): Collection that contains data that references 'shared_ids' Returns: - list[bpy.types.ID]: List of 'other_ids' + list[bpy.types.ID]: List of 'shared_ids' """ ref_map = get_id_reference_map() all_ids_of_coll = get_all_referenced_ids(collection, ref_map) @@ -22,21 +20,21 @@ def get_other_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: ] -def init_other_ids(scene: bpy.types.Scene) -> list[bpy.types.ID]: - """Intilizes any ID not covered by the transfer process as an 'other_id' - and marks all 'other_ids' without an owner to the current task layer +def init_shared_ids(scene: bpy.types.Scene) -> list[bpy.types.ID]: + """Intilizes any ID not covered by the transfer process as an 'shared_id' + and marks all 'shared_ids' without an owner to the current task layer Args: scene (bpy.types.Scene): Scene that contains a the file's asset Returns: - list[bpy.types.ID]: A list of new 'other_ids' owned by the file's task layer + list[bpy.types.ID]: A list of new 'shared_ids' owned by the file's task layer """ - other_ids = [] + shared_ids = [] asset_pipe = scene.asset_pipeline local_col = asset_pipe.asset_collection - for id in get_other_ids(local_col): + for id in get_shared_ids(local_col): if id.asset_id_owner == 'NONE': id.asset_id_owner = asset_pipe.task_layer_name - other_ids.append(id) - return other_ids + shared_ids.append(id) + return shared_ids diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 2dd5e4fd..2140da1e 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -4,7 +4,7 @@ from .merge.publish import ( find_sync_target, find_all_published, ) -from .merge.other_ids import init_other_ids +from .merge.shared_ids import init_shared_ids from .merge.core import ( ownership_get, ownership_set, @@ -43,7 +43,7 @@ def sync_invoke(self, context): # TODO Remove Invalid Objs Explicitly, some will be auto removed but not all self._invalid_objs = get_invalid_objects(local_col, context.scene) - self._other_ids = init_other_ids(context.scene) + self._shared_ids = init_shared_ids(context.scene) def sync_draw(self, context): @@ -57,10 +57,10 @@ def sync_draw(self, context): for obj in self._invalid_objs: box.label(text=obj.name, icon="OBJECT_DATA") - if len(self._other_ids) != 0: + if len(self._shared_ids) != 0: box = layout.box() box.label(text="New 'Other IDs' found") - for id in self._other_ids: + for id in self._shared_ids: box.label(text=id.name) if len(self._temp_transfer_data) == 0: -- 2.30.2 From 84981faed4c83d06c9c1c3b55dbecfac49997e03 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 15:48:50 -0400 Subject: [PATCH 256/429] Asset Pipe: Explicitly Remove Invalid Objects during Sync --- scripts-blender/addons/asset_pipeline_2/sync.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 2140da1e..c1d9d2e9 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -41,7 +41,6 @@ def sync_invoke(self, context): ownership_get(local_col, context.scene) - # TODO Remove Invalid Objs Explicitly, some will be auto removed but not all self._invalid_objs = get_invalid_objects(local_col, context.scene) self._shared_ids = init_shared_ids(context.scene) @@ -104,6 +103,9 @@ def sync_execute_prepare_sync(self, context): self.report({'ERROR'}, "Sync Target could not be determined") return {'CANCELLED'} + for obj in self._invalid_objs: + bpy.data.objects.remove(obj) + def sync_execute_pull(self, context): temp_file = self._temp_dir.joinpath( -- 2.30.2 From 95d0750b5809ecc3bd5ad00d35f0064c9899c9a3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:02:50 -0400 Subject: [PATCH 257/429] Asset Pipe: Override Obj Visibility during contex.temp_override --- .../merge/transfer_data/transfer_functions.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 6ef42759..f3d24164 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -2,6 +2,7 @@ import bpy from bpy import context from ..naming import get_basename from ..drivers import find_drivers, copy_driver +from ..visibility import override_obj_visability from .transfer_util import ( transfer_data_clean, transfer_data_item_is_missing, @@ -223,8 +224,11 @@ def transfer_modifier(modifier_name, target_obj, source_obj): for target_mod_i, target_mod in enumerate(target_obj.modifiers): if target_mod.name == name_prev: idx = target_mod_i + 1 - with context.temp_override(object=target_obj): - bpy.ops.object.modifier_move_to_index(modifier=mod_new.name, index=idx) + with override_obj_visability(obj=target_obj): + with context.temp_override(object=target_obj): + bpy.ops.object.modifier_move_to_index( + modifier=mod_new.name, index=idx + ) mod_target = target_obj.modifiers.get(mod.name) props = [p.identifier for p in mod.bl_rna.properties if not p.is_readonly] for prop in props: @@ -310,10 +314,11 @@ def transfer_constraint(constraint_name, target_obj, source_obj): # TODO use toggle visibility here if idx != i: - with context.temp_override(object=target_obj): - bpy.ops.constraint.move_to_index( - constraint=constraint_new.name, index=idx - ) + with override_obj_visability(obj=target_obj): + with context.temp_override(object=target_obj): + bpy.ops.constraint.move_to_index( + constraint=constraint_new.name, index=idx + ) constraint_target = target_obj.constraints.get(constraint.name) props = [ p.identifier for p in constraint.bl_rna.properties if not p.is_readonly -- 2.30.2 From dfa494a767637ff0c2a3646cacc599ade4ef804a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:09:14 -0400 Subject: [PATCH 258/429] Asset Pipe: Use Blender 4.0's new is_required for attributes - Fix attributes_get_editable() - Update required Blender Version --- scripts-blender/addons/asset_pipeline_2/__init__.py | 2 +- .../merge/transfer_data/transfer_functions.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/__init__.py b/scripts-blender/addons/asset_pipeline_2/__init__.py index 3fdd8d54..a9663b89 100644 --- a/scripts-blender/addons/asset_pipeline_2/__init__.py +++ b/scripts-blender/addons/asset_pipeline_2/__init__.py @@ -6,7 +6,7 @@ bl_info = { "name": "Asset Pipeline 2", "author": "Nick Alberelli", "description": "Blender Studio Asset Pipeline Add-on", - "blender": (3, 1, 0), + "blender": (4, 0, 0), "version": (0, 1, 2), "location": "View3D", "warning": "", diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index f3d24164..720d7620 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -587,12 +587,14 @@ def transfer_shape_key( # ATTRIBUTE def attributes_get_editable(attributes): - # TODO replace 'position' HACK with is_required once https://projects.blender.org/blender/blender/pulls/111468 is merged return [ - item - for item in attributes + attribute + for attribute in attributes if not ( - item.is_internal or item.name == 'position' or item.name == 'material_index' + attribute.is_internal + or attribute.is_required + # Material Index is part of material transfer and should be skipped + or attribute.name == 'material_index' ) ] -- 2.30.2 From bd026f483e5a858c4859fa898a5828f6fd8883ba Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:09:43 -0400 Subject: [PATCH 259/429] Asset Pipe: Clear Old TODO --- .../asset_pipeline_2/merge/transfer_data/transfer_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 720d7620..2e154cb5 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -312,7 +312,6 @@ def transfer_constraint(constraint_name, target_obj, source_obj): if target_constraint.name == name_prev: idx = target_mod_i + 1 - # TODO use toggle visibility here if idx != i: with override_obj_visability(obj=target_obj): with context.temp_override(object=target_obj): -- 2.30.2 From 6798e2becaaffed8e22b30674f4ca0e837868523 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:13:42 -0400 Subject: [PATCH 260/429] Asset Pipe: Fix Naming of shared_id_map --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 12 ++++++------ .../addons/asset_pipeline_2/merge/core.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 72fce187..5c51d7db 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -47,7 +47,7 @@ class AssetTransferMapping: self.object_map = self._gen_object_map() self.collection_map = self._gen_collection_map() self.transfer_data_map = self._gen_transfer_data_map() - self.other_id_map = self._gen_other_id_map() + self.shared_id_map = self._gen_shared_id_map() def _get_external_object(self, local_obj): external_obj_name = get_target_name( @@ -204,8 +204,8 @@ class AssetTransferMapping: transfer_data_map[name] = map_item return transfer_data_map - def _gen_other_id_map(self): - other_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} + def _gen_shared_id_map(self): + shared_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} for local_id in get_shared_ids(self._local_col): external_id_name = get_target_name(local_id.name) id_storage = get_storage_of_id(local_id) @@ -216,9 +216,9 @@ class AssetTransferMapping: and local_id.asset_id_owner != "NONE" ): if external_id: - other_id_map[external_id] = local_id + shared_id_map[external_id] = local_id else: if external_id: - other_id_map[local_id] = external_id + shared_id_map[local_id] = external_id - return other_id_map + return shared_id_map diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index ce8e4334..b5a46566 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -200,8 +200,8 @@ def merge_task_layer( for col in map.collection_map: remap_user(col, map.collection_map[col]) - for id in map.other_id_map: - remap_user(id, map.other_id_map[id]) + for id in map.shared_id_map: + remap_user(id, map.shared_id_map[id]) bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True -- 2.30.2 From 2e974a22f0e719d6f179bd110693d15a87a2f96b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:27:47 -0400 Subject: [PATCH 261/429] Asset Pipe: Add Function to get cosmentic name of an id's type --- .../addons/asset_pipeline_2/merge/naming.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 8a00743c..65008c3f 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -146,3 +146,15 @@ def get_task_layer_col_name(task_layer_key) -> str: """ task_layer_name = constants.TASK_LAYER_TYPES[task_layer_key] return get_name_with_asset_prefix(task_layer_name) + + +def get_id_type_name(id_type: bpy.types) -> str: + """Return the cosmetic name of a given ID type + + Args: + id_type (bpy.types): An ID type e.g. bpy.types.Object + + Returns: + str: Name of an ID type e.g. bpy.types.Object will return 'Object' + """ + return str(id_type).split("'bpy_types.")[1].replace("'>", "") -- 2.30.2 From 0552c265e10268d11afa937547f048f7af21d66a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:29:02 -0400 Subject: [PATCH 262/429] Asset Pipe: Check for conflicts in shared_id mapping, and improve user feedback --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 1 + scripts-blender/addons/asset_pipeline_2/merge/core.py | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 5c51d7db..39efd7e9 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -211,6 +211,7 @@ class AssetTransferMapping: id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) # TODO Check for conflicts + self._check_id_conflict(external_id, local_id) if ( local_id.asset_id_owner in self._local_tls and local_id.asset_id_owner != "NONE" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index b5a46566..740318f9 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -13,6 +13,7 @@ from .naming import ( remove_suffix_from_hierarchy, get_name_with_asset_prefix, get_task_layer_col_name, + get_id_type_name, ) @@ -179,10 +180,13 @@ def merge_task_layer( error_msg += f"Transfer Data conflict found for '{conflict.name}' on obj '{conflict.id_data.name}'\n" return error_msg - if len(map.conflict_objects) != 0: + if len(map.conflict_ids) != 0: error_msg = '' - for conflict_obj in map.conflict_objects: - error_msg += f"Ownership conflict found for '{conflict_obj.name}'\n" + for conflict_obj in map.conflict_ids: + type_name = get_id_type_name(type(conflict_obj)) + error_msg += ( + f"Ownership conflict found for {type_name}: '{conflict_obj.name}'\n" + ) return error_msg # Remove all transfer data from target objects -- 2.30.2 From 1381a735e4a1ca707c02626800675242fc58c586 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:30:03 -0400 Subject: [PATCH 263/429] Asset Pipe: Fix Checking Conflicts in Object Mapping --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 39efd7e9..2984ec87 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -38,7 +38,7 @@ class AssetTransferMapping: self._no_match_source_colls: Set[bpy.types.Object] = set() self._no_match_target_colls: Set[bpy.types.Object] = set() - self.conflict_objects = [] + self.conflict_ids = [] self.conflict_trasnfer_data = [] self.generate_mapping() @@ -60,9 +60,9 @@ class AssetTransferMapping: return return external_obj - def _check_obj_conflict(self, external_obj, local_obj): - if external_obj.asset_id_owner != local_obj.asset_id_owner: - self.conflict_objects.append(local_obj) + def _check_id_conflict(self, external_id, local_id): + if external_id.asset_id_owner != local_id.asset_id_owner: + self.conflict_ids.append(local_id) def _gen_object_map(self) -> Dict[bpy.types.Object, bpy.types.Object]: """ @@ -77,14 +77,12 @@ class AssetTransferMapping: external_obj = self._get_external_object(local_obj) if not external_obj: continue + self._check_id_conflict(external_obj, local_obj) # IF ITEM IS OWNED BY LOCAL TASK LAYERS if local_obj.asset_id_owner in self._local_tls: - self._check_obj_conflict(external_obj, local_obj) object_map[external_obj] = local_obj - # IF ITEM IS NOT OWNED BY LOCAL TASK LAYERS else: - self._check_obj_conflict(external_obj, local_obj) object_map[local_obj] = external_obj # Find new objects to add to local_col -- 2.30.2 From 655963aa24308f8e58aa0fd813d8498d8d71c8ca Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:38:14 -0400 Subject: [PATCH 264/429] Asset Pipe: Remove old TODO --- scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 2984ec87..0046f983 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -208,7 +208,6 @@ class AssetTransferMapping: external_id_name = get_target_name(local_id.name) id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) - # TODO Check for conflicts self._check_id_conflict(external_id, local_id) if ( local_id.asset_id_owner in self._local_tls -- 2.30.2 From 237acc7f52ab24cc430aa4fcd639f50c8313b743 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:38:50 -0400 Subject: [PATCH 265/429] Asset Pipe: Clean Up `_gen_transfer_data_map` --- .../asset_pipeline_2/merge/asset_mapping.py | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 0046f983..90ac10d6 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -165,7 +165,6 @@ class AssetTransferMapping: return True def _gen_transfer_data_map(self): - # TODO Clean up this mess context = bpy.context transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data @@ -174,32 +173,26 @@ class AssetTransferMapping: target_obj = self.object_map[source_obj] objs = [source_obj, target_obj] for obj in objs: - if obj.name.endswith(constants.LOCAL_SUFFIX): - for transfer_data_item in obj.transfer_data_ownership: - if transfer_data_item.owner in self._local_tls: - conflict = self._check_transfer_data_conflict( - obj, transfer_data_item - ) - if not conflict: - name, map_item = self._get_transfer_data_map_item( - obj, target_obj, transfer_data_item - ) - transfer_data_map[name] = map_item + for transfer_data_item in obj.transfer_data_ownership: + self._check_transfer_data_conflict(obj, transfer_data_item) + if ( + transfer_data_item.owner in self._local_tls + and obj.name.endswith(constants.LOCAL_SUFFIX) + ): + name, map_item = self._get_transfer_data_map_item( + obj, target_obj, transfer_data_item + ) + transfer_data_map[name] = map_item - if obj.name.endswith(constants.EXTERNAL_SUFFIX): - for transfer_data_item in obj.transfer_data_ownership: - if ( - transfer_data_item.owner not in self._local_tls - and transfer_data_item.owner != "NONE" - ): - conflict = self._check_transfer_data_conflict( - obj, transfer_data_item - ) - if not conflict: - name, map_item = self._get_transfer_data_map_item( - obj, target_obj, transfer_data_item - ) - transfer_data_map[name] = map_item + if ( + transfer_data_item.owner not in self._local_tls + and transfer_data_item.owner != "NONE" + and obj.name.endswith(constants.EXTERNAL_SUFFIX) + ): + name, map_item = self._get_transfer_data_map_item( + obj, target_obj, transfer_data_item + ) + transfer_data_map[name] = map_item return transfer_data_map def _gen_shared_id_map(self): -- 2.30.2 From 741bac74395928b67c5ef185fd8e9e764f44054d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 22 Sep 2023 16:46:12 -0400 Subject: [PATCH 266/429] Asset Pipe: Clean-up stored lists/sets --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 9 +++------ scripts-blender/addons/asset_pipeline_2/merge/core.py | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 90ac10d6..d5b41ed9 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -25,21 +25,18 @@ class AssetTransferMapping: external_coll: bpy.types.Collection, local_tls: Set[str], ): - # TODO Check if any of the below properties can be removed self._local_col = local_coll self._external_col = external_coll self._local_tls = local_tls - self.local_obj_to_remove: Set[bpy.types.Object] = set() self.external_obj_to_add: Set[bpy.types.Object] = set() self._no_match_source_objs: Set[bpy.types.Object] = set() - self._no_match_target_objs: Set[bpy.types.Object] = set() self._no_match_source_colls: Set[bpy.types.Object] = set() self._no_match_target_colls: Set[bpy.types.Object] = set() - self.conflict_ids = [] - self.conflict_trasnfer_data = [] + self.conflict_ids: list[bpy.types.ID] = [] + self.conflict_transfer_data = [] # Item of bpy.types.CollectionProperty self.generate_mapping() @@ -160,7 +157,7 @@ class AssetTransferMapping: if check_transfer_data_item is None: return if check_transfer_data_item.owner != transfer_data_item.owner: - self.conflict_trasnfer_data.append(transfer_data_item) + self.conflict_transfer_data.append(transfer_data_item) print("CONFLICT FOUND") return True diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 740318f9..907c1da4 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -174,9 +174,9 @@ def merge_task_layer( map = AssetTransferMapping(local_col, external_col, local_tls) - if len(map.conflict_trasnfer_data) != 0: + if len(map.conflict_transfer_data) != 0: error_msg = '' - for conflict in map.conflict_trasnfer_data: + for conflict in map.conflict_transfer_data: error_msg += f"Transfer Data conflict found for '{conflict.name}' on obj '{conflict.id_data.name}'\n" return error_msg -- 2.30.2 From cb2ec2e1efa56ad94f236cb75b7c113771e517bd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 11:43:29 -0400 Subject: [PATCH 267/429] Asset Pipe: Fix Bug in Checking Transfer Data Conflict --- scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index d5b41ed9..003f856d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -198,6 +198,8 @@ class AssetTransferMapping: external_id_name = get_target_name(local_id.name) id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) + if not external_id: + continue self._check_id_conflict(external_id, local_id) if ( local_id.asset_id_owner in self._local_tls -- 2.30.2 From c4218a8f5b29b667f52db85b3ebdf8c53bbe360c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 11:48:55 -0400 Subject: [PATCH 268/429] Asset Pipe: Fix Local Transfer Data Messages in UI --- scripts-blender/addons/asset_pipeline_2/sync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index c1d9d2e9..4befb34b 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -63,9 +63,9 @@ def sync_draw(self, context): box.label(text=id.name) if len(self._temp_transfer_data) == 0: - layout.label(text="No New Transfer Data found") + layout.label(text="No new local Transfer Data found") else: - layout.label(text="New Transfer Data will be Pushed to Publish") + layout.label(text="New local Transfer Data will be Pushed to Publish") row = layout.row() row.prop(self, "expand", text="", icon="COLLAPSEMENU", toggle=False) row.label(text="Show New Transfer Data") -- 2.30.2 From e82295aaa7779758db775b6c3ae4f2fe76831c27 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 12:41:15 -0400 Subject: [PATCH 269/429] Asset Pipe: Remove Un-used transfer functions --- .../merge/transfer_data/transfer_core.py | 18 ---- .../merge/transfer_data/transfer_functions.py | 102 ------------------ 2 files changed, 120 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 48220383..fa40373e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -36,8 +36,6 @@ def copy_transfer_data_ownership( def transfer_data_clean(obj): transfer_functions.vertex_groups_clean(obj) - # transfer_functions.vertex_colors_clean(obj) - # transfer_functions.uv_layer_clean(obj) transfer_functions.modifiers_clean(obj) transfer_functions.constraints_clean(obj) transfer_functions.material_slots_clean(obj) @@ -60,8 +58,6 @@ def transfer_data_is_missing(transfer_data_item) -> bool: or transfer_functions.modifier_is_missing(transfer_data_item) or transfer_functions.material_slots_is_missing(transfer_data_item) or transfer_functions.constraint_is_missing(transfer_data_item) - # or transfer_functions.vertex_color_is_missing(transfer_data_item) - # or transfer_functions.uv_layer_is_missing(transfer_data_item) or transfer_functions.shape_key_is_missing(transfer_data_item) or transfer_functions.attribute_is_missing(transfer_data_item) or transfer_functions.parent_is_missing(transfer_data_item) @@ -83,8 +79,6 @@ def init_transfer_data( transfer_functions.init_material_slots(scene, obj) transfer_functions.init_modifiers(scene, obj) transfer_functions.init_constraints(scene, obj) - # transfer_functions.init_vertex_colors(scene, obj) - # transfer_functions.init_uv_layers(scene, obj) transfer_functions.init_shape_keys(scene, obj) transfer_functions.init_attributes(scene, obj) transfer_functions.init_parent(scene, obj) @@ -124,12 +118,6 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: target_obj=target_obj, source_obj=source_obj, ) - # if transfer_data_item.type == constants.VERTEX_COLOR_KEY: - # transfer_functions.transfer_vertex_color( - # vertex_color_name=transfer_data_item.name, - # target_obj=target_obj, - # source_obj=source_obj, - # ) if transfer_data_item.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") transfer_functions.transfer_modifier( @@ -149,12 +137,6 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: target_obj=target_obj, source_obj=source_obj, ) - # if transfer_data_item.type == constants.UV_LAYERS_KEY: - # transfer_functions.transfer_uv_layer( - # target_obj=target_obj, - # source_obj=source_obj, - # uv_name=transfer_data_item.name, - # ) if transfer_data_item.type == constants.SHAPE_KEY_KEY: transfer_functions.transfer_shape_key( context=context, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 2e154cb5..3d5a1022 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -80,108 +80,6 @@ def transfer_vertex_group( return -# VERTEX COLORS -def vertex_colors_clean(obj): - if not obj.type == "MESH": - return - transfer_data_clean( - obj=obj, - data_list=obj.data.vertex_colors, - td_type_key=constants.VERTEX_COLOR_KEY, - ) - - -def vertex_color_is_missing(transfer_data_item): - return transfer_data_item_is_missing( - transfer_data_item=transfer_data_item, - td_type_key=constants.VERTEX_COLOR_KEY, - data_list=transfer_data_item.id_data.vertex_colors, - ) - - -def init_vertex_colors(scene, obj): - if not obj.type == "MESH": - return - transfer_data_item_init( - scene=scene, - obj=obj, - data_list=obj.data.vertex_colors, - td_type_key=constants.VERTEX_COLOR_KEY, - ) - - -def transfer_vertex_color( - vertex_color_name: str, - target_obj: bpy.types.Object, - source_obj: bpy.types.Object, -): - old_color = target_obj.data.vertex_colors.get(vertex_color_name) - if old_color: - target_obj.data.vertex_colors.remove(old_color) - transfer_color = source_obj.data.vertex_colors.get(vertex_color_name) - new_color = target_obj.data.vertex_colors.new( - name=transfer_color.name, do_init=False - ) - for loop in target_obj.data.loops: - new_color.data[loop.index].color = transfer_color.data[loop.index].color - # ABOVE FOR LOOP IS FOR TOPOLOGY THAT MATCHES - # BELOW COMMENTED OUT CODE IS FOR TOPOLOGY THAT DOESN'T MATCH - # else: - # for vcol_from in obj_source.data.vertex_colors: - # vcol_to = obj_target.data.vertex_colors.new(name=vcol_from.name, do_init=False) - # transfer_corner_data(obj_source, obj_target, vcol_from.data, vcol_to.data, data_suffix = 'color') - - -# UV LAYERS -def uv_layer_clean(obj): - if not obj.type == "MESH": - return - transfer_data_clean( - obj=obj, data_list=obj.data.uv_layers, td_type_key=constants.UV_LAYERS_KEY - ) - - -def uv_layer_is_missing(transfer_data_item): - return transfer_data_item_is_missing( - transfer_data_item=transfer_data_item, - td_type_key=constants.UV_LAYERS_KEY, - data_list=transfer_data_item.id_data.data.uv_layers, - ) - - -def init_uv_layers(scene, obj): - if not obj.type == "MESH": - return - transfer_data_item_init( - scene=scene, - obj=obj, - data_list=obj.data.uv_layers, - td_type_key=constants.UV_LAYERS_KEY, - ) - - -def transfer_uv_layer(source_obj, target_obj, uv_name): - old_uv_layer = target_obj.data.uv_layers.get(uv_name) - if old_uv_layer: - target_obj.data.uv_layers.remove(old_uv_layer) - - transfer_uv = source_obj.data.uv_layers.get(uv_name) - new_uv = target_obj.data.uv_layers.new(name=uv_name, do_init=False) - for loop in target_obj.data.loops: - new_uv.data[loop.index].uv = transfer_uv.data[loop.index].uv - # BELOW CODE IS FOR NON MATCHING TOPOLOGY - # else: - # for uv_from in obj_source.data.uv_layers: - # uv_to = obj_target.data.uv_layers.new(name=uv_from.name, do_init=False) - # transfer_corner_data(obj_source, obj_target, uv_from.data, uv_to.data, data_suffix = 'uv') - - # Make sure correct layer is set to active - for uv_l in source_obj.data.uv_layers: - if uv_l.active_render: - target_obj.data.uv_layers[uv_l.name].active_render = True - break - - # MODIFIERS def modifiers_clean(obj): transfer_data_clean( -- 2.30.2 From e1a2847fb28fcc617a28c69b7b0317a484a5affb Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 13:12:04 -0400 Subject: [PATCH 270/429] Asset Pipe: Add Task Layer Prefix to Modifiers and Constraints --- .../addons/asset_pipeline_2/merge/naming.py | 37 ++++++++++++++-- .../merge/transfer_data/transfer_functions.py | 42 +++++++++++++------ 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 65008c3f..7734af09 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -115,6 +115,23 @@ def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) pass +# TODO Cleanup prefix doc strings +def get_name_with_prefix(name: str, prefix: str) -> str: + """Returns a string with the prefix. + + Args: + name (str): Name to add prefix to + prefix (str): Prefix to add to name + + Returns: + str: Returns name with prefix + """ + if name.startswith(prefix + "."): + return name + prefix = prefix + "." if prefix != "" else "" + return prefix + name + + def get_name_with_asset_prefix(name: str) -> str: """Returns a string with the prefix if it is not already set. Users can specify a prefix to live on all objects during the @@ -127,10 +144,22 @@ def get_name_with_asset_prefix(name: str) -> str: str: Returns name with prefix """ asset_pipe = bpy.context.scene.asset_pipeline - if name.startswith(asset_pipe.prefix + "."): - return name - prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" - return prefix + name + return get_name_with_prefix(name, asset_pipe.prefix) + + +def get_name_with_task_layer_prefix(name: str) -> str: + """Returns a string with the prefix if it is not already set. + Users can specify a prefix to live on all objects during the + asset creation process. This prefix is stored in the scene. + + Args: + name (str): Name to add prefix to + + Returns: + str: Returns name with prefix + """ + asset_pipe = bpy.context.scene.asset_pipeline + return get_name_with_prefix(name, asset_pipe.task_layer_name) def get_task_layer_col_name(task_layer_key) -> str: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 3d5a1022..31db9dee 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -1,6 +1,6 @@ import bpy from bpy import context -from ..naming import get_basename +from ..naming import get_basename, get_name_with_task_layer_prefix from ..drivers import find_drivers, copy_driver from ..visibility import override_obj_visability from .transfer_util import ( @@ -96,12 +96,20 @@ def modifier_is_missing(transfer_data_item): def init_modifiers(scene, obj): - transfer_data_item_init( - scene=scene, - obj=obj, - data_list=obj.modifiers, - td_type_key=constants.MODIFIER_KEY, - ) + td_type_key = constants.MODIFIER_KEY + transfer_data = obj.transfer_data_ownership + task_layer_key = scene.asset_pipeline.task_layer_name + for mod in obj.modifiers: + mod.name = get_name_with_task_layer_prefix(mod.name) + # Only add new ownership transfer_data_item if vertex group doesn't have an owner + matches = check_transfer_data_entry(transfer_data, mod.name, td_type_key) + if len(matches) == 0: + scene.asset_pipeline.add_temp_transfer_data( + name=mod.name, + owner=task_layer_key, + type=td_type_key, + obj=obj, + ) def transfer_modifier(modifier_name, target_obj, source_obj): @@ -180,12 +188,20 @@ def constraint_is_missing(transfer_data_item): def init_constraints(scene, obj): - transfer_data_item_init( - scene=scene, - obj=obj, - data_list=obj.constraints, - td_type_key=constants.CONSTRAINT_KEY, - ) + td_type_key = constants.CONSTRAINT_KEY + transfer_data = obj.transfer_data_ownership + task_layer_key = scene.asset_pipeline.task_layer_name + for const in obj.constraints: + const.name = get_name_with_task_layer_prefix(const.name) + # Only add new ownership transfer_data_item if vertex group doesn't have an owner + matches = check_transfer_data_entry(transfer_data, const.name, td_type_key) + if len(matches) == 0: + scene.asset_pipeline.add_temp_transfer_data( + name=const.name, + owner=task_layer_key, + type=td_type_key, + obj=obj, + ) def transfer_constraint(constraint_name, target_obj, source_obj): -- 2.30.2 From 651a67c768ab25d4f56b1381acdbc50163236e3b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 13:12:18 -0400 Subject: [PATCH 271/429] Asset Pipe: Shorten Task Layer Keys --- scripts-blender/addons/asset_pipeline_2/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 5b048e47..c1e5789d 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -6,9 +6,9 @@ # {Task Layer Key: Collection/UI name} TASK_LAYER_TYPES = { "NONE": "None", - "MODEL": "Modeling", + "MOD": "Modeling", "RIG": "Rigging", - "SHADE": "Shading", + "SHD": "Shading", } # When creating a new asset, start in this task layer's file. -- 2.30.2 From 476da05ff1b4d4b32b5dbfce5dca6d25e33232a3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 13:58:55 -0400 Subject: [PATCH 272/429] Asset Pipe: Name Task Layer Files with Full Name --- scripts-blender/addons/asset_pipeline_2/constants.py | 2 +- scripts-blender/addons/asset_pipeline_2/ops.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index c1e5789d..0c7c9b51 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -12,7 +12,7 @@ TASK_LAYER_TYPES = { } # When creating a new asset, start in this task layer's file. -STARTING_FILE = 'MODEL' +STARTING_FILE = 'MOD' # Convert it to the format that EnumProperty.items wants: # List of 3-tuples, re-use name as description at 3rd element. diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index b65347e4..e57e9259 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -90,7 +90,9 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE": continue - name = self._name + "." + task_layer_key + ".blend" + name = ( + self._name + "." + constants.TASK_LAYER_TYPES[task_layer_key] + ".blend" + ) task_layer_file = os.path.join(asset_path, name) asset_pipe.task_layer_name = task_layer_key if task_layer_key == constants.STARTING_FILE: -- 2.30.2 From bde07ba382bee42ad3d1f1d3be2455f427b761e7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 15:07:17 -0400 Subject: [PATCH 273/429] Asset PIpe: Allow Users to add mulitple top level collections --- .../asset_pipeline_2/merge/asset_mapping.py | 36 ++++++++++++++----- .../addons/asset_pipeline_2/merge/core.py | 9 +++++ .../addons/asset_pipeline_2/ops.py | 4 ++- scripts-blender/addons/asset_pipeline_2/ui.py | 9 ++++- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 003f856d..4f61fe2d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -25,10 +25,12 @@ class AssetTransferMapping: external_coll: bpy.types.Collection, local_tls: Set[str], ): - self._local_col = local_coll + self._local_top_col = local_coll self._external_col = external_coll self._local_tls = local_tls + self.external_col_to_remove: Set[bpy.types.Object] = set() + self.external_col_to_add: Set[bpy.types.Object] = set() self.external_obj_to_add: Set[bpy.types.Object] = set() self._no_match_source_objs: Set[bpy.types.Object] = set() @@ -67,7 +69,7 @@ class AssetTransferMapping: target collection. Uses suffixes to match them up. """ object_map: Dict[bpy.types.Object, bpy.types.Object] = {} - for local_obj in self._local_col.all_objects: + for local_obj in self._local_top_col.all_objects: # Skip items with no owner if local_obj.asset_id_owner == "NONE": continue @@ -84,7 +86,7 @@ class AssetTransferMapping: # Find new objects to add to local_col for external_obj in self._external_col.all_objects: - local_col_objs = self._local_col.all_objects + local_col_objs = self._local_top_col.all_objects obj = local_col_objs.get(get_target_name(external_obj.name)) if not obj and external_obj.asset_id_owner not in self._local_tls: self.external_obj_to_add.add(external_obj) @@ -103,19 +105,35 @@ class AssetTransferMapping: if tl_key in self._local_tls ] - for local_task_layer_col in self._local_col.children: - if get_basename(local_task_layer_col.name) not in local_tl_names: + for local_task_layer_col in self._local_top_col.children: + if local_task_layer_col.asset_id_owner not in self._local_tls: # Replace source object suffix with target suffix to get target object. external_col_name = get_target_name(local_task_layer_col.name) - external_col = bpy.data.collections.get(external_col_name) - if external_col: - coll_map[local_task_layer_col] = external_col + local_col = bpy.data.collections.get(external_col_name) + if local_col: + coll_map[local_task_layer_col] = local_col else: print( f"Failed to find match collection {local_task_layer_col.name} for {external_col_name}" ) self._no_match_source_colls.add(local_task_layer_col) + external_top_col_name = get_target_name(self._local_top_col.name) + external_top_col = bpy.data.collections.get(external_top_col_name) + + # TODO Refactor + for external_col in external_top_col.children: + local_col_name = get_target_name(external_col.name) + local_col = bpy.data.collections.get(local_col_name) + if not local_col and external_col.asset_id_owner not in self._local_tls: + self.external_col_to_add.add(external_col) + + for local_col in self._local_top_col.children: + external_col_name = get_target_name(local_col.name) + external_col = bpy.data.collections.get(external_col_name) + if not external_col and local_col.asset_id_owner not in self._local_tls: + self.external_col_to_remove.add(local_col) + all_tgt_colls = set(self._external_col.children_recursive) all_tgt_colls.add(self._external_col) match_target_colls = set([coll for coll in coll_map.values()]) @@ -194,7 +212,7 @@ class AssetTransferMapping: def _gen_shared_id_map(self): shared_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} - for local_id in get_shared_ids(self._local_col): + for local_id in get_shared_ids(self._local_top_col): external_id_name = get_target_name(local_id.name) id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 907c1da4..7a5700da 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -75,6 +75,9 @@ def ownership_get( continue ownership_transfer_data_cleanup(obj, task_layer_key) init_transfer_data(scene, obj) + for col in asset_pipe.asset_collection.children: + if col.asset_id_owner == "NONE": + col.asset_id_owner = task_layer_key def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: @@ -204,6 +207,12 @@ def merge_task_layer( for col in map.collection_map: remap_user(col, map.collection_map[col]) + for col in map.external_col_to_add: + local_col.children.link(col) + + for col in map.external_col_to_remove: + local_col.children.unlink(col) + for id in map.shared_id_map: remap_user(id, map.shared_id_map[id]) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index e57e9259..12f949cd 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -83,7 +83,9 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): continue col_name = get_task_layer_col_name(task_layer_key) bpy.data.collections.new(col_name) - asset_col.children.link(bpy.data.collections.get(col_name)) + new_col = bpy.data.collections.get(col_name) + asset_col.children.link(new_col) + new_col.asset_id_owner = task_layer_key starting_file = "" # Create the file for each task layer. diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 779317df..3df74610 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -55,8 +55,15 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): if not asset_pipe.is_asset_pipeline_file: layout.label(text="Open valid 'Asset Pipeline' file", icon="ERROR") return + + if context.collection in list(asset_pipe.asset_collection.children): + layout.label( + text=f"{context.collection.name} : '{context.collection.asset_id_owner}' (Active Collection)", + icon="OUTLINER_COLLECTION", + ) + if not context.active_object: - layout.label(text="Set an Active Object to Inspect") + layout.label(text="Set an Active Object to Inspect", icon="OBJECT_DATA") return obj = context.active_object transfer_data = obj.transfer_data_ownership -- 2.30.2 From 7fc699a58fc99e57368707fd2de2c79e59ea4553 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 28 Sep 2023 17:20:21 -0400 Subject: [PATCH 274/429] Asset Pipe: Fix Bug in Modifier/Constraint Prefixes --- .../addons/asset_pipeline_2/merge/naming.py | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 7734af09..925eda39 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -116,22 +116,6 @@ def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) # TODO Cleanup prefix doc strings -def get_name_with_prefix(name: str, prefix: str) -> str: - """Returns a string with the prefix. - - Args: - name (str): Name to add prefix to - prefix (str): Prefix to add to name - - Returns: - str: Returns name with prefix - """ - if name.startswith(prefix + "."): - return name - prefix = prefix + "." if prefix != "" else "" - return prefix + name - - def get_name_with_asset_prefix(name: str) -> str: """Returns a string with the prefix if it is not already set. Users can specify a prefix to live on all objects during the @@ -144,7 +128,10 @@ def get_name_with_asset_prefix(name: str) -> str: str: Returns name with prefix """ asset_pipe = bpy.context.scene.asset_pipeline - return get_name_with_prefix(name, asset_pipe.prefix) + if name.startswith(asset_pipe.prefix + "."): + return name + prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" + return prefix + name def get_name_with_task_layer_prefix(name: str) -> str: @@ -159,7 +146,11 @@ def get_name_with_task_layer_prefix(name: str) -> str: str: Returns name with prefix """ asset_pipe = bpy.context.scene.asset_pipeline - return get_name_with_prefix(name, asset_pipe.task_layer_name) + for task_layer_key in constants.TASK_LAYER_TYPES.keys(): + if name.startswith(task_layer_key + "."): + return name + prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" + return prefix + name def get_task_layer_col_name(task_layer_key) -> str: -- 2.30.2 From f7078b26f3c1bb55e10d006abe8aecb5a9206314 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 29 Sep 2023 11:53:24 -0400 Subject: [PATCH 275/429] Asset Pipe: Fix Getting Task Layer Collection Objects --- .../addons/asset_pipeline_2/merge/core.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 7a5700da..3d66f6d4 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -12,7 +12,6 @@ from .naming import ( add_suffix_to_hierarchy, remove_suffix_from_hierarchy, get_name_with_asset_prefix, - get_task_layer_col_name, get_id_type_name, ) @@ -63,11 +62,10 @@ def ownership_get( asset_pipe = scene.asset_pipeline asset_pipe.temp_transfer_data.clear() task_layer_key = asset_pipe.task_layer_name - task_layer_col_name = get_task_layer_col_name(task_layer_key) - task_layer_col = local_col.children.get(task_layer_col_name) + task_layer_objs = get_task_layer_objects() for obj in local_col.all_objects: # Mark Asset ID Owner for objects in the current task layers collection - if obj.asset_id_owner == "NONE" and obj in list(task_layer_col.all_objects): + if obj.asset_id_owner == "NONE" and obj in task_layer_objs: obj.asset_id_owner = task_layer_key obj.name = get_name_with_asset_prefix(obj.name) # Skip items that have no owner @@ -114,17 +112,13 @@ def get_invalid_objects( list[bpy.types.Object]: List of Invalid Objects """ task_layer_key = scene.asset_pipeline.task_layer_name - task_layer_col_name = get_task_layer_col_name(task_layer_key) - task_layer_col = local_col.children.get(task_layer_col_name) + task_layer_objs = get_task_layer_objects() invalid_obj = [] for obj in scene.objects: if obj.asset_id_owner == "NONE": invalid_obj.append(obj) - if ( - obj not in list(task_layer_col.all_objects) - and obj.asset_id_owner == task_layer_key - ): + if obj not in task_layer_objs and obj.asset_id_owner == task_layer_key: invalid_obj.append(obj) return invalid_obj @@ -273,3 +267,14 @@ def import_data_from_lib( ) return eval(f"bpy.data.{data_category}['{data_name}']") + + +def get_task_layer_objects(): + asset_pipe = bpy.context.scene.asset_pipeline + task_layer_key = asset_pipe.task_layer_name + local_col = asset_pipe.asset_collection + task_layer_objs = [] + for col in local_col.children: + if col.asset_id_owner == task_layer_key: + task_layer_objs = task_layer_objs + list(col.all_objects) + return task_layer_objs -- 2.30.2 From a572f385040cad48e062654da4d1249119114ea3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 29 Sep 2023 11:54:07 -0400 Subject: [PATCH 276/429] Asset Pipe: Fix Overrides Transfer Modifiers --- .../merge/transfer_data/transfer_functions.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 31db9dee..62d32d32 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -147,26 +147,30 @@ def transfer_modifier(modifier_name, target_obj, source_obj): if not mod.is_bound: continue for i in range(2): - bpy.ops.object.surfacedeform_bind( - {"object": target_obj, "active_object": target_obj}, - modifier=mod.name, - ) + with override_obj_visability(obj=target_obj): + with context.temp_override( + object=target_obj, active_object=target_obj + ): + bpy.ops.object.surfacedeform_bind(modifier=mod.name) elif mod.type == 'MESH_DEFORM': if not mod.is_bound: continue for i in range(2): - bpy.ops.object.meshdeform_bind( - {"object": target_obj, "active_object": target_obj}, - modifier=mod.name, - ) + with override_obj_visability(obj=target_obj): + with context.temp_override( + object=target_obj, active_object=target_obj + ): + bpy.ops.object.meshdeform_bind(modifier=mod.name) elif mod.type == 'CORRECTIVE_SMOOTH': if not mod.is_bind: continue for i in range(2): - bpy.ops.object.correctivesmooth_bind( - {"object": target_obj, "active_object": target_obj}, - modifier=mod.name, - ) + # TODO Create override for modifier visibility in these cases + with override_obj_visability(obj=target_obj): + with context.temp_override( + object=target_obj, active_object=target_obj + ): + bpy.ops.object.correctivesmooth_bind(modifier=mod.name) fcurves = find_drivers(source_obj, 'modifiers', modifier_name) for fcurve in fcurves: copy_driver(from_fcurve=fcurve, target=target_obj) -- 2.30.2 From dfc1a5d687566ff38bb2b109cfad29b465fae1d5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 29 Sep 2023 11:54:32 -0400 Subject: [PATCH 277/429] Asset Pipe: Make Attributes Compatible with 3.6 (Temporary) --- .../merge/transfer_data/transfer_functions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 62d32d32..59ff5f50 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -509,9 +509,11 @@ def attributes_get_editable(attributes): for attribute in attributes if not ( attribute.is_internal - or attribute.is_required + # or attribute.is_required # Material Index is part of material transfer and should be skipped or attribute.name == 'material_index' + or attribute.name + == 'position' # TODO replace position with is_required in 4.0 ) ] -- 2.30.2 From 433c03803f5251c56a3e24ffe6de23849214c08c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 29 Sep 2023 12:22:47 -0400 Subject: [PATCH 278/429] Asset Pipe: Fix Bug in Task Layer Prefix --- scripts-blender/addons/asset_pipeline_2/merge/naming.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 925eda39..c63c839d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -146,6 +146,7 @@ def get_name_with_task_layer_prefix(name: str) -> str: str: Returns name with prefix """ asset_pipe = bpy.context.scene.asset_pipeline + prefix = asset_pipe.task_layer_name for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if name.startswith(task_layer_key + "."): return name -- 2.30.2 From b31043cd566330954886cc8bc7a113e9a823f7ac Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 30 Sep 2023 15:26:08 -0400 Subject: [PATCH 279/429] Asset Pipe: Save before Push (after ownership update) --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 12f949cd..99db0c56 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -232,7 +232,7 @@ class ASSETPIPE_OT_sync_push(bpy.types.Operator): if self.pull: sync_execute_pull(self, context) - bpy.ops.wm.save_mainfile(filepath=self._current_file.__str__()) + bpy.ops.wm.save_mainfile(filepath=self._current_file.__str__()) sync_execute_push(self, context) return {'FINISHED'} -- 2.30.2 From 39fcd2bf3ded5a91f8326211a68882cb76f69148 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 30 Sep 2023 15:26:26 -0400 Subject: [PATCH 280/429] Asset Pipe: Fix Bug in Task Layer Prefix --- scripts-blender/addons/asset_pipeline_2/merge/naming.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index c63c839d..b0d5aa08 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -150,8 +150,7 @@ def get_name_with_task_layer_prefix(name: str) -> str: for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if name.startswith(task_layer_key + "."): return name - prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" - return prefix + name + return prefix + "." + name def get_task_layer_col_name(task_layer_key) -> str: -- 2.30.2 From 91bcf7ec2b1e306054465e574a35465218bd2448 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 30 Sep 2023 15:27:14 -0400 Subject: [PATCH 281/429] Asset PipeL: Add Workaround for Failing Vertex Transfer --- .../merge/transfer_data/transfer_functions.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 59ff5f50..99c89567 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -13,6 +13,7 @@ from ... import constants import mathutils import bmesh import numpy as np +import time ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES @@ -59,12 +60,13 @@ def transfer_vertex_group( return source_obj.vertex_groups.active = source_obj.vertex_groups.get(vertex_group_name) - context = bpy.context - override = context.copy() - override["selected_editable_objects"] = [target_obj, source_obj] - override["active_object"] = source_obj - override["object"] = source_obj - with context.temp_override(**override): + # HACK without this sleep function Push will crash when transferring large amount of vertex groups + time.sleep(0.00000000000001) + + # DEBUG WHY THIS FAILS TO TRANSFER VERTEX GROUPS IN 4.0 + with context.temp_override( + object=source_obj, selected_editable_objects=[target_obj, source_obj] + ): bpy.ops.object.data_transfer( data_type="VGROUP_WEIGHTS", use_create=True, -- 2.30.2 From 32ff159916bc6c6758a780a4c6f41e5c22e06e70 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 30 Sep 2023 15:53:24 -0400 Subject: [PATCH 282/429] Asset Pipe: Save FIle & Images before Push/Pull --- .../addons/asset_pipeline_2/images.py | 12 ++++++ .../addons/asset_pipeline_2/ops.py | 39 ++++++++++--------- 2 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/images.py diff --git a/scripts-blender/addons/asset_pipeline_2/images.py b/scripts-blender/addons/asset_pipeline_2/images.py new file mode 100644 index 00000000..a98eb86e --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/images.py @@ -0,0 +1,12 @@ +import bpy +from pathlib import Path + + +def save_images(): + # TODO Make customizable + save_path = Path(bpy.data.filepath).parent.joinpath("images") + for img in bpy.data.images: + if img.is_dirty: + filepath = save_path.joinpath(img.name).__str__() + img.filepath = filepath + img.save(filepath=filepath) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 99db0c56..4c04d488 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -8,6 +8,7 @@ from .merge.publish import ( from .merge.naming import get_task_layer_col_name from .merge.transfer_data.transfer_ui import draw_transfer_data +from .images import save_images from . import constants from .sync import ( sync_poll, @@ -157,25 +158,24 @@ class ASSETPIPE_OT_sync_pull(bpy.types.Operator): default=False, description="Show New Transfer Data", ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - if any([img.is_dirty for img in bpy.data.images]): - cls.poll_message_set("Please save unsaved Images") - return False - if bpy.data.is_dirty: - cls.poll_message_set("Please save current .blend file") - return False - return True + save: bpy.props.BoolProperty( + name="Save File & Images", + default=True, + description="Save Current File and Images before Push", + ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): sync_invoke(self, context) return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context: bpy.types.Context): + self.layout.prop(self, "save") sync_draw(self, context) def execute(self, context: bpy.types.Context): + if self.save: + save_images() + bpy.ops.wm.save_mainfile() # Find current task Layer sync_execute_update_ownership(self, context) sync_execute_prepare_sync(self, context) @@ -207,15 +207,11 @@ class ASSETPIPE_OT_sync_push(bpy.types.Operator): description="Pull in any new data from the Published file before Pushing", ) - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - if any([img.is_dirty for img in bpy.data.images]): - cls.poll_message_set("Please save unsaved Images") - return False - if bpy.data.is_dirty: - cls.poll_message_set("Please save current .blend file") - return False - return True + save: bpy.props.BoolProperty( + name="Save File & Images", + default=True, + description="Save Current File and Images before Push", + ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): sync_invoke(self, context) @@ -223,9 +219,14 @@ class ASSETPIPE_OT_sync_push(bpy.types.Operator): def draw(self, context: bpy.types.Context): self.layout.prop(self, "pull") + self.layout.prop(self, "save") sync_draw(self, context) def execute(self, context: bpy.types.Context): + if self.save: + save_images() + bpy.ops.wm.save_mainfile() + # Find current task Layer sync_execute_update_ownership(self, context) sync_execute_prepare_sync(self, context) -- 2.30.2 From e437fca1d3c800151f462977832bab204d2c9124 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 30 Sep 2023 16:08:42 -0400 Subject: [PATCH 283/429] Asset Pipe: Add Shared ID Icons --- scripts-blender/addons/asset_pipeline_2/constants.py | 6 ++++++ .../addons/asset_pipeline_2/merge/shared_ids.py | 10 ++++++++++ scripts-blender/addons/asset_pipeline_2/sync.py | 5 +++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 0c7c9b51..92ed5b35 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -83,3 +83,9 @@ EXTERNAL_SUFFIX = "EXTERNAL" MATERIAL_ATTRIBUTE_NAME = "material_index" + + +## SHARED ID Icons +GEO_NODE = "GEOMETRY_NODES" +IMAGE = "IMAGE_DATA" +BLANK = "BLANK1" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py index 81c36841..8686c478 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py @@ -1,5 +1,7 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids +from .util import get_fundamental_id_type +from .. import constants def get_shared_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: @@ -38,3 +40,11 @@ def init_shared_ids(scene: bpy.types.Scene) -> list[bpy.types.ID]: id.asset_id_owner = asset_pipe.task_layer_name shared_ids.append(id) return shared_ids + + +def get_shared_id_icon(id: bpy.types.ID) -> str: + if bpy.types.NodeTree == get_fundamental_id_type(id): + return constants.GEO_NODE + if bpy.types.Image == get_fundamental_id_type(id): + return constants.IMAGE + return constants.BLANK diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 4befb34b..e06a67e8 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -12,6 +12,7 @@ from .merge.core import ( merge_task_layer, ) from .merge.transfer_data.transfer_ui import draw_transfer_data +from .merge.shared_ids import get_shared_id_icon from . import constants @@ -58,9 +59,9 @@ def sync_draw(self, context): if len(self._shared_ids) != 0: box = layout.box() - box.label(text="New 'Other IDs' found") + box.label(text="New 'Shared IDs' found") for id in self._shared_ids: - box.label(text=id.name) + box.label(text=id.name, icon=get_shared_id_icon(id)) if len(self._temp_transfer_data) == 0: layout.label(text="No new local Transfer Data found") -- 2.30.2 From e4f4454506f7cdca3301c557cf8d1a3670c1fcce Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Sat, 30 Sep 2023 17:59:37 -0400 Subject: [PATCH 284/429] Asset Pipe: Add Test Files for Sky Character --- .../addons/asset_pipeline_2/.gitignore | 2 + .../testing/resources/Modelling_Import.py | 74 ++++++++ .../testing/resources/Rigging_Import.py | 172 ++++++++++++++++++ .../testing/resources/Shading_Import.py | 126 +++++++++++++ .../resources/sky_for_asset_test.blend | 3 + 5 files changed, 377 insertions(+) create mode 100644 scripts-blender/addons/asset_pipeline_2/.gitignore create mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py create mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py create mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py create mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend diff --git a/scripts-blender/addons/asset_pipeline_2/.gitignore b/scripts-blender/addons/asset_pipeline_2/.gitignore new file mode 100644 index 00000000..a098b5c3 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/.gitignore @@ -0,0 +1,2 @@ +# Temporary for Testing +testing/test_chr \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py new file mode 100644 index 00000000..4fda3084 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py @@ -0,0 +1,74 @@ +import bpy +from pathlib import Path + + +def import_data_from_lib( + libpath: Path, + data_category: str, + data_name: str, + link: bool = False, +) -> bpy.data: + """Appends/Links data from an external file into the current file. + + Args: + libpath (Path): path to .blend file that contains library + data_category (str): bpy.types, like object or collection + data_name (str): name of datablock to link/append + link (bool, optional): Set to link library otherwise append. Defaults to False. + + Returns: + bpy.data: returns whichever data_category/type that was linked/appended + """ + + noun = "Appended" + if link: + noun = "Linked" + + with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( + data_from, + data_to, + ): + if data_name not in eval(f"data_from.{data_category}"): + print( + f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", + ) + + # Check if datablock with same name already exists in blend file. + try: + eval(f"bpy.data.{data_category}['{data_name}']") + except KeyError: + pass + else: + print( + f"{data_name} already in bpy.data.{data_category} of this blendfile.", + ) + + # Append data block. + eval(f"data_to.{data_category}.append('{data_name}')") + print(f"{noun}:{data_name} from library: {libpath.as_posix()}") + + if link: + return eval( + f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" + ) + + return eval(f"bpy.data.{data_category}['{data_name}']") + + +## EXECUTION +task_layer_name = Path(bpy.data.filepath).name.split(".")[1] +task_layer_col = bpy.data.collections[task_layer_name] +external_file = ( + Path(bpy.data.filepath) + .parent.parent.parent.joinpath("resources") + .joinpath("sky_for_asset_test.blend") +) +appended_col = import_data_from_lib( + external_file, "collections", f"sky.{task_layer_name.lower()}" +) +bpy.context.scene.collection.children.link(appended_col) + +for obj in appended_col.objects: + task_layer_col.objects.link(obj) + +bpy.data.collections.remove(appended_col) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py new file mode 100644 index 00000000..4a675b67 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py @@ -0,0 +1,172 @@ +import bpy +from pathlib import Path + + +def import_data_from_lib( + libpath: Path, + data_category: str, + data_name: str, + link: bool = False, +) -> bpy.data: + """Appends/Links data from an external file into the current file. + + Args: + libpath (Path): path to .blend file that contains library + data_category (str): bpy.types, like object or collection + data_name (str): name of datablock to link/append + link (bool, optional): Set to link library otherwise append. Defaults to False. + + Returns: + bpy.data: returns whichever data_category/type that was linked/appended + """ + + noun = "Appended" + if link: + noun = "Linked" + + with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( + data_from, + data_to, + ): + if data_name not in eval(f"data_from.{data_category}"): + print( + f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", + ) + + # Check if datablock with same name already exists in blend file. + try: + eval(f"bpy.data.{data_category}['{data_name}']") + except KeyError: + pass + else: + print( + f"{data_name} already in bpy.data.{data_category} of this blendfile.", + ) + + # Append data block. + eval(f"data_to.{data_category}.append('{data_name}')") + print(f"{noun}:{data_name} from library: {libpath.as_posix()}") + + if link: + return eval( + f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" + ) + + return eval(f"bpy.data.{data_category}['{data_name}']") + + +def transfer_constraint(constraint_name, target_obj, source_obj): + context = bpy.context + # remove old and sync existing modifiers + old_mod = target_obj.constraints.get(constraint_name) + if old_mod: + target_obj.constraints.remove(old_mod) + + # transfer new modifiers + for i, constraint in enumerate(source_obj.constraints): + if constraint.name == constraint_name: + constraint_new = target_obj.constraints.new(constraint.type) + constraint_new.name = constraint.name + # sort new modifier at correct index (default to beginning of the stack) + idx = 0 + if i > 0: + name_prev = source_obj.constraints[i - 1].name + for target_mod_i, target_constraint in enumerate( + target_obj.constraints + ): + if target_constraint.name == name_prev: + idx = target_mod_i + 1 + + if idx != i: + with override_obj_visability(obj=target_obj): + with context.temp_override(object=target_obj): + bpy.ops.constraint.move_to_index( + constraint=constraint_new.name, index=idx + ) + constraint_target = target_obj.constraints.get(constraint.name) + props = [ + p.identifier for p in constraint.bl_rna.properties if not p.is_readonly + ] + for prop in props: + value = getattr(constraint, prop) + setattr(constraint_target, prop, value) + + # HACK to cover edge case of armature constraints + if constraint.type == "ARMATURE": + for target_item in constraint.targets: + new_target = constraint_new.targets.new() + new_target.target = target_item.target + new_target.subtarget = target_item.subtarget + + +def transfer_vertex_groups(context, target_obj, source_obj): + with context.temp_override( + object=source_obj, selected_editable_objects=[target_obj, source_obj] + ): + bpy.ops.object.data_transfer( + data_type="VGROUP_WEIGHTS", + use_create=True, + vert_mapping='POLYINTERP_NEAREST', + layers_select_src="ALL", + layers_select_dst="NAME", + mix_mode="REPLACE", + ) + + +## EXECUTION +task_layer_name = Path(bpy.data.filepath).name.split(".")[1] +task_layer_col = bpy.data.collections[task_layer_name] +external_file = ( + Path(bpy.data.filepath) + .parent.parent.parent.joinpath("resources") + .joinpath("sky_for_asset_test.blend") +) +appended_col = import_data_from_lib( + external_file, "collections", f"sky.{task_layer_name.lower()}" +) +bpy.context.scene.collection.children.link(appended_col) + +rig = None + + +# Link Armature into Scene +for obj in appended_col.objects: + if obj.type == "ARMATURE": + task_layer_col.objects.link(obj) + rig = obj + + +for obj in bpy.data.collections["Modeling"].objects: + source_obj = bpy.data.objects[f"{obj.name}.rigging"] + + ## Set Parent + obj.parent = rig + obj.matrix_parent_inverse = source_obj.parent.matrix_world.inverted() + + ## Transfer Vertex Groups + transfer_vertex_groups(bpy.context, obj, source_obj) + + ## Copy Constraints + for constraint in source_obj.constraints: + transfer_constraint(constraint.name, obj, source_obj) + + +main_body_obj = bpy.data.objects["GEO-Body"] +mod = main_body_obj.modifiers.new("Armature", type="ARMATURE") +mod.object = rig + + +for col in appended_col.children: + task_layer_col.children.link(col) + +for obj in task_layer_col.all_objects: + if obj.name.endswith(".rigging"): + obj.name = obj.name.replace(".rigging", "") + + +## REMOVE EVERYTHING ELSE +for obj in bpy.data.objects: + if obj.name.endswith(".rigging"): + bpy.data.objects.remove(obj) + +bpy.data.collections.remove(appended_col) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py new file mode 100644 index 00000000..4e447e09 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py @@ -0,0 +1,126 @@ +import bpy +from pathlib import Path + + +def import_data_from_lib( + libpath: Path, + data_category: str, + data_name: str, + link: bool = False, +) -> bpy.data: + """Appends/Links data from an external file into the current file. + + Args: + libpath (Path): path to .blend file that contains library + data_category (str): bpy.types, like object or collection + data_name (str): name of datablock to link/append + link (bool, optional): Set to link library otherwise append. Defaults to False. + + Returns: + bpy.data: returns whichever data_category/type that was linked/appended + """ + + noun = "Appended" + if link: + noun = "Linked" + + with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( + data_from, + data_to, + ): + if data_name not in eval(f"data_from.{data_category}"): + print( + f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", + ) + + # Check if datablock with same name already exists in blend file. + try: + eval(f"bpy.data.{data_category}['{data_name}']") + except KeyError: + pass + else: + print( + f"{data_name} already in bpy.data.{data_category} of this blendfile.", + ) + + # Append data block. + eval(f"data_to.{data_category}.append('{data_name}')") + print(f"{noun}:{data_name} from library: {libpath.as_posix()}") + + if link: + return eval( + f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" + ) + + return eval(f"bpy.data.{data_category}['{data_name}']") + + +def transfer_material_slots(target_obj: bpy.types.Object, source_obj): + # Delete all material slots of target object. + target_obj.data.materials.clear() + + # Transfer material slots + for idx in range(len(source_obj.material_slots)): + target_obj.data.materials.append(source_obj.material_slots[idx].material) + target_obj.material_slots[idx].link = source_obj.material_slots[idx].link + + +def transfer_attribute( + attribute_name: str, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, +): + source_attributes = source_obj.data.attributes + target_attributes = target_obj.data.attributes + source_attribute = source_attributes.get(attribute_name) + + target_attribute = target_attributes.get(attribute_name) + if target_attribute: + target_attributes.remove(target_attribute) + + target_attribute = target_attributes.new( + name=attribute_name, + type=source_attribute.data_type, + domain=source_attribute.domain, + ) + # print(f"Transfering Attribute {attribute_name}") + for source_data_item in source_attribute.data.items(): + index = source_data_item[0] + source_data = source_data_item[1] + keys = set(source_data.bl_rna.properties.keys()) - set( + bpy.types.Attribute.bl_rna.properties.keys() + ) + for key in list(keys): + target_data = target_attribute.data[index] + setattr(target_data, key, getattr(source_data, key)) + + +## EXECUTION +task_layer_name = Path(bpy.data.filepath).name.split(".")[1] +task_layer_col = bpy.data.collections[task_layer_name] +external_file = ( + Path(bpy.data.filepath) + .parent.parent.parent.joinpath("resources") + .joinpath("sky_for_asset_test.blend") +) +appended_col = import_data_from_lib( + external_file, "collections", f"sky.{task_layer_name.lower()}" +) +bpy.context.scene.collection.children.link(appended_col) + +source_body_obj = bpy.data.objects["GEO-Body.shading"] +target_body_obj = bpy.data.objects["GEO-Body"] + + +for obj in bpy.data.collections["Modeling"].objects: + source_obj = bpy.data.objects[f"{obj.name}.shading"] + transfer_material_slots(obj, source_obj) + +transfer_attribute( + attribute_name="material_index", + target_obj=target_body_obj, + source_obj=source_body_obj, +) + + +bpy.data.collections.remove(appended_col) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend b/scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend new file mode 100644 index 00000000..b745f940 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:221d07c3dc474f9c349b7ce0eaaa9892167b78c373f8418e9d863815b5bb6246 +size 2054192 -- 2.30.2 From 64bbed70ca93e80842acd76a264e8c413df614a0 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 2 Oct 2023 15:43:23 -0400 Subject: [PATCH 285/429] Asset Pipe: Support Multiple Task Layers per File --- .../addons/asset_pipeline_2/merge/core.py | 24 ++++++++++--------- .../addons/asset_pipeline_2/merge/naming.py | 21 +++------------- .../asset_pipeline_2/merge/shared_ids.py | 5 +++- .../asset_pipeline_2/merge/task_layer.py | 10 ++++++++ .../merge/transfer_data/transfer_functions.py | 16 +++++++------ .../merge/transfer_data/transfer_util.py | 5 ++-- .../addons/asset_pipeline_2/ops.py | 17 +++++++------ .../addons/asset_pipeline_2/props.py | 5 +--- .../addons/asset_pipeline_2/sync.py | 23 ++++++++++-------- scripts-blender/addons/asset_pipeline_2/ui.py | 11 +++++---- 10 files changed, 70 insertions(+), 67 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/task_layer.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 3d66f6d4..5e72bd41 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -15,6 +15,7 @@ from .naming import ( get_id_type_name, ) +from .task_layer import get_local_task_layers from pathlib import Path from typing import Dict @@ -22,18 +23,18 @@ from .. import constants def ownership_transfer_data_cleanup( - obj: bpy.types.Object, task_layer_name: str + obj: bpy.types.Object, ) -> None: """Remove Transfer Data ownership items if the corrisponding data is missing Args: obj (bpy.types.Object): Object that contains the transfer data - task_layer_name (str): Name of the current task layer that owns the data """ + local_task_layer_keys = get_local_task_layers() transfer_data = obj.transfer_data_ownership to_remove = [] for transfer_data_item in transfer_data: - if transfer_data_item.owner == task_layer_name: + if transfer_data_item.owner in local_task_layer_keys: if transfer_data_is_missing(transfer_data_item): to_remove.append(transfer_data_item.name) @@ -61,21 +62,22 @@ def ownership_get( """ asset_pipe = scene.asset_pipeline asset_pipe.temp_transfer_data.clear() - task_layer_key = asset_pipe.task_layer_name + # TODO Figure out default in this case + default_task_layer = get_local_task_layers()[0] task_layer_objs = get_task_layer_objects() for obj in local_col.all_objects: # Mark Asset ID Owner for objects in the current task layers collection if obj.asset_id_owner == "NONE" and obj in task_layer_objs: - obj.asset_id_owner = task_layer_key + obj.asset_id_owner = default_task_layer obj.name = get_name_with_asset_prefix(obj.name) # Skip items that have no owner if obj.asset_id_owner == "NONE": continue - ownership_transfer_data_cleanup(obj, task_layer_key) + ownership_transfer_data_cleanup(obj) init_transfer_data(scene, obj) for col in asset_pipe.asset_collection.children: if col.asset_id_owner == "NONE": - col.asset_id_owner = task_layer_key + col.asset_id_owner = default_task_layer def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: @@ -111,14 +113,14 @@ def get_invalid_objects( Returns: list[bpy.types.Object]: List of Invalid Objects """ - task_layer_key = scene.asset_pipeline.task_layer_name + local_task_layer_keys = get_local_task_layers() task_layer_objs = get_task_layer_objects() invalid_obj = [] for obj in scene.objects: if obj.asset_id_owner == "NONE": invalid_obj.append(obj) - if obj not in task_layer_objs and obj.asset_id_owner == task_layer_key: + if obj not in task_layer_objs and obj.asset_id_owner in local_task_layer_keys: invalid_obj.append(obj) return invalid_obj @@ -271,10 +273,10 @@ def import_data_from_lib( def get_task_layer_objects(): asset_pipe = bpy.context.scene.asset_pipeline - task_layer_key = asset_pipe.task_layer_name + local_task_layer_keys = get_local_task_layers() local_col = asset_pipe.asset_collection task_layer_objs = [] for col in local_col.children: - if col.asset_id_owner == task_layer_key: + if col.asset_id_owner in local_task_layer_keys: task_layer_objs = task_layer_objs + list(col.all_objects) return task_layer_objs diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index b0d5aa08..2005c85d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -22,6 +22,7 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from .util import get_storage_of_id from .. import constants +from .task_layer import get_default_task_layer DELIMITER = "." @@ -134,7 +135,7 @@ def get_name_with_asset_prefix(name: str) -> str: return prefix + name -def get_name_with_task_layer_prefix(name: str) -> str: +def get_name_with_task_layer_prefix(name: str, td_type_key: str) -> str: """Returns a string with the prefix if it is not already set. Users can specify a prefix to live on all objects during the asset creation process. This prefix is stored in the scene. @@ -145,29 +146,13 @@ def get_name_with_task_layer_prefix(name: str) -> str: Returns: str: Returns name with prefix """ - asset_pipe = bpy.context.scene.asset_pipeline - prefix = asset_pipe.task_layer_name + prefix = get_default_task_layer(td_type_key) for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if name.startswith(task_layer_key + "."): return name return prefix + "." + name -def get_task_layer_col_name(task_layer_key) -> str: - """Returns the name of a givem task layer colection via - the task layer key. Task Layer Collection names are a combination - of a prefix if any and the task_layer_name. - - Args: - task_layer_key (_type_): Key of a given task layer - - Returns: - str: Task Layer Collection name including prefix if exists - """ - task_layer_name = constants.TASK_LAYER_TYPES[task_layer_key] - return get_name_with_asset_prefix(task_layer_name) - - def get_id_type_name(id_type: bpy.types) -> str: """Return the cosmetic name of a given ID type diff --git a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py index 8686c478..ed986e1c 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py @@ -2,6 +2,7 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from .util import get_fundamental_id_type from .. import constants +from .task_layer import get_local_task_layers def get_shared_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: @@ -32,12 +33,14 @@ def init_shared_ids(scene: bpy.types.Scene) -> list[bpy.types.ID]: Returns: list[bpy.types.ID]: A list of new 'shared_ids' owned by the file's task layer """ + # TODO Figure our what is the default in this case + task_layer_key = get_local_task_layers()[0] shared_ids = [] asset_pipe = scene.asset_pipeline local_col = asset_pipe.asset_collection for id in get_shared_ids(local_col): if id.asset_id_owner == 'NONE': - id.asset_id_owner = asset_pipe.task_layer_name + id.asset_id_owner = task_layer_key shared_ids.append(id) return shared_ids diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py new file mode 100644 index 00000000..2084a8fc --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -0,0 +1,10 @@ +import bpy + + +def get_local_task_layers(): + local_task_layer_strings = bpy.context.scene.asset_pipeline.local_task_layers + return local_task_layer_strings.split(",") + + +def get_default_task_layer(td_type: str): + return get_local_task_layers()[0] diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 99c89567..539dc1d4 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -9,6 +9,8 @@ from .transfer_util import ( transfer_data_item_init, check_transfer_data_entry, ) + +from ..task_layer import get_default_task_layer from ... import constants import mathutils import bmesh @@ -100,9 +102,9 @@ def modifier_is_missing(transfer_data_item): def init_modifiers(scene, obj): td_type_key = constants.MODIFIER_KEY transfer_data = obj.transfer_data_ownership - task_layer_key = scene.asset_pipeline.task_layer_name + task_layer_key = get_default_task_layer(td_type_key) for mod in obj.modifiers: - mod.name = get_name_with_task_layer_prefix(mod.name) + mod.name = get_name_with_task_layer_prefix(mod.name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, mod.name, td_type_key) if len(matches) == 0: @@ -196,9 +198,9 @@ def constraint_is_missing(transfer_data_item): def init_constraints(scene, obj): td_type_key = constants.CONSTRAINT_KEY transfer_data = obj.transfer_data_ownership - task_layer_key = scene.asset_pipeline.task_layer_name + task_layer_key = get_default_task_layer(td_type_key) for const in obj.constraints: - const.name = get_name_with_task_layer_prefix(const.name) + const.name = get_name_with_task_layer_prefix(const.name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, const.name, td_type_key) if len(matches) == 0: @@ -286,8 +288,8 @@ def material_slots_is_missing(transfer_data_item): def init_material_slots(scene, obj): - task_layer_key = scene.asset_pipeline.task_layer_name td_type_key = constants.MATERIAL_SLOT_KEY + task_layer_key = get_default_task_layer(td_type_key) name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership @@ -553,8 +555,8 @@ def init_attributes(scene, obj): if obj.type != "MESH": return transfer_data = obj.transfer_data_ownership - task_layer_key = scene.asset_pipeline.task_layer_name td_type_key = constants.ATTRIBUTE_KEY + task_layer_key = get_default_task_layer(td_type_key) for atttribute in attributes_get_editable(obj.data.attributes): # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) @@ -620,8 +622,8 @@ def parent_is_missing(transfer_data_item): def init_parent(scene, obj): - task_layer_key = scene.asset_pipeline.task_layer_name td_type_key = constants.PARENT_KEY + task_layer_key = get_default_task_layer(td_type_key) name = constants.PARENT_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index 39daeb46..b4d117e3 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -1,5 +1,6 @@ import bpy from ..naming import get_basename +from ..task_layer import get_default_task_layer def check_transfer_data_entry( @@ -101,14 +102,14 @@ def transfer_data_item_init( td_type_key (str): Key for the transfer data type """ transfer_data = obj.transfer_data_ownership - task_layer_key = scene.asset_pipeline.task_layer_name + default_task_layer = get_default_task_layer(td_type_key) for item in data_list: # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, item.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=item.name, - owner=task_layer_key, + owner=default_task_layer, type=td_type_key, obj=obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 4c04d488..72f0cc01 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -5,7 +5,6 @@ from pathlib import Path from .merge.publish import ( get_next_published_file, ) -from .merge.naming import get_task_layer_col_name from .merge.transfer_data.transfer_ui import draw_transfer_data from .images import save_images @@ -79,14 +78,14 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.prefix = self._prefix # Create the collections for each task layer. - for task_layer_key in constants.TASK_LAYER_TYPES.keys(): - if task_layer_key == "NONE": - continue - col_name = get_task_layer_col_name(task_layer_key) - bpy.data.collections.new(col_name) - new_col = bpy.data.collections.get(col_name) - asset_col.children.link(new_col) - new_col.asset_id_owner = task_layer_key + # for task_layer_key in constants.TASK_LAYER_TYPES.keys(): + # if task_layer_key == "NONE": + # continue + # col_name = get_task_layer_col_name(task_layer_key) + # bpy.data.collections.new(col_name) + # new_col = bpy.data.collections.get(col_name) + # asset_col.children.link(new_col) + # new_col.asset_id_owner = task_layer_key starting_file = "" # Create the file for each task layer. diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index c6daae1c..37b085d9 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -51,10 +51,7 @@ class AssetPipeline(bpy.types.PropertyGroup): temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) - # TODO Rename to Current_Task_Layer - task_layer_name: bpy.props.EnumProperty( - name="Task Layer Name", items=constants.TASK_LAYER_TYPES_ENUM_ITEMS - ) + local_task_layers: bpy.props.StringProperty(name="Local Task Layers", default="") def add_temp_transfer_data(self, name, owner, type, obj): new_transfer_data = self.temp_transfer_data diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index e06a67e8..5fcafc98 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -14,6 +14,7 @@ from .merge.core import ( from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.shared_ids import get_shared_id_icon from . import constants +from .merge.task_layer import get_local_task_layers def sync_poll(cls, context): @@ -35,10 +36,11 @@ def sync_invoke(self, context): if not local_col: self.report({'ERROR'}, "Top level collection could not be found") return {'CANCELLED'} - task_layer_key = context.scene.asset_pipeline.task_layer_name - if task_layer_key == "NONE": - self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") - return {'CANCELLED'} + # TODO Check if file contains a valid task layer + # task_layer_key = context.scene.asset_pipeline.task_layer_name + # if task_layer_key == "NONE": + # self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + # return {'CANCELLED'} ownership_get(local_col, context.scene) @@ -94,10 +96,11 @@ def sync_execute_update_ownership(self, context): def sync_execute_prepare_sync(self, context): self._current_file = Path(bpy.data.filepath) self._temp_dir = Path(bpy.app.tempdir).parent - self._task_layer_key = context.scene.asset_pipeline.task_layer_name - if self._task_layer_key == "NONE": - self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") - return {'CANCELLED'} + self._task_layer_keys = get_local_task_layers() + # TODO Check if file contains a valid task layer + # if self._task_layer_key == "NONE": + # self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") + # return {'CANCELLED'} self._sync_target = find_sync_target(self._current_file) if not self._sync_target.exists(): @@ -115,7 +118,7 @@ def sync_execute_pull(self, context): bpy.ops.wm.save_as_mainfile(filepath=temp_file.__str__(), copy=True) error_msg = merge_task_layer( context, - local_tls=[self._task_layer_key], + local_tls=self._task_layer_keys, external_file=self._sync_target, ) @@ -142,7 +145,7 @@ def sync_execute_push(self, context): local_tls = [ task_layer for task_layer in constants.TASK_LAYER_TYPES.keys() - if task_layer != self._task_layer_key + if task_layer not in self._task_layer_keys ] error_msg = merge_task_layer( diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 3df74610..9f3ab266 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -36,11 +36,12 @@ class ASSETPIPE_sync(bpy.types.Panel): ) layout.operator("assetpipe.publish_new_version", icon="PLUS") - if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE": - asset_pipe = context.scene.asset_pipeline - box = layout.box() - box.label(text="Published File Settings") - box.prop(asset_pipe, "is_depreciated") + # TODO Find new way to determine if we are in a published file more explicitly + # if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE": + # asset_pipe = context.scene.asset_pipeline + # box = layout.box() + # box.label(text="Published File Settings") + # box.prop(asset_pipe, "is_depreciated") class ASSETPIPE_ownership_inspector(bpy.types.Panel): -- 2.30.2 From 317441b9df3ea6a2d49049e29402e88d9c80a880 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 2 Oct 2023 16:54:40 -0400 Subject: [PATCH 286/429] Asset Pipe: Create File with Multiple Task Layers in New File --- .../addons/asset_pipeline_2/ops.py | 99 +++++++++++++------ .../addons/asset_pipeline_2/props.py | 8 ++ 2 files changed, 76 insertions(+), 31 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 72f0cc01..9e9c83b0 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -6,11 +6,9 @@ from .merge.publish import ( get_next_published_file, ) -from .merge.transfer_data.transfer_ui import draw_transfer_data from .images import save_images from . import constants from .sync import ( - sync_poll, sync_invoke, sync_draw, sync_execute_update_ownership, @@ -29,6 +27,10 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): _dir = None _prefix = None + create_files: bpy.props.BoolProperty( + name="Create Files for Unselected Task Layers", default=True + ) + @classmethod def poll(cls, context: bpy.types.Context) -> bool: asset_pipe = context.scene.asset_pipeline @@ -38,35 +40,59 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): if os.path.exists(os.path.join(asset_pipe.dir, asset_pipe.name)): cls.poll_message_set("Asset Folder already exists") return False + if bpy.data.filepath == "": + cls.poll_message_set("Please Save File before creating asset") + return False + # TODO Add warning if file is unsaved like a general startup file return True + def invoke(self, context: bpy.types.Context, event): + # Dynamically Create Task Layer Bools + task_layer_settings = context.scene.asset_pipeline.task_layer_settings + task_layer_settings.clear() + for task_layer_key in constants.TASK_LAYER_TYPES.keys(): + if task_layer_key == "NONE": + continue + transfer_data_item = task_layer_settings.add() + transfer_data_item.name = constants.TASK_LAYER_TYPES[task_layer_key] + transfer_data_item.key = task_layer_key + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + box = self.layout.box() + task_layer_settings = context.scene.asset_pipeline.task_layer_settings + + box.label(text="Choose Which Task Layers will be local the current file") + for task_layer_bool in task_layer_settings: + box.prop(task_layer_bool, "is_local", text=task_layer_bool.name) + self.layout.prop(self, "create_files") + def execute(self, context: bpy.types.Context): # New File is Createed so Props need to be saved asset_pipe = context.scene.asset_pipeline self._name = asset_pipe.name - self._dir = asset_pipe.dir + self._dir = bpy.path.abspath(asset_pipe.dir) self._prefix = asset_pipe.prefix + task_layer_settings = context.scene.asset_pipeline.task_layer_settings + local_tls = [] + for task_layer_bool in task_layer_settings: + if task_layer_bool.is_local: + local_tls.append(task_layer_bool.key) + + if not any(task_layer_bool.is_local for task_layer_bool in task_layer_settings): + self.report( + {'ERROR'}, + "Please select at least one task layer to be local to the current file", + ) + return {'CANCELLED'} + # Create Asset Folder at Directory asset_path = os.path.join(self._dir, self._name) os.mkdir(asset_path) for publish_type in constants.PUBLISH_KEYS: os.mkdir(os.path.join(asset_path, publish_type)) - - # Prepare New File - bpy.ops.wm.read_homefile(app_template="") - - # Remove All Data - for col in bpy.data.collections: - bpy.data.collections.remove(col) - for obj in bpy.data.objects: - bpy.data.objects.remove(obj) - - bpy.ops.outliner.orphans_purge( - do_local_ids=True, do_linked_ids=False, do_recursive=True - ) - # Setup New File asset_pipe = context.scene.asset_pipeline asset_pipe.is_asset_pipeline_file = True @@ -77,28 +103,39 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.name = self._name asset_pipe.prefix = self._prefix - # Create the collections for each task layer. - # for task_layer_key in constants.TASK_LAYER_TYPES.keys(): - # if task_layer_key == "NONE": - # continue - # col_name = get_task_layer_col_name(task_layer_key) - # bpy.data.collections.new(col_name) - # new_col = bpy.data.collections.get(col_name) - # asset_col.children.link(new_col) - # new_col.asset_id_owner = task_layer_key - starting_file = "" + local_tls_string = "" + for task_layer in local_tls: + local_tls_string += task_layer + "," + asset_pipe.local_task_layers = local_tls_string + first_file = os.path.join(asset_path, Path(bpy.data.filepath).name) + bpy.ops.wm.save_as_mainfile(filepath=first_file, copy=True) + starting_file = first_file + + # TODO Support mulitple task layers in same file # Create the file for each task layer. for task_layer_key in constants.TASK_LAYER_TYPES.keys(): - if task_layer_key == "NONE": + if task_layer_key == "NONE" or task_layer_key in local_tls: continue name = ( self._name + "." + constants.TASK_LAYER_TYPES[task_layer_key] + ".blend" ) + + asset_pipe.local_task_layers = task_layer_key + "," + + # Remove Data From Newly Created Files that aren't the user's main file + for col in bpy.data.collections: + if not col == asset_col: + bpy.data.collections.remove(col) + for obj in bpy.data.objects: + bpy.data.objects.remove(obj) + + bpy.ops.outliner.orphans_purge( + do_local_ids=True, do_linked_ids=False, do_recursive=True + ) + task_layer_file = os.path.join(asset_path, name) - asset_pipe.task_layer_name = task_layer_key - if task_layer_key == constants.STARTING_FILE: - starting_file = task_layer_file + asset_pipe.local_task_layers = task_layer_key + "," bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) # Create intial publish based on task layers. diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 37b085d9..02d076ec 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -34,6 +34,11 @@ class AssetTransferDataTemp(bpy.types.PropertyGroup): obj: bpy.props.PointerProperty(type=bpy.types.Object) +class TaskLayerSettings(bpy.types.PropertyGroup): + is_local: bpy.props.BoolProperty(name="Task Layer is Local", default=False) + key: bpy.props.StringProperty(name="Key for Task Layer", default="") + + class AssetPipeline(bpy.types.PropertyGroup): """Properties to manage the status of asset pipeline files""" @@ -74,10 +79,13 @@ class AssetPipeline(bpy.types.PropertyGroup): name="Prefix", description="Prefix for new Asset", default="" ) + task_layer_settings: bpy.props.CollectionProperty(type=TaskLayerSettings) + classes = ( AssetTransferData, AssetTransferDataTemp, + TaskLayerSettings, AssetPipeline, ) -- 2.30.2 From 13c71d38d744fd4575205ddb424ef1561817fbba Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 2 Oct 2023 17:48:32 -0400 Subject: [PATCH 287/429] Asset Pipe: Add Basic Implementation of Default Ownership on New Objects --- .../addons/asset_pipeline_2/constants.py | 15 ++++++++ .../addons/asset_pipeline_2/merge/core.py | 3 ++ .../asset_pipeline_2/merge/task_layer.py | 15 +++++++- .../merge/transfer_data/transfer_core.py | 37 +++++++++++++++---- .../merge/transfer_data/transfer_functions.py | 35 +++++++++--------- .../merge/transfer_data/transfer_util.py | 7 ++-- 6 files changed, 82 insertions(+), 30 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 92ed5b35..3ef88b3a 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -46,6 +46,21 @@ TRANSFER_DATA_TYPES = { PARENT_KEY: ("Parent", 'FILE_PARENT'), } +# Default of which task layer owns each data type +DEFAULT_OWNERSHIP = { + VERTEX_GROUP_KEY: "RIG", + MODIFIER_KEY: "RIG", + CONSTRAINT_KEY: "RIG", + MATERIAL_SLOT_KEY: "SHD", + SHAPE_KEY_KEY: "MOD", + ATTRIBUTE_KEY: "RIG", + PARENT_KEY: "RIG", +} + +DEFAULT_OWNERSHIP_ATTRIBUTES = { + "sharp_face": "MOD", + "UVMap": "SHD", # Hard Coded name of default UVMap +} # Convert it to the format that EnumProperty.items wants: # List of 5-tuples; Re-use name as description at 3rd element, add index at 5th. TRANSFER_DATA_TYPES_ENUM_ITEMS = [ diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 5e72bd41..fa77009b 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -2,6 +2,7 @@ import bpy from .asset_mapping import AssetTransferMapping from .transfer_data.transfer_core import ( init_transfer_data, + init_transfer_data_with_defaults, transfer_data_is_missing, apply_transfer_data, transfer_data_clean, @@ -70,6 +71,8 @@ def ownership_get( if obj.asset_id_owner == "NONE" and obj in task_layer_objs: obj.asset_id_owner = default_task_layer obj.name = get_name_with_asset_prefix(obj.name) + init_transfer_data_with_defaults(scene, obj) + continue # Skip items that have no owner if obj.asset_id_owner == "NONE": continue diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 2084a8fc..c94857c2 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -1,4 +1,5 @@ import bpy +from .. import constants def get_local_task_layers(): @@ -6,5 +7,15 @@ def get_local_task_layers(): return local_task_layer_strings.split(",") -def get_default_task_layer(td_type: str): - return get_local_task_layers()[0] +def get_default_task_layer(td_type: str, name=""): + if td_type == constants.ATTRIBUTE_KEY: + if name in constants.DEFAULT_OWNERSHIP_ATTRIBUTES: + return constants.DEFAULT_OWNERSHIP_ATTRIBUTES[name] + return constants.DEFAULT_OWNERSHIP[td_type] + + +def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): + if use_default_owner: + return get_default_task_layer(td_type_key, name) + else: + return get_local_task_layers()[0] diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index fa40373e..29ea86ae 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -9,6 +9,8 @@ from .transfer_util import ( check_transfer_data_entry, ) +from ..task_layer import get_local_task_layers + def copy_transfer_data_ownership( transfer_data_item, target_obj: bpy.types.Object @@ -75,13 +77,34 @@ def init_transfer_data( task_layer_name (str): Name of task layer temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP """ - transfer_functions.init_vertex_groups(scene, obj) - transfer_functions.init_material_slots(scene, obj) - transfer_functions.init_modifiers(scene, obj) - transfer_functions.init_constraints(scene, obj) - transfer_functions.init_shape_keys(scene, obj) - transfer_functions.init_attributes(scene, obj) - transfer_functions.init_parent(scene, obj) + transfer_functions.init_vertex_groups(scene, obj, False) + transfer_functions.init_material_slots(scene, obj, False) + transfer_functions.init_modifiers(scene, obj, False) + transfer_functions.init_constraints(scene, obj, False) + transfer_functions.init_shape_keys(scene, obj, False) + transfer_functions.init_attributes(scene, obj, False) + transfer_functions.init_parent(scene, obj, False) + + +def init_transfer_data_with_defaults( + scene: bpy.types.Scene, + obj: bpy.types.Object, +): + """Collect Transfer Data Items on a given object with default ownership data, + this can only be run on new objects + + Args: + obj (bpy.types.Object): Target object for transfer data + task_layer_name (str): Name of task layer + temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP + """ + transfer_functions.init_vertex_groups(scene, obj, True) + transfer_functions.init_material_slots(scene, obj, True) + transfer_functions.init_modifiers(scene, obj, True) + transfer_functions.init_constraints(scene, obj, True) + transfer_functions.init_shape_keys(scene, obj, True) + transfer_functions.init_attributes(scene, obj, True) + transfer_functions.init_parent(scene, obj, True) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 539dc1d4..38be5e48 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -10,7 +10,7 @@ from .transfer_util import ( check_transfer_data_entry, ) -from ..task_layer import get_default_task_layer +from ..task_layer import get_transfer_data_owner from ... import constants import mathutils import bmesh @@ -36,12 +36,13 @@ def vertex_group_is_missing(transfer_data_item): ) -def init_vertex_groups(scene, obj): +def init_vertex_groups(scene, obj, use_default_owner: bool): transfer_data_item_init( scene=scene, obj=obj, data_list=obj.vertex_groups, td_type_key=constants.VERTEX_GROUP_KEY, + use_default_owner=use_default_owner, ) @@ -99,10 +100,9 @@ def modifier_is_missing(transfer_data_item): ) -def init_modifiers(scene, obj): +def init_modifiers(scene, obj, use_default_owner: bool): td_type_key = constants.MODIFIER_KEY transfer_data = obj.transfer_data_ownership - task_layer_key = get_default_task_layer(td_type_key) for mod in obj.modifiers: mod.name = get_name_with_task_layer_prefix(mod.name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner @@ -110,7 +110,7 @@ def init_modifiers(scene, obj): if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=mod.name, - owner=task_layer_key, + owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, ) @@ -195,10 +195,9 @@ def constraint_is_missing(transfer_data_item): ) -def init_constraints(scene, obj): +def init_constraints(scene, obj, use_default_owner: bool): td_type_key = constants.CONSTRAINT_KEY transfer_data = obj.transfer_data_ownership - task_layer_key = get_default_task_layer(td_type_key) for const in obj.constraints: const.name = get_name_with_task_layer_prefix(const.name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner @@ -206,7 +205,7 @@ def init_constraints(scene, obj): if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=const.name, - owner=task_layer_key, + owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, ) @@ -287,9 +286,8 @@ def material_slots_is_missing(transfer_data_item): return True -def init_material_slots(scene, obj): +def init_material_slots(scene, obj, use_default_owner: bool): td_type_key = constants.MATERIAL_SLOT_KEY - task_layer_key = get_default_task_layer(td_type_key) name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership @@ -301,7 +299,7 @@ def init_material_slots(scene, obj): if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=name, - owner=task_layer_key, + owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, ) @@ -402,7 +400,7 @@ def shape_key_is_missing(transfer_data_item): ) -def init_shape_keys(scene, obj): +def init_shape_keys(scene, obj, use_default_owner: bool): if obj.type != "MESH" or obj.data.shape_keys is None: return @@ -421,6 +419,7 @@ def init_shape_keys(scene, obj): obj=obj, data_list=obj.data.shape_keys.key_blocks, td_type_key=constants.SHAPE_KEY_KEY, + use_default_owner=use_default_owner, ) @@ -551,19 +550,20 @@ def attribute_is_missing(transfer_data_item): return True -def init_attributes(scene, obj): +def init_attributes(scene, obj, use_default_owner: bool): if obj.type != "MESH": return transfer_data = obj.transfer_data_ownership td_type_key = constants.ATTRIBUTE_KEY - task_layer_key = get_default_task_layer(td_type_key) for atttribute in attributes_get_editable(obj.data.attributes): # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=atttribute.name, - owner=task_layer_key, + owner=get_transfer_data_owner( + td_type_key, use_default_owner, atttribute.name + ), type=td_type_key, obj=obj, ) @@ -621,9 +621,8 @@ def parent_is_missing(transfer_data_item): return True -def init_parent(scene, obj): +def init_parent(scene, obj, use_default_owner: bool): td_type_key = constants.PARENT_KEY - task_layer_key = get_default_task_layer(td_type_key) name = constants.PARENT_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership @@ -635,7 +634,7 @@ def init_parent(scene, obj): if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=name, - owner=task_layer_key, + owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index b4d117e3..83dfef52 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -1,6 +1,6 @@ import bpy from ..naming import get_basename -from ..task_layer import get_default_task_layer +from ..task_layer import get_transfer_data_owner def check_transfer_data_entry( @@ -92,6 +92,7 @@ def transfer_data_item_init( obj: bpy.types.Object, data_list: bpy.types.CollectionProperty, td_type_key: str, + use_default_owner=bool, ): """_summary_ @@ -102,14 +103,14 @@ def transfer_data_item_init( td_type_key (str): Key for the transfer data type """ transfer_data = obj.transfer_data_ownership - default_task_layer = get_default_task_layer(td_type_key) + for item in data_list: # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, item.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=item.name, - owner=default_task_layer, + owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, ) -- 2.30.2 From 4485c17aff75b7ab46370c3efd1fc9266ad8d9c4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 2 Oct 2023 19:28:39 -0400 Subject: [PATCH 288/429] Asset Pipe: Use Default Ownership if it is in Local Task Layers --- .../addons/asset_pipeline_2/merge/task_layer.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index c94857c2..82f46c33 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -4,7 +4,7 @@ from .. import constants def get_local_task_layers(): local_task_layer_strings = bpy.context.scene.asset_pipeline.local_task_layers - return local_task_layer_strings.split(",") + return [string for string in local_task_layer_strings.split(",") if string != ""] def get_default_task_layer(td_type: str, name=""): @@ -15,7 +15,10 @@ def get_default_task_layer(td_type: str, name=""): def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): - if use_default_owner: + if ( + use_default_owner + or get_default_task_layer(td_type_key, name) in get_local_task_layers() + ): return get_default_task_layer(td_type_key, name) else: return get_local_task_layers()[0] -- 2.30.2 From dba57f62d0f87fc703dfa74deeca87fcde105be9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 12:48:13 -0400 Subject: [PATCH 289/429] Asset Pipe: Use Collection Property to Store All/Local Task Layers --- .../asset_pipeline_2/merge/task_layer.py | 4 +- .../addons/asset_pipeline_2/ops.py | 44 +++++++++++-------- .../addons/asset_pipeline_2/props.py | 5 ++- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 82f46c33..ef9a9e65 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -3,8 +3,8 @@ from .. import constants def get_local_task_layers(): - local_task_layer_strings = bpy.context.scene.asset_pipeline.local_task_layers - return [string for string in local_task_layer_strings.split(",") if string != ""] + local_task_layers = bpy.context.scene.asset_pipeline.local_task_layers + return [task_layer.name for task_layer in local_task_layers] def get_default_task_layer(td_type: str, name=""): diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 9e9c83b0..c822e894 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -48,22 +48,21 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): def invoke(self, context: bpy.types.Context, event): # Dynamically Create Task Layer Bools - task_layer_settings = context.scene.asset_pipeline.task_layer_settings - task_layer_settings.clear() + all_task_layers = context.scene.asset_pipeline.all_task_layers + all_task_layers.clear() for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE": continue - transfer_data_item = task_layer_settings.add() - transfer_data_item.name = constants.TASK_LAYER_TYPES[task_layer_key] - transfer_data_item.key = task_layer_key + new_task_layer = all_task_layers.add() + new_task_layer.name = task_layer_key return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context: bpy.types.Context): box = self.layout.box() - task_layer_settings = context.scene.asset_pipeline.task_layer_settings + all_task_layers = context.scene.asset_pipeline.all_task_layers box.label(text="Choose Which Task Layers will be local the current file") - for task_layer_bool in task_layer_settings: + for task_layer_bool in all_task_layers: box.prop(task_layer_bool, "is_local", text=task_layer_bool.name) self.layout.prop(self, "create_files") @@ -74,13 +73,13 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): self._dir = bpy.path.abspath(asset_pipe.dir) self._prefix = asset_pipe.prefix - task_layer_settings = context.scene.asset_pipeline.task_layer_settings + all_task_layers = context.scene.asset_pipeline.all_task_layers local_tls = [] - for task_layer_bool in task_layer_settings: + for task_layer_bool in all_task_layers: if task_layer_bool.is_local: - local_tls.append(task_layer_bool.key) + local_tls.append(task_layer_bool.name) - if not any(task_layer_bool.is_local for task_layer_bool in task_layer_settings): + if not any(task_layer_bool.is_local for task_layer_bool in all_task_layers): self.report( {'ERROR'}, "Please select at least one task layer to be local to the current file", @@ -93,7 +92,8 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): for publish_type in constants.PUBLISH_KEYS: os.mkdir(os.path.join(asset_path, publish_type)) - # Setup New File + + # Setup Base File asset_pipe = context.scene.asset_pipeline asset_pipe.is_asset_pipeline_file = True bpy.data.collections.new(self._name) @@ -104,11 +104,16 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.prefix = self._prefix starting_file = "" - local_tls_string = "" - for task_layer in local_tls: - local_tls_string += task_layer + "," - asset_pipe.local_task_layers = local_tls_string first_file = os.path.join(asset_path, Path(bpy.data.filepath).name) + local_task_layers = context.scene.asset_pipeline.local_task_layers + + # Update Local Task Layers for New File + local_task_layers.clear() + for task_layer in all_task_layers: + if task_layer.is_local == True: + new_local_task_layer = local_task_layers.add() + new_local_task_layer.name = task_layer.name + bpy.ops.wm.save_as_mainfile(filepath=first_file, copy=True) starting_file = first_file @@ -121,8 +126,6 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): self._name + "." + constants.TASK_LAYER_TYPES[task_layer_key] + ".blend" ) - asset_pipe.local_task_layers = task_layer_key + "," - # Remove Data From Newly Created Files that aren't the user's main file for col in bpy.data.collections: if not col == asset_col: @@ -134,8 +137,11 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): do_local_ids=True, do_linked_ids=False, do_recursive=True ) + local_task_layers.clear() + new_local_task_layer = local_task_layers.add() + new_local_task_layer.name = task_layer_key + task_layer_file = os.path.join(asset_path, name) - asset_pipe.local_task_layers = task_layer_key + "," bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) # Create intial publish based on task layers. diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 02d076ec..143e2b3b 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -1,5 +1,6 @@ import bpy from . import constants +from .merge.task_layer import get_local_task_layers """ NOTE Items in these properties groups should be generated by a function that finds the avaliable task layers from the task_layer_defaults.json file that needs to be created. @@ -36,7 +37,6 @@ class AssetTransferDataTemp(bpy.types.PropertyGroup): class TaskLayerSettings(bpy.types.PropertyGroup): is_local: bpy.props.BoolProperty(name="Task Layer is Local", default=False) - key: bpy.props.StringProperty(name="Key for Task Layer", default="") class AssetPipeline(bpy.types.PropertyGroup): @@ -79,7 +79,8 @@ class AssetPipeline(bpy.types.PropertyGroup): name="Prefix", description="Prefix for new Asset", default="" ) - task_layer_settings: bpy.props.CollectionProperty(type=TaskLayerSettings) + all_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) + local_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) classes = ( -- 2.30.2 From d06322e777f526985c1b2b79db3adb4ff4c4eac3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 12:50:35 -0400 Subject: [PATCH 290/429] Asset Pipe: Use Prop Search to Change Local Task Layers on Transfer Data - Written with help from @Mets --- .../merge/transfer_data/transfer_ui.py | 25 +++++++++++++++++-- .../merge/transfer_data/transfer_util.py | 4 +-- .../addons/asset_pipeline_2/props.py | 10 ++------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 9528323d..98d9f179 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -1,5 +1,6 @@ import bpy from ... import constants +from ..task_layer import get_local_task_layers def draw_transfer_data_type( @@ -11,9 +12,29 @@ def draw_transfer_data_type( name, icon = constants.TRANSFER_DATA_TYPES[transfer_data[0].type] box = layout.box() box.label(text=name, icon=icon) + scene = bpy.context.scene for transfer_data_item in transfer_data: - owner_tl_ui_name = constants.TASK_LAYER_TYPES[transfer_data_item.owner] - box.label(text=f"{transfer_data_item.name}: '{owner_tl_ui_name}'") + row = layout.row() + row.label(text=f"{transfer_data_item.name}: ") + if transfer_data_item.owner not in [ + tl.name for tl in scene.asset_pipeline.local_task_layers + ]: + row.enabled = False + row.prop_search( + transfer_data_item, + 'owner', + scene.asset_pipeline, + 'all_task_layers', + text="", + ) + else: + row.prop_search( + transfer_data_item, + 'owner', + scene.asset_pipeline, + 'local_task_layers', + text="", + ) def draw_transfer_data( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index 83dfef52..39ea41d3 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -1,6 +1,6 @@ import bpy from ..naming import get_basename -from ..task_layer import get_transfer_data_owner +from ..task_layer import get_transfer_data_owner, get_local_task_layers def check_transfer_data_entry( @@ -40,7 +40,7 @@ def transfer_data_add_entry( """ transfer_data_item = transfer_data.add() transfer_data_item.name = name - transfer_data_item.owner = task_layer_name.upper() + transfer_data_item.owner = task_layer_name transfer_data_item.type = td_type_key return transfer_data_item diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 143e2b3b..d96739ae 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -10,10 +10,7 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr class AssetTransferData(bpy.types.PropertyGroup): """Properties to track transferable data on an object""" - owner: bpy.props.EnumProperty( - name="Transfer Data Owner", - items=constants.TASK_LAYER_TYPES_ENUM_ITEMS, - ) + owner: bpy.props.StringProperty() type: bpy.props.EnumProperty( name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, @@ -24,10 +21,7 @@ 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.EnumProperty( - name="Transfer Data Owner", - items=constants.TASK_LAYER_TYPES_ENUM_ITEMS, - ) + owner: bpy.props.StringProperty() type: bpy.props.EnumProperty( name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, -- 2.30.2 From 51d9ecc23dd6e5c67f9a0c8268155b48d81c9ce4 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 12:51:09 -0400 Subject: [PATCH 291/429] Asset Pipe: Fix Get Transfer Data Owner --- .../addons/asset_pipeline_2/merge/task_layer.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index ef9a9e65..74d49a38 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -15,10 +15,11 @@ def get_default_task_layer(td_type: str, name=""): def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): - if ( - use_default_owner - or get_default_task_layer(td_type_key, name) in get_local_task_layers() - ): - return get_default_task_layer(td_type_key, name) + default_tl = get_default_task_layer(td_type_key, name) + if use_default_owner: + return default_tl else: - return get_local_task_layers()[0] + if default_tl in get_local_task_layers(): + return default_tl + else: + return get_local_task_layers()[0] -- 2.30.2 From 843b759c7101827343fcb614f3c7b8ef55eb065c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:23:14 -0400 Subject: [PATCH 292/429] Asset Pipe: Remove Task Layer Keys --- .../addons/asset_pipeline_2/constants.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 3ef88b3a..4549ef3a 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -4,15 +4,17 @@ # There is no behaviour that is specific to a particular task layer. # You could even choose to name your task layers after artists in your team. # {Task Layer Key: Collection/UI name} + +# TODO Replace ENUM with List TASK_LAYER_TYPES = { "NONE": "None", - "MOD": "Modeling", - "RIG": "Rigging", - "SHD": "Shading", + "Modeling": "Modeling", + "Rigging": "Rigging", + "Shading": "Shading", } # When creating a new asset, start in this task layer's file. -STARTING_FILE = 'MOD' +STARTING_FILE = 'Modeling' # Convert it to the format that EnumProperty.items wants: # List of 3-tuples, re-use name as description at 3rd element. @@ -48,18 +50,18 @@ TRANSFER_DATA_TYPES = { # Default of which task layer owns each data type DEFAULT_OWNERSHIP = { - VERTEX_GROUP_KEY: "RIG", - MODIFIER_KEY: "RIG", - CONSTRAINT_KEY: "RIG", - MATERIAL_SLOT_KEY: "SHD", - SHAPE_KEY_KEY: "MOD", - ATTRIBUTE_KEY: "RIG", - PARENT_KEY: "RIG", + VERTEX_GROUP_KEY: "Rigging", + MODIFIER_KEY: "Rigging", + CONSTRAINT_KEY: "Rigging", + MATERIAL_SLOT_KEY: "Shading", + SHAPE_KEY_KEY: "Modeling", + ATTRIBUTE_KEY: "Rigging", + PARENT_KEY: "Rigging", } DEFAULT_OWNERSHIP_ATTRIBUTES = { - "sharp_face": "MOD", - "UVMap": "SHD", # Hard Coded name of default UVMap + "sharp_face": "Modeling", + "UVMap": "Shading", # Hard Coded name of default UVMap } # Convert it to the format that EnumProperty.items wants: # List of 5-tuples; Re-use name as description at 3rd element, add index at 5th. -- 2.30.2 From 7c9dcf69124f0827e33b765fc2116732a1ac6a39 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:36:09 -0400 Subject: [PATCH 293/429] Asset Pipe: Create Common Function to Draw Ownership Selection --- .../asset_pipeline_2/merge/task_layer.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 74d49a38..aaf7fd85 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -23,3 +23,29 @@ def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): return default_tl else: return get_local_task_layers()[0] + + +def draw_task_layer_selection( + row, + scene, + data, + data_owner, + data_owner_name, +): + if data_owner not in [tl.name for tl in scene.asset_pipeline.local_task_layers]: + row.enabled = False + row.prop_search( + data, + data_owner_name, + scene.asset_pipeline, + 'all_task_layers', + text="", + ) + else: + row.prop_search( + data, + data_owner_name, + scene.asset_pipeline, + 'local_task_layers', + text="", + ) -- 2.30.2 From 2f838a9b41ae4e42f93613ca8eda4d8162ac89f2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:36:35 -0400 Subject: [PATCH 294/429] Asset Pipe: Use Common Function to Draw Transfer Data Ownership --- .../merge/transfer_data/transfer_ui.py | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 98d9f179..3cf9b2c8 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -1,6 +1,6 @@ import bpy from ... import constants -from ..task_layer import get_local_task_layers +from ..task_layer import draw_task_layer_selection def draw_transfer_data_type( @@ -14,27 +14,11 @@ def draw_transfer_data_type( box.label(text=name, icon=icon) scene = bpy.context.scene for transfer_data_item in transfer_data: - row = layout.row() + row = box.row() row.label(text=f"{transfer_data_item.name}: ") - if transfer_data_item.owner not in [ - tl.name for tl in scene.asset_pipeline.local_task_layers - ]: - row.enabled = False - row.prop_search( - transfer_data_item, - 'owner', - scene.asset_pipeline, - 'all_task_layers', - text="", - ) - else: - row.prop_search( - transfer_data_item, - 'owner', - scene.asset_pipeline, - 'local_task_layers', - text="", - ) + draw_task_layer_selection( + row, scene, transfer_data_item, transfer_data_item.owner, "owner" + ) def draw_transfer_data( -- 2.30.2 From 35f19fd68427990eefb661197ae47652be28562d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:37:09 -0400 Subject: [PATCH 295/429] Asset Pipe: Use Common Function to Draw OBJ and COL ownership --- scripts-blender/addons/asset_pipeline_2/ui.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 9f3ab266..b60a12dc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -2,7 +2,7 @@ import bpy from pathlib import Path from .merge.transfer_data.transfer_ui import draw_transfer_data -from . import constants +from .merge.task_layer import draw_task_layer_selection class ASSETPIPE_sync(bpy.types.Panel): @@ -53,15 +53,21 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout asset_pipe = context.scene.asset_pipeline + scene = context.scene if not asset_pipe.is_asset_pipeline_file: layout.label(text="Open valid 'Asset Pipeline' file", icon="ERROR") return if context.collection in list(asset_pipe.asset_collection.children): - layout.label( - text=f"{context.collection.name} : '{context.collection.asset_id_owner}' (Active Collection)", + col = context.collection + row = layout.row() + row.label( + text=f"{col.name}: ", icon="OUTLINER_COLLECTION", ) + draw_task_layer_selection( + row, scene, col, col.asset_id_owner, "asset_id_owner" + ) if not context.active_object: layout.label(text="Set an Active Object to Inspect", icon="OBJECT_DATA") @@ -69,8 +75,9 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): obj = context.active_object transfer_data = obj.transfer_data_ownership layout = layout.box() - owner_tl_ui_name = constants.TASK_LAYER_TYPES[obj.asset_id_owner] - layout.label(text=f"{obj.name}: '{owner_tl_ui_name}'", icon="OBJECT_DATA") + row = layout.row() + row.label(text=f"{obj.name}: ", icon="OBJECT_DATA") + draw_task_layer_selection(row, scene, obj, obj.asset_id_owner, "asset_id_owner") draw_transfer_data(transfer_data, layout) -- 2.30.2 From 0f1859dcd9599409b2eb5e6617c373a1479c9e10 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:37:49 -0400 Subject: [PATCH 296/429] Asset Pipe: Clean-up Ownership Reference in Ops --- scripts-blender/addons/asset_pipeline_2/ops.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index c822e894..3a89e95f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -122,9 +122,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): for task_layer_key in constants.TASK_LAYER_TYPES.keys(): if task_layer_key == "NONE" or task_layer_key in local_tls: continue - name = ( - self._name + "." + constants.TASK_LAYER_TYPES[task_layer_key] + ".blend" - ) + name = self._name + "." + task_layer_key + ".blend" # Remove Data From Newly Created Files that aren't the user's main file for col in bpy.data.collections: -- 2.30.2 From 27af5a98148c848e0e90d4d199eeb02e056d8625 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:38:08 -0400 Subject: [PATCH 297/429] Asset Pipe: Add Default Value to All Ownership --- scripts-blender/addons/asset_pipeline_2/props.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index d96739ae..4d5080eb 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -10,7 +10,7 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr class AssetTransferData(bpy.types.PropertyGroup): """Properties to track transferable data on an object""" - owner: bpy.props.StringProperty() + owner: bpy.props.StringProperty(name="Owner", default="NONE") type: bpy.props.EnumProperty( name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, @@ -21,7 +21,7 @@ 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() + owner: bpy.props.StringProperty(name="Owner", default="NONE") type: bpy.props.EnumProperty( name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, @@ -92,10 +92,7 @@ def register(): type=AssetTransferData ) bpy.types.Scene.asset_pipeline = bpy.props.PointerProperty(type=AssetPipeline) - bpy.types.ID.asset_id_owner = bpy.props.EnumProperty( - name="ID Owner", - items=constants.TASK_LAYER_TYPES_ENUM_ITEMS, - ) + bpy.types.ID.asset_id_owner = bpy.props.StringProperty(name="Owner", default="NONE") def unregister(): -- 2.30.2 From 9525dfac8b1d372998ab82dc17e6522a3b599001 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:43:00 -0400 Subject: [PATCH 298/429] Asset Pipe: Fix Bug "Objects in new Collections are invalid" --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index fa77009b..de9da881 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -64,8 +64,15 @@ def ownership_get( asset_pipe = scene.asset_pipeline asset_pipe.temp_transfer_data.clear() # TODO Figure out default in this case + default_task_layer = get_local_task_layers()[0] + + for col in asset_pipe.asset_collection.children: + if col.asset_id_owner == "NONE": + col.asset_id_owner = default_task_layer + task_layer_objs = get_task_layer_objects() + for obj in local_col.all_objects: # Mark Asset ID Owner for objects in the current task layers collection if obj.asset_id_owner == "NONE" and obj in task_layer_objs: @@ -78,9 +85,6 @@ def ownership_get( continue ownership_transfer_data_cleanup(obj) init_transfer_data(scene, obj) - for col in asset_pipe.asset_collection.children: - if col.asset_id_owner == "NONE": - col.asset_id_owner = default_task_layer def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: -- 2.30.2 From 5bc955a6364a2af4ebdf96a25db58fa9bde10f1c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:50:18 -0400 Subject: [PATCH 299/429] Asset Pipe: Update Test Files --- .../asset_pipeline_2/testing/resources/Modelling_Import.py | 7 +++++-- .../asset_pipeline_2/testing/resources/Rigging_Import.py | 6 ++++-- .../asset_pipeline_2/testing/resources/Shading_Import.py | 5 ++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py index 4fda3084..467e76d2 100644 --- a/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py +++ b/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py @@ -56,8 +56,7 @@ def import_data_from_lib( ## EXECUTION -task_layer_name = Path(bpy.data.filepath).name.split(".")[1] -task_layer_col = bpy.data.collections[task_layer_name] +task_layer_name = "Modeling" external_file = ( Path(bpy.data.filepath) .parent.parent.parent.joinpath("resources") @@ -66,8 +65,12 @@ external_file = ( appended_col = import_data_from_lib( external_file, "collections", f"sky.{task_layer_name.lower()}" ) +asset_collection = bpy.context.scene.asset_pipeline.asset_collection bpy.context.scene.collection.children.link(appended_col) +task_layer_col = bpy.data.collections.new(task_layer_name) +asset_collection.children.link(task_layer_col) + for obj in appended_col.objects: task_layer_col.objects.link(obj) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py index 4a675b67..20a6cfd1 100644 --- a/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py +++ b/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py @@ -114,8 +114,10 @@ def transfer_vertex_groups(context, target_obj, source_obj): ## EXECUTION -task_layer_name = Path(bpy.data.filepath).name.split(".")[1] -task_layer_col = bpy.data.collections[task_layer_name] +task_layer_name = "Rigging" +task_layer_col = bpy.data.collections.new(task_layer_name) +asset_collection = bpy.context.scene.asset_pipeline.asset_collection +asset_collection.children.link(task_layer_col) external_file = ( Path(bpy.data.filepath) .parent.parent.parent.joinpath("resources") diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py index 4e447e09..8ff90490 100644 --- a/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py +++ b/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py @@ -95,9 +95,8 @@ def transfer_attribute( setattr(target_data, key, getattr(source_data, key)) -## EXECUTION -task_layer_name = Path(bpy.data.filepath).name.split(".")[1] -task_layer_col = bpy.data.collections[task_layer_name] +## EXECUTION" +task_layer_name = "Shading" external_file = ( Path(bpy.data.filepath) .parent.parent.parent.joinpath("resources") -- 2.30.2 From 494e3b600ed1dc52bd1fe5b5f69cafdf1277ea22 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 13:55:39 -0400 Subject: [PATCH 300/429] Asset Pipe: Replace Task Layer Enum with List --- .../addons/asset_pipeline_2/constants.py | 20 +++++-------------- .../asset_pipeline_2/merge/asset_mapping.py | 6 ------ .../addons/asset_pipeline_2/merge/naming.py | 2 +- .../addons/asset_pipeline_2/ops.py | 4 ++-- .../addons/asset_pipeline_2/sync.py | 2 +- 5 files changed, 9 insertions(+), 25 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 4549ef3a..16a291f1 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -5,23 +5,13 @@ # You could even choose to name your task layers after artists in your team. # {Task Layer Key: Collection/UI name} -# TODO Replace ENUM with List -TASK_LAYER_TYPES = { - "NONE": "None", - "Modeling": "Modeling", - "Rigging": "Rigging", - "Shading": "Shading", -} - -# When creating a new asset, start in this task layer's file. -STARTING_FILE = 'Modeling' - -# Convert it to the format that EnumProperty.items wants: -# List of 3-tuples, re-use name as description at 3rd element. -TASK_LAYER_TYPES_ENUM_ITEMS = [ - (key, value, value) for key, value in TASK_LAYER_TYPES.items() +TASK_LAYER_TYPES = [ + "Modeling", + "Rigging", + "Shading", ] + NONE_KEY = "NONE" VERTEX_GROUP_KEY = "GROUP_VERTEX" VERTEX_COLOR_KEY = "COLOR_ATTRIBUTE" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 4f61fe2d..ba7c5f24 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -99,12 +99,6 @@ class AssetTransferMapping: """ coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - local_tl_names = [ - get_name_with_asset_prefix(tl_ui_name) - for tl_key, tl_ui_name in constants.TASK_LAYER_TYPES.items() - if tl_key in self._local_tls - ] - for local_task_layer_col in self._local_top_col.children: if local_task_layer_col.asset_id_owner not in self._local_tls: # Replace source object suffix with target suffix to get target object. diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 2005c85d..2d9d70f0 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -147,7 +147,7 @@ def get_name_with_task_layer_prefix(name: str, td_type_key: str) -> str: str: Returns name with prefix """ prefix = get_default_task_layer(td_type_key) - for task_layer_key in constants.TASK_LAYER_TYPES.keys(): + for task_layer_key in constants.TASK_LAYER_TYPES: if name.startswith(task_layer_key + "."): return name return prefix + "." + name diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 3a89e95f..d89fa0c7 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -50,7 +50,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): # Dynamically Create Task Layer Bools all_task_layers = context.scene.asset_pipeline.all_task_layers all_task_layers.clear() - for task_layer_key in constants.TASK_LAYER_TYPES.keys(): + for task_layer_key in constants.TASK_LAYER_TYPES: if task_layer_key == "NONE": continue new_task_layer = all_task_layers.add() @@ -119,7 +119,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): # TODO Support mulitple task layers in same file # Create the file for each task layer. - for task_layer_key in constants.TASK_LAYER_TYPES.keys(): + for task_layer_key in constants.TASK_LAYER_TYPES: if task_layer_key == "NONE" or task_layer_key in local_tls: continue name = self._name + "." + task_layer_key + ".blend" diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 5fcafc98..c5766698 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -144,7 +144,7 @@ def sync_execute_push(self, context): local_tls = [ task_layer - for task_layer in constants.TASK_LAYER_TYPES.keys() + for task_layer in constants.TASK_LAYER_TYPES if task_layer not in self._task_layer_keys ] -- 2.30.2 From 3c3c04e5970f088e4cf6a22ab3da278666f439d9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 16:51:24 -0400 Subject: [PATCH 301/429] Asset Pipe: Create Default Task Layer Files --- .../task_layer_configs/Character.json | 20 +++++++++++++++++++ .../task_layer_configs/Set.json | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json create mode 100644 scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json new file mode 100644 index 00000000..49c43237 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json @@ -0,0 +1,20 @@ +{ + "TASK_LAYER_TYPES": [ + "Modeling", + "Rigging", + "Shading" + ], + "DEFAULT_OWNERSHIP": { + "GROUP_VERTEX": "Rigging", + "MODIFIER": "Rigging", + "CONSTRAINT": "Rigging", + "MATERIAL": "Shading", + "SHAPE_KEY": "Modeling", + "ATTRIBUTE": "Rigging", + "PARENT": "Rigging" + }, + "DEFAULT_OWNERSHIP_ATTRIBUTES": { + "sharp_face": "Modeling", + "UVMap": "Shading" + } +} \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json new file mode 100644 index 00000000..313c2bce --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json @@ -0,0 +1,19 @@ +{ + "TASK_LAYER_TYPES": [ + "Modeling", + "Shading" + ], + "DEFAULT_OWNERSHIP": { + "GROUP_VERTEX": "Modeling", + "MODIFIER": "Modeling", + "CONSTRAINT": "Modeling", + "MATERIAL": "Shading", + "SHAPE_KEY": "Modeling", + "ATTRIBUTE": "Modeling", + "PARENT": "Modeling" + }, + "DEFAULT_OWNERSHIP_ATTRIBUTES": { + "sharp_face": "Modeling", + "UVMap": "Shading" + } +} \ No newline at end of file -- 2.30.2 From c6518a9246b8148f5fd5ad6c31191453e9c5d158 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 16:53:19 -0400 Subject: [PATCH 302/429] Asset Pipe: Load Task Layers from JSON File --- .../addons/asset_pipeline_2/config.py | 49 +++++++++++++++++++ .../addons/asset_pipeline_2/constants.py | 25 +--------- .../addons/asset_pipeline_2/merge/naming.py | 4 +- .../asset_pipeline_2/merge/task_layer.py | 7 +-- .../addons/asset_pipeline_2/sync.py | 3 +- scripts-blender/addons/asset_pipeline_2/ui.py | 7 +++ 6 files changed, 66 insertions(+), 29 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/config.py diff --git a/scripts-blender/addons/asset_pipeline_2/config.py b/scripts-blender/addons/asset_pipeline_2/config.py new file mode 100644 index 00000000..ab2e1cf9 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/config.py @@ -0,0 +1,49 @@ +import bpy +from pathlib import Path +import json +from . import constants + +TASK_LAYER_TYPES = [] +DEFAULT_OWNERSHIP = {} +DEFAULT_OWNERSHIP_ATTRIBUTES = {} + + +def get_json_file(): + directory = Path(bpy.data.filepath).parent + json_file_path = directory.joinpath(constants.TASK_LAYER_CONFIG_NAME) + if json_file_path.exists(): + return json_file_path + return + + +def get_task_layer_presets_path(): + return Path(__file__).parent.joinpath(constants.TASK_LAYER_CONFIG_DIR_NAME) + + +def verify_json_data(json_file_path=""): + global TASK_LAYER_TYPES + global DEFAULT_OWNERSHIP + global DEFAULT_OWNERSHIP_ATTRIBUTES + directory = Path(bpy.data.filepath).parent + if json_file_path == "": + json_file_path = directory.joinpath(constants.TASK_LAYER_CONFIG_NAME) + if not json_file_path.exists(): + return + json_file = open(json_file_path) + json_content = json.load(json_file) + try: + TASK_LAYER_TYPES = json_content["TASK_LAYER_TYPES"] + DEFAULT_OWNERSHIP = json_content["DEFAULT_OWNERSHIP"] + DEFAULT_OWNERSHIP_ATTRIBUTES = json_content["DEFAULT_OWNERSHIP_ATTRIBUTES"] + return True + except KeyError: + return + + +def write_json_file(asset_path: Path, source_file_path: Path): + json_file_path = asset_path.joinpath(constants.TASK_LAYER_CONFIG_NAME) + json_file = open(source_file_path) + json_content = json.load(json_file) + json_dump = json.dumps(json_content, indent=4) + with open(json_file_path, "w") as config_output: + config_output.write(json_dump) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 16a291f1..7683ed72 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -1,16 +1,10 @@ -# TODO Tie this into props and generate based on JSON file instead - # Information about the list of task layers. # There is no behaviour that is specific to a particular task layer. # You could even choose to name your task layers after artists in your team. # {Task Layer Key: Collection/UI name} -TASK_LAYER_TYPES = [ - "Modeling", - "Rigging", - "Shading", -] - +TASK_LAYER_CONFIG_NAME = "task_layers.json" +TASK_LAYER_CONFIG_DIR_NAME = "task_layer_configs" NONE_KEY = "NONE" VERTEX_GROUP_KEY = "GROUP_VERTEX" @@ -38,21 +32,6 @@ TRANSFER_DATA_TYPES = { PARENT_KEY: ("Parent", 'FILE_PARENT'), } -# Default of which task layer owns each data type -DEFAULT_OWNERSHIP = { - VERTEX_GROUP_KEY: "Rigging", - MODIFIER_KEY: "Rigging", - CONSTRAINT_KEY: "Rigging", - MATERIAL_SLOT_KEY: "Shading", - SHAPE_KEY_KEY: "Modeling", - ATTRIBUTE_KEY: "Rigging", - PARENT_KEY: "Rigging", -} - -DEFAULT_OWNERSHIP_ATTRIBUTES = { - "sharp_face": "Modeling", - "UVMap": "Shading", # Hard Coded name of default UVMap -} # Convert it to the format that EnumProperty.items wants: # List of 5-tuples; Re-use name as description at 3rd element, add index at 5th. TRANSFER_DATA_TYPES_ENUM_ITEMS = [ diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 2d9d70f0..00803886 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -21,7 +21,7 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from .util import get_storage_of_id -from .. import constants +from .. import constants, config from .task_layer import get_default_task_layer DELIMITER = "." @@ -147,7 +147,7 @@ def get_name_with_task_layer_prefix(name: str, td_type_key: str) -> str: str: Returns name with prefix """ prefix = get_default_task_layer(td_type_key) - for task_layer_key in constants.TASK_LAYER_TYPES: + for task_layer_key in config.TASK_LAYER_TYPES: if name.startswith(task_layer_key + "."): return name return prefix + "." + name diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index aaf7fd85..4d57a886 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -1,5 +1,6 @@ import bpy from .. import constants +from .. import config def get_local_task_layers(): @@ -9,9 +10,9 @@ def get_local_task_layers(): def get_default_task_layer(td_type: str, name=""): if td_type == constants.ATTRIBUTE_KEY: - if name in constants.DEFAULT_OWNERSHIP_ATTRIBUTES: - return constants.DEFAULT_OWNERSHIP_ATTRIBUTES[name] - return constants.DEFAULT_OWNERSHIP[td_type] + if name in config.DEFAULT_OWNERSHIP_ATTRIBUTES: + return config.DEFAULT_OWNERSHIP_ATTRIBUTES[name] + return config.DEFAULT_OWNERSHIP[td_type] def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index c5766698..6de55068 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -14,6 +14,7 @@ from .merge.core import ( from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.shared_ids import get_shared_id_icon from . import constants +from . import config from .merge.task_layer import get_local_task_layers @@ -144,7 +145,7 @@ def sync_execute_push(self, context): local_tls = [ task_layer - for task_layer in constants.TASK_LAYER_TYPES + for task_layer in config.TASK_LAYER_TYPES if task_layer not in self._task_layer_keys ] diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index b60a12dc..7247ba10 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -3,6 +3,7 @@ import bpy from pathlib import Path from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.task_layer import draw_task_layer_selection +from .config import verify_json_data class ASSETPIPE_sync(bpy.types.Panel): @@ -18,6 +19,7 @@ class ASSETPIPE_sync(bpy.types.Panel): layout.prop(asset_pipe, "dir") layout.prop(asset_pipe, "name") layout.prop(asset_pipe, "prefix") + layout.prop(asset_pipe, "task_layer_config_type") layout.operator("assetpipe.create_new_asset") # layout.operator("") return @@ -26,6 +28,11 @@ class ASSETPIPE_sync(bpy.types.Panel): layout.label(text="File is not saved", icon="ERROR") return + # TODO Move this call out of the UI because we keep re-loading this file every draw + if not verify_json_data(): + layout.label(text="Task Layer Config is invalid", icon="ERROR") + return + layout.prop(asset_pipe, "asset_collection", text="Asset") layout.label(text="Test UI") -- 2.30.2 From 1af6a98a3e720c062ede3cf3a689a4c6637d07c2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 16:53:57 -0400 Subject: [PATCH 303/429] Asset Pipe: Allow User to Add Custom Task Layers --- .../addons/asset_pipeline_2/__init__.py | 6 +++- .../addons/asset_pipeline_2/ops.py | 21 +++++++++++--- .../addons/asset_pipeline_2/prefs.py | 28 +++++++++++++++++++ .../addons/asset_pipeline_2/props.py | 22 +++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 scripts-blender/addons/asset_pipeline_2/prefs.py diff --git a/scripts-blender/addons/asset_pipeline_2/__init__.py b/scripts-blender/addons/asset_pipeline_2/__init__.py index a9663b89..99decd0c 100644 --- a/scripts-blender/addons/asset_pipeline_2/__init__.py +++ b/scripts-blender/addons/asset_pipeline_2/__init__.py @@ -1,6 +1,6 @@ import importlib -from . import ui, ops, props +from . import ui, ops, props, prefs bl_info = { "name": "Asset Pipeline 2", @@ -20,9 +20,11 @@ def reload() -> None: global ui global ops global props + global prefs importlib.reload(ui) importlib.reload(ops) importlib.reload(props) + importlib.reload(prefs) _need_reload = "ui" in locals() @@ -36,9 +38,11 @@ def register() -> None: ui.register() ops.register() props.register() + prefs.register() def unregister() -> None: ui.unregister() ops.unregister() props.unregister() + prefs.unregister() diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index d89fa0c7..6d27af47 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -1,5 +1,5 @@ import bpy - +from . import config import os from pathlib import Path from .merge.publish import ( @@ -26,6 +26,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): _name = None _dir = None _prefix = None + _json_path = None create_files: bpy.props.BoolProperty( name="Create Files for Unselected Task Layers", default=True @@ -48,9 +49,15 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): def invoke(self, context: bpy.types.Context, event): # Dynamically Create Task Layer Bools - all_task_layers = context.scene.asset_pipeline.all_task_layers + asset_pipe = context.scene.asset_pipeline + + # TODO Check if this fails + config.verify_json_data(Path(asset_pipe.task_layer_config_type)) + + all_task_layers = asset_pipe.all_task_layers all_task_layers.clear() - for task_layer_key in constants.TASK_LAYER_TYPES: + + for task_layer_key in config.TASK_LAYER_TYPES: if task_layer_key == "NONE": continue new_task_layer = all_task_layers.add() @@ -93,6 +100,12 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): for publish_type in constants.PUBLISH_KEYS: os.mkdir(os.path.join(asset_path, publish_type)) + # TODO Save Task Layer Config File + config.write_json_file( + asset_path=Path(asset_path), + source_file_path=Path(asset_pipe.task_layer_config_type), + ) + # Setup Base File asset_pipe = context.scene.asset_pipeline asset_pipe.is_asset_pipeline_file = True @@ -119,7 +132,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): # TODO Support mulitple task layers in same file # Create the file for each task layer. - for task_layer_key in constants.TASK_LAYER_TYPES: + for task_layer_key in config.TASK_LAYER_TYPES: if task_layer_key == "NONE" or task_layer_key in local_tls: continue name = self._name + "." + task_layer_key + ".blend" diff --git a/scripts-blender/addons/asset_pipeline_2/prefs.py b/scripts-blender/addons/asset_pipeline_2/prefs.py new file mode 100644 index 00000000..0a47a866 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/prefs.py @@ -0,0 +1,28 @@ +import bpy + + +class ASSET_PIPELINE_addon_preferences(bpy.types.AddonPreferences): + bl_idname = __package__ + + custom_task_layers_dir: bpy.props.StringProperty( # type: ignore + name="Custom Task Layers", + description="TODO", # TODO Add Description + default="", + subtype="DIR_PATH", + ) + + def draw(self, context): + self.layout.prop(self, "custom_task_layers_dir") + + +classes = (ASSET_PIPELINE_addon_preferences,) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + +def unregister(): + for cls in reversed(classes): + bpy.utils.unregister_class(cls) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 4d5080eb..59cf3a57 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -1,12 +1,29 @@ import bpy from . import constants from .merge.task_layer import get_local_task_layers +from .config import get_task_layer_presets_path +from pathlib import Path """ NOTE Items in these properties groups should be generated by a function that finds the avaliable task layers from the task_layer_defaults.json file that needs to be created. """ +def get_task_layer_presets(self, context): + prefs = context.preferences.addons["asset_pipeline_2"].preferences + user_tls = Path(prefs.custom_task_layers_dir) + + presets_dir = 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""" @@ -73,6 +90,11 @@ class AssetPipeline(bpy.types.PropertyGroup): name="Prefix", description="Prefix for new Asset", default="" ) + task_layer_config_type: bpy.props.EnumProperty( + name="Task Layer Preset", + items=get_task_layer_presets, + ) + all_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) local_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) -- 2.30.2 From 7eb2e9b332c908124308d65229d6db5e547cf64c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 3 Oct 2023 17:20:15 -0400 Subject: [PATCH 304/429] Asset Pipe: Add UI to show Local Task Layers --- scripts-blender/addons/asset_pipeline_2/ui.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 7247ba10..b2a76aab 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -33,8 +33,13 @@ class ASSETPIPE_sync(bpy.types.Panel): layout.label(text="Task Layer Config is invalid", icon="ERROR") return + layout.label(text="Local Task Layers:") + box = layout.box() + row = box.row(align=True) + for task_layer in asset_pipe.local_task_layers: + row.label(text=task_layer.name) + layout.prop(asset_pipe, "asset_collection", text="Asset") - layout.label(text="Test UI") layout.operator("assetpipe.update_ownership", text="Update Ownership") layout.operator("assetpipe.sync_push", text="Push to Publish", icon="TRIA_UP") -- 2.30.2 From d54227489ecf7abed4963a2cf80b646d4c357c0a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 17 Oct 2023 20:58:47 -0400 Subject: [PATCH 305/429] Asset Pipe: Always assign material ownershup even if no material exists --- .../merge/transfer_data/transfer_core.py | 4 ++-- .../merge/transfer_data/transfer_functions.py | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 29ea86ae..c6e072f9 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -40,7 +40,7 @@ def transfer_data_clean(obj): transfer_functions.vertex_groups_clean(obj) transfer_functions.modifiers_clean(obj) transfer_functions.constraints_clean(obj) - transfer_functions.material_slots_clean(obj) + # transfer_functions.material_slots_clean(obj) transfer_functions.shape_keys_clean(obj) transfer_functions.attribute_clean(obj) transfer_functions.parent_clean(obj) @@ -58,7 +58,7 @@ def transfer_data_is_missing(transfer_data_item) -> bool: return bool( transfer_functions.vertex_group_is_missing(transfer_data_item) or transfer_functions.modifier_is_missing(transfer_data_item) - or transfer_functions.material_slots_is_missing(transfer_data_item) + # or transfer_functions.material_slots_is_missing(transfer_data_item) or transfer_functions.constraint_is_missing(transfer_data_item) or transfer_functions.shape_key_is_missing(transfer_data_item) or transfer_functions.attribute_is_missing(transfer_data_item) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 38be5e48..4f515e69 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -291,8 +291,19 @@ def init_material_slots(scene, obj, use_default_owner: bool): name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership + material_objects = [ + 'ARMATURE', + 'CURVE', + 'GPENCIL', + 'META', + 'MESH', + 'SURFACE', + 'FONT', + 'VOLUME', + ] + # Only Execute if Material Slots exist on object - if len(obj.material_slots) == 0: + if obj.type not in material_objects: return matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner -- 2.30.2 From c392b533fbe5e19fbcdcbf5b54373f63bc5e10c5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 18 Oct 2023 19:54:30 -0400 Subject: [PATCH 306/429] Asset Pipe: Fix Bug Naming New Published Files --- scripts-blender/addons/asset_pipeline_2/merge/publish.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/publish.py b/scripts-blender/addons/asset_pipeline_2/merge/publish.py index 4cfabffc..24165892 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/publish.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/publish.py @@ -1,5 +1,6 @@ from pathlib import Path from .. import constants +import bpy def find_file_version(published_file: Path) -> int: @@ -28,7 +29,7 @@ def get_next_published_file( Path: Path where the next published file should be saved to, path doesn't exist yet """ """""" last_publish = find_latest_publish(current_file, publish_type) - base_name = current_file.name.split(".")[0] + base_name = bpy.context.scene.asset_pipeline.name publish_dir = current_file.parent.joinpath(publish_type) if not last_publish: new_version_number = 1 -- 2.30.2 From cf868e5c5ac5f070d84e2c7997d36011c1140719 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 18 Oct 2023 20:18:42 -0400 Subject: [PATCH 307/429] Asset Pipe: Mark Latest Asset as Publish --- .../addons/asset_pipeline_2/ops.py | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 6d27af47..7cc558c1 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,10 +2,7 @@ import bpy from . import config import os from pathlib import Path -from .merge.publish import ( - get_next_published_file, -) - +from .merge.publish import get_next_published_file, find_all_published from .images import save_images from . import constants from .sync import ( @@ -159,6 +156,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.task_layer_name = "NONE" publish_path = os.path.join(asset_path, constants.ACTIVE_PUBLISH_KEY) name = self._name + "." + "v001" + ".blend" + asset_pipe.asset_collection.asset_mark() publish_file = os.path.join(publish_path, name) bpy.ops.wm.save_as_mainfile(filepath=publish_file, copy=True) if starting_file: @@ -316,9 +314,33 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): "Please save the current file and/or Pull from last publish before creating new Publish", ) return {'CANCELLED'} + current_file = Path(bpy.data.filepath) + + if self.publish_types == constants.ACTIVE_PUBLISH_KEY: + context.scene.asset_pipeline.asset_collection.asset_mark() + + push_targets = find_all_published( + Path(bpy.data.filepath), constants.ACTIVE_PUBLISH_KEY + ) + + for file in push_targets: + file_path = Path(file.__str__()) + + bpy.ops.wm.open_mainfile(filepath=file_path.__str__()) + + # Clear old Assets + context.scene.asset_pipeline.asset_collection.asset_clear() + bpy.ops.wm.save_as_mainfile(filepath=file_path.__str__()) + + # Re-open Current File to use as source for Publish + bpy.ops.wm.open_mainfile(filepath=current_file.__str__()) new_file_path = get_next_published_file(current_file, self.publish_types) + + # Save Latest Publish File & Mark as Asset + context.scene.asset_pipeline.asset_collection.asset_mark() bpy.ops.wm.save_as_mainfile(filepath=new_file_path.__str__(), copy=True) + context.scene.asset_pipeline.asset_collection.asset_clear() return {'FINISHED'} -- 2.30.2 From a41f8523f7387c7dc01e1f0c19b2091c4e402bc9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 19 Oct 2023 11:56:28 -0400 Subject: [PATCH 308/429] Docs: Remove Armature from Material Ownership Types --- .../asset_pipeline_2/merge/transfer_data/transfer_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 4f515e69..65d65ac9 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -292,7 +292,6 @@ def init_material_slots(scene, obj, use_default_owner: bool): transfer_data = obj.transfer_data_ownership material_objects = [ - 'ARMATURE', 'CURVE', 'GPENCIL', 'META', -- 2.30.2 From 3d20440a37f9bb3a16a5f264f52713c12f53bbc7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 10:41:47 -0500 Subject: [PATCH 309/429] Asset Pipe: Include Task Layer Prefix in JSON Files --- scripts-blender/addons/asset_pipeline_2/config.py | 2 +- .../asset_pipeline_2/task_layer_configs/Character.json | 10 +++++----- .../asset_pipeline_2/task_layer_configs/Set.json | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/config.py b/scripts-blender/addons/asset_pipeline_2/config.py index ab2e1cf9..119835e7 100644 --- a/scripts-blender/addons/asset_pipeline_2/config.py +++ b/scripts-blender/addons/asset_pipeline_2/config.py @@ -3,7 +3,7 @@ from pathlib import Path import json from . import constants -TASK_LAYER_TYPES = [] +TASK_LAYER_TYPES = {} DEFAULT_OWNERSHIP = {} DEFAULT_OWNERSHIP_ATTRIBUTES = {} diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json index 49c43237..74e70542 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json @@ -1,9 +1,9 @@ { - "TASK_LAYER_TYPES": [ - "Modeling", - "Rigging", - "Shading" - ], + "TASK_LAYER_TYPES": { + "Modeling":"MOD", + "Rigging":"RIG", + "Shading":"SHD" + }, "DEFAULT_OWNERSHIP": { "GROUP_VERTEX": "Rigging", "MODIFIER": "Rigging", diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json index 313c2bce..8c755dad 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json @@ -1,8 +1,8 @@ { - "TASK_LAYER_TYPES": [ - "Modeling", - "Shading" - ], + "TASK_LAYER_TYPES": { + "Modeling":"MOD", + "Shading":"SHD" + }, "DEFAULT_OWNERSHIP": { "GROUP_VERTEX": "Modeling", "MODIFIER": "Modeling", -- 2.30.2 From e9c8523ca673385e1fe890831d75ffc559e41cf1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 10:42:36 -0500 Subject: [PATCH 310/429] Asaset Pipe: Use Task Layer Prefix in naming modifiers and constraints --- scripts-blender/addons/asset_pipeline_2/merge/naming.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 00803886..2418f58d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -146,10 +146,12 @@ def get_name_with_task_layer_prefix(name: str, td_type_key: str) -> str: Returns: str: Returns name with prefix """ - prefix = get_default_task_layer(td_type_key) + default_task_layer = get_default_task_layer(td_type_key) + for task_layer_key in config.TASK_LAYER_TYPES: - if name.startswith(task_layer_key + "."): + if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): return name + prefix = config.TASK_LAYER_TYPES[default_task_layer] return prefix + "." + name -- 2.30.2 From 1fb2486f565981cf7d85862e3e5570e0760de5ed Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 10:45:04 -0500 Subject: [PATCH 311/429] Rename Material Slot to Materials --- scripts-blender/addons/asset_pipeline_2/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 7683ed72..9d23f999 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -25,7 +25,7 @@ TRANSFER_DATA_TYPES = { VERTEX_COLOR_KEY: ("Color Attribute", 'GROUP_VCOL'), MODIFIER_KEY: ("Modifier", 'MODIFIER'), CONSTRAINT_KEY: ("Constraint", 'CONSTRAINT'), - MATERIAL_SLOT_KEY: ("Material Slot", 'MATERIAL'), + MATERIAL_SLOT_KEY: ("Materials", 'MATERIAL'), UV_LAYERS_KEY: ("UV Maps", 'GROUP_UVS'), SHAPE_KEY_KEY: ("Shape Key", 'SHAPEKEY_DATA'), ATTRIBUTE_KEY: ("Attribute", 'EVENT_A'), @@ -39,7 +39,7 @@ TRANSFER_DATA_TYPES_ENUM_ITEMS = [ for i, tup in enumerate(TRANSFER_DATA_TYPES.items()) ] -MATERIAL_TRANSFER_DATA_ITEM_NAME = "All Material Slots" +MATERIAL_TRANSFER_DATA_ITEM_NAME = "All Materials" PARENT_TRANSFER_DATA_ITEM_NAME = "Parent Relationship" PUBLISH_TYPES = [ -- 2.30.2 From 86dad8dc7e1ca22b8be8e8595b269a4a2cdd81ac Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 11:04:22 -0500 Subject: [PATCH 312/429] Asset Pipe: Rename all Transfer Data names to Plural --- .../addons/asset_pipeline_2/constants.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 9d23f999..b833fa20 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -21,14 +21,14 @@ PARENT_KEY = "PARENT" # {Key string : ("UI Name", 'ICON')} TRANSFER_DATA_TYPES = { NONE_KEY: ("None", "BLANK1"), - VERTEX_GROUP_KEY: ("Vertex Group", 'GROUP_VERTEX'), - VERTEX_COLOR_KEY: ("Color Attribute", 'GROUP_VCOL'), - MODIFIER_KEY: ("Modifier", 'MODIFIER'), - CONSTRAINT_KEY: ("Constraint", 'CONSTRAINT'), + VERTEX_GROUP_KEY: ("Vertex Groups", 'GROUP_VERTEX'), + VERTEX_COLOR_KEY: ("Color Attributes", 'GROUP_VCOL'), # TODO Remove + MODIFIER_KEY: ("Modifiers", 'MODIFIER'), + CONSTRAINT_KEY: ("Constraints", 'CONSTRAINT'), MATERIAL_SLOT_KEY: ("Materials", 'MATERIAL'), - UV_LAYERS_KEY: ("UV Maps", 'GROUP_UVS'), - SHAPE_KEY_KEY: ("Shape Key", 'SHAPEKEY_DATA'), - ATTRIBUTE_KEY: ("Attribute", 'EVENT_A'), + UV_LAYERS_KEY: ("UV Maps", 'GROUP_UVS'), # TODO Remove + SHAPE_KEY_KEY: ("Shape Keys", 'SHAPEKEY_DATA'), + ATTRIBUTE_KEY: ("Attributes", 'EVENT_A'), PARENT_KEY: ("Parent", 'FILE_PARENT'), } -- 2.30.2 From 868647d96fb106928b02f89d685bfe01b2d3ce7c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 11:54:10 -0500 Subject: [PATCH 313/429] Asset Pipe: Show/Hide Transfer Data Ownership --- .../merge/transfer_data/transfer_ui.py | 13 ++++++++++++- scripts-blender/addons/asset_pipeline_2/props.py | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 3cf9b2c8..b0b53f52 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -7,11 +7,22 @@ def draw_transfer_data_type( layout: bpy.types.UILayout, transfer_data: bpy.types.CollectionProperty ) -> None: """Draw UI Element for items of a transfer data type""" + asset_pipe = bpy.context.scene.asset_pipeline if transfer_data == []: return name, icon = constants.TRANSFER_DATA_TYPES[transfer_data[0].type] box = layout.box() - box.label(text=name, icon=icon) + row = box.row() + row.prop( + asset_pipe, + f"{icon}_BOOL", + icon=icon, + text="", + ) + row.label(text=name) + if not bool(asset_pipe.get(f"{icon}_BOOL")): + return + scene = bpy.context.scene for transfer_data_item in transfer_data: row = box.row() diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 59cf3a57..d992cb5d 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -98,6 +98,21 @@ class AssetPipeline(bpy.types.PropertyGroup): all_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) local_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) + # UI BOOLS + # The names of the bools are the ICON keys for each transfer data type with the name _BOOL appened to it + # TODO See if there is a better way to handle hide/expand panels without creating bools like this + GROUP_VERTEX_BOOL: bpy.props.BoolProperty( + name="Show/Hide Vertex Groups", default=False + ) + MODIFIER_BOOL: bpy.props.BoolProperty(name="Show/Hide Modifiers", default=False) + CONSTRAINT_BOOL: bpy.props.BoolProperty(name="Show/Hide Constraints", default=False) + MATERIAL_BOOL: bpy.props.BoolProperty(name="Show/Hide Materials", default=False) + SHAPEKEY_DATA_BOOL: bpy.props.BoolProperty( + name="Show/Hide Shape Keys", default=False + ) + EVENT_A_BOOL: bpy.props.BoolProperty(name="Show/Hide Attributes", default=False) + FILE_PARENT_BOOL: bpy.props.BoolProperty(name="Show/Hide Parent", default=False) + classes = ( AssetTransferData, -- 2.30.2 From 45fc523dc3a1608164f4b4430ecd56335c4fcb7f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 12:06:34 -0500 Subject: [PATCH 314/429] Asset Pipe: Fix Task Layer Prefix Naming --- .../addons/asset_pipeline_2/merge/naming.py | 6 ++---- .../merge/transfer_data/transfer_functions.py | 10 ++++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 2418f58d..1831862f 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -135,7 +135,7 @@ def get_name_with_asset_prefix(name: str) -> str: return prefix + name -def get_name_with_task_layer_prefix(name: str, td_type_key: str) -> str: +def get_name_with_task_layer_prefix(name: str, task_layer_owner: str) -> str: """Returns a string with the prefix if it is not already set. Users can specify a prefix to live on all objects during the asset creation process. This prefix is stored in the scene. @@ -146,12 +146,10 @@ def get_name_with_task_layer_prefix(name: str, td_type_key: str) -> str: Returns: str: Returns name with prefix """ - default_task_layer = get_default_task_layer(td_type_key) - for task_layer_key in config.TASK_LAYER_TYPES: if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): return name - prefix = config.TASK_LAYER_TYPES[default_task_layer] + prefix = config.TASK_LAYER_TYPES[task_layer_owner] return prefix + "." + name diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 65d65ac9..130a803c 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -103,14 +103,15 @@ def modifier_is_missing(transfer_data_item): def init_modifiers(scene, obj, use_default_owner: bool): td_type_key = constants.MODIFIER_KEY transfer_data = obj.transfer_data_ownership + task_layer_owner = get_transfer_data_owner(td_type_key, use_default_owner) for mod in obj.modifiers: - mod.name = get_name_with_task_layer_prefix(mod.name, td_type_key) + mod.name = get_name_with_task_layer_prefix(mod.name, task_layer_owner) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, mod.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=mod.name, - owner=get_transfer_data_owner(td_type_key, use_default_owner), + owner=task_layer_owner, type=td_type_key, obj=obj, ) @@ -198,14 +199,15 @@ def constraint_is_missing(transfer_data_item): def init_constraints(scene, obj, use_default_owner: bool): td_type_key = constants.CONSTRAINT_KEY transfer_data = obj.transfer_data_ownership + task_layer_owner = get_transfer_data_owner(td_type_key, use_default_owner) for const in obj.constraints: - const.name = get_name_with_task_layer_prefix(const.name, td_type_key) + const.name = get_name_with_task_layer_prefix(const.name, task_layer_owner) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, const.name, td_type_key) if len(matches) == 0: scene.asset_pipeline.add_temp_transfer_data( name=const.name, - owner=get_transfer_data_owner(td_type_key, use_default_owner), + owner=task_layer_owner, type=td_type_key, obj=obj, ) -- 2.30.2 From 05ccedbc0c06e05ce4bb78d66a286a027c71b769 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 12:14:30 -0500 Subject: [PATCH 315/429] Asset Pipe: Fix UI Panel Names --- scripts-blender/addons/asset_pipeline_2/ui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index b2a76aab..e4ea09d8 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -6,7 +6,7 @@ from .merge.task_layer import draw_task_layer_selection from .config import verify_json_data -class ASSETPIPE_sync(bpy.types.Panel): +class ASSETPIPE_PT_sync(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Asset Pipe 2' @@ -56,7 +56,7 @@ class ASSETPIPE_sync(bpy.types.Panel): # box.prop(asset_pipe, "is_depreciated") -class ASSETPIPE_ownership_inspector(bpy.types.Panel): +class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Asset Pipe 2' @@ -94,8 +94,8 @@ class ASSETPIPE_ownership_inspector(bpy.types.Panel): classes = ( - ASSETPIPE_sync, - ASSETPIPE_ownership_inspector, + ASSETPIPE_PT_sync, + ASSETPIPE_PT_ownership_inspector, ) -- 2.30.2 From e0d3aad343ad8dfea09d4d4ea4be63040a2b6adc Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 12:16:33 -0500 Subject: [PATCH 316/429] Asset Pipe: Add Advanced Sub-Panel --- scripts-blender/addons/asset_pipeline_2/ui.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index e4ea09d8..2c0b2f2d 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -56,6 +56,18 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): # box.prop(asset_pipe, "is_depreciated") +class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'Asset Pipe 2' + bl_label = "Advanced" + bl_parent_id = "ASSETPIPE_PT_sync" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context: bpy.types.Context) -> None: + self.layout.label(text="FOO") + + class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -95,6 +107,7 @@ class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): classes = ( ASSETPIPE_PT_sync, + ASSETPIPE_PT_sync_advanced, ASSETPIPE_PT_ownership_inspector, ) -- 2.30.2 From 618e17edbf9fd5eed5be556d8bae1cc95e55a66e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 12:27:36 -0500 Subject: [PATCH 317/429] Asset Pipe: Operator to Reset Ownership on selected objects --- .../addons/asset_pipeline_2/ops.py | 21 +++++++++++++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 7cc558c1..4014f70d 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -344,12 +344,33 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator): return {'FINISHED'} +class ASSETPIPE_OT_reset_ownership(bpy.types.Operator): + bl_idname = "assetpipe.reset_ownership" + bl_label = "Reset Ownership" + bl_description = ( + """Reset the Object owner and transfer data on selected object(s)""" + ) + + def execute(self, context: bpy.types.Context): + objs = context.selected_objects + for obj in objs: + obj = context.active_object + obj.asset_id_owner = "NONE" + obj.transfer_data_ownership.clear() + self.report( + {'INFO'}, + f"'{obj.name}' ownership data cleared ", + ) + return {'FINISHED'} + + classes = ( ASSETPIPE_OT_update_ownership, ASSETPIPE_OT_sync_push, ASSETPIPE_OT_sync_pull, ASSETPIPE_OT_publish_new_version, ASSETPIPE_OT_create_new_asset, + ASSETPIPE_OT_reset_ownership, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 2c0b2f2d..76eda737 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -65,7 +65,8 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): bl_options = {'DEFAULT_CLOSED'} def draw(self, context: bpy.types.Context) -> None: - self.layout.label(text="FOO") + layout = self.layout + layout.operator("assetpipe.reset_ownership") class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): -- 2.30.2 From 7b58817b51c8832e423d69c6b187722b15d7a7f5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 13:21:34 -0500 Subject: [PATCH 318/429] Asset Pipe: Set Local Task Layers in New Function --- .../addons/asset_pipeline_2/merge/task_layer.py | 11 +++++++++++ scripts-blender/addons/asset_pipeline_2/ops.py | 13 +++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 4d57a886..9e84b791 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -3,6 +3,17 @@ from .. import constants from .. import config +def set_local_task_layers(task_layer_keys: [str]): + local_task_layers = bpy.context.scene.asset_pipeline.local_task_layers + all_task_layers = bpy.context.scene.asset_pipeline.all_task_layers + # Update Local Task Layers for New File + local_task_layers.clear() + for task_layer in all_task_layers: + if task_layer.name in task_layer_keys: + new_local_task_layer = local_task_layers.add() + new_local_task_layer.name = task_layer.name + + def get_local_task_layers(): local_task_layers = bpy.context.scene.asset_pipeline.local_task_layers return [task_layer.name for task_layer in local_task_layers] diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 4014f70d..bf5dfd72 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,6 +2,7 @@ import bpy from . import config import os from pathlib import Path +from .merge.task_layer import set_local_task_layers from .merge.publish import get_next_published_file, find_all_published from .images import save_images from . import constants @@ -115,14 +116,8 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): starting_file = "" first_file = os.path.join(asset_path, Path(bpy.data.filepath).name) - local_task_layers = context.scene.asset_pipeline.local_task_layers - # Update Local Task Layers for New File - local_task_layers.clear() - for task_layer in all_task_layers: - if task_layer.is_local == True: - new_local_task_layer = local_task_layers.add() - new_local_task_layer.name = task_layer.name + set_local_task_layers(local_tls) bpy.ops.wm.save_as_mainfile(filepath=first_file, copy=True) starting_file = first_file @@ -145,9 +140,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): do_local_ids=True, do_linked_ids=False, do_recursive=True ) - local_task_layers.clear() - new_local_task_layer = local_task_layers.add() - new_local_task_layer.name = task_layer_key + set_local_task_layers([task_layer_key]) task_layer_file = os.path.join(asset_path, name) bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) -- 2.30.2 From de8eae829d9bf6476d1625980500787046234a21 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 13:21:59 -0500 Subject: [PATCH 319/429] Asset Pipe: Add UI/Operator to change local task layers --- .../addons/asset_pipeline_2/ops.py | 42 +++++++++++++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 14 ++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index bf5dfd72..00d142d1 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -357,6 +357,47 @@ class ASSETPIPE_OT_reset_ownership(bpy.types.Operator): return {'FINISHED'} +class ASSETPIPE_OT_update_local_task_layers(bpy.types.Operator): + bl_idname = "assetpipe.update_local_task_layers" + bl_label = "Update Local Task Layers" + bl_description = """Change the Task Layers that are Local to your file""" + + # box + + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + asset_pipe = context.scene.asset_pipeline + new_local_tl = [ + tl.name for tl in asset_pipe.all_task_layers if tl.is_local == True + ] + local_tl = [tl.name for tl in asset_pipe.local_task_layers] + if new_local_tl == local_tl: + cls.poll_message_set("Local Task Layers already match current selection") + return False + return True + + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + layout = self.layout + layout.alert = True + layout.label( + text="Caution, this only affects current file.", + icon="ERROR", + ) # TODO Improve warning + layout.label( + text="Two files owning the same task layer can break merge process." + ) + + def execute(self, context: bpy.types.Context): + asset_pipe = context.scene.asset_pipeline + all_task_layers = asset_pipe.all_task_layers + local_tl = [tl.name for tl in all_task_layers if tl.is_local == True] + set_local_task_layers(local_tl) + return {'FINISHED'} + + classes = ( ASSETPIPE_OT_update_ownership, ASSETPIPE_OT_sync_push, @@ -364,6 +405,7 @@ classes = ( ASSETPIPE_OT_publish_new_version, ASSETPIPE_OT_create_new_asset, ASSETPIPE_OT_reset_ownership, + ASSETPIPE_OT_update_local_task_layers, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 76eda737..4118c283 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -66,7 +66,19 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout - layout.operator("assetpipe.reset_ownership") + box = layout.box() + box.operator("assetpipe.reset_ownership") + + # Task Layer Updater + box = layout.box() + box.label(text="Change Local Task Layers") + + row = box.row() + asset_pipe = context.scene.asset_pipeline + all_task_layers = asset_pipe.all_task_layers + for task_layer in all_task_layers: + row.prop(task_layer, "is_local", text=task_layer.name) + box.operator("assetpipe.update_local_task_layers") class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): -- 2.30.2 From 3af61ce46349d979bba12f0c040abcb224b3c3cf Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 13:25:10 -0500 Subject: [PATCH 320/429] Asset Pipe: Clear old TODO --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 00d142d1..e27893a6 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -385,7 +385,7 @@ class ASSETPIPE_OT_update_local_task_layers(bpy.types.Operator): layout.label( text="Caution, this only affects current file.", icon="ERROR", - ) # TODO Improve warning + ) layout.label( text="Two files owning the same task layer can break merge process." ) -- 2.30.2 From 98e2cce9223d576af2e13b4cdbbf80d4d60aab2f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 14:07:32 -0500 Subject: [PATCH 321/429] Asset Pipe: Allow Users to change to any ownership during intial object set-up --- .../addons/asset_pipeline_2/merge/task_layer.py | 16 +++++++++++----- .../merge/transfer_data/transfer_functions.py | 5 +++++ .../merge/transfer_data/transfer_ui.py | 8 +++++++- .../merge/transfer_data/transfer_util.py | 1 + scripts-blender/addons/asset_pipeline_2/props.py | 10 ++++++++-- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 9e84b791..6d9cef6e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -38,12 +38,17 @@ def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): def draw_task_layer_selection( - row, - scene, - data, - data_owner, - data_owner_name, + row, scene, data, data_owner, data_owner_name, use_default_owner=False ): + if use_default_owner == True: + row.prop_search( + data, + data_owner_name, + scene.asset_pipeline, + 'all_task_layers', + text="", + ) + return if data_owner not in [tl.name for tl in scene.asset_pipeline.local_task_layers]: row.enabled = False row.prop_search( @@ -53,6 +58,7 @@ def draw_task_layer_selection( 'all_task_layers', text="", ) + return else: row.prop_search( data, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 130a803c..183d2ec0 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -114,6 +114,7 @@ def init_modifiers(scene, obj, use_default_owner: bool): owner=task_layer_owner, type=td_type_key, obj=obj, + use_default_owner=use_default_owner, ) @@ -210,6 +211,7 @@ def init_constraints(scene, obj, use_default_owner: bool): owner=task_layer_owner, type=td_type_key, obj=obj, + use_default_owner=use_default_owner, ) @@ -314,6 +316,7 @@ def init_material_slots(scene, obj, use_default_owner: bool): owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, + use_default_owner=use_default_owner, ) @@ -578,6 +581,7 @@ def init_attributes(scene, obj, use_default_owner: bool): ), type=td_type_key, obj=obj, + use_default_owner=use_default_owner, ) @@ -649,6 +653,7 @@ def init_parent(scene, obj, use_default_owner: bool): owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, + use_default_owner=use_default_owner, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index b0b53f52..018dfaf8 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -27,8 +27,14 @@ def draw_transfer_data_type( for transfer_data_item in transfer_data: row = box.row() row.label(text=f"{transfer_data_item.name}: ") + draw_task_layer_selection( - row, scene, transfer_data_item, transfer_data_item.owner, "owner" + row, + scene, + transfer_data_item, + transfer_data_item.owner, + "owner", + transfer_data_item.get("use_default_owner"), ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index 39ea41d3..bf3e04a1 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -113,4 +113,5 @@ def transfer_data_item_init( owner=get_transfer_data_owner(td_type_key, use_default_owner), type=td_type_key, obj=obj, + use_default_owner=use_default_owner, ) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index d992cb5d..ac43d5f2 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -38,12 +38,17 @@ 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") + owner: bpy.props.StringProperty(name="OwneAr", default="NONE") type: bpy.props.EnumProperty( name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) obj: bpy.props.PointerProperty(type=bpy.types.Object) + use_default_owner: bpy.props.BoolProperty( + name="Defaults Ownership", + description="Default ownership has been used when initializing these properties", + default=False, + ) class TaskLayerSettings(bpy.types.PropertyGroup): @@ -69,13 +74,14 @@ class AssetPipeline(bpy.types.PropertyGroup): local_task_layers: bpy.props.StringProperty(name="Local Task Layers", default="") - def add_temp_transfer_data(self, name, owner, type, obj): + def add_temp_transfer_data(self, name, owner, type, obj, use_default_owner=False): 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 = obj + transfer_data_item.use_default_owner = use_default_owner ## NEW FILE -- 2.30.2 From e34b0afb650a634aa7e9a55697ab4ed0fe8b9219 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 16:06:17 -0500 Subject: [PATCH 322/429] Asset Pipe: Add UI to Revert File after Merge Fails --- .../addons/asset_pipeline_2/ops.py | 21 +++++++++++++-- .../addons/asset_pipeline_2/props.py | 4 +++ .../addons/asset_pipeline_2/sync.py | 26 +++++++++++++++---- scripts-blender/addons/asset_pipeline_2/ui.py | 14 +++++++++- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index e27893a6..68dafebc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -362,8 +362,6 @@ class ASSETPIPE_OT_update_local_task_layers(bpy.types.Operator): bl_label = "Update Local Task Layers" bl_description = """Change the Task Layers that are Local to your file""" - # box - @classmethod def poll(cls, context: bpy.types.Context) -> bool: asset_pipe = context.scene.asset_pipeline @@ -398,6 +396,24 @@ class ASSETPIPE_OT_update_local_task_layers(bpy.types.Operator): return {'FINISHED'} +class ASSETPIPE_OT_revert_file(bpy.types.Operator): + bl_idname = "assetpipe.revert_file" + bl_label = "Revert File" + bl_description = """Revert File to Pre-Sync State. Revert will not affect Published files""" + + _temp_file = "" + _source_file = "" + + def execute(self, context: bpy.types.Context): + asset_pipe = context.scene.asset_pipeline + self._temp_file = asset_pipe.temp_file + self._source_file = asset_pipe.source_file + # TODO Add Error Messages if path is empty + bpy.ops.wm.open_mainfile(filepath=self._temp_file) + bpy.ops.wm.save_as_mainfile(filepath=self._source_file) + return {'FINISHED'} + + classes = ( ASSETPIPE_OT_update_ownership, ASSETPIPE_OT_sync_push, @@ -406,6 +422,7 @@ classes = ( ASSETPIPE_OT_create_new_asset, ASSETPIPE_OT_reset_ownership, ASSETPIPE_OT_update_local_task_layers, + ASSETPIPE_OT_revert_file, ) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index ac43d5f2..baee8f55 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -101,6 +101,10 @@ class AssetPipeline(bpy.types.PropertyGroup): items=get_task_layer_presets, ) + 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) diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 6de55068..75d0a010 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -112,11 +112,23 @@ def sync_execute_prepare_sync(self, context): bpy.data.objects.remove(obj) -def sync_execute_pull(self, context): +def create_temp_file_backup(self, context): temp_file = self._temp_dir.joinpath( self._current_file.name.replace(".blend", "") + "_Asset_Pipe_Backup.blend" ) - bpy.ops.wm.save_as_mainfile(filepath=temp_file.__str__(), copy=True) + context.scene.asset_pipeline.temp_file = temp_file.__str__() + return temp_file.__str__() + +def update_temp_file_paths(self, context, temp_file_path): + asset_pipe = context.scene.asset_pipeline + asset_pipe.temp_file = temp_file_path + asset_pipe.source_file = self._current_file.__str__() + +def sync_execute_pull(self, context): + temp_file_path = create_temp_file_backup(self, context) + update_temp_file_paths(self, context, temp_file_path) + bpy.ops.wm.save_as_mainfile(filepath=temp_file_path, copy=True) + error_msg = merge_task_layer( context, local_tls=self._task_layer_keys, @@ -124,21 +136,25 @@ def sync_execute_pull(self, context): ) if error_msg: - bpy.ops.wm.open_mainfile(filepath=temp_file.__str__()) - bpy.ops.wm.save_as_mainfile(filepath=self._current_file.__str__()) + context.scene.asset_pipeline.sync_error = True self.report({'ERROR'}, error_msg) return {'CANCELLED'} def sync_execute_push(self, context): + temp_file_path = create_temp_file_backup(self, context) push_targets = find_all_published(self._current_file, constants.ACTIVE_PUBLISH_KEY) + if self._sync_target not in push_targets: push_targets.append(self._sync_target) + for file in push_targets: file_path = file.__str__() bpy.ops.wm.open_mainfile(filepath=file_path) + update_temp_file_paths(self, context, temp_file_path) + # SKIP DEPRECIATED FILES if context.scene.asset_pipeline.is_depreciated: continue @@ -155,7 +171,7 @@ def sync_execute_push(self, context): external_file=self._current_file, ) if error_msg: - bpy.ops.wm.open_mainfile(filepath=self._current_file.__str__()) + context.scene.asset_pipeline.sync_error = True self.report({'ERROR'}, error_msg) return {'CANCELLED'} diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 4118c283..47fdb05f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -4,6 +4,7 @@ from pathlib import Path from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.task_layer import draw_task_layer_selection from .config import verify_json_data +from . import constants class ASSETPIPE_PT_sync(bpy.types.Panel): @@ -27,6 +28,15 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): if not Path(bpy.data.filepath).exists: layout.label(text="File is not saved", icon="ERROR") return + + if asset_pipe.sync_error or asset_pipe.asset_collection.name.endswith( + constants.LOCAL_SUFFIX + ): + layout.alert = True + row = layout.row() + row.label(text="Merge Process has Failed", icon='ERROR') + row.operator("assetpipe.revert_file", text="Revert", icon="FILE_TICK") + return # TODO Move this call out of the UI because we keep re-loading this file every draw if not verify_json_data(): @@ -67,7 +77,9 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout box = layout.box() - box.operator("assetpipe.reset_ownership") + box.operator("assetpipe.reset_ownership", icon="LOOP_BACK") + box.operator("assetpipe.revert_file", icon="FILE_TICK") + # Task Layer Updater box = layout.box() -- 2.30.2 From 1a35ca5aa47556a9b841e152396d821fac13b31c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 16:59:33 -0500 Subject: [PATCH 323/429] Asset Pipe: Fix bug in Reset Ownership --- scripts-blender/addons/asset_pipeline_2/ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 68dafebc..8108cb75 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -347,7 +347,6 @@ class ASSETPIPE_OT_reset_ownership(bpy.types.Operator): def execute(self, context: bpy.types.Context): objs = context.selected_objects for obj in objs: - obj = context.active_object obj.asset_id_owner = "NONE" obj.transfer_data_ownership.clear() self.report( -- 2.30.2 From 3c7e7e17fd7eb2cdf5f38ecec4e248df7e74151d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 16:59:49 -0500 Subject: [PATCH 324/429] Asset Pipe: Check for selected obs in reset ownership --- scripts-blender/addons/asset_pipeline_2/ops.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 8108cb75..830abc02 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -343,6 +343,13 @@ class ASSETPIPE_OT_reset_ownership(bpy.types.Operator): bl_description = ( """Reset the Object owner and transfer data on selected object(s)""" ) + + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + if len(context.selected_objects) == 0: + cls.poll_message_set("No Objects Selected") + return False + return True def execute(self, context: bpy.types.Context): objs = context.selected_objects -- 2.30.2 From 39af630d53c05c6ee78e3b4dea9aa852b3749fa5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 18:15:15 -0500 Subject: [PATCH 325/429] Asset Pipe: Add Function to get Data for Transfer Data Type --- .../merge/transfer_data/transfer_core.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index c6e072f9..7bcb4bd6 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -183,3 +183,20 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: transfer_data_item=transfer_data_item, target_obj=target_obj, ) + +def data_type_from_transfer_data_key(obj:bpy.types.Object, td_type:str): + """ Returns the data on an object that is referred to by the transfer data type""" + if td_type == constants.VERTEX_GROUP_KEY: + return obj.vertex_groups + if td_type == constants.MODIFIER_KEY: + return obj.modifiers + if td_type == constants.CONSTRAINT_KEY: + return obj.constraints + if td_type == constants.MATERIAL_SLOT_KEY: + return obj.material_slots + if td_type == constants.SHAPE_KEY_KEY: + return obj.data.shape_keys.key_blocks + if td_type == constants.ATTRIBUTE_KEY: + return obj.data.attributes + if td_type == constants.PARENT_KEY: + return obj.parent \ No newline at end of file -- 2.30.2 From 2935ed5aec6f39e93d0ef0a67658483ef0f0ddfa Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 18:16:23 -0500 Subject: [PATCH 326/429] Asset Pipe: Remove Old Prefix in `get_name_with_task_layer_prefix()` --- scripts-blender/addons/asset_pipeline_2/merge/naming.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 1831862f..24684352 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -146,9 +146,13 @@ def get_name_with_task_layer_prefix(name: str, task_layer_owner: str) -> str: Returns: str: Returns name with prefix """ + if name.startswith(config.TASK_LAYER_TYPES[task_layer_owner] + "."): + return name for task_layer_key in config.TASK_LAYER_TYPES: if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): - return name + name = name.replace(name.split(".")[0], "")[1:] + break + prefix = config.TASK_LAYER_TYPES[task_layer_owner] return prefix + "." + name -- 2.30.2 From 891d61e05643abec3cd8e2894ec65938ddd4912b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 18:21:48 -0500 Subject: [PATCH 327/429] Asset Pipe: Add Operator to Fix Prefixes --- .../addons/asset_pipeline_2/ops.py | 55 +++++++++++++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 830abc02..d2ec8796 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -3,6 +3,8 @@ from . import config import os from pathlib import Path from .merge.task_layer import set_local_task_layers +from .merge.naming import get_name_with_task_layer_prefix +from .merge.transfer_data.transfer_core import data_type_from_transfer_data_key from .merge.publish import get_next_published_file, find_all_published from .images import save_images from . import constants @@ -419,6 +421,58 @@ class ASSETPIPE_OT_revert_file(bpy.types.Operator): bpy.ops.wm.save_as_mainfile(filepath=self._source_file) return {'FINISHED'} +class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): + bl_idname = "assetpipe.fix_prefixes" + bl_label = "Fix Prefixes" + bl_description = """Fix Prefixes for Modifiers and Constraints so they match Transfer Data Owner on selected object(s)""" + + _updated_prefix = False + + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + if len(context.selected_objects) == 0: + cls.poll_message_set("No Objects Selected") + return False + return True + + def transfer_data_update_prefix(self, context, transfer_data_item): + obj = transfer_data_item.id_data + td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) + new_name = get_name_with_task_layer_prefix(transfer_data_item.name, transfer_data_item.owner) + + if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): + return + + td_data[transfer_data_item.name].name = new_name + transfer_data_item.name = new_name + self.report( + {'INFO'}, + f"Renamed {transfer_data_item.type} on '{obj.name}' to {new_name}", + ) + self._updated_prefix = True + + def transfer_data_using_prefix_get(self, context, obj): + types_to_update = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] + return [td for td in obj.transfer_data_ownership if td.type in types_to_update] + + def transfer_data_update_all_prefixes(self, context, obj): + transfer_data_items = self.transfer_data_using_prefix_get(context, obj) + for transfer_data_item in transfer_data_items: + self.transfer_data_update_prefix(context, transfer_data_item) + + + def execute(self, context: bpy.types.Context): + objs = context.selected_objects + for obj in objs: + self.transfer_data_update_all_prefixes(context, obj) + + if not self._updated_prefix: + self.report( + {'WARNING'}, + f"No Prefixes found to update", + ) + + return {'FINISHED'} classes = ( ASSETPIPE_OT_update_ownership, @@ -429,6 +483,7 @@ classes = ( ASSETPIPE_OT_reset_ownership, ASSETPIPE_OT_update_local_task_layers, ASSETPIPE_OT_revert_file, + ASSETPIPE_OT_fix_prefixes, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 47fdb05f..46190fa5 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -78,9 +78,9 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): layout = self.layout box = layout.box() box.operator("assetpipe.reset_ownership", icon="LOOP_BACK") + box.operator("assetpipe.fix_prefixes", icon="CHECKMARK") box.operator("assetpipe.revert_file", icon="FILE_TICK") - # Task Layer Updater box = layout.box() box.label(text="Change Local Task Layers") -- 2.30.2 From 42e462fe79853efea3862c747c11b75d3ebacd68 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 8 Nov 2023 18:47:53 -0500 Subject: [PATCH 328/429] Asset Pipe: Add Error Message for Multi User Meshes --- .../addons/asset_pipeline_2/merge/core.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index de9da881..efc02d36 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -179,15 +179,14 @@ def merge_task_layer( external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] map = AssetTransferMapping(local_col, external_col, local_tls) - + error_msg = '' if len(map.conflict_transfer_data) != 0: - error_msg = '' for conflict in map.conflict_transfer_data: error_msg += f"Transfer Data conflict found for '{conflict.name}' on obj '{conflict.id_data.name}'\n" return error_msg if len(map.conflict_ids) != 0: - error_msg = '' + for conflict_obj in map.conflict_ids: type_name = get_id_type_name(type(conflict_obj)) error_msg += ( @@ -197,12 +196,22 @@ def merge_task_layer( # Remove all transfer data from target objects for source_obj in map.object_map: + if source_obj.data.users > 1: + error_msg += ( + f"Object {source_obj.name} contains multi-user datablock'\n" + ) + return error_msg target_obj = map.object_map[source_obj] target_obj.transfer_data_ownership.clear() apply_transfer_data(context, map.transfer_data_map) for source_obj in map.object_map: + if target_obj.data.users > 1: + error_msg += ( + f"Object {source_obj.name} contains multi-user datablock'\n" + ) + return error_msg target_obj = map.object_map[source_obj] remap_user(source_obj, target_obj) transfer_data_clean(target_obj) -- 2.30.2 From 7348e16e036305ba3c74df073250a16dba094a61 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:06:11 -0500 Subject: [PATCH 329/429] Asset Pipe: Fix Bug in Attribute Clean --- .../merge/transfer_data/transfer_functions.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 183d2ec0..ede5d4ee 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -540,7 +540,7 @@ def attribute_clean(obj): if obj.type != "MESH": return attributes = attributes_get_editable(obj.data.attributes) - + attributes_to_remove = [] for attribute in attributes: matches = check_transfer_data_entry( obj.transfer_data_ownership, @@ -548,8 +548,12 @@ def attribute_clean(obj): constants.ATTRIBUTE_KEY, ) if len(matches) == 0: - print(f"Cleaning attribute {attribute.name}") - obj.data.attributes.remove(attribute) + attributes_to_remove.append(attribute.name) + + for attribute_name_to_remove in reversed(attributes_to_remove): + attribute_to_remove = obj.data.attributes.get(attribute_name_to_remove) + print(f"Cleaning attribute {attribute.name}") + obj.data.attributes.remove(attribute_to_remove) def attribute_is_missing(transfer_data_item): -- 2.30.2 From c35cee9e87e44051093bdb896d87f71ba4dea4c3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:08:29 -0500 Subject: [PATCH 330/429] Asset Pipe: Enforce Black Formatting --- .../addons/asset_pipeline_2/ops.py | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index d2ec8796..ee821f5a 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -345,7 +345,7 @@ class ASSETPIPE_OT_reset_ownership(bpy.types.Operator): bl_description = ( """Reset the Object owner and transfer data on selected object(s)""" ) - + @classmethod def poll(cls, context: bpy.types.Context) -> bool: if len(context.selected_objects) == 0: @@ -407,8 +407,10 @@ class ASSETPIPE_OT_update_local_task_layers(bpy.types.Operator): class ASSETPIPE_OT_revert_file(bpy.types.Operator): bl_idname = "assetpipe.revert_file" bl_label = "Revert File" - bl_description = """Revert File to Pre-Sync State. Revert will not affect Published files""" - + bl_description = ( + """Revert File to Pre-Sync State. Revert will not affect Published files""" + ) + _temp_file = "" _source_file = "" @@ -416,61 +418,65 @@ class ASSETPIPE_OT_revert_file(bpy.types.Operator): asset_pipe = context.scene.asset_pipeline self._temp_file = asset_pipe.temp_file self._source_file = asset_pipe.source_file - # TODO Add Error Messages if path is empty + # TODO Add Error Messages if path is empty bpy.ops.wm.open_mainfile(filepath=self._temp_file) bpy.ops.wm.save_as_mainfile(filepath=self._source_file) return {'FINISHED'} + class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): bl_idname = "assetpipe.fix_prefixes" bl_label = "Fix Prefixes" bl_description = """Fix Prefixes for Modifiers and Constraints so they match Transfer Data Owner on selected object(s)""" - + _updated_prefix = False - + @classmethod def poll(cls, context: bpy.types.Context) -> bool: if len(context.selected_objects) == 0: cls.poll_message_set("No Objects Selected") return False return True - + def transfer_data_update_prefix(self, context, transfer_data_item): obj = transfer_data_item.id_data td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) - new_name = get_name_with_task_layer_prefix(transfer_data_item.name, transfer_data_item.owner) - - if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): + new_name = get_name_with_task_layer_prefix( + transfer_data_item.name, transfer_data_item.owner + ) + + if new_name == transfer_data_item.name or not td_data.get( + transfer_data_item.name + ): return - + td_data[transfer_data_item.name].name = new_name transfer_data_item.name = new_name self.report( - {'INFO'}, - f"Renamed {transfer_data_item.type} on '{obj.name}' to {new_name}", - ) + {'INFO'}, + f"Renamed {transfer_data_item.type} on '{obj.name}' to {new_name}", + ) self._updated_prefix = True - + def transfer_data_using_prefix_get(self, context, obj): types_to_update = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] return [td for td in obj.transfer_data_ownership if td.type in types_to_update] - + def transfer_data_update_all_prefixes(self, context, obj): transfer_data_items = self.transfer_data_using_prefix_get(context, obj) for transfer_data_item in transfer_data_items: self.transfer_data_update_prefix(context, transfer_data_item) - - + def execute(self, context: bpy.types.Context): objs = context.selected_objects for obj in objs: self.transfer_data_update_all_prefixes(context, obj) - + if not self._updated_prefix: self.report( - {'WARNING'}, - f"No Prefixes found to update", - ) + {'WARNING'}, + f"No Prefixes found to update", + ) return {'FINISHED'} -- 2.30.2 From 14dfea3582ed91947a1f11312cc6cb697900f794 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:18:03 -0500 Subject: [PATCH 331/429] Asset Pipe: Add Op/UI for Surrendering Ownership --- .../asset_pipeline_2/merge/asset_mapping.py | 56 +++++++++++++++++++ .../addons/asset_pipeline_2/merge/core.py | 1 + .../asset_pipeline_2/merge/task_layer.py | 17 +++++- .../merge/transfer_data/transfer_core.py | 1 + .../merge/transfer_data/transfer_ui.py | 7 ++- .../merge/transfer_data/transfer_util.py | 2 + .../addons/asset_pipeline_2/ops.py | 53 +++++++++++++++++- .../addons/asset_pipeline_2/props.py | 2 + 8 files changed, 134 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index ba7c5f24..ca00ed14 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -143,6 +143,7 @@ class AssetTransferMapping: name=transfer_data_item.name, td_type_key=transfer_data_item.type, task_layer_name=transfer_data_item.owner, + surrender=transfer_data_item.surrender, ) map_item = { @@ -173,6 +174,54 @@ class AssetTransferMapping: print("CONFLICT FOUND") return True + def transfer_data_map_surrendered(self, transfer_data_item): + obj = transfer_data_item.id_data + other_obj = bpy.data.objects.get(get_target_name(obj.name)) + other_transfer_data_item = None + # Find Related Transfer Data Item on Target/Source Object + for other_obj_transfer_data_item in other_obj.transfer_data_ownership: + if other_obj_transfer_data_item.name == transfer_data_item.name: + other_transfer_data_item = other_obj_transfer_data_item + + # TODO CONSIDER PREFIXES + + if not other_transfer_data_item: + return None, None + + # If the other transfer data is surrendered map it accordingly + if ( + other_transfer_data_item.surrender + and not transfer_data_item.surrender + and other_transfer_data_item.owner != transfer_data_item.owner + ): + if transfer_data_item.owner in self._local_tls: + target_obj = obj + source_obj = other_obj + else: + target_obj = other_obj + source_obj = obj + + return self._get_transfer_data_map_item( + source_obj, target_obj, transfer_data_item + ) + + if ( + transfer_data_item.surrender + and not other_transfer_data_item.surrender + and other_transfer_data_item.owner != transfer_data_item.owner + ): + if other_transfer_data_item.owner in self._local_tls: + target_obj = obj + source_obj = other_obj + else: + target_obj = other_obj + source_obj = obj + + return self._get_transfer_data_map_item( + source_obj, target_obj, other_transfer_data_item + ) + return None, None + def _gen_transfer_data_map(self): context = bpy.context transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} @@ -183,6 +232,13 @@ class AssetTransferMapping: objs = [source_obj, target_obj] for obj in objs: for transfer_data_item in obj.transfer_data_ownership: + name, map_item = self.transfer_data_map_surrendered( + transfer_data_item + ) + if bool(name and map_item): + transfer_data_map[name] = map_item + continue + self._check_transfer_data_conflict(obj, transfer_data_item) if ( transfer_data_item.owner in self._local_tls diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index efc02d36..4c5d4b25 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -102,6 +102,7 @@ def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: transfer_data_item.name, transfer_data_item.type, transfer_data_item.owner, + transfer_data_item.surrender, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 6d9cef6e..6ef11403 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -1,4 +1,5 @@ import bpy +import contextlib from .. import constants from .. import config @@ -38,9 +39,16 @@ def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): def draw_task_layer_selection( - row, scene, data, data_owner, data_owner_name, use_default_owner=False + row, + scene, + data, + data_owner, + data_owner_name, + show_all_task_layers=False, + show_local=False, ): - if use_default_owner == True: + # TODO Simplify Arguments and add Type Hints / Doc String + if show_all_task_layers == True: row.prop_search( data, data_owner_name, @@ -49,7 +57,10 @@ def draw_task_layer_selection( text="", ) return - if data_owner not in [tl.name for tl in scene.asset_pipeline.local_task_layers]: + if ( + data_owner not in [tl.name for tl in scene.asset_pipeline.local_task_layers] + and not show_local + ): row.enabled = False row.prop_search( data, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 7bcb4bd6..4a894a35 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -33,6 +33,7 @@ def copy_transfer_data_ownership( transfer_data_item.name, transfer_data_item.type, transfer_data_item.owner, + transfer_data_item.surrender, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 018dfaf8..341e9871 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -27,7 +27,7 @@ def draw_transfer_data_type( for transfer_data_item in transfer_data: row = box.row() row.label(text=f"{transfer_data_item.name}: ") - + row.prop(transfer_data_item, "surrender") draw_task_layer_selection( row, scene, @@ -36,6 +36,11 @@ def draw_transfer_data_type( "owner", transfer_data_item.get("use_default_owner"), ) + if transfer_data_item.get("surrender"): + row.operator( + "assetpipe.update_surrendered_transfer_data" + ).transfer_data_item_name = transfer_data_item.name + row.enabled = True def draw_transfer_data( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index bf3e04a1..df063f35 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -29,6 +29,7 @@ def transfer_data_add_entry( name: str, td_type_key: str, task_layer_name: str, + surrender: bool, ): """Add entry to transfer data ownership @@ -42,6 +43,7 @@ def transfer_data_add_entry( transfer_data_item.name = name transfer_data_item.owner = task_layer_name transfer_data_item.type = td_type_key + transfer_data_item.surrender = surrender return transfer_data_item diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index ee821f5a..45e76349 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,7 +2,10 @@ import bpy from . import config import os from pathlib import Path -from .merge.task_layer import set_local_task_layers +from .merge.task_layer import ( + set_local_task_layers, + draw_task_layer_selection, +) from .merge.naming import get_name_with_task_layer_prefix from .merge.transfer_data.transfer_core import data_type_from_transfer_data_key from .merge.publish import get_next_published_file, find_all_published @@ -480,6 +483,53 @@ class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): return {'FINISHED'} + +class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): + bl_idname = "assetpipe.update_surrendered_transfer_data" + bl_label = "Update Surrendered" + bl_description = """Update Surrended Transfer Data Owner""" + + transfer_data_item_name: bpy.props.StringProperty(name="Transfer Data Item Name") + + _surrendered_transfer_data = None + _old_onwer = "" + + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + obj = context.active_object + for transfer_data_item in obj.transfer_data_ownership: + if transfer_data_item.name == self.transfer_data_item_name: + self._surrendered_transfer_data = transfer_data_item + self._old_onwer = self._surrendered_transfer_data.owner + print(f"Found Surrended Item: {self._surrendered_transfer_data.name}") + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + layout = self.layout + row = layout.row() + + draw_task_layer_selection( + row, + context.scene, + self._surrendered_transfer_data, + self._surrendered_transfer_data.owner, + "owner", + self._surrendered_transfer_data.get("use_default_owner"), + True, + ) + + def execute(self, context: bpy.types.Context): + # TODO Report Cancelled if new owner not in local task layer + if self._surrendered_transfer_data.owner == self._old_onwer: + self.report( + {'ERROR'}, + f"Transfer Data Owner was not updated", + ) + return {'CANCELLED'} + self._surrendered_transfer_data.surrender = False + # TODO RENAME PREFIXES HERE + return {'FINISHED'} + + classes = ( ASSETPIPE_OT_update_ownership, ASSETPIPE_OT_sync_push, @@ -490,6 +540,7 @@ classes = ( ASSETPIPE_OT_update_local_task_layers, ASSETPIPE_OT_revert_file, ASSETPIPE_OT_fix_prefixes, + ASSETPIPE_OT_update_surrendered_transfer_data, ) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index baee8f55..1eb90f02 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -32,6 +32,7 @@ class AssetTransferData(bpy.types.PropertyGroup): name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) + surrender: bpy.props.BoolProperty(name="Surrender Ownership", default=False) class AssetTransferDataTemp(bpy.types.PropertyGroup): @@ -43,6 +44,7 @@ class AssetTransferDataTemp(bpy.types.PropertyGroup): name="Transfer Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) + surrender: bpy.props.BoolProperty(name="Surrender Ownership", default=False) obj: bpy.props.PointerProperty(type=bpy.types.Object) use_default_owner: bpy.props.BoolProperty( name="Defaults Ownership", -- 2.30.2 From 8e0481e4acbd7b897aa11000e782460760ace001 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:18:19 -0500 Subject: [PATCH 332/429] Asset Pipe: Enforce Black Formatting --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 9 ++------- .../merge/transfer_data/transfer_core.py | 7 ++++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 4c5d4b25..fac96f92 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -187,7 +187,6 @@ def merge_task_layer( return error_msg if len(map.conflict_ids) != 0: - for conflict_obj in map.conflict_ids: type_name = get_id_type_name(type(conflict_obj)) error_msg += ( @@ -198,9 +197,7 @@ def merge_task_layer( # Remove all transfer data from target objects for source_obj in map.object_map: if source_obj.data.users > 1: - error_msg += ( - f"Object {source_obj.name} contains multi-user datablock'\n" - ) + error_msg += f"Object {source_obj.name} contains multi-user datablock'\n" return error_msg target_obj = map.object_map[source_obj] target_obj.transfer_data_ownership.clear() @@ -209,9 +206,7 @@ def merge_task_layer( for source_obj in map.object_map: if target_obj.data.users > 1: - error_msg += ( - f"Object {source_obj.name} contains multi-user datablock'\n" - ) + error_msg += f"Object {source_obj.name} contains multi-user datablock'\n" return error_msg target_obj = map.object_map[source_obj] remap_user(source_obj, target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 4a894a35..e76ecfb7 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -185,8 +185,9 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: target_obj=target_obj, ) -def data_type_from_transfer_data_key(obj:bpy.types.Object, td_type:str): - """ Returns the data on an object that is referred to by the transfer data type""" + +def data_type_from_transfer_data_key(obj: bpy.types.Object, td_type: str): + """Returns the data on an object that is referred to by the transfer data type""" if td_type == constants.VERTEX_GROUP_KEY: return obj.vertex_groups if td_type == constants.MODIFIER_KEY: @@ -200,4 +201,4 @@ def data_type_from_transfer_data_key(obj:bpy.types.Object, td_type:str): if td_type == constants.ATTRIBUTE_KEY: return obj.data.attributes if td_type == constants.PARENT_KEY: - return obj.parent \ No newline at end of file + return obj.parent -- 2.30.2 From 7088f1cacd68cfa61c35d81fa9f3aa13de27caaa Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:39:57 -0500 Subject: [PATCH 333/429] Assset Pipe: Remove No-Op Code --- scripts-blender/addons/asset_pipeline_2/merge/naming.py | 1 - .../asset_pipeline_2/merge/transfer_data/transfer_util.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 24684352..d1094928 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -22,7 +22,6 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from .util import get_storage_of_id from .. import constants, config -from .task_layer import get_default_task_layer DELIMITER = "." diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index df063f35..d03fe30d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -1,6 +1,6 @@ import bpy from ..naming import get_basename -from ..task_layer import get_transfer_data_owner, get_local_task_layers +from ..task_layer import get_transfer_data_owner def check_transfer_data_entry( -- 2.30.2 From 5b6717227fabe59de2be34556b108d98286d8239 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:41:23 -0500 Subject: [PATCH 334/429] Asset Pipe: Move Transfer Data Prefix to `naming.py` - Also Move `data_type_from_transfer_data_key` to util to avoid circular import --- .../addons/asset_pipeline_2/merge/naming.py | 22 +++++++++- .../merge/transfer_data/transfer_core.py | 18 -------- .../addons/asset_pipeline_2/merge/util.py | 20 ++++++++- .../addons/asset_pipeline_2/ops.py | 41 ++++--------------- 4 files changed, 49 insertions(+), 52 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index d1094928..3045b60e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -22,6 +22,7 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from .util import get_storage_of_id from .. import constants, config +from .util import data_type_from_transfer_data_key DELIMITER = "." @@ -151,7 +152,7 @@ def get_name_with_task_layer_prefix(name: str, task_layer_owner: str) -> str: if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): name = name.replace(name.split(".")[0], "")[1:] break - + prefix = config.TASK_LAYER_TYPES[task_layer_owner] return prefix + "." + name @@ -166,3 +167,22 @@ def get_id_type_name(id_type: bpy.types) -> str: str: Name of an ID type e.g. bpy.types.Object will return 'Object' """ return str(id_type).split("'bpy_types.")[1].replace("'>", "") + + +def transfer_data_update_prefix(transfer_data_item): + prefix_types = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] + if transfer_data_item.type not in prefix_types: + return + + obj = transfer_data_item.id_data + td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) + new_name = get_name_with_task_layer_prefix( + transfer_data_item.name, transfer_data_item.owner + ) + + if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): + return + + td_data[transfer_data_item.name].name = new_name + transfer_data_item.name = new_name + return True diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index e76ecfb7..6d055b20 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -184,21 +184,3 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: transfer_data_item=transfer_data_item, target_obj=target_obj, ) - - -def data_type_from_transfer_data_key(obj: bpy.types.Object, td_type: str): - """Returns the data on an object that is referred to by the transfer data type""" - if td_type == constants.VERTEX_GROUP_KEY: - return obj.vertex_groups - if td_type == constants.MODIFIER_KEY: - return obj.modifiers - if td_type == constants.CONSTRAINT_KEY: - return obj.constraints - if td_type == constants.MATERIAL_SLOT_KEY: - return obj.material_slots - if td_type == constants.SHAPE_KEY_KEY: - return obj.data.shape_keys.key_blocks - if td_type == constants.ATTRIBUTE_KEY: - return obj.data.attributes - if td_type == constants.PARENT_KEY: - return obj.parent diff --git a/scripts-blender/addons/asset_pipeline_2/merge/util.py b/scripts-blender/addons/asset_pipeline_2/merge/util.py index 26f590d5..0d62f0be 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/util.py @@ -19,7 +19,7 @@ # (c) 2021, Blender Foundation - Paul Golter from typing import Dict, Any, Tuple, Generator - +from .. import constants import bpy from bpy import types @@ -99,3 +99,21 @@ def traverse_collection_tree( yield collection for child in collection.children: yield from traverse_collection_tree(child) + + +def data_type_from_transfer_data_key(obj: bpy.types.Object, td_type: str): + """Returns the data on an object that is referred to by the transfer data type""" + if td_type == constants.VERTEX_GROUP_KEY: + return obj.vertex_groups + if td_type == constants.MODIFIER_KEY: + return obj.modifiers + if td_type == constants.CONSTRAINT_KEY: + return obj.constraints + if td_type == constants.MATERIAL_SLOT_KEY: + return obj.material_slots + if td_type == constants.SHAPE_KEY_KEY: + return obj.data.shape_keys.key_blocks + if td_type == constants.ATTRIBUTE_KEY: + return obj.data.attributes + if td_type == constants.PARENT_KEY: + return obj.parent diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 45e76349..5f94a8fb 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,12 +2,11 @@ import bpy from . import config import os from pathlib import Path +from .merge.naming import transfer_data_update_prefix from .merge.task_layer import ( set_local_task_layers, draw_task_layer_selection, ) -from .merge.naming import get_name_with_task_layer_prefix -from .merge.transfer_data.transfer_core import data_type_from_transfer_data_key from .merge.publish import get_next_published_file, find_all_published from .images import save_images from . import constants @@ -441,39 +440,17 @@ class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): return False return True - def transfer_data_update_prefix(self, context, transfer_data_item): - obj = transfer_data_item.id_data - td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) - new_name = get_name_with_task_layer_prefix( - transfer_data_item.name, transfer_data_item.owner - ) - - if new_name == transfer_data_item.name or not td_data.get( - transfer_data_item.name - ): - return - - td_data[transfer_data_item.name].name = new_name - transfer_data_item.name = new_name - self.report( - {'INFO'}, - f"Renamed {transfer_data_item.type} on '{obj.name}' to {new_name}", - ) - self._updated_prefix = True - - def transfer_data_using_prefix_get(self, context, obj): - types_to_update = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] - return [td for td in obj.transfer_data_ownership if td.type in types_to_update] - - def transfer_data_update_all_prefixes(self, context, obj): - transfer_data_items = self.transfer_data_using_prefix_get(context, obj) - for transfer_data_item in transfer_data_items: - self.transfer_data_update_prefix(context, transfer_data_item) - def execute(self, context: bpy.types.Context): objs = context.selected_objects for obj in objs: - self.transfer_data_update_all_prefixes(context, obj) + transfer_data_items = obj.transfer_data_ownership + for transfer_data_item in transfer_data_items: + if transfer_data_update_prefix(transfer_data_item): + self.report( + {'INFO'}, + f"Renamed {transfer_data_item.type} on '{obj.name}'", + ) + self._updated_prefix = True if not self._updated_prefix: self.report( -- 2.30.2 From adb024efb2e63992835ab23abca6cdc882c8a43d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:57:56 -0500 Subject: [PATCH 335/429] Asset Pipe: Clean-Up Task Layer Prefix Functions + Add Basename Function --- .../addons/asset_pipeline_2/merge/naming.py | 57 +++++++++++-------- .../merge/transfer_data/transfer_functions.py | 6 +- .../addons/asset_pipeline_2/ops.py | 4 +- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 3045b60e..94e07bf4 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -135,7 +135,7 @@ def get_name_with_asset_prefix(name: str) -> str: return prefix + name -def get_name_with_task_layer_prefix(name: str, task_layer_owner: str) -> str: +def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: """Returns a string with the prefix if it is not already set. Users can specify a prefix to live on all objects during the asset creation process. This prefix is stored in the scene. @@ -148,13 +148,39 @@ def get_name_with_task_layer_prefix(name: str, task_layer_owner: str) -> str: """ if name.startswith(config.TASK_LAYER_TYPES[task_layer_owner] + "."): return name - for task_layer_key in config.TASK_LAYER_TYPES: - if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): - name = name.replace(name.split(".")[0], "")[1:] - break + + base_name = task_layer_prefix_basename_get(name) + if name == base_name: + return prefix = config.TASK_LAYER_TYPES[task_layer_owner] - return prefix + "." + name + return prefix + "." + base_name + + +def task_layer_prefix_basename_get(name: str) -> str: + for task_layer_key in config.TASK_LAYER_TYPES: + if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): + return name.replace(name.split(".")[0], "")[1:] + return name + + +def task_layer_prefix_update_name(transfer_data_item): + prefix_types = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] + if transfer_data_item.type not in prefix_types: + return + + obj = transfer_data_item.id_data + td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) + new_name = task_layer_prefix_name_get( + transfer_data_item.name, transfer_data_item.owner + ) + + if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): + return + + td_data[transfer_data_item.name].name = new_name + transfer_data_item.name = new_name + return True def get_id_type_name(id_type: bpy.types) -> str: @@ -167,22 +193,3 @@ def get_id_type_name(id_type: bpy.types) -> str: str: Name of an ID type e.g. bpy.types.Object will return 'Object' """ return str(id_type).split("'bpy_types.")[1].replace("'>", "") - - -def transfer_data_update_prefix(transfer_data_item): - prefix_types = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] - if transfer_data_item.type not in prefix_types: - return - - obj = transfer_data_item.id_data - td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) - new_name = get_name_with_task_layer_prefix( - transfer_data_item.name, transfer_data_item.owner - ) - - if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): - return - - td_data[transfer_data_item.name].name = new_name - transfer_data_item.name = new_name - return True diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index ede5d4ee..4d2a83bf 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -1,6 +1,6 @@ import bpy from bpy import context -from ..naming import get_basename, get_name_with_task_layer_prefix +from ..naming import get_basename, task_layer_prefix_name_get from ..drivers import find_drivers, copy_driver from ..visibility import override_obj_visability from .transfer_util import ( @@ -105,7 +105,7 @@ def init_modifiers(scene, obj, use_default_owner: bool): transfer_data = obj.transfer_data_ownership task_layer_owner = get_transfer_data_owner(td_type_key, use_default_owner) for mod in obj.modifiers: - mod.name = get_name_with_task_layer_prefix(mod.name, task_layer_owner) + mod.name = task_layer_prefix_name_get(mod.name, task_layer_owner) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, mod.name, td_type_key) if len(matches) == 0: @@ -202,7 +202,7 @@ def init_constraints(scene, obj, use_default_owner: bool): transfer_data = obj.transfer_data_ownership task_layer_owner = get_transfer_data_owner(td_type_key, use_default_owner) for const in obj.constraints: - const.name = get_name_with_task_layer_prefix(const.name, task_layer_owner) + const.name = task_layer_prefix_name_get(const.name, task_layer_owner) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, const.name, td_type_key) if len(matches) == 0: diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 5f94a8fb..84a39a0b 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,7 +2,7 @@ import bpy from . import config import os from pathlib import Path -from .merge.naming import transfer_data_update_prefix +from .merge.naming import task_layer_prefix_update_name from .merge.task_layer import ( set_local_task_layers, draw_task_layer_selection, @@ -445,7 +445,7 @@ class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): for obj in objs: transfer_data_items = obj.transfer_data_ownership for transfer_data_item in transfer_data_items: - if transfer_data_update_prefix(transfer_data_item): + if task_layer_prefix_update_name(transfer_data_item): self.report( {'INFO'}, f"Renamed {transfer_data_item.type} on '{obj.name}'", -- 2.30.2 From a421b6fa44f61e71d47fc51f091922cf9d196387 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 18:59:08 -0500 Subject: [PATCH 336/429] Asset Pipe: Clear old TODO --- scripts-blender/addons/asset_pipeline_2/ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 84a39a0b..f1b59f02 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -495,7 +495,6 @@ class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): ) def execute(self, context: bpy.types.Context): - # TODO Report Cancelled if new owner not in local task layer if self._surrendered_transfer_data.owner == self._old_onwer: self.report( {'ERROR'}, -- 2.30.2 From 5550ac97805380750733ca87c957590204221b76 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 19:07:45 -0500 Subject: [PATCH 337/429] Asset Pipe: Fix bug in `task_layer_prefix_name_get()` --- scripts-blender/addons/asset_pipeline_2/merge/naming.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 94e07bf4..27bac0e7 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -150,8 +150,6 @@ def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: return name base_name = task_layer_prefix_basename_get(name) - if name == base_name: - return prefix = config.TASK_LAYER_TYPES[task_layer_owner] return prefix + "." + base_name -- 2.30.2 From 1afa4f42c1769b9491e53dd3dc07187ca5d99c87 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 19:08:30 -0500 Subject: [PATCH 338/429] Asset Pipe: Consider Prefixes during Surrender Set/Transfer --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 11 +++++++++-- .../addons/asset_pipeline_2/merge/naming.py | 2 +- scripts-blender/addons/asset_pipeline_2/ops.py | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index ca00ed14..0de4e825 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -1,6 +1,11 @@ import bpy from typing import Dict, Set -from .naming import get_target_name, get_basename, get_name_with_asset_prefix +from .naming import ( + get_target_name, + get_basename, + get_name_with_asset_prefix, + task_layer_prefix_basename_get, +) from .util import get_storage_of_id from .transfer_data.transfer_util import transfer_data_add_entry from .shared_ids import get_shared_ids @@ -180,7 +185,9 @@ class AssetTransferMapping: other_transfer_data_item = None # Find Related Transfer Data Item on Target/Source Object for other_obj_transfer_data_item in other_obj.transfer_data_ownership: - if other_obj_transfer_data_item.name == transfer_data_item.name: + if task_layer_prefix_basename_get( + other_obj_transfer_data_item.name + ) == task_layer_prefix_basename_get(transfer_data_item.name): other_transfer_data_item = other_obj_transfer_data_item # TODO CONSIDER PREFIXES diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 27bac0e7..58c9fb0d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -162,7 +162,7 @@ def task_layer_prefix_basename_get(name: str) -> str: return name -def task_layer_prefix_update_name(transfer_data_item): +def task_layer_prefix_transfer_data_update(transfer_data_item): prefix_types = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] if transfer_data_item.type not in prefix_types: return diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index f1b59f02..1a8ef843 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -2,7 +2,7 @@ import bpy from . import config import os from pathlib import Path -from .merge.naming import task_layer_prefix_update_name +from .merge.naming import task_layer_prefix_transfer_data_update from .merge.task_layer import ( set_local_task_layers, draw_task_layer_selection, @@ -445,7 +445,7 @@ class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): for obj in objs: transfer_data_items = obj.transfer_data_ownership for transfer_data_item in transfer_data_items: - if task_layer_prefix_update_name(transfer_data_item): + if task_layer_prefix_transfer_data_update(transfer_data_item): self.report( {'INFO'}, f"Renamed {transfer_data_item.type} on '{obj.name}'", @@ -502,7 +502,7 @@ class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): ) return {'CANCELLED'} self._surrendered_transfer_data.surrender = False - # TODO RENAME PREFIXES HERE + task_layer_prefix_transfer_data_update(self._surrendered_transfer_data) return {'FINISHED'} -- 2.30.2 From f73920b4e3aeaf19ea39930fea9bd2065c999bd3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 19:34:59 -0500 Subject: [PATCH 339/429] Asset Pipe: Rename Surrender Property in UI --- .../addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 341e9871..c591fa19 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -27,7 +27,7 @@ def draw_transfer_data_type( for transfer_data_item in transfer_data: row = box.row() row.label(text=f"{transfer_data_item.name}: ") - row.prop(transfer_data_item, "surrender") + row.prop(transfer_data_item, "surrender", text="Surrender") draw_task_layer_selection( row, scene, -- 2.30.2 From 544de328699bf0398306e331d58d78a667867a38 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 19:35:13 -0500 Subject: [PATCH 340/429] Asset Pipe: Fix Bug in Surrender Logic --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 0de4e825..df23c787 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -202,11 +202,11 @@ class AssetTransferMapping: and other_transfer_data_item.owner != transfer_data_item.owner ): if transfer_data_item.owner in self._local_tls: - target_obj = obj - source_obj = other_obj - else: target_obj = other_obj source_obj = obj + else: + target_obj = obj + source_obj = other_obj return self._get_transfer_data_map_item( source_obj, target_obj, transfer_data_item -- 2.30.2 From a9ddc42ba2a87861e30c1a94f0b9a83b893a2be7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 19:40:01 -0500 Subject: [PATCH 341/429] Revert "Asset Pipe: Fix Bug in Surrender Logic" This reverts commit 544de328699bf0398306e331d58d78a667867a38. --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index df23c787..0de4e825 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -202,11 +202,11 @@ class AssetTransferMapping: and other_transfer_data_item.owner != transfer_data_item.owner ): if transfer_data_item.owner in self._local_tls: - target_obj = other_obj - source_obj = obj - else: target_obj = obj source_obj = other_obj + else: + target_obj = other_obj + source_obj = obj return self._get_transfer_data_map_item( source_obj, target_obj, transfer_data_item -- 2.30.2 From 909f3eea9b94e6940a704b0025f8084d700d20fa Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 9 Nov 2023 20:11:15 -0500 Subject: [PATCH 342/429] Asset Pipe: Fix Bug in Task Layer Prefix Naming --- .../addons/asset_pipeline_2/merge/naming.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 58c9fb0d..5d2935ab 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -146,13 +146,11 @@ def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: Returns: str: Returns name with prefix """ - if name.startswith(config.TASK_LAYER_TYPES[task_layer_owner] + "."): - return name - - base_name = task_layer_prefix_basename_get(name) - + for task_layer_key in config.TASK_LAYER_TYPES: + if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): + return name prefix = config.TASK_LAYER_TYPES[task_layer_owner] - return prefix + "." + base_name + return prefix + "." + name def task_layer_prefix_basename_get(name: str) -> str: @@ -169,10 +167,9 @@ def task_layer_prefix_transfer_data_update(transfer_data_item): obj = transfer_data_item.id_data td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) - new_name = task_layer_prefix_name_get( - transfer_data_item.name, transfer_data_item.owner - ) - + base_name = task_layer_prefix_basename_get(transfer_data_item.name) + prefix = config.TASK_LAYER_TYPES[transfer_data_item.owner] + new_name = prefix + "." + base_name if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): return -- 2.30.2 From 7cd96b68e6269ff04ad74015e8322ab6840eff85 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 10 Nov 2023 13:43:04 -0500 Subject: [PATCH 343/429] Asset Pipe: Fix Surrender Logic --- .../asset_pipeline_2/merge/asset_mapping.py | 80 +++++++------------ 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 0de4e825..1c4be2b6 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -167,67 +167,31 @@ class AssetTransferMapping: if not other_obj: return for other_transfer_data_item in other_obj.transfer_data_ownership: - if ( - other_transfer_data_item.type == transfer_data_item.type - and other_transfer_data_item.name == transfer_data_item.name + if other_transfer_data_item.type == transfer_data_item.type and ( + task_layer_prefix_basename_get(other_transfer_data_item.name) + == task_layer_prefix_basename_get(transfer_data_item.name) ): check_transfer_data_item = other_transfer_data_item if check_transfer_data_item is None: return - if check_transfer_data_item.owner != transfer_data_item.owner: + if check_transfer_data_item.owner != transfer_data_item.owner and not ( + check_transfer_data_item.surrender or transfer_data_item.surrender + ): self.conflict_transfer_data.append(transfer_data_item) print("CONFLICT FOUND") return True - def transfer_data_map_surrendered(self, transfer_data_item): + def transfer_data_get_other(self, transfer_data_item): + # THIS IS FOR WHEN SURRENDERED DATA HAS BEEN REPLACED obj = transfer_data_item.id_data other_obj = bpy.data.objects.get(get_target_name(obj.name)) - other_transfer_data_item = None # Find Related Transfer Data Item on Target/Source Object for other_obj_transfer_data_item in other_obj.transfer_data_ownership: if task_layer_prefix_basename_get( other_obj_transfer_data_item.name ) == task_layer_prefix_basename_get(transfer_data_item.name): - other_transfer_data_item = other_obj_transfer_data_item - - # TODO CONSIDER PREFIXES - - if not other_transfer_data_item: - return None, None - - # If the other transfer data is surrendered map it accordingly - if ( - other_transfer_data_item.surrender - and not transfer_data_item.surrender - and other_transfer_data_item.owner != transfer_data_item.owner - ): - if transfer_data_item.owner in self._local_tls: - target_obj = obj - source_obj = other_obj - else: - target_obj = other_obj - source_obj = obj - - return self._get_transfer_data_map_item( - source_obj, target_obj, transfer_data_item - ) - - if ( - transfer_data_item.surrender - and not other_transfer_data_item.surrender - and other_transfer_data_item.owner != transfer_data_item.owner - ): - if other_transfer_data_item.owner in self._local_tls: - target_obj = obj - source_obj = other_obj - else: - target_obj = other_obj - source_obj = obj - - return self._get_transfer_data_map_item( - source_obj, target_obj, other_transfer_data_item - ) - return None, None + return other_obj_transfer_data_item + return None def _gen_transfer_data_map(self): context = bpy.context @@ -239,18 +203,19 @@ class AssetTransferMapping: objs = [source_obj, target_obj] for obj in objs: for transfer_data_item in obj.transfer_data_ownership: - name, map_item = self.transfer_data_map_surrendered( - transfer_data_item - ) - if bool(name and map_item): - transfer_data_map[name] = map_item - continue - self._check_transfer_data_conflict(obj, transfer_data_item) if ( transfer_data_item.owner in self._local_tls and obj.name.endswith(constants.LOCAL_SUFFIX) ): + other_td = self.transfer_data_get_other(transfer_data_item) + if other_td: + if ( + transfer_data_item.surrender + and not other_td.surrender + and transfer_data_item.owner != other_td.owner + ): + continue name, map_item = self._get_transfer_data_map_item( obj, target_obj, transfer_data_item ) @@ -261,6 +226,15 @@ class AssetTransferMapping: and transfer_data_item.owner != "NONE" and obj.name.endswith(constants.EXTERNAL_SUFFIX) ): + other_td = self.transfer_data_get_other(transfer_data_item) + if other_td: + if ( + transfer_data_item.surrender + and not other_td.surrender + and transfer_data_item.owner != other_td.owner + ): + continue + name, map_item = self._get_transfer_data_map_item( obj, target_obj, transfer_data_item ) -- 2.30.2 From 4e90b4fe623d75200668c812c4a07cad315e8b13 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 10 Nov 2023 13:54:30 -0500 Subject: [PATCH 344/429] Asset Pipe: Improve Surrender Ownership UI --- .../merge/transfer_data/transfer_ui.py | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index c591fa19..5bd9561c 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -1,6 +1,6 @@ import bpy from ... import constants -from ..task_layer import draw_task_layer_selection +from ..task_layer import draw_task_layer_selection, get_local_task_layers def draw_transfer_data_type( @@ -27,20 +27,26 @@ def draw_transfer_data_type( for transfer_data_item in transfer_data: row = box.row() row.label(text=f"{transfer_data_item.name}: ") - row.prop(transfer_data_item, "surrender", text="Surrender") - draw_task_layer_selection( - row, - scene, - transfer_data_item, - transfer_data_item.owner, - "owner", - transfer_data_item.get("use_default_owner"), - ) + if transfer_data_item.get("surrender"): + enabled = ( + False if transfer_data_item.owner in get_local_task_layers() else True + ) row.operator( "assetpipe.update_surrendered_transfer_data" ).transfer_data_item_name = transfer_data_item.name - row.enabled = True + row.enabled = enabled + return + else: + row.prop(transfer_data_item, "surrender", text="Surrender") + draw_task_layer_selection( + row, + scene, + transfer_data_item, + transfer_data_item.owner, + "owner", + transfer_data_item.get("use_default_owner"), + ) def draw_transfer_data( -- 2.30.2 From 019c610f7830e102294f4a44ccfd96509bf0475a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 10 Nov 2023 16:41:03 -0500 Subject: [PATCH 345/429] Asset Pipe: Fix Vertex Transfer Bug co-authorted by demeter@blender.org --- .../merge/transfer_data/transfer_functions.py | 174 +++++++++++++++--- 1 file changed, 149 insertions(+), 25 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 4d2a83bf..a3c7d9b3 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -1,5 +1,5 @@ import bpy -from bpy import context +from typing import Dict, Tuple, List from ..naming import get_basename, task_layer_prefix_name_get from ..drivers import find_drivers, copy_driver from ..visibility import override_obj_visability @@ -15,8 +15,7 @@ from ... import constants import mathutils import bmesh import numpy as np -import time - +from mathutils import Vector, kdtree ## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES @@ -55,35 +54,160 @@ def transfer_vertex_group( if target_obj == source_obj: return - if target_obj.vertex_groups.get(vertex_group_name): - target_obj.vertex_groups.remove(target_obj.vertex_groups.get(vertex_group_name)) - if not source_obj.vertex_groups.get(vertex_group_name): print(f"ERROR Vertex Group {vertex_group_name} not found in {source_obj.name}") return + vgroups = source_obj.vertex_groups + + kd_tree = build_kdtree(source_obj.data) + tgt_vg = target_obj.vertex_groups.get(vertex_group_name) + if tgt_vg: + target_obj.vertex_groups.remove(tgt_vg) - source_obj.vertex_groups.active = source_obj.vertex_groups.get(vertex_group_name) - # HACK without this sleep function Push will crash when transferring large amount of vertex groups - time.sleep(0.00000000000001) + vert_influence_map = build_vert_influence_map( + source_obj, target_obj, kd_tree, 2 + ) + transfer_vertex_groups(source_obj, target_obj, vert_influence_map, vgroups) - # DEBUG WHY THIS FAILS TO TRANSFER VERTEX GROUPS IN 4.0 - with context.temp_override( - object=source_obj, selected_editable_objects=[target_obj, source_obj] - ): - bpy.ops.object.data_transfer( - data_type="VGROUP_WEIGHTS", - use_create=True, - vert_mapping='POLYINTERP_NEAREST', - layers_select_src="ACTIVE", - layers_select_dst="NAME", - mix_mode="REPLACE", + +def precalc_and_transfer_single_group(source_obj, target_obj, vgroup_name, expand=2): + """Convenience function to transfer a single group. For transferring multiple groups, + this is very inefficient and shouldn't be used. + + Instead, you should: + - build_kd_tree ONCE per source mesh. + - build_vert_influence_map and transfer_vertex_groups ONCE per object pair. + """ + + # Remove group from the target obj if it already exists. + tgt_vg = target_obj.vertex_groups.get(vgroup_name) + if tgt_vg: + target_obj.vertex_groups.remove(tgt_vg) + + kd_tree = build_kdtree(source_obj.data) + vert_influence_map = build_vert_influence_map( + source_obj, target_obj, kd_tree, expand + ) + transfer_vertex_groups( + source_obj, + target_obj, + vert_influence_map, + vgroups=[source_obj.vertex_groups[vgroup_name]], + ) + + +def build_kdtree(mesh): + kd = kdtree.KDTree(len(mesh.vertices)) + for i, v in enumerate(mesh.vertices): + kd.insert(v.co, i) + kd.balance() + return kd + + +def build_vert_influence_map(obj_from, obj_to, kd_tree, expand=2): + verts_of_edge = { + i: (e.vertices[0], e.vertices[1]) for i, e in enumerate(obj_from.data.edges) + } + + edges_of_vert: Dict[int, List[int]] = {} + for edge_idx, edge in enumerate(obj_from.data.edges): + for vert_idx in edge.vertices: + if vert_idx not in edges_of_vert: + edges_of_vert[vert_idx] = [] + edges_of_vert[vert_idx].append(edge_idx) + + # A mapping from target vertex index to a list of source vertex indicies and + # their influence. + # This can be pre-calculated once per object pair, to minimize re-calculations + # of subsequent transferring of individual vertex groups. + vert_influence_map: List[int, List[Tuple[int, float]]] = {} + for i, dest_vert in enumerate(obj_to.data.vertices): + vert_influence_map[i] = get_source_vert_influences( + dest_vert, obj_from, kd_tree, expand, edges_of_vert, verts_of_edge ) - if not target_obj.vertex_groups.get(vertex_group_name): - print( - f"FAILED to Transfer Vertex Group {vertex_group_name} to {target_obj.name}" - ) - return + return vert_influence_map + + +def get_source_vert_influences( + target_vert, obj_from, kd_tree, expand=2, edges_of_vert={}, verts_of_edge={} +) -> List[Tuple[int, float]]: + _coord, idx, dist = get_nearest_vert(target_vert.co, kd_tree) + source_vert_indices = [idx] + + if dist == 0: + # If the vertex position is a perfect match, just use that one vertex with max influence. + return [(idx, 1)] + + for i in range(0, expand): + new_indices = [] + for vert_idx in source_vert_indices: + for edge in edges_of_vert[vert_idx]: + vert_other = other_vert_of_edge(edge, vert_idx, verts_of_edge) + if vert_other not in source_vert_indices: + new_indices.append(vert_other) + source_vert_indices.extend(new_indices) + + distances: List[Tuple[int, float]] = [] + distance_total = 0 + for src_vert_idx in source_vert_indices: + distance = (target_vert.co - obj_from.data.vertices[src_vert_idx].co).length + distance_total += distance + distances.append((src_vert_idx, distance)) + + # Calculate influences such that the total of all influences adds up to 1.0, + # and the influence is inversely correlated with the distance. + parts = [1 / (dist / distance_total) for idx, dist in distances] + parts_sum = sum(parts) + + influences = [ + (idx, 1 if dist == 0 else part / parts_sum) + for part, dist in zip(parts, distances) + ] + + return influences + + +def get_nearest_vert( + coords: Vector, kd_tree: kdtree.KDTree +) -> Tuple[Vector, int, float]: + """Return coordinate, index, and distance of nearest vert to coords in kd_tree.""" + return kd_tree.find(coords) + + +def other_vert_of_edge( + edge: int, vert: int, verts_of_edge: Dict[int, Tuple[int, int]] +) -> int: + verts = verts_of_edge[edge] + assert vert in verts, f"Vert {vert} not part of edge {edge}." + return verts[0] if vert == verts[1] else verts[1] + + +def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups): + """Transfer src_vgroups in obj_from to obj_to using a pre-calculated vert_influence_map.""" + + for i, dest_vert in enumerate(obj_to.data.vertices): + source_verts = vert_influence_map[i] + + # Vertex Group Name : Weight + vgroup_weights = {} + + for src_vert_idx, influence in source_verts: + for group in obj_from.data.vertices[src_vert_idx].groups: + group_idx = group.group + vg = obj_from.vertex_groups[group_idx] + if vg.name not in src_vgroups: + continue + if vg.name not in vgroup_weights: + vgroup_weights[vg.name] = 0 + vgroup_weights[vg.name] += vg.weight(src_vert_idx) * influence + + # Assign final weights of this vertex in the vertex groups. + for vg_name in vgroup_weights.keys(): + target_vg = obj_to.vertex_groups.get(vg_name) + if target_vg == None: + target_vg = obj_to.vertex_groups.new(name=vg_name) + target_vg.add([dest_vert.index], vgroup_weights[vg_name], 'REPLACE') # MODIFIERS def modifiers_clean(obj): -- 2.30.2 From adbbcebff98f0988fdb265f4cfbf820bd3954ce1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 11:29:09 -0500 Subject: [PATCH 346/429] Asset Pipe: Clean-Up Vertex Transfer Code - Ensure only ony vertex group is transferred via `precalc_and_transfer_single_group()` - Add Debug TODO because this code doesn't appear to be working on a single vertex transfer --- .../merge/transfer_data/transfer_functions.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index a3c7d9b3..ac8ed1f9 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -57,17 +57,11 @@ def transfer_vertex_group( if not source_obj.vertex_groups.get(vertex_group_name): print(f"ERROR Vertex Group {vertex_group_name} not found in {source_obj.name}") return - vgroups = source_obj.vertex_groups - - kd_tree = build_kdtree(source_obj.data) - tgt_vg = target_obj.vertex_groups.get(vertex_group_name) - if tgt_vg: - target_obj.vertex_groups.remove(tgt_vg) - vert_influence_map = build_vert_influence_map( - source_obj, target_obj, kd_tree, 2 + # TODO DEBUG WHY THIS ISN'T WORKING + precalc_and_transfer_single_group( + source_obj, target_obj, vertex_group_name, expand=2 ) - transfer_vertex_groups(source_obj, target_obj, vert_influence_map, vgroups) def precalc_and_transfer_single_group(source_obj, target_obj, vgroup_name, expand=2): @@ -92,7 +86,7 @@ def precalc_and_transfer_single_group(source_obj, target_obj, vgroup_name, expan source_obj, target_obj, vert_influence_map, - vgroups=[source_obj.vertex_groups[vgroup_name]], + [source_obj.vertex_groups[vgroup_name]], ) @@ -209,6 +203,7 @@ def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups): target_vg = obj_to.vertex_groups.new(name=vg_name) target_vg.add([dest_vert.index], vgroup_weights[vg_name], 'REPLACE') + # MODIFIERS def modifiers_clean(obj): transfer_data_clean( -- 2.30.2 From 31b178db96b757c0e8271718283b110b24272c4e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 12:19:24 -0500 Subject: [PATCH 347/429] Asset Pipe: Fix Vertex Transfer Bug - co-authorted by demeter@blender.org https://projects.blender.org/studio/blender-studio-pipeline/commit/9846af3db2048fe906b3812c58102487a5659148#diff-6fc85c9f456e2162ac39b83055405feb47d0ab1a --- .../merge/transfer_data/transfer_functions.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index ac8ed1f9..8306a112 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -58,7 +58,6 @@ def transfer_vertex_group( print(f"ERROR Vertex Group {vertex_group_name} not found in {source_obj.name}") return - # TODO DEBUG WHY THIS ISN'T WORKING precalc_and_transfer_single_group( source_obj, target_obj, vertex_group_name, expand=2 ) @@ -180,6 +179,11 @@ def other_vert_of_edge( def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups): """Transfer src_vgroups in obj_from to obj_to using a pre-calculated vert_influence_map.""" + for src_vg in src_vgroups: + target_vg = obj_to.vertex_groups.get(src_vg.name) + if target_vg == None: + target_vg = obj_to.vertex_groups.new(name=src_vg.name) + for i, dest_vert in enumerate(obj_to.data.vertices): source_verts = vert_influence_map[i] @@ -190,7 +194,7 @@ def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups): for group in obj_from.data.vertices[src_vert_idx].groups: group_idx = group.group vg = obj_from.vertex_groups[group_idx] - if vg.name not in src_vgroups: + if vg not in src_vgroups: continue if vg.name not in vgroup_weights: vgroup_weights[vg.name] = 0 @@ -199,8 +203,6 @@ def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups): # Assign final weights of this vertex in the vertex groups. for vg_name in vgroup_weights.keys(): target_vg = obj_to.vertex_groups.get(vg_name) - if target_vg == None: - target_vg = obj_to.vertex_groups.new(name=vg_name) target_vg.add([dest_vert.index], vgroup_weights[vg_name], 'REPLACE') -- 2.30.2 From dcfd29dfb9077e61c9b071c51d96fe697f9ea604 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 15:04:35 -0500 Subject: [PATCH 348/429] Asset Pipe: Add Operator to Batch Change Ownership --- .../asset_pipeline_2/merge/task_layer.py | 7 +- .../addons/asset_pipeline_2/ops.py | 175 ++++++++++++++++++ scripts-blender/addons/asset_pipeline_2/ui.py | 3 +- 3 files changed, 181 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 6ef11403..17eb0666 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -46,6 +46,7 @@ def draw_task_layer_selection( data_owner_name, show_all_task_layers=False, show_local=False, + text="", ): # TODO Simplify Arguments and add Type Hints / Doc String if show_all_task_layers == True: @@ -54,7 +55,7 @@ def draw_task_layer_selection( data_owner_name, scene.asset_pipeline, 'all_task_layers', - text="", + text=text, ) return if ( @@ -67,7 +68,7 @@ def draw_task_layer_selection( data_owner_name, scene.asset_pipeline, 'all_task_layers', - text="", + text=text, ) return else: @@ -76,5 +77,5 @@ def draw_task_layer_selection( data_owner_name, scene.asset_pipeline, 'local_task_layers', - text="", + text=text, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 1a8ef843..e8d04e83 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -6,6 +6,7 @@ from .merge.naming import task_layer_prefix_transfer_data_update from .merge.task_layer import ( set_local_task_layers, draw_task_layer_selection, + get_local_task_layers, ) from .merge.publish import get_next_published_file, find_all_published from .images import save_images @@ -506,6 +507,179 @@ class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): return {'FINISHED'} +def get_task_layer_types(self, context): + return + + +class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): + bl_idname = "assetpipe.batch_ownership_change" + bl_label = "Batch Re-Assign" + bl_description = """Re-Assign Ownership in a batch operation""" + + name_filter: bpy.props.StringProperty( + name="Filter by Name", + description="Filter Object or Transfer Data items by name", + default="", + ) + + data_source: bpy.props.EnumProperty( + name="Source", + items=( + ('SELECT', "Selected", "Update Selected Objects Only"), + ('ALL', "All", "Update All Objects"), + ), + ) + + data_status: bpy.props.EnumProperty( + name="Owner Status", + items=( + ('OWNED', "Owned", "Only objects that already have assignment"), + ('ALL', "Any", "Set Ownership on any objects, even without an owner"), + ), + ) + + owner_filter: bpy.props.EnumProperty( + name="Owner Status", + items=( + ('LOCAL', "Local Task Layers", "Only objects that already have assignment"), + ( + 'ALL', + "All Task Layers", + "Set Ownership on any objects, even without an owner", + ), + ), + ) + + data_type: bpy.props.EnumProperty( + name="Type", + items=( + ('OBJECT', "Object", "Update Owner of Objects"), + ( + 'TRANSFER_DATA', + "Transfer Data", + "Update Owner of Transfer Data with Objects", + ), + ), + ) + + transfer_data_type: bpy.props.EnumProperty( + name="Type Filter", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS + ) + owner_selection: bpy.props.StringProperty(name="Set Owner") + surrender_selection: bpy.props.BoolProperty(name="Set Surrender", default=False) + + def _filter_by_name(self, context, unfiltered_list: []): + if self.name_filter == "": + return unfiltered_list + return [item for item in unfiltered_list if self.name_filter in item.name] + + def _get_transfer_data_to_update(self, context, objs): + transfer_data_items_to_update = [] + if self.data_type == "TRANSFER_DATA": + for obj in objs: + filtered_transfer_data = self._filter_by_name( + context, obj.transfer_data_ownership + ) + for transfer_data_item in filtered_transfer_data: + if self.transfer_data_type != "NONE": + if transfer_data_item.type == self.transfer_data_type: + transfer_data_items_to_update.append(transfer_data_item) + else: + transfer_data_items_to_update.append(transfer_data_item) + return transfer_data_items_to_update + + def _get_object_to_update(self, context): + objs = ( + context.selected_objects + if self.data_source == "SELECT" + else context.scene.objects + ) + if self.data_status == "OWNED": + return [ + item + for item in self._filter_by_name(context, objs) + if item.asset_id_owner != "NONE" + ] + return self._filter_by_name(context, objs) + + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + # if self.owner_selection == "NONE": + # self.owner_selection = get_local_task_layers()[0] + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + layout = self.layout + split = layout.split(align=True) + layout.row(align=True).prop(self, "data_source", expand=True) + + layout.prop(self, "data_type", text="Update Owner of") + + if self.data_type == "OBJECT": + box = layout.box() + box.prop(self, "data_status") + + if self.data_type == "TRANSFER_DATA": + box = layout.box() + box.prop(self, "transfer_data_type") + box.prop(self, "owner_filter", text="Owner Filter") + box.prop(self, "name_filter", text="Name Filter") + + if self.owner_filter == "LOCAL": + show_local = True + show_all_task_layers = False + else: + show_local = False + show_all_task_layers = True + draw_task_layer_selection( + box, + context.scene, + self, + self.owner_selection, + 'owner_selection', + show_all_task_layers=show_all_task_layers, + show_local=show_local, + text="Set Owner", + ) + # TODO Logically this can't be assigned to all objects, only ones I Own? + # if self.data_type == "TRANSFER_DATA": + # box.prop(self, "surrender_selection", expand=True) + + objs = self._get_object_to_update(context) + if self.data_type == "OBJECT": + data_type_name = "Object(s)" + length = len(objs) if objs else 0 + else: + transfer_data_items_to_update = self._get_transfer_data_to_update( + context, objs + ) + data_type_name = "Transfer Data Item(s)" + length = ( + len(transfer_data_items_to_update) + if transfer_data_items_to_update + else 0 + ) + + layout.label(text=f"Change Ownership on {length} {data_type_name}") + + def execute(self, context: bpy.types.Context): + objs = self._get_object_to_update(context) + + if self.data_type == "OBJECT": + for obj in objs: + obj.asset_id_owner = self.owner_selection + else: + transfer_data_items_to_update = self._get_transfer_data_to_update( + context, objs + ) + + for transfer_data_item_to_update in transfer_data_items_to_update: + transfer_data_item_to_update.owner = self.owner_selection + # if self.surrender_selection: + # transfer_data_item_to_update.surrender = True + + return {'FINISHED'} + + classes = ( ASSETPIPE_OT_update_ownership, ASSETPIPE_OT_sync_push, @@ -517,6 +691,7 @@ classes = ( ASSETPIPE_OT_revert_file, ASSETPIPE_OT_fix_prefixes, ASSETPIPE_OT_update_surrendered_transfer_data, + ASSETPIPE_OT_batch_ownership_change, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 46190fa5..c76f57be 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -28,7 +28,7 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): if not Path(bpy.data.filepath).exists: layout.label(text="File is not saved", icon="ERROR") return - + if asset_pipe.sync_error or asset_pipe.asset_collection.name.endswith( constants.LOCAL_SUFFIX ): @@ -79,6 +79,7 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): box = layout.box() box.operator("assetpipe.reset_ownership", icon="LOOP_BACK") box.operator("assetpipe.fix_prefixes", icon="CHECKMARK") + box.operator("assetpipe.batch_ownership_change") box.operator("assetpipe.revert_file", icon="FILE_TICK") # Task Layer Updater -- 2.30.2 From 4ef5744b3604a73f7dadbae6a368c3e20a85993b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 16:08:47 -0500 Subject: [PATCH 349/429] Asset Pipe: Filter by Owner in Batch Operation --- .../addons/asset_pipeline_2/ops.py | 74 +++++++++++-------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index e8d04e83..ce8d949f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -530,26 +530,6 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ), ) - data_status: bpy.props.EnumProperty( - name="Owner Status", - items=( - ('OWNED', "Owned", "Only objects that already have assignment"), - ('ALL', "Any", "Set Ownership on any objects, even without an owner"), - ), - ) - - owner_filter: bpy.props.EnumProperty( - name="Owner Status", - items=( - ('LOCAL', "Local Task Layers", "Only objects that already have assignment"), - ( - 'ALL', - "All Task Layers", - "Set Ownership on any objects, even without an owner", - ), - ), - ) - data_type: bpy.props.EnumProperty( name="Type", items=( @@ -562,6 +542,27 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ), ) + filter_owners: bpy.props.EnumProperty( + name="Owner Filter", + items=( + ('LOCAL', "If Locally Owned", "Only data that is owned locally"), + ('OWNED', "If Owned", "Only data that already have assignment"), + ('ALL', "No Filter", "Set Ownership on any data, even without an owner"), + ), + ) + + avaliable_owners: bpy.props.EnumProperty( + name="Avaliable Owners", + items=( + ('LOCAL', "Local Task Layers", "Only show local task layers as options"), + ( + 'ALL', + "All Task Layers", + "Show all task layers as options", + ), + ), + ) + transfer_data_type: bpy.props.EnumProperty( name="Type Filter", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS ) @@ -586,6 +587,12 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): transfer_data_items_to_update.append(transfer_data_item) else: transfer_data_items_to_update.append(transfer_data_item) + if self.filter_owners == "LOCAL": + return [ + item + for item in transfer_data_items_to_update + if item.owner in get_local_task_layers() + ] return transfer_data_items_to_update def _get_object_to_update(self, context): @@ -594,7 +601,13 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): if self.data_source == "SELECT" else context.scene.objects ) - if self.data_status == "OWNED": + if self.filter_owners == "LOCAL": + return [ + item + for item in self._filter_by_name(context, objs) + if item.asset_id_owner in get_local_task_layers() + ] + if self.filter_owners == "OWNED": return [ item for item in self._filter_by_name(context, objs) @@ -612,34 +625,37 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): split = layout.split(align=True) layout.row(align=True).prop(self, "data_source", expand=True) + # TODO SET HIGHER LEVEL ENUM IF USER WANTS TO UPDATE ONLY LOCALLY OWNED OR ALL OBJECTS + layout.prop(self, "data_type", text="Update Owner of") - if self.data_type == "OBJECT": - box = layout.box() - box.prop(self, "data_status") + box = layout.box() + + box.prop(self, "filter_owners") if self.data_type == "TRANSFER_DATA": - box = layout.box() box.prop(self, "transfer_data_type") - box.prop(self, "owner_filter", text="Owner Filter") box.prop(self, "name_filter", text="Name Filter") - if self.owner_filter == "LOCAL": + if self.avaliable_owners == "LOCAL": show_local = True show_all_task_layers = False else: show_local = False show_all_task_layers = True + row = box.row(align=True) + draw_task_layer_selection( - box, + row, context.scene, self, self.owner_selection, 'owner_selection', show_all_task_layers=show_all_task_layers, show_local=show_local, - text="Set Owner", + text="Owner", ) + row.prop(self, "avaliable_owners", text="") # TODO Logically this can't be assigned to all objects, only ones I Own? # if self.data_type == "TRANSFER_DATA": # box.prop(self, "surrender_selection", expand=True) -- 2.30.2 From f3d40b7ab3313fb3e72a0b7f3599b7146c4dabeb Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 16:21:49 -0500 Subject: [PATCH 350/429] Asset Pipe: Fix bug when drawing surrender UI --- .../addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 5bd9561c..24b32353 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -36,7 +36,6 @@ def draw_transfer_data_type( "assetpipe.update_surrendered_transfer_data" ).transfer_data_item_name = transfer_data_item.name row.enabled = enabled - return else: row.prop(transfer_data_item, "surrender", text="Surrender") draw_task_layer_selection( -- 2.30.2 From 0fb370cadc072837b933835e99cf307b2459d3f6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 16:23:59 -0500 Subject: [PATCH 351/429] Asset Pipe: Fix Bug in Filter by Owner during Batch Operation --- scripts-blender/addons/asset_pipeline_2/ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index ce8d949f..4cb05a65 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -601,13 +601,13 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): if self.data_source == "SELECT" else context.scene.objects ) - if self.filter_owners == "LOCAL": + if self.filter_owners == "LOCAL" and self.data_type == "OBJECT": return [ item for item in self._filter_by_name(context, objs) if item.asset_id_owner in get_local_task_layers() ] - if self.filter_owners == "OWNED": + if self.filter_owners == "OWNED" and self.data_type == "OBJECT": return [ item for item in self._filter_by_name(context, objs) -- 2.30.2 From 6aead7150fd4718f913538bbe0eac396f5f1162b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 16:36:29 -0500 Subject: [PATCH 352/429] Asset Pipe: Fix Surrender Logic in Batch Operation --- .../addons/asset_pipeline_2/ops.py | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 4cb05a65..4632a17f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -533,7 +533,11 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): data_type: bpy.props.EnumProperty( name="Type", items=( - ('OBJECT', "Object", "Update Owner of Objects"), + ( + 'OBJECT', + "Object", + "Update Owner of Objects", + ), ( 'TRANSFER_DATA', "Transfer Data", @@ -567,7 +571,11 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): name="Type Filter", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS ) owner_selection: bpy.props.StringProperty(name="Set Owner") - surrender_selection: bpy.props.BoolProperty(name="Set Surrender", default=False) + surrender_selection: bpy.props.BoolProperty( + name="Set Surrender", + default=False, + description="Surrender can only be set on objects/transfer data that are locally owned. Ownership cannot be changed while surrendering", + ) def _filter_by_name(self, context, unfiltered_list: []): if self.name_filter == "": @@ -616,22 +624,24 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): return self._filter_by_name(context, objs) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): - # if self.owner_selection == "NONE": - # self.owner_selection = get_local_task_layers()[0] return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context: bpy.types.Context): - layout = self.layout - split = layout.split(align=True) - layout.row(align=True).prop(self, "data_source", expand=True) + grey_out = True + if self.surrender_selection and self.data_type == "TRANSFER_DATA": + grey_out = False + self.filter_owners = "LOCAL" - # TODO SET HIGHER LEVEL ENUM IF USER WANTS TO UPDATE ONLY LOCALLY OWNED OR ALL OBJECTS + layout = self.layout + layout.row(align=True).prop(self, "data_source", expand=True) layout.prop(self, "data_type", text="Update Owner of") box = layout.box() - box.prop(self, "filter_owners") + filter_owner_row = box.row() + filter_owner_row.enabled = grey_out + filter_owner_row.prop(self, "filter_owners") if self.data_type == "TRANSFER_DATA": box.prop(self, "transfer_data_type") @@ -643,10 +653,11 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): else: show_local = False show_all_task_layers = True - row = box.row(align=True) + owner_row = box.row(align=True) + owner_row.enabled = grey_out draw_task_layer_selection( - row, + owner_row, context.scene, self, self.owner_selection, @@ -655,10 +666,10 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): show_local=show_local, text="Owner", ) - row.prop(self, "avaliable_owners", text="") - # TODO Logically this can't be assigned to all objects, only ones I Own? - # if self.data_type == "TRANSFER_DATA": - # box.prop(self, "surrender_selection", expand=True) + owner_row.prop(self, "avaliable_owners", text="") + + if self.data_type == "TRANSFER_DATA": + box.prop(self, "surrender_selection", expand=True) objs = self._get_object_to_update(context) if self.data_type == "OBJECT": @@ -689,9 +700,12 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ) for transfer_data_item_to_update in transfer_data_items_to_update: + if self.surrender_selection: + if transfer_data_item_to_update.owner in get_local_task_layers(): + transfer_data_item_to_update.surrender = True + continue + # TODO Update Prefixes when setting new ownership transfer_data_item_to_update.owner = self.owner_selection - # if self.surrender_selection: - # transfer_data_item_to_update.surrender = True return {'FINISHED'} -- 2.30.2 From 30eb7c7f85407cb50038a1f5a5befe120fa5cd4d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 17:54:17 -0500 Subject: [PATCH 353/429] Asset Pipe: Update Prefix During Batch Operation --- scripts-blender/addons/asset_pipeline_2/ops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 4632a17f..a272cf1f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -706,6 +706,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): continue # TODO Update Prefixes when setting new ownership transfer_data_item_to_update.owner = self.owner_selection + task_layer_prefix_transfer_data_update(transfer_data_item_to_update) return {'FINISHED'} -- 2.30.2 From 83b95ad6113b1b0f6882c51ad80017969d39d80d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 18:09:54 -0500 Subject: [PATCH 354/429] Asset Pipe: Fix Conflict Bug when Both Owners are Local --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 1c4be2b6..0e6e89f7 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -177,6 +177,14 @@ class AssetTransferMapping: if check_transfer_data_item.owner != transfer_data_item.owner and not ( check_transfer_data_item.surrender or transfer_data_item.surrender ): + # In the case where we have a modifier where I changed my local task layer + # this should not return as a conflict + # Do this only if both task layers are local to the current file + if ( + check_transfer_data_item.owner in self._local_tls + and transfer_data_item.owner in self._local_tls + ): + return self.conflict_transfer_data.append(transfer_data_item) print("CONFLICT FOUND") return True -- 2.30.2 From 4a110ca68e0aa4028a7c5293b663d90b8be2aa24 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 18:10:28 -0500 Subject: [PATCH 355/429] Asset Pipe: Update Prefixes during Merge --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index fac96f92..8b98b7cc 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -1,4 +1,5 @@ import bpy +from ..merge.naming import task_layer_prefix_transfer_data_update from .asset_mapping import AssetTransferMapping from .transfer_data.transfer_core import ( init_transfer_data, @@ -74,6 +75,9 @@ def ownership_get( task_layer_objs = get_task_layer_objects() for obj in local_col.all_objects: + for transfer_data_item in obj.transfer_data_ownership: + task_layer_prefix_transfer_data_update(transfer_data_item) + # Mark Asset ID Owner for objects in the current task layers collection if obj.asset_id_owner == "NONE" and obj in task_layer_objs: obj.asset_id_owner = default_task_layer -- 2.30.2 From b7bb0158d4117cc992ce31773ac0da2f81373847 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 18:11:14 -0500 Subject: [PATCH 356/429] Asset Pipe: Add TODO --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 8b98b7cc..825952f7 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -75,6 +75,7 @@ def ownership_get( task_layer_objs = get_task_layer_objects() for obj in local_col.all_objects: + # TODO REPLACE This is expensive to loop over everything again for transfer_data_item in obj.transfer_data_ownership: task_layer_prefix_transfer_data_update(transfer_data_item) -- 2.30.2 From ba5dcb112c11ba573a98b338b2c3290af1cd521d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 18:17:58 -0500 Subject: [PATCH 357/429] Asset Pipe: Update Batch Operator UI --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index a272cf1f..912f41bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -635,7 +635,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): layout = self.layout layout.row(align=True).prop(self, "data_source", expand=True) - layout.prop(self, "data_type", text="Update Owner of") + layout.prop(self, "data_type", expand=True) box = layout.box() -- 2.30.2 From 9670748501da750b69e2d4029f78608b7654f28d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 13 Nov 2023 19:12:11 -0500 Subject: [PATCH 358/429] Asset Pipe: Fix Typo in Batch Operator --- scripts-blender/addons/asset_pipeline_2/ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 912f41bc..2ba3963c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -513,7 +513,7 @@ def get_task_layer_types(self, context): class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): bl_idname = "assetpipe.batch_ownership_change" - bl_label = "Batch Re-Assign" + bl_label = "Batch Ownership" bl_description = """Re-Assign Ownership in a batch operation""" name_filter: bpy.props.StringProperty( @@ -541,7 +541,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ( 'TRANSFER_DATA', "Transfer Data", - "Update Owner of Transfer Data with Objects", + "Update Owner of Transfer Data within Objects", ), ), ) -- 2.30.2 From e698daa528f401ecd3a599ee51fc6f582d34ea9b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 12:01:11 -0500 Subject: [PATCH 359/429] Asset Pipe: WIP Surrender Object Ownership --- .../asset_pipeline_2/merge/asset_mapping.py | 19 ++++++++- .../addons/asset_pipeline_2/ops.py | 42 +++++++++++++++++++ .../addons/asset_pipeline_2/props.py | 3 ++ scripts-blender/addons/asset_pipeline_2/ui.py | 12 +++++- 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 0e6e89f7..01c7b99b 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -37,6 +37,7 @@ class AssetTransferMapping: self.external_col_to_remove: Set[bpy.types.Object] = set() self.external_col_to_add: Set[bpy.types.Object] = set() self.external_obj_to_add: Set[bpy.types.Object] = set() + self.surrendered_obj_to_remove: Set[bpy.types.Object] = set() self._no_match_source_objs: Set[bpy.types.Object] = set() self._no_match_source_colls: Set[bpy.types.Object] = set() @@ -65,7 +66,9 @@ class AssetTransferMapping: return external_obj def _check_id_conflict(self, external_id, local_id): - if external_id.asset_id_owner != local_id.asset_id_owner: + if external_id.asset_id_owner != local_id.asset_id_owner and ( + local_id.asset_id_surrender == external_id.asset_id_owner + ): self.conflict_ids.append(local_id) def _gen_object_map(self) -> Dict[bpy.types.Object, bpy.types.Object]: @@ -84,9 +87,23 @@ class AssetTransferMapping: self._check_id_conflict(external_obj, local_obj) # IF ITEM IS OWNED BY LOCAL TASK LAYERS if local_obj.asset_id_owner in self._local_tls: + if ( + local_obj.asset_id_surrender + and not external_obj.asset_id_surrender + and local_obj.asset_id_owner != external_obj.asset_id_owner + ): + self.surrendered_obj_to_remove.add(local_obj) + continue object_map[external_obj] = local_obj # IF ITEM IS NOT OWNED BY LOCAL TASK LAYERS else: + if ( + external_obj.asset_id_surrender + and not local_obj.asset_id_surrender + and local_obj.asset_id_owner != external_obj.asset_id_owner + ): + self.surrendered_obj_to_remove.add(external_obj) + continue object_map[local_obj] = external_obj # Find new objects to add to local_col diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 2ba3963c..663e9cc0 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -462,6 +462,47 @@ class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): return {'FINISHED'} +class ASSETPIPE_OT_update_surrendered_object(bpy.types.Operator): + bl_idname = "assetpipe.update_surrendered_object" + bl_label = "Update Surrendered" + bl_description = """Update Surrended Object Owner""" + + # TODO This is throwing an error + obj: bpy.props.PointerProperty(type=bpy.types.Object) + + _surrendered_transfer_data = None + _old_onwer = "" + + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + self.obj = context.active_object + self._old_onwer = self.obj.asset_id_owner + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: bpy.types.Context): + layout = self.layout + row = layout.row() + + draw_task_layer_selection( + row, + context.scene, + self.obj, + self.obj.asset_id_owner, + "asset_id_owner", + False, + True, + ) + + def execute(self, context: bpy.types.Context): + if self.obj.asset_id_owner == self._old_onwer: + self.report( + {'ERROR'}, + f"Object Owner was not updated", + ) + return {'CANCELLED'} + self.obj.asset_id_surrender = False + return {'FINISHED'} + + class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): bl_idname = "assetpipe.update_surrendered_transfer_data" bl_label = "Update Surrendered" @@ -721,6 +762,7 @@ classes = ( ASSETPIPE_OT_update_local_task_layers, ASSETPIPE_OT_revert_file, ASSETPIPE_OT_fix_prefixes, + ASSETPIPE_OT_update_surrendered_object, ASSETPIPE_OT_update_surrendered_transfer_data, ASSETPIPE_OT_batch_ownership_change, ) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 1eb90f02..98b8d9f0 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -142,6 +142,9 @@ def register(): ) 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 + ) def unregister(): diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index c76f57be..8db19231 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -5,6 +5,7 @@ from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.task_layer import draw_task_layer_selection from .config import verify_json_data from . import constants +from .merge.task_layer import get_local_task_layers class ASSETPIPE_PT_sync(bpy.types.Panel): @@ -127,7 +128,16 @@ class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): layout = layout.box() row = layout.row() row.label(text=f"{obj.name}: ", icon="OBJECT_DATA") - draw_task_layer_selection(row, scene, obj, obj.asset_id_owner, "asset_id_owner") + + if obj.get("asset_id_surrender"): + enabled = False if obj.asset_id_owner in get_local_task_layers() else True + row.operator("assetpipe.update_surrendered_object") + row.enabled = enabled + else: + row.prop(obj, "asset_id_surrender", text="Surrender") + draw_task_layer_selection( + row, scene, obj, obj.asset_id_owner, "asset_id_owner" + ) draw_transfer_data(transfer_data, layout) -- 2.30.2 From 7b6a6ca5dccdc8e8be262bb6eb296bc6fc48ae43 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 12:52:48 -0500 Subject: [PATCH 360/429] Asset Pipe: Fix Object Surrender --- .../asset_pipeline_2/merge/asset_mapping.py | 34 +++++++++++-------- .../addons/asset_pipeline_2/ops.py | 17 ++++------ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 01c7b99b..ddcf08e8 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -83,27 +83,33 @@ class AssetTransferMapping: continue external_obj = self._get_external_object(local_obj) if not external_obj: + print(f"Couldn't find external obj for {local_obj}") continue self._check_id_conflict(external_obj, local_obj) # IF ITEM IS OWNED BY LOCAL TASK LAYERS + + if ( + external_obj.asset_id_surrender + and not local_obj.asset_id_surrender + and local_obj.asset_id_owner != external_obj.asset_id_owner + ): + print(f"Skipping {external_obj} is surrendered") + object_map[external_obj] = local_obj + continue + + if ( + local_obj.asset_id_surrender + and not external_obj.asset_id_surrender + and local_obj.asset_id_owner != external_obj.asset_id_owner + ): + print(f"Skipping {local_obj} is surrendered") + object_map[local_obj] = external_obj + continue + if local_obj.asset_id_owner in self._local_tls: - if ( - local_obj.asset_id_surrender - and not external_obj.asset_id_surrender - and local_obj.asset_id_owner != external_obj.asset_id_owner - ): - self.surrendered_obj_to_remove.add(local_obj) - continue object_map[external_obj] = local_obj # IF ITEM IS NOT OWNED BY LOCAL TASK LAYERS else: - if ( - external_obj.asset_id_surrender - and not local_obj.asset_id_surrender - and local_obj.asset_id_owner != external_obj.asset_id_owner - ): - self.surrendered_obj_to_remove.add(external_obj) - continue object_map[local_obj] = external_obj # Find new objects to add to local_col diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 663e9cc0..c572231c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -467,15 +467,12 @@ class ASSETPIPE_OT_update_surrendered_object(bpy.types.Operator): bl_label = "Update Surrendered" bl_description = """Update Surrended Object Owner""" - # TODO This is throwing an error - obj: bpy.props.PointerProperty(type=bpy.types.Object) - - _surrendered_transfer_data = None + _obj = None _old_onwer = "" def invoke(self, context: bpy.types.Context, event: bpy.types.Event): - self.obj = context.active_object - self._old_onwer = self.obj.asset_id_owner + self._obj = context.active_object + self._old_onwer = self._obj.asset_id_owner return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context: bpy.types.Context): @@ -485,21 +482,21 @@ class ASSETPIPE_OT_update_surrendered_object(bpy.types.Operator): draw_task_layer_selection( row, context.scene, - self.obj, - self.obj.asset_id_owner, + self._obj, + self._obj.asset_id_owner, "asset_id_owner", False, True, ) def execute(self, context: bpy.types.Context): - if self.obj.asset_id_owner == self._old_onwer: + if self._obj.asset_id_owner == self._old_onwer: self.report( {'ERROR'}, f"Object Owner was not updated", ) return {'CANCELLED'} - self.obj.asset_id_surrender = False + self._obj.asset_id_surrender = False return {'FINISHED'} -- 2.30.2 From 052ab0f446106b15df54268096fc0a2ed161f949 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 13:56:23 -0500 Subject: [PATCH 361/429] Asset Pipe: Fix Bug in Batch Operator --- .../addons/asset_pipeline_2/ops.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index c572231c..d28e8076 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -641,12 +641,15 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ] return transfer_data_items_to_update - def _get_object_to_update(self, context): - objs = ( - context.selected_objects - if self.data_source == "SELECT" - else context.scene.objects - ) + def _get_objects(self, context): + asset_objs = context.scene.asset_pipeline.asset_collection.all_objects + selected_asset_objs = [ + obj for obj in asset_objs if obj in context.selected_objects + ] + return asset_objs if self.data_source == "ALL" else selected_asset_objs + + def _get_filtered_objects(self, context): + objs = self._get_objects(context) if self.filter_owners == "LOCAL" and self.data_type == "OBJECT": return [ item @@ -709,14 +712,12 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): if self.data_type == "TRANSFER_DATA": box.prop(self, "surrender_selection", expand=True) - objs = self._get_object_to_update(context) + objs = self._get_filtered_objects(context) if self.data_type == "OBJECT": data_type_name = "Object(s)" length = len(objs) if objs else 0 else: - transfer_data_items_to_update = self._get_transfer_data_to_update( - context, objs - ) + transfer_data_items_to_update = self._get_transfer_data_to_update(context) data_type_name = "Transfer Data Item(s)" length = ( len(transfer_data_items_to_update) @@ -727,15 +728,13 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): layout.label(text=f"Change Ownership on {length} {data_type_name}") def execute(self, context: bpy.types.Context): - objs = self._get_object_to_update(context) + objs = self._get_filtered_objects(context) if self.data_type == "OBJECT": for obj in objs: obj.asset_id_owner = self.owner_selection else: - transfer_data_items_to_update = self._get_transfer_data_to_update( - context, objs - ) + transfer_data_items_to_update = self._get_transfer_data_to_update(context) for transfer_data_item_to_update in transfer_data_items_to_update: if self.surrender_selection: -- 2.30.2 From f3500b49a47bfaa0a04dcf45a44d3cc91b217556 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 13:56:40 -0500 Subject: [PATCH 362/429] Asset Pipe: Clean Up Batch Operator UI --- .../addons/asset_pipeline_2/ops.py | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index d28e8076..2cfb5be7 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -551,7 +551,7 @@ def get_task_layer_types(self, context): class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): bl_idname = "assetpipe.batch_ownership_change" - bl_label = "Batch Ownership" + bl_label = "Batch Set Ownership" bl_description = """Re-Assign Ownership in a batch operation""" name_filter: bpy.props.StringProperty( @@ -561,7 +561,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ) data_source: bpy.props.EnumProperty( - name="Source", + name="Objects", items=( ('SELECT', "Selected", "Update Selected Objects Only"), ('ALL', "All", "Update All Objects"), @@ -569,7 +569,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ) data_type: bpy.props.EnumProperty( - name="Type", + name="Ownership Type", items=( ( 'OBJECT', @@ -578,7 +578,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ), ( 'TRANSFER_DATA', - "Transfer Data", + "Transferable Data", "Update Owner of Transfer Data within Objects", ), ), @@ -588,7 +588,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): name="Owner Filter", items=( ('LOCAL', "If Locally Owned", "Only data that is owned locally"), - ('OWNED', "If Owned", "Only data that already have assignment"), + ('OWNED', "If Owned By Any", "Only data that already have assignment"), ('ALL', "No Filter", "Set Ownership on any data, even without an owner"), ), ) @@ -620,7 +620,8 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): return unfiltered_list return [item for item in unfiltered_list if self.name_filter in item.name] - def _get_transfer_data_to_update(self, context, objs): + def _get_transfer_data_to_update(self, context): + objs = self._get_objects(context) transfer_data_items_to_update = [] if self.data_type == "TRANSFER_DATA": for obj in objs: @@ -665,7 +666,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): return self._filter_by_name(context, objs) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): - return context.window_manager.invoke_props_dialog(self, width=400) + return context.window_manager.invoke_props_dialog(self, width=500) def draw(self, context: bpy.types.Context): grey_out = True @@ -674,19 +675,18 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): self.filter_owners = "LOCAL" layout = self.layout + layout.use_property_split = True layout.row(align=True).prop(self, "data_source", expand=True) layout.prop(self, "data_type", expand=True) - box = layout.box() - - filter_owner_row = box.row() + filter_owner_row = layout.row() filter_owner_row.enabled = grey_out filter_owner_row.prop(self, "filter_owners") if self.data_type == "TRANSFER_DATA": - box.prop(self, "transfer_data_type") - box.prop(self, "name_filter", text="Name Filter") + layout.prop(self, "transfer_data_type") + layout.prop(self, "name_filter", text="Name Filter") if self.avaliable_owners == "LOCAL": show_local = True @@ -694,7 +694,10 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): else: show_local = False show_all_task_layers = True - owner_row = box.row(align=True) + + layout.separator() + + owner_row = layout.row(align=True) owner_row.enabled = grey_out draw_task_layer_selection( @@ -705,12 +708,12 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): 'owner_selection', show_all_task_layers=show_all_task_layers, show_local=show_local, - text="Owner", + text="Set To", ) owner_row.prop(self, "avaliable_owners", text="") if self.data_type == "TRANSFER_DATA": - box.prop(self, "surrender_selection", expand=True) + layout.prop(self, "surrender_selection", expand=True) objs = self._get_filtered_objects(context) if self.data_type == "OBJECT": @@ -724,8 +727,10 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): if transfer_data_items_to_update else 0 ) - - layout.label(text=f"Change Ownership on {length} {data_type_name}") + bottom_label = layout.row() + bottom_label_split = bottom_label.split(factor=0.4) + bottom_label_split.row() + bottom_label_split.label(text=f"Change Ownership on {length} {data_type_name}") def execute(self, context: bpy.types.Context): objs = self._get_filtered_objects(context) -- 2.30.2 From 5885d0e41fdd79f203eeb090fd6c05c15862f86e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:11:43 -0500 Subject: [PATCH 363/429] Asset Pipe: Remove Old Transfer Data Items - This commit will break any existing assets/test files --- scripts-blender/addons/asset_pipeline_2/constants.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index b833fa20..95852948 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -8,11 +8,9 @@ TASK_LAYER_CONFIG_DIR_NAME = "task_layer_configs" NONE_KEY = "NONE" VERTEX_GROUP_KEY = "GROUP_VERTEX" -VERTEX_COLOR_KEY = "COLOR_ATTRIBUTE" MODIFIER_KEY = "MODIFIER" CONSTRAINT_KEY = "CONSTRAINT" MATERIAL_SLOT_KEY = "MATERIAL" -UV_LAYERS_KEY = "UV_MAP" SHAPE_KEY_KEY = "SHAPE_KEY" ATTRIBUTE_KEY = "ATTRIBUTE" PARENT_KEY = "PARENT" @@ -22,11 +20,9 @@ PARENT_KEY = "PARENT" TRANSFER_DATA_TYPES = { NONE_KEY: ("None", "BLANK1"), VERTEX_GROUP_KEY: ("Vertex Groups", 'GROUP_VERTEX'), - VERTEX_COLOR_KEY: ("Color Attributes", 'GROUP_VCOL'), # TODO Remove MODIFIER_KEY: ("Modifiers", 'MODIFIER'), CONSTRAINT_KEY: ("Constraints", 'CONSTRAINT'), MATERIAL_SLOT_KEY: ("Materials", 'MATERIAL'), - UV_LAYERS_KEY: ("UV Maps", 'GROUP_UVS'), # TODO Remove SHAPE_KEY_KEY: ("Shape Keys", 'SHAPEKEY_DATA'), ATTRIBUTE_KEY: ("Attributes", 'EVENT_A'), PARENT_KEY: ("Parent", 'FILE_PARENT'), -- 2.30.2 From 415d9e39938fa9c7f87e1f6b4a6fa5ecb96ce11d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:12:08 -0500 Subject: [PATCH 364/429] Asset Pipe: Store UI Bool Names in `TRANSFER_DATA_TYPES` --- .../addons/asset_pipeline_2/constants.py | 19 ++++++++------- .../merge/transfer_data/transfer_ui.py | 6 ++--- .../addons/asset_pipeline_2/props.py | 24 ++++++++++--------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 95852948..92446b57 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -16,16 +16,17 @@ ATTRIBUTE_KEY = "ATTRIBUTE" PARENT_KEY = "PARENT" # Information about supported transferable data. -# {Key string : ("UI Name", 'ICON')} +# UI Bools are defined in props.py file +# {Key string : ("UI Name", 'ICON', "UI_BOOL_KEY")} TRANSFER_DATA_TYPES = { - NONE_KEY: ("None", "BLANK1"), - VERTEX_GROUP_KEY: ("Vertex Groups", 'GROUP_VERTEX'), - MODIFIER_KEY: ("Modifiers", 'MODIFIER'), - CONSTRAINT_KEY: ("Constraints", 'CONSTRAINT'), - MATERIAL_SLOT_KEY: ("Materials", 'MATERIAL'), - SHAPE_KEY_KEY: ("Shape Keys", 'SHAPEKEY_DATA'), - ATTRIBUTE_KEY: ("Attributes", 'EVENT_A'), - PARENT_KEY: ("Parent", 'FILE_PARENT'), + NONE_KEY: ("None", "BLANK1", 'none'), + VERTEX_GROUP_KEY: ("Vertex Groups", 'GROUP_VERTEX', 'group_vertex_ui_bool'), + MODIFIER_KEY: ("Modifiers", 'MODIFIER', 'modifier_ui_bool'), + CONSTRAINT_KEY: ("Constraints", 'CONSTRAINT', 'constraint_ui_bool'), + MATERIAL_SLOT_KEY: ("Materials", 'MATERIAL', 'material_ui_bool'), + SHAPE_KEY_KEY: ("Shape Keys", 'SHAPEKEY_DATA', 'shapekey_ui_bool'), + ATTRIBUTE_KEY: ("Attributes", 'EVENT_A', 'attribute_ui_bool'), + PARENT_KEY: ("Parent", 'FILE_PARENT', 'file_parent_ui_bool'), } # Convert it to the format that EnumProperty.items wants: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 24b32353..d1d91f06 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -10,17 +10,17 @@ def draw_transfer_data_type( asset_pipe = bpy.context.scene.asset_pipeline if transfer_data == []: return - name, icon = constants.TRANSFER_DATA_TYPES[transfer_data[0].type] + name, icon, ui_bool = constants.TRANSFER_DATA_TYPES[transfer_data[0].type] box = layout.box() row = box.row() row.prop( asset_pipe, - f"{icon}_BOOL", + ui_bool, icon=icon, text="", ) row.label(text=name) - if not bool(asset_pipe.get(f"{icon}_BOOL")): + if not bool(asset_pipe.get(ui_bool)): return scene = bpy.context.scene diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 98b8d9f0..89fb376a 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -110,20 +110,22 @@ class AssetPipeline(bpy.types.PropertyGroup): all_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) local_task_layers: bpy.props.CollectionProperty(type=TaskLayerSettings) - # UI BOOLS - # The names of the bools are the ICON keys for each transfer data type with the name _BOOL appened to it - # TODO See if there is a better way to handle hide/expand panels without creating bools like this - GROUP_VERTEX_BOOL: bpy.props.BoolProperty( + # UI BOOLS: used to show/hide transfer data elements + # The names are also hard coded in constants.py under TRANSFER_DATA_TYPES + # any changes will need to be reflected both here and in that enum + group_vertex_ui_bool: bpy.props.BoolProperty( name="Show/Hide Vertex Groups", default=False ) - MODIFIER_BOOL: bpy.props.BoolProperty(name="Show/Hide Modifiers", default=False) - CONSTRAINT_BOOL: bpy.props.BoolProperty(name="Show/Hide Constraints", default=False) - MATERIAL_BOOL: bpy.props.BoolProperty(name="Show/Hide Materials", default=False) - SHAPEKEY_DATA_BOOL: bpy.props.BoolProperty( - name="Show/Hide Shape Keys", default=False + modifier_ui_bool: bpy.props.BoolProperty(name="Show/Hide Modifiers", default=False) + constraint_ui_bool: bpy.props.BoolProperty( + name="Show/Hide Constraints", default=False ) - EVENT_A_BOOL: bpy.props.BoolProperty(name="Show/Hide Attributes", default=False) - FILE_PARENT_BOOL: bpy.props.BoolProperty(name="Show/Hide Parent", default=False) + material_ui_bool: bpy.props.BoolProperty(name="Show/Hide Materials", default=False) + shapekey_ui_bool: bpy.props.BoolProperty(name="Show/Hide Shape Keys", default=False) + attribute_ui_bool: bpy.props.BoolProperty( + name="Show/Hide Attributes", default=False + ) + file_parent_ui_bool: bpy.props.BoolProperty(name="Show/Hide Parent", default=False) classes = ( -- 2.30.2 From 0e98976eea16380b783e18f1ca95044d2983adca Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:16:44 -0500 Subject: [PATCH 365/429] Asset Pipe: Add Description to Custom Task Layers --- scripts-blender/addons/asset_pipeline_2/prefs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/prefs.py b/scripts-blender/addons/asset_pipeline_2/prefs.py index 0a47a866..289b1527 100644 --- a/scripts-blender/addons/asset_pipeline_2/prefs.py +++ b/scripts-blender/addons/asset_pipeline_2/prefs.py @@ -6,7 +6,7 @@ class ASSET_PIPELINE_addon_preferences(bpy.types.AddonPreferences): custom_task_layers_dir: bpy.props.StringProperty( # type: ignore name="Custom Task Layers", - description="TODO", # TODO Add Description + description="Specify directory to add additonal Task Layer Presets to use as templates when cerating new assets", default="", subtype="DIR_PATH", ) -- 2.30.2 From 249f1950cd990ea0f54ebad2153a444c0d5a4d74 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:26:52 -0500 Subject: [PATCH 366/429] Asset Pipe: Add Function to get Add-On Preferences --- scripts-blender/addons/asset_pipeline_2/constants.py | 2 ++ scripts-blender/addons/asset_pipeline_2/prefs.py | 5 +++++ scripts-blender/addons/asset_pipeline_2/props.py | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 92446b57..513949b2 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -3,6 +3,8 @@ # You could even choose to name your task layers after artists in your team. # {Task Layer Key: Collection/UI name} +ADDON_NAME = "asset_pipeline_2" + TASK_LAYER_CONFIG_NAME = "task_layers.json" TASK_LAYER_CONFIG_DIR_NAME = "task_layer_configs" diff --git a/scripts-blender/addons/asset_pipeline_2/prefs.py b/scripts-blender/addons/asset_pipeline_2/prefs.py index 289b1527..0de805a7 100644 --- a/scripts-blender/addons/asset_pipeline_2/prefs.py +++ b/scripts-blender/addons/asset_pipeline_2/prefs.py @@ -1,4 +1,9 @@ import bpy +from . import constants + + +def get_addon_prefs(): + return bpy.context.preferences.addons[constants.ADDON_NAME].preferences class ASSET_PIPELINE_addon_preferences(bpy.types.AddonPreferences): diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 89fb376a..886a683d 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -3,6 +3,7 @@ from . import constants from .merge.task_layer import get_local_task_layers from .config import get_task_layer_presets_path from pathlib import Path +from .prefs import get_addon_prefs """ NOTE Items in these properties groups should be generated by a function that finds the avaliable task layers from the task_layer_defaults.json file that needs to be created. @@ -10,7 +11,7 @@ avaliable task layers from the task_layer_defaults.json file that needs to be cr def get_task_layer_presets(self, context): - prefs = context.preferences.addons["asset_pipeline_2"].preferences + prefs = get_addon_prefs() user_tls = Path(prefs.custom_task_layers_dir) presets_dir = get_task_layer_presets_path() -- 2.30.2 From 1888a70e64b3bdef74e4c93b55bb2555865321ca Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:44:50 -0500 Subject: [PATCH 367/429] Asset Pipe: Allow for Custom Path when Saving Images --- scripts-blender/addons/asset_pipeline_2/images.py | 10 ++++++---- scripts-blender/addons/asset_pipeline_2/prefs.py | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/images.py b/scripts-blender/addons/asset_pipeline_2/images.py index a98eb86e..2b9b1c6c 100644 --- a/scripts-blender/addons/asset_pipeline_2/images.py +++ b/scripts-blender/addons/asset_pipeline_2/images.py @@ -1,12 +1,14 @@ import bpy from pathlib import Path +from .prefs import get_addon_prefs def save_images(): - # TODO Make customizable - save_path = Path(bpy.data.filepath).parent.joinpath("images") + prefs = get_addon_prefs() + user_path = Path(prefs.save_images_path) + default_path = Path(bpy.data.filepath).parent.joinpath("images") + save_path = default_path if prefs.save_images_path == "" else user_path for img in bpy.data.images: if img.is_dirty: - filepath = save_path.joinpath(img.name).__str__() - img.filepath = filepath + filepath = save_path.joinpath(img.name).__str__() + ".png" img.save(filepath=filepath) diff --git a/scripts-blender/addons/asset_pipeline_2/prefs.py b/scripts-blender/addons/asset_pipeline_2/prefs.py index 0de805a7..160bd1b9 100644 --- a/scripts-blender/addons/asset_pipeline_2/prefs.py +++ b/scripts-blender/addons/asset_pipeline_2/prefs.py @@ -16,8 +16,16 @@ class ASSET_PIPELINE_addon_preferences(bpy.types.AddonPreferences): subtype="DIR_PATH", ) + save_images_path: bpy.props.StringProperty( # type: ignore + name="Save Images Path", + description="Path to save un-saved images to, if left blank images will save in a called 'images' folder relative to the asset", + default="", + subtype="DIR_PATH", + ) + def draw(self, context): self.layout.prop(self, "custom_task_layers_dir") + self.layout.prop(self, "save_images_path") classes = (ASSET_PIPELINE_addon_preferences,) -- 2.30.2 From e5aebf294d7e0b35f99e4f1baab5e8e7fdc5abaf Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:48:27 -0500 Subject: [PATCH 368/429] Asset Pipe: Use `is_required` when sorting atributes --- .../merge/transfer_data/transfer_functions.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 8306a112..cfdfc514 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -648,11 +648,9 @@ def attributes_get_editable(attributes): for attribute in attributes if not ( attribute.is_internal - # or attribute.is_required + or attribute.is_required # Material Index is part of material transfer and should be skipped or attribute.name == 'material_index' - or attribute.name - == 'position' # TODO replace position with is_required in 4.0 ) ] @@ -784,5 +782,4 @@ def init_parent(scene, obj, use_default_owner: bool): def transfer_parent(target_obj, source_obj): target_obj.parent = source_obj.parent - # TODO Test if this works in all cases target_obj.matrix_parent_inverse = source_obj.parent.matrix_world.inverted() -- 2.30.2 From f502c0c75cd242494ac963a9691f0af3713344dd Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:49:01 -0500 Subject: [PATCH 369/429] Asset Pipe: Clear Old TODO --- .../asset_pipeline_2/merge/transfer_data/transfer_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index cfdfc514..df4dadfe 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -292,7 +292,6 @@ def transfer_modifier(modifier_name, target_obj, source_obj): if not mod.is_bind: continue for i in range(2): - # TODO Create override for modifier visibility in these cases with override_obj_visability(obj=target_obj): with context.temp_override( object=target_obj, active_object=target_obj -- 2.30.2 From 2b949d51e34e63a8b2fab109c300d20618dbe011 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 14:55:12 -0500 Subject: [PATCH 370/429] Asset Pipe: Pass Scene as Variable to `override_obj_visability()` --- .../merge/transfer_data/transfer_functions.py | 11 ++++++----- .../addons/asset_pipeline_2/merge/visibility.py | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index df4dadfe..c693845a 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -242,6 +242,7 @@ def init_modifiers(scene, obj, use_default_owner: bool): def transfer_modifier(modifier_name, target_obj, source_obj): # remove old and sync existing modifiers context = bpy.context + scene = context.scene old_mod = target_obj.modifiers.get(modifier_name) if old_mod: target_obj.modifiers.remove(old_mod) @@ -257,7 +258,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): for target_mod_i, target_mod in enumerate(target_obj.modifiers): if target_mod.name == name_prev: idx = target_mod_i + 1 - with override_obj_visability(obj=target_obj): + with override_obj_visability(obj=target_obj, scene=scene): with context.temp_override(object=target_obj): bpy.ops.object.modifier_move_to_index( modifier=mod_new.name, index=idx @@ -274,7 +275,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): if not mod.is_bound: continue for i in range(2): - with override_obj_visability(obj=target_obj): + with override_obj_visability(obj=target_obj, scene=scene): with context.temp_override( object=target_obj, active_object=target_obj ): @@ -283,7 +284,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): if not mod.is_bound: continue for i in range(2): - with override_obj_visability(obj=target_obj): + with override_obj_visability(obj=target_obj, scene=scene): with context.temp_override( object=target_obj, active_object=target_obj ): @@ -292,7 +293,7 @@ def transfer_modifier(modifier_name, target_obj, source_obj): if not mod.is_bind: continue for i in range(2): - with override_obj_visability(obj=target_obj): + with override_obj_visability(obj=target_obj, scene=scene): with context.temp_override( object=target_obj, active_object=target_obj ): @@ -358,7 +359,7 @@ def transfer_constraint(constraint_name, target_obj, source_obj): idx = target_mod_i + 1 if idx != i: - with override_obj_visability(obj=target_obj): + with override_obj_visability(obj=target_obj, scene=context.scene): with context.temp_override(object=target_obj): bpy.ops.constraint.move_to_index( constraint=constraint_new.name, index=idx diff --git a/scripts-blender/addons/asset_pipeline_2/merge/visibility.py b/scripts-blender/addons/asset_pipeline_2/merge/visibility.py index 8154c6cc..f93ccbd5 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/visibility.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/visibility.py @@ -12,7 +12,14 @@ def get_visibility_driver(obj) -> Optional[bpy.types.FCurve]: @contextlib.contextmanager -def override_obj_visability(obj): +def override_obj_visability(obj: bpy.types.Object, scene: bpy.types.Scene): + """Temporarily Change the Visability of an Object so an bpy.ops or other + function that requires the object to be visable can be called. + + Args: + obj (bpy.types.Object): Object to un-hide + scene (bpy.types.Scene): Scene Object is in + """ hide = obj.hide_get() # eye icon hide_viewport = obj.hide_viewport # hide viewport select = obj.hide_select # selectable @@ -29,9 +36,9 @@ def override_obj_visability(obj): driver.mute = True assigned_to_scene_root = False - if obj.name not in bpy.context.scene.collection.objects: + if obj.name not in scene.collection.objects: assigned_to_scene_root = True - bpy.context.scene.collection.objects.link(obj) # TODO Pass Current Scene + scene.collection.objects.link(obj) yield @@ -42,5 +49,5 @@ def override_obj_visability(obj): if driver: driver.mute = driver_mute - if assigned_to_scene_root and obj.name in bpy.context.scene.collection.objects: - bpy.context.scene.collection.objects.unlink(obj) # TODO Pass Current Scene + if assigned_to_scene_root and obj.name in scene.collection.objects: + scene.collection.objects.unlink(obj) -- 2.30.2 From 11a23c7e6421f0c06289853bfd66f776a24b5581 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 15:44:23 -0500 Subject: [PATCH 371/429] Asset Pipe: Improve/Document `draw_task_layer_selection()` --- .../asset_pipeline_2/merge/task_layer.py | 66 ++++++++++++++----- .../merge/transfer_data/transfer_ui.py | 9 +-- .../addons/asset_pipeline_2/ops.py | 35 +++++----- scripts-blender/addons/asset_pipeline_2/ui.py | 8 +-- 4 files changed, 69 insertions(+), 49 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 17eb0666..a012f0e3 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -39,43 +39,75 @@ def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): def draw_task_layer_selection( - row, - scene, - data, - data_owner, - data_owner_name, + layout: bpy.types.UILayout, + data: bpy.types.CollectionProperty or bpy.types.ID, show_all_task_layers=False, - show_local=False, + show_local_task_layers=False, text="", -): - # TODO Simplify Arguments and add Type Hints / Doc String + data_owner_name="", + current_data_owner=None, +) -> None: + """Draw an prop search UI for ownership of either OBJ/COL or Task Layer. + It has three modes, 'Show All Task Layers" "Show All Task Layers Greyed Out" and + "Only Show Local Task Layers" + + - When the property is already set to a local task layer show: "Only Show Local Task Layers" + - When a property is owned by an external task layer: "Show All Task Layers Greyed Out" so they user cannot edit it + - When a user is overriding or the object is new (using default ownership): "Show All Task Layers" + Args: + layout (bpy.types.UILayout): Any UI Layout element like self.layout or row + data (bpy.types.CollectionProperty or bpy.types.ID): Object, Collection or Transfer Data Item + show_all_task_layers (bool, optional): Used when user is overriding or default ownership is set. Defaults to False. + show_local_task_layers (bool, optional): Force Showing Local Task Layers Only. Defaults to False. + text (str, optional): Title of prop search. Defaults to "". + data_owner_name(str, optional): Name of Data if it needs to be specified + current_data_owner(str, optional): Property that is named by data_owner_name so it can be checked, property should return a string + """ + + # Set data_owner_name based on type of it hasn't been passed + if data_owner_name == "": + # These rna_type.names are defined by class names in props.py + if data.rna_type.name in ["AssetTransferData", 'AssetTransferDataTemp']: + data_owner_name = "owner" + else: + data_owner_name = "asset_id_owner" + + # Get the current data owner from OBJ/COL or Transfer Data Item if it hasn't been passed + if current_data_owner is None: + current_data_owner = data.get(data_owner_name) + + asset_pipe = bpy.context.scene.asset_pipeline + if show_all_task_layers == True: - row.prop_search( + # Show All Task Layers + layout.prop_search( data, data_owner_name, - scene.asset_pipeline, + asset_pipe, 'all_task_layers', text=text, ) return if ( - data_owner not in [tl.name for tl in scene.asset_pipeline.local_task_layers] - and not show_local + current_data_owner not in [tl.name for tl in asset_pipe.local_task_layers] + and not show_local_task_layers ): - row.enabled = False - row.prop_search( + # Show All Task Layers Greyed Out + layout.enabled = False + layout.prop_search( data, data_owner_name, - scene.asset_pipeline, + asset_pipe, 'all_task_layers', text=text, ) return else: - row.prop_search( + # Only Show Local Task Layers + layout.prop_search( data, data_owner_name, - scene.asset_pipeline, + asset_pipe, 'local_task_layers', text=text, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index d1d91f06..b1c16eb5 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -39,12 +39,9 @@ def draw_transfer_data_type( else: row.prop(transfer_data_item, "surrender", text="Surrender") draw_task_layer_selection( - row, - scene, - transfer_data_item, - transfer_data_item.owner, - "owner", - transfer_data_item.get("use_default_owner"), + layout=row, + data=transfer_data_item, + show_all_task_layers=transfer_data_item.get("use_default_owner"), ) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 2cfb5be7..7cf6587e 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -480,13 +480,10 @@ class ASSETPIPE_OT_update_surrendered_object(bpy.types.Operator): row = layout.row() draw_task_layer_selection( - row, - context.scene, - self._obj, - self._obj.asset_id_owner, - "asset_id_owner", - False, - True, + layout=row, + data=self._obj, + show_all_task_layers=False, + show_local_task_layers=True, ) def execute(self, context: bpy.types.Context): @@ -524,13 +521,12 @@ class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): row = layout.row() draw_task_layer_selection( - row, - context.scene, - self._surrendered_transfer_data, - self._surrendered_transfer_data.owner, - "owner", - self._surrendered_transfer_data.get("use_default_owner"), - True, + layout=row, + data=self._surrendered_transfer_data, + show_all_task_layers=self._surrendered_transfer_data.get( + "use_default_owner" + ), + show_local_task_layers=True, ) def execute(self, context: bpy.types.Context): @@ -701,13 +697,12 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): owner_row.enabled = grey_out draw_task_layer_selection( - owner_row, - context.scene, - self, - self.owner_selection, - 'owner_selection', + layout=owner_row, + data=self, + data_owner_name='owner_selection', + current_data_owner=self.owner_selection, show_all_task_layers=show_all_task_layers, - show_local=show_local, + show_local_task_layers=show_local, text="Set To", ) owner_row.prop(self, "avaliable_owners", text="") diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 8db19231..a79a8434 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -116,9 +116,7 @@ class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): text=f"{col.name}: ", icon="OUTLINER_COLLECTION", ) - draw_task_layer_selection( - row, scene, col, col.asset_id_owner, "asset_id_owner" - ) + draw_task_layer_selection(layout=row, data=col) if not context.active_object: layout.label(text="Set an Active Object to Inspect", icon="OBJECT_DATA") @@ -135,9 +133,7 @@ class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): row.enabled = enabled else: row.prop(obj, "asset_id_surrender", text="Surrender") - draw_task_layer_selection( - row, scene, obj, obj.asset_id_owner, "asset_id_owner" - ) + draw_task_layer_selection(layout=row, data=obj) draw_transfer_data(transfer_data, layout) -- 2.30.2 From fd17b6db87787007b149d353ba6c0b5dc7c916c7 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 15:45:42 -0500 Subject: [PATCH 372/429] Asset Pipe: Remove Old TODOs --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 1 - scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py | 1 - 2 files changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 825952f7..cdb08a7b 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -64,7 +64,6 @@ def ownership_get( """ asset_pipe = scene.asset_pipeline asset_pipe.temp_transfer_data.clear() - # TODO Figure out default in this case default_task_layer = get_local_task_layers()[0] diff --git a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py index ed986e1c..70f79d63 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py @@ -33,7 +33,6 @@ def init_shared_ids(scene: bpy.types.Scene) -> list[bpy.types.ID]: Returns: list[bpy.types.ID]: A list of new 'shared_ids' owned by the file's task layer """ - # TODO Figure our what is the default in this case task_layer_key = get_local_task_layers()[0] shared_ids = [] asset_pipe = scene.asset_pipeline -- 2.30.2 From a743446945a154abb290030d2013a39210ba0787 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 16:06:57 -0500 Subject: [PATCH 373/429] Asset Pipe: Rename `get_name_with_asset_prefix()` to `asset_prefix_name_get()` --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 2 -- scripts-blender/addons/asset_pipeline_2/merge/core.py | 4 ++-- scripts-blender/addons/asset_pipeline_2/merge/naming.py | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index ddcf08e8..2f397d22 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -2,8 +2,6 @@ import bpy from typing import Dict, Set from .naming import ( get_target_name, - get_basename, - get_name_with_asset_prefix, task_layer_prefix_basename_get, ) from .util import get_storage_of_id diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index cdb08a7b..903935dc 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -13,7 +13,7 @@ from .transfer_data.transfer_util import transfer_data_add_entry from .naming import ( add_suffix_to_hierarchy, remove_suffix_from_hierarchy, - get_name_with_asset_prefix, + asset_prefix_name_get, get_id_type_name, ) @@ -81,7 +81,7 @@ def ownership_get( # Mark Asset ID Owner for objects in the current task layers collection if obj.asset_id_owner == "NONE" and obj in task_layer_objs: obj.asset_id_owner = default_task_layer - obj.name = get_name_with_asset_prefix(obj.name) + obj.name = asset_prefix_name_get(obj.name) init_transfer_data_with_defaults(scene, obj) continue # Skip items that have no owner diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 5d2935ab..31d67be6 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -116,9 +116,7 @@ def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) pass -# TODO Cleanup prefix doc strings -def get_name_with_asset_prefix(name: str) -> str: - """Returns a string with the prefix if it is not already set. +def asset_prefix_name_get(name: str) -> str: Users can specify a prefix to live on all objects during the asset creation process. This prefix is stored in the scene. -- 2.30.2 From 82fd347ae8903b78cc534c2e1efc6cdd38e1673d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 16:14:55 -0500 Subject: [PATCH 374/429] Asset Pipe: Clean-Up Prefix Functions and Add Documentation --- .../addons/asset_pipeline_2/merge/naming.py | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 31d67be6..02c213fd 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -117,6 +117,7 @@ def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) def asset_prefix_name_get(name: str) -> str: + """Returns a string with the asset prefix if it is not already set. Users can specify a prefix to live on all objects during the asset creation process. This prefix is stored in the scene. @@ -134,12 +135,13 @@ def asset_prefix_name_get(name: str) -> str: def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: - """Returns a string with the prefix if it is not already set. - Users can specify a prefix to live on all objects during the - asset creation process. This prefix is stored in the scene. + """Returns a string with the task layer prefix if one is not already set. + Prefix for assets is defined task_layer.json file within TASK_LAYER_TYPES + Will return early if any prefix is found, cannot replace existing prefixes. Args: name (str): Name to add prefix to + task_layer_owner (str): Returns: str: Returns name with prefix @@ -152,13 +154,36 @@ def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: def task_layer_prefix_basename_get(name: str) -> str: + """Get the base of a name if it contains a task layer prefix. + This prefix is set on some transfer data items, this functions + removes the prefixes and returns the basename + + Args: + name (str): Original name including prefix + + Returns: + str: Returns name without task layer prefix + """ for task_layer_key in config.TASK_LAYER_TYPES: if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): return name.replace(name.split(".")[0], "")[1:] return name -def task_layer_prefix_transfer_data_update(transfer_data_item): +def task_layer_prefix_transfer_data_update( + transfer_data_item: bpy.types.CollectionProperty, +) -> bool: + """Task Layer Prefix can become out of date with the actual owner of the task layer. + This will update the existing prefixes on transfer_data_item so it can match the + owner of that transfer_data_item. Will update both the transfer_data_item.name and the + name of the actual data the transfer_data_item is referring to. + + Args: + transfer_data_item (bpy.types.CollectionProperty): Transfer Data Item that is named with prefix + + Returns: + bool: Returns True if a change to the name was completed + """ prefix_types = [constants.MODIFIER_KEY, constants.CONSTRAINT_KEY] if transfer_data_item.type not in prefix_types: return -- 2.30.2 From ee5b69dc4afe26172e404772d692468f88adfe65 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 14 Nov 2023 16:17:57 -0500 Subject: [PATCH 375/429] Asset Pipe: Add Error Message if Revert File isn't found --- scripts-blender/addons/asset_pipeline_2/ops.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 7cf6587e..4690ecce 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -421,7 +421,14 @@ class ASSETPIPE_OT_revert_file(bpy.types.Operator): asset_pipe = context.scene.asset_pipeline self._temp_file = asset_pipe.temp_file self._source_file = asset_pipe.source_file - # TODO Add Error Messages if path is empty + + if not Path(self._temp_file).exists(): + self.report( + {'ERROR'}, + "Revert failed; no file found", + ) + return {'CANCELLED'} + bpy.ops.wm.open_mainfile(filepath=self._temp_file) bpy.ops.wm.save_as_mainfile(filepath=self._source_file) return {'FINISHED'} @@ -741,7 +748,6 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): if transfer_data_item_to_update.owner in get_local_task_layers(): transfer_data_item_to_update.surrender = True continue - # TODO Update Prefixes when setting new ownership transfer_data_item_to_update.owner = self.owner_selection task_layer_prefix_transfer_data_update(transfer_data_item_to_update) -- 2.30.2 From af625f24e2c4b8131fc81692b031f2c608a852be Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Thu, 16 Nov 2023 15:46:14 +0100 Subject: [PATCH 376/429] AssetPipe: Init files with lowercase TL names --- scripts-blender/addons/asset_pipeline_2/ops.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 4690ecce..afb05658 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -127,12 +127,11 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): bpy.ops.wm.save_as_mainfile(filepath=first_file, copy=True) starting_file = first_file - # TODO Support mulitple task layers in same file # Create the file for each task layer. for task_layer_key in config.TASK_LAYER_TYPES: if task_layer_key == "NONE" or task_layer_key in local_tls: continue - name = self._name + "." + task_layer_key + ".blend" + name = self._name + "." + task_layer_key.lower() + ".blend" # Remove Data From Newly Created Files that aren't the user's main file for col in bpy.data.collections: -- 2.30.2 From e853a323367b6eb0546db5f4735c8bce9d82e80a Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Thu, 16 Nov 2023 16:26:15 +0100 Subject: [PATCH 377/429] AssetPipe: get_invalid_objects only test asset objs --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 3 +-- scripts-blender/addons/asset_pipeline_2/sync.py | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 903935dc..0d115f10 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -112,7 +112,6 @@ def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: def get_invalid_objects( local_col: bpy.types.Collection, - scene: bpy.types.Scene, ) -> list[bpy.types.Object]: """Returns a list of objects not used in the merge processing, which are considered invalid. The objects will be excluded from @@ -129,7 +128,7 @@ def get_invalid_objects( task_layer_objs = get_task_layer_objects() invalid_obj = [] - for obj in scene.objects: + for obj in local_col.all_objects: if obj.asset_id_owner == "NONE": invalid_obj.append(obj) if obj not in task_layer_objs and obj.asset_id_owner in local_task_layer_keys: diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 75d0a010..95da9903 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -45,7 +45,7 @@ def sync_invoke(self, context): ownership_get(local_col, context.scene) - self._invalid_objs = get_invalid_objects(local_col, context.scene) + self._invalid_objs = get_invalid_objects(local_col) self._shared_ids = init_shared_ids(context.scene) @@ -119,16 +119,18 @@ def create_temp_file_backup(self, context): context.scene.asset_pipeline.temp_file = temp_file.__str__() return temp_file.__str__() + def update_temp_file_paths(self, context, temp_file_path): asset_pipe = context.scene.asset_pipeline asset_pipe.temp_file = temp_file_path asset_pipe.source_file = self._current_file.__str__() + def sync_execute_pull(self, context): temp_file_path = create_temp_file_backup(self, context) update_temp_file_paths(self, context, temp_file_path) bpy.ops.wm.save_as_mainfile(filepath=temp_file_path, copy=True) - + error_msg = merge_task_layer( context, local_tls=self._task_layer_keys, @@ -147,7 +149,6 @@ def sync_execute_push(self, context): if self._sync_target not in push_targets: push_targets.append(self._sync_target) - for file in push_targets: file_path = file.__str__() -- 2.30.2 From cb035c9ba315416dfd856611f63d69a60efa3cca Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 11:29:25 -0500 Subject: [PATCH 378/429] Asset Pipe: Create Task Layer Collections when setting up a new asset --- scripts-blender/addons/asset_pipeline_2/ops.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index afb05658..e1435156 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -119,6 +119,11 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_pipe.name = self._name asset_pipe.prefix = self._prefix + for task_layer_key in config.TASK_LAYER_TYPES: + bpy.data.collections.new(task_layer_key) + task_layer_col = bpy.data.collections.get(task_layer_key) + asset_col.children.link(task_layer_col) + starting_file = "" first_file = os.path.join(asset_path, Path(bpy.data.filepath).name) -- 2.30.2 From 5bd0643b06852e7bc81ac3d011903821d26349be Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 12:01:48 -0500 Subject: [PATCH 379/429] Asset Pipe: Assign Task Layer Collection Onwership --- scripts-blender/addons/asset_pipeline_2/ops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index e1435156..81e3d476 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -122,6 +122,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): for task_layer_key in config.TASK_LAYER_TYPES: bpy.data.collections.new(task_layer_key) task_layer_col = bpy.data.collections.get(task_layer_key) + task_layer_col.asset_id_owner = task_layer_key asset_col.children.link(task_layer_col) starting_file = "" -- 2.30.2 From f935473e8b375129c774ad29c881b08716df280c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 12:48:55 -0500 Subject: [PATCH 380/429] Asset Pipe: Remove No-Op Code --- scripts-blender/addons/asset_pipeline_2/props.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 886a683d..d258f958 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -75,8 +75,6 @@ class AssetPipeline(bpy.types.PropertyGroup): temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) - local_task_layers: bpy.props.StringProperty(name="Local Task Layers", default="") - def add_temp_transfer_data(self, name, owner, type, obj, use_default_owner=False): new_transfer_data = self.temp_transfer_data transfer_data_item = new_transfer_data.add() -- 2.30.2 From f4c0c96b9fedde18a4649e37f6be9b52b0be8a3b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 12:59:30 -0500 Subject: [PATCH 381/429] Asset Pipe: Do not set Asset Prefix on Objects --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 0d115f10..0ea0c880 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -81,7 +81,7 @@ def ownership_get( # Mark Asset ID Owner for objects in the current task layers collection if obj.asset_id_owner == "NONE" and obj in task_layer_objs: obj.asset_id_owner = default_task_layer - obj.name = asset_prefix_name_get(obj.name) + # obj.name = asset_prefix_name_get(obj.name) init_transfer_data_with_defaults(scene, obj) continue # Skip items that have no owner -- 2.30.2 From e468e875323f1f351d1debc267fa6e27658e43a3 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 14:37:20 -0500 Subject: [PATCH 382/429] Asset Pipe: Use existing File/Folder to make new Asset --- .../addons/asset_pipeline_2/ops.py | 80 ++++++++++++------- .../addons/asset_pipeline_2/props.py | 8 ++ scripts-blender/addons/asset_pipeline_2/ui.py | 11 ++- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 81e3d476..6a4d5e19 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -26,9 +26,8 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): bl_label = "Create New Asset" bl_description = """Create a new Asset Files and Folders at a given directory""" - _name = None - _dir = None - _prefix = None + _name = "" + _prefix = "" _json_path = None create_files: bpy.props.BoolProperty( @@ -38,16 +37,14 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): @classmethod def poll(cls, context: bpy.types.Context) -> bool: asset_pipe = context.scene.asset_pipeline - if asset_pipe.name == "" or asset_pipe.dir == "": - cls.poll_message_set("Asset Name and Directory must be valid") - return False - if os.path.exists(os.path.join(asset_pipe.dir, asset_pipe.name)): - cls.poll_message_set("Asset Folder already exists") - return False - if bpy.data.filepath == "": - cls.poll_message_set("Please Save File before creating asset") - return False - # TODO Add warning if file is unsaved like a general startup file + if asset_pipe.new_file_mode == "KEEP": + if not asset_pipe.asset_collection: + cls.poll_message_set("Missing Top Level Collection") + return False + else: + if asset_pipe.name == "" or asset_pipe.dir == "": + cls.poll_message_set("Asset Name and Directory must be valid") + return False return True def invoke(self, context: bpy.types.Context, event): @@ -77,11 +74,24 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): self.layout.prop(self, "create_files") def execute(self, context: bpy.types.Context): - # New File is Createed so Props need to be saved asset_pipe = context.scene.asset_pipeline - self._name = asset_pipe.name - self._dir = bpy.path.abspath(asset_pipe.dir) - self._prefix = asset_pipe.prefix + if asset_pipe.new_file_mode == "KEEP": + # New File is Createed so Props need to be saved + asset_col = asset_pipe.asset_collection + self._name == "FOO" + self._name = ( + asset_col.name + if "-" not in asset_col.name + else asset_col.name.split("-", 1)[1] + ) + asset_path = Path(bpy.data.filepath).parent.__str__() + + else: + self._name = asset_pipe.name + user_dir = bpy.path.abspath(asset_pipe.dir) + self._prefix = asset_pipe.prefix + # Create Asset Folder at Directory + asset_path = os.path.join(user_dir, self._name) all_task_layers = context.scene.asset_pipeline.all_task_layers local_tls = [] @@ -96,12 +106,18 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): ) return {'CANCELLED'} - # Create Asset Folder at Directory - asset_path = os.path.join(self._dir, self._name) - os.mkdir(asset_path) + if not os.path.exists(asset_path): + os.mkdir(asset_path) for publish_type in constants.PUBLISH_KEYS: - os.mkdir(os.path.join(asset_path, publish_type)) + new_dir_path = os.path.join(asset_path, publish_type) + if os.path.exists(new_dir_path): + self.report( + {'ERROR'}, + f"Directory for '{publish_type}' already exists", + ) + return {'CANCELLED'} + os.mkdir(new_dir_path) # TODO Save Task Layer Config File config.write_json_file( @@ -112,10 +128,17 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): # Setup Base File asset_pipe = context.scene.asset_pipeline asset_pipe.is_asset_pipeline_file = True - bpy.data.collections.new(self._name) - asset_col = bpy.data.collections.get(self._name) - context.scene.collection.children.link(asset_col) - asset_pipe.asset_collection = asset_col + + if asset_pipe.new_file_mode == "KEEP": + asset_col = asset_pipe.asset_collection + for col in asset_col.children: + col.asset_id_owner = local_tls[0] + else: + bpy.data.collections.new(self._name) + asset_col = bpy.data.collections.get(self._name) + context.scene.collection.children.link(asset_col) + asset_pipe.asset_collection = asset_col + asset_pipe.name = self._name asset_pipe.prefix = self._prefix @@ -126,7 +149,11 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_col.children.link(task_layer_col) starting_file = "" - first_file = os.path.join(asset_path, Path(bpy.data.filepath).name) + if bpy.data.filepath != "": + first_file_name = Path(bpy.data.filepath).name + else: + first_file_name = self._name + "." + local_tls[0].lower() + ".blend" + first_file = os.path.join(asset_path, first_file_name) set_local_task_layers(local_tls) @@ -156,7 +183,6 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) # Create intial publish based on task layers. - asset_pipe.task_layer_name = "NONE" publish_path = os.path.join(asset_path, constants.ACTIVE_PUBLISH_KEY) name = self._name + "." + "v001" + ".blend" asset_pipe.asset_collection.asset_mark() diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index d258f958..9d876ea0 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -86,6 +86,14 @@ class AssetPipeline(bpy.types.PropertyGroup): ## NEW FILE + new_file_mode: bpy.props.EnumProperty( + name="New File Mode", + items=( + ('KEEP', "Current File", "Update Selected Objects Only"), + ('BLANK', "Blank", "Update All Objects"), + ), + ) + dir: bpy.props.StringProperty( name="Directory", description="Target Path for new asset files", diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index a79a8434..8be15eb0 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -18,12 +18,15 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): layout = self.layout asset_pipe = context.scene.asset_pipeline if not asset_pipe.is_asset_pipeline_file: - layout.prop(asset_pipe, "dir") - layout.prop(asset_pipe, "name") - layout.prop(asset_pipe, "prefix") + layout.prop(asset_pipe, "new_file_mode", expand=True) layout.prop(asset_pipe, "task_layer_config_type") + if asset_pipe.new_file_mode == "BLANK": + layout.prop(asset_pipe, "name") + layout.prop(asset_pipe, "prefix") + layout.prop(asset_pipe, "dir") + else: + layout.prop(asset_pipe, "asset_collection") layout.operator("assetpipe.create_new_asset") - # layout.operator("") return if not Path(bpy.data.filepath).exists: -- 2.30.2 From 1d74f6e268c1a8d1f9e7811ad0a4f8b3f371957d Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 15:33:56 -0500 Subject: [PATCH 383/429] Asset Pipe: Refactor New File Operator to be Easier to Read --- .../addons/asset_pipeline_2/ops.py | 161 +++++++++++------- 1 file changed, 98 insertions(+), 63 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 6a4d5e19..dbef5ae3 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -29,6 +29,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): _name = "" _prefix = "" _json_path = None + _asset_pipe = None create_files: bpy.props.BoolProperty( name="Create Files for Unselected Task Layers", default=True @@ -49,12 +50,12 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): def invoke(self, context: bpy.types.Context, event): # Dynamically Create Task Layer Bools - asset_pipe = context.scene.asset_pipeline + self._asset_pipe = context.scene.asset_pipeline # TODO Check if this fails - config.verify_json_data(Path(asset_pipe.task_layer_config_type)) + config.verify_json_data(Path(self._asset_pipe.task_layer_config_type)) - all_task_layers = asset_pipe.all_task_layers + all_task_layers = self._asset_pipe.all_task_layers all_task_layers.clear() for task_layer_key in config.TASK_LAYER_TYPES: @@ -66,34 +67,47 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): def draw(self, context: bpy.types.Context): box = self.layout.box() - all_task_layers = context.scene.asset_pipeline.all_task_layers + all_task_layers = self._asset_pipe.all_task_layers box.label(text="Choose Which Task Layers will be local the current file") for task_layer_bool in all_task_layers: box.prop(task_layer_bool, "is_local", text=task_layer_bool.name) self.layout.prop(self, "create_files") - def execute(self, context: bpy.types.Context): - asset_pipe = context.scene.asset_pipeline - if asset_pipe.new_file_mode == "KEEP": - # New File is Createed so Props need to be saved - asset_col = asset_pipe.asset_collection - self._name == "FOO" - self._name = ( + def _asset_name_set(self, context) -> None: + if self._asset_pipe.new_file_mode == "KEEP": + asset_col = self._asset_pipe.asset_collection + name = ( asset_col.name if "-" not in asset_col.name else asset_col.name.split("-", 1)[1] ) - asset_path = Path(bpy.data.filepath).parent.__str__() + prefix = ( + "" if "-" not in asset_col.name else asset_col.name.split("-", 1)[0] + ) else: - self._name = asset_pipe.name - user_dir = bpy.path.abspath(asset_pipe.dir) - self._prefix = asset_pipe.prefix - # Create Asset Folder at Directory - asset_path = os.path.join(user_dir, self._name) + name = self._asset_pipe.name + prefix = self._asset_pipe.prefix - all_task_layers = context.scene.asset_pipeline.all_task_layers + # Set to easily access these properties + self._name = name + self._prefix = prefix + + # Store these in the asset pipeline props group + self._asset_pipe.name = name + self._asset_pipe.prefix = prefix + + def _asset_dir_get(self, context) -> str: + if self._asset_pipe.new_file_mode == "KEEP": + return Path(bpy.data.filepath).parent.__str__() + + else: + user_dir = bpy.path.abspath(self._asset_pipe.dir) + return os.path.join(user_dir, self._name) + + def _load_task_layers(self, context): + all_task_layers = self._asset_pipe.all_task_layers local_tls = [] for task_layer_bool in all_task_layers: if task_layer_bool.is_local: @@ -105,12 +119,11 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): "Please select at least one task layer to be local to the current file", ) return {'CANCELLED'} + return local_tls - if not os.path.exists(asset_path): - os.mkdir(asset_path) - + def _create_publish_directories(self, context, asset_directory): for publish_type in constants.PUBLISH_KEYS: - new_dir_path = os.path.join(asset_path, publish_type) + new_dir_path = os.path.join(asset_directory, publish_type) if os.path.exists(new_dir_path): self.report( {'ERROR'}, @@ -119,75 +132,97 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): return {'CANCELLED'} os.mkdir(new_dir_path) - # TODO Save Task Layer Config File - config.write_json_file( - asset_path=Path(asset_path), - source_file_path=Path(asset_pipe.task_layer_config_type), - ) - - # Setup Base File - asset_pipe = context.scene.asset_pipeline - asset_pipe.is_asset_pipeline_file = True - - if asset_pipe.new_file_mode == "KEEP": - asset_col = asset_pipe.asset_collection + def _asset_collection_get(self, context, local_tls): + if self._asset_pipe.new_file_mode == "KEEP": + asset_col = self._asset_pipe.asset_collection for col in asset_col.children: col.asset_id_owner = local_tls[0] else: bpy.data.collections.new(self._name) asset_col = bpy.data.collections.get(self._name) context.scene.collection.children.link(asset_col) - asset_pipe.asset_collection = asset_col + self._asset_pipe.asset_collection = asset_col + return asset_col - asset_pipe.name = self._name - asset_pipe.prefix = self._prefix + def _remove_collections(self, context): + # Remove Data From task layer Files except for asset_collection + for col in bpy.data.collections: + if not col == self._asset_pipe.asset_collection: + bpy.data.collections.remove(col) + for obj in bpy.data.objects: + bpy.data.objects.remove(obj) + bpy.ops.outliner.orphans_purge( + do_local_ids=True, do_linked_ids=False, do_recursive=True + ) + + def _task_layer_collections_set(self, context, asset_col): for task_layer_key in config.TASK_LAYER_TYPES: bpy.data.collections.new(task_layer_key) task_layer_col = bpy.data.collections.get(task_layer_key) task_layer_col.asset_id_owner = task_layer_key asset_col.children.link(task_layer_col) - starting_file = "" + def _first_file_create(self, context, local_tls, asset_directory) -> str: + self._asset_pipe.is_asset_pipeline_file = True + + asset_col = self._asset_collection_get(context, local_tls) + self._task_layer_collections_set(context, asset_col) + if bpy.data.filepath != "": first_file_name = Path(bpy.data.filepath).name else: first_file_name = self._name + "." + local_tls[0].lower() + ".blend" - first_file = os.path.join(asset_path, first_file_name) + + first_file = os.path.join(asset_directory, first_file_name) set_local_task_layers(local_tls) bpy.ops.wm.save_as_mainfile(filepath=first_file, copy=True) - starting_file = first_file + return first_file + + def _task_layer_file_create(self, context, task_layer_key, asset_directory): + name = self._name + "." + task_layer_key.lower() + ".blend" + + set_local_task_layers([task_layer_key]) + + task_layer_file = os.path.join(asset_directory, name) + bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) + + def _publish_file_create(self, context, asset_directory): + publish_path = os.path.join(asset_directory, constants.ACTIVE_PUBLISH_KEY) + name = self._name + "." + "v001" + ".blend" + self._asset_pipe.asset_collection.asset_mark() + publish_file = os.path.join(publish_path, name) + bpy.ops.wm.save_as_mainfile(filepath=publish_file, copy=True) + + def execute(self, context: bpy.types.Context): + self._asset_name_set(context) + asset_directory = self._asset_dir_get(context) + local_tls = self._load_task_layers(context) + + if not os.path.exists(asset_directory): + os.mkdir(asset_directory) + + self._create_publish_directories(context, asset_directory) + + # Save Task Layer Config File + config.write_json_file( + asset_path=Path(asset_directory), + source_file_path=Path(self._asset_pipe.task_layer_config_type), + ) + + starting_file = self._first_file_create(context, local_tls, asset_directory) - # Create the file for each task layer. for task_layer_key in config.TASK_LAYER_TYPES: if task_layer_key == "NONE" or task_layer_key in local_tls: continue - name = self._name + "." + task_layer_key.lower() + ".blend" - - # Remove Data From Newly Created Files that aren't the user's main file - for col in bpy.data.collections: - if not col == asset_col: - bpy.data.collections.remove(col) - for obj in bpy.data.objects: - bpy.data.objects.remove(obj) - - bpy.ops.outliner.orphans_purge( - do_local_ids=True, do_linked_ids=False, do_recursive=True - ) - - set_local_task_layers([task_layer_key]) - - task_layer_file = os.path.join(asset_path, name) - bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) + self._remove_collections(context) + self._task_layer_file_create(context, task_layer_key, asset_directory) # Create intial publish based on task layers. - publish_path = os.path.join(asset_path, constants.ACTIVE_PUBLISH_KEY) - name = self._name + "." + "v001" + ".blend" - asset_pipe.asset_collection.asset_mark() - publish_file = os.path.join(publish_path, name) - bpy.ops.wm.save_as_mainfile(filepath=publish_file, copy=True) + self._remove_collections(context) + self._publish_file_create(context, asset_directory) if starting_file: bpy.ops.wm.open_mainfile(filepath=starting_file) return {'FINISHED'} -- 2.30.2 From 5ace90e33e8e96f674c265d4f3a704b01d4f9170 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 15:40:39 -0500 Subject: [PATCH 384/429] Asset Pipe: Clean Up UI for creating new assets --- scripts-blender/addons/asset_pipeline_2/props.py | 10 +++++++--- scripts-blender/addons/asset_pipeline_2/ui.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 9d876ea0..ce0d952a 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -71,7 +71,11 @@ class AssetPipeline(bpy.types.PropertyGroup): description="Depreciated files do not recieve any updates when syncing from a task layer", default=False, ) - asset_collection: bpy.props.PointerProperty(type=bpy.types.Collection) + 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) @@ -89,8 +93,8 @@ class AssetPipeline(bpy.types.PropertyGroup): new_file_mode: bpy.props.EnumProperty( name="New File Mode", items=( - ('KEEP', "Current File", "Update Selected Objects Only"), - ('BLANK', "Blank", "Update All Objects"), + ('KEEP', "Current File", "Setup the Existing File/Directory as an Asset"), + ('BLANK', "Blank File", "Create a New Blank Asset in a New Directory"), ), ) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 8be15eb0..a77c1542 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -53,7 +53,7 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): for task_layer in asset_pipe.local_task_layers: row.label(text=task_layer.name) - layout.prop(asset_pipe, "asset_collection", text="Asset") + layout.prop(asset_pipe, "asset_collection") layout.operator("assetpipe.update_ownership", text="Update Ownership") layout.operator("assetpipe.sync_push", text="Push to Publish", icon="TRIA_UP") -- 2.30.2 From b0328e26ba7a238f89fc21f4b1fdf8b8c9b98084 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 15:46:49 -0500 Subject: [PATCH 385/429] Asset Pipe: Clean Out any Objects if new Asset Mode is set to "BLANK" --- scripts-blender/addons/asset_pipeline_2/ops.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index dbef5ae3..bd6013e9 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -212,6 +212,9 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): source_file_path=Path(self._asset_pipe.task_layer_config_type), ) + if self._asset_pipe.new_file_mode == "BLANK": + self._remove_collections(context) + starting_file = self._first_file_create(context, local_tls, asset_directory) for task_layer_key in config.TASK_LAYER_TYPES: -- 2.30.2 From 8e37024b9b617c919bca35120fa42571fe7686c5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 16 Nov 2023 15:54:54 -0500 Subject: [PATCH 386/429] Asset Pipe: Add Task Layer Collections to Files during New Asset Operation --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index bd6013e9..7a2851c1 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -183,8 +183,8 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): def _task_layer_file_create(self, context, task_layer_key, asset_directory): name = self._name + "." + task_layer_key.lower() + ".blend" - set_local_task_layers([task_layer_key]) + self._task_layer_collections_set(context, self._asset_pipe.asset_collection) task_layer_file = os.path.join(asset_directory, name) bpy.ops.wm.save_as_mainfile(filepath=task_layer_file, copy=True) -- 2.30.2 From ac16ebf2a9c91fddd25a78427b5c9c05a2de45be Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Fri, 17 Nov 2023 13:41:07 +0100 Subject: [PATCH 387/429] AssetPipe: Move set_local_task_layers to props.py --- .../addons/asset_pipeline_2/merge/task_layer.py | 11 ----------- scripts-blender/addons/asset_pipeline_2/ops.py | 7 +++---- scripts-blender/addons/asset_pipeline_2/props.py | 9 +++++++++ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index a012f0e3..4f709d6d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -4,17 +4,6 @@ from .. import constants from .. import config -def set_local_task_layers(task_layer_keys: [str]): - local_task_layers = bpy.context.scene.asset_pipeline.local_task_layers - all_task_layers = bpy.context.scene.asset_pipeline.all_task_layers - # Update Local Task Layers for New File - local_task_layers.clear() - for task_layer in all_task_layers: - if task_layer.name in task_layer_keys: - new_local_task_layer = local_task_layers.add() - new_local_task_layer.name = task_layer.name - - def get_local_task_layers(): local_task_layers = bpy.context.scene.asset_pipeline.local_task_layers return [task_layer.name for task_layer in local_task_layers] diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 7a2851c1..bc43cccf 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -4,7 +4,6 @@ import os from pathlib import Path from .merge.naming import task_layer_prefix_transfer_data_update from .merge.task_layer import ( - set_local_task_layers, draw_task_layer_selection, get_local_task_layers, ) @@ -176,14 +175,14 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): first_file = os.path.join(asset_directory, first_file_name) - set_local_task_layers(local_tls) + self._asset_pipe.set_local_task_layers(local_tls) bpy.ops.wm.save_as_mainfile(filepath=first_file, copy=True) return first_file def _task_layer_file_create(self, context, task_layer_key, asset_directory): name = self._name + "." + task_layer_key.lower() + ".blend" - set_local_task_layers([task_layer_key]) + self._asset_pipe.set_local_task_layers([task_layer_key]) self._task_layer_collections_set(context, self._asset_pipe.asset_collection) task_layer_file = os.path.join(asset_directory, name) @@ -472,7 +471,7 @@ class ASSETPIPE_OT_update_local_task_layers(bpy.types.Operator): asset_pipe = context.scene.asset_pipeline all_task_layers = asset_pipe.all_task_layers local_tl = [tl.name for tl in all_task_layers if tl.is_local == True] - set_local_task_layers(local_tl) + asset_pipe.set_local_task_layers(local_tl) return {'FINISHED'} diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index ce0d952a..6ac979aa 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -1,4 +1,5 @@ import bpy +from typing import List from . import constants from .merge.task_layer import get_local_task_layers from .config import get_task_layer_presets_path @@ -121,6 +122,14 @@ class AssetPipeline(bpy.types.PropertyGroup): 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 + # UI BOOLS: used to show/hide transfer data elements # The names are also hard coded in constants.py under TRANSFER_DATA_TYPES # any changes will need to be reflected both here and in that enum -- 2.30.2 From 7156375d08b8476408e332f8b9c592b666b0750b Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Fri, 17 Nov 2023 13:41:12 +0100 Subject: [PATCH 388/429] AssetPipe: Move get_local_task_layers() into props This also required shuffling some other things, like passing asset_pipe to get_task_layer_objects & get_transfer_data_owner. Would like to move more functions to the asset_pipeline PropertyGroup, wherever it may make sense. --- .../addons/asset_pipeline_2/merge/core.py | 21 +++++++------- .../asset_pipeline_2/merge/shared_ids.py | 5 ++-- .../asset_pipeline_2/merge/task_layer.py | 16 +++++----- .../merge/transfer_data/transfer_core.py | 2 -- .../merge/transfer_data/transfer_functions.py | 29 ++++++++++++------- .../merge/transfer_data/transfer_ui.py | 6 ++-- .../merge/transfer_data/transfer_util.py | 7 +++-- .../addons/asset_pipeline_2/ops.py | 13 ++++++--- .../addons/asset_pipeline_2/props.py | 4 ++- .../addons/asset_pipeline_2/sync.py | 9 +++--- scripts-blender/addons/asset_pipeline_2/ui.py | 7 +++-- 11 files changed, 70 insertions(+), 49 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 0ea0c880..9f132c87 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -17,14 +17,13 @@ from .naming import ( get_id_type_name, ) -from .task_layer import get_local_task_layers - from pathlib import Path from typing import Dict from .. import constants def ownership_transfer_data_cleanup( + asset_pipe: 'bpy.types.AssetPipeline', obj: bpy.types.Object, ) -> None: """Remove Transfer Data ownership items if the corrisponding data is missing @@ -32,7 +31,7 @@ def ownership_transfer_data_cleanup( Args: obj (bpy.types.Object): Object that contains the transfer data """ - local_task_layer_keys = get_local_task_layers() + local_task_layer_keys = asset_pipe.get_local_task_layers() transfer_data = obj.transfer_data_ownership to_remove = [] for transfer_data_item in transfer_data: @@ -65,13 +64,13 @@ def ownership_get( asset_pipe = scene.asset_pipeline asset_pipe.temp_transfer_data.clear() - default_task_layer = get_local_task_layers()[0] + default_task_layer = asset_pipe.get_local_task_layers()[0] for col in asset_pipe.asset_collection.children: if col.asset_id_owner == "NONE": col.asset_id_owner = default_task_layer - task_layer_objs = get_task_layer_objects() + task_layer_objs = get_task_layer_objects(asset_pipe) for obj in local_col.all_objects: # TODO REPLACE This is expensive to loop over everything again @@ -87,7 +86,7 @@ def ownership_get( # Skip items that have no owner if obj.asset_id_owner == "NONE": continue - ownership_transfer_data_cleanup(obj) + ownership_transfer_data_cleanup(asset_pipe, obj) init_transfer_data(scene, obj) @@ -111,6 +110,7 @@ def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: def get_invalid_objects( + asset_pipe: 'bpy.types.AssetPipeline', local_col: bpy.types.Collection, ) -> list[bpy.types.Object]: """Returns a list of objects not used in the merge processing, @@ -124,8 +124,8 @@ def get_invalid_objects( Returns: list[bpy.types.Object]: List of Invalid Objects """ - local_task_layer_keys = get_local_task_layers() - task_layer_objs = get_task_layer_objects() + local_task_layer_keys = asset_pipe.get_local_task_layers() + task_layer_objs = get_task_layer_objects(asset_pipe) invalid_obj = [] for obj in local_col.all_objects: @@ -286,9 +286,8 @@ def import_data_from_lib( return eval(f"bpy.data.{data_category}['{data_name}']") -def get_task_layer_objects(): - asset_pipe = bpy.context.scene.asset_pipeline - local_task_layer_keys = get_local_task_layers() +def get_task_layer_objects(asset_pipe): + local_task_layer_keys = asset_pipe.get_local_task_layers() local_col = asset_pipe.asset_collection task_layer_objs = [] for col in local_col.children: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py index 70f79d63..7feae62b 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py @@ -2,7 +2,6 @@ import bpy from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids from .util import get_fundamental_id_type from .. import constants -from .task_layer import get_local_task_layers def get_shared_ids(collection: bpy.types.Collection) -> list[bpy.types.ID]: @@ -33,9 +32,9 @@ def init_shared_ids(scene: bpy.types.Scene) -> list[bpy.types.ID]: Returns: list[bpy.types.ID]: A list of new 'shared_ids' owned by the file's task layer """ - task_layer_key = get_local_task_layers()[0] - shared_ids = [] asset_pipe = scene.asset_pipeline + task_layer_key = asset_pipe.get_local_task_layers()[0] + shared_ids = [] local_col = asset_pipe.asset_collection for id in get_shared_ids(local_col): if id.asset_id_owner == 'NONE': diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 4f709d6d..908d38a4 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -4,11 +4,6 @@ from .. import constants from .. import config -def get_local_task_layers(): - local_task_layers = bpy.context.scene.asset_pipeline.local_task_layers - return [task_layer.name for task_layer in local_task_layers] - - def get_default_task_layer(td_type: str, name=""): if td_type == constants.ATTRIBUTE_KEY: if name in config.DEFAULT_OWNERSHIP_ATTRIBUTES: @@ -16,15 +11,20 @@ def get_default_task_layer(td_type: str, name=""): return config.DEFAULT_OWNERSHIP[td_type] -def get_transfer_data_owner(td_type_key: str, use_default_owner: bool, name=""): +def get_transfer_data_owner( + asset_pipe: 'bpy.types.AssetPipeline', + td_type_key: str, + use_default_owner: bool, + name="", +): default_tl = get_default_task_layer(td_type_key, name) if use_default_owner: return default_tl else: - if default_tl in get_local_task_layers(): + if default_tl in asset_pipe.get_local_task_layers(): return default_tl else: - return get_local_task_layers()[0] + return asset_pipe.get_local_task_layers()[0] def draw_task_layer_selection( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 6d055b20..4f948429 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -9,8 +9,6 @@ from .transfer_util import ( check_transfer_data_entry, ) -from ..task_layer import get_local_task_layers - def copy_transfer_data_ownership( transfer_data_item, target_obj: bpy.types.Object diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index c693845a..13e34232 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -222,15 +222,18 @@ def modifier_is_missing(transfer_data_item): def init_modifiers(scene, obj, use_default_owner: bool): + asset_pipe = scene.asset_pipeline td_type_key = constants.MODIFIER_KEY transfer_data = obj.transfer_data_ownership - task_layer_owner = get_transfer_data_owner(td_type_key, use_default_owner) + task_layer_owner = get_transfer_data_owner( + asset_pipe, td_type_key, use_default_owner + ) for mod in obj.modifiers: mod.name = task_layer_prefix_name_get(mod.name, task_layer_owner) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, mod.name, td_type_key) if len(matches) == 0: - scene.asset_pipeline.add_temp_transfer_data( + asset_pipe.add_temp_transfer_data( name=mod.name, owner=task_layer_owner, type=td_type_key, @@ -321,13 +324,16 @@ def constraint_is_missing(transfer_data_item): def init_constraints(scene, obj, use_default_owner: bool): td_type_key = constants.CONSTRAINT_KEY transfer_data = obj.transfer_data_ownership - task_layer_owner = get_transfer_data_owner(td_type_key, use_default_owner) + asset_pipe = scene.asset_pipeline + task_layer_owner = get_transfer_data_owner( + asset_pipe, td_type_key, use_default_owner + ) for const in obj.constraints: const.name = task_layer_prefix_name_get(const.name, task_layer_owner) # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, const.name, td_type_key) if len(matches) == 0: - scene.asset_pipeline.add_temp_transfer_data( + asset_pipe.add_temp_transfer_data( name=const.name, owner=task_layer_owner, type=td_type_key, @@ -412,6 +418,7 @@ def material_slots_is_missing(transfer_data_item): def init_material_slots(scene, obj, use_default_owner: bool): + asset_pipe = scene.asset_pipeline td_type_key = constants.MATERIAL_SLOT_KEY name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership @@ -432,9 +439,9 @@ def init_material_slots(scene, obj, use_default_owner: bool): matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner if len(matches) == 0: - scene.asset_pipeline.add_temp_transfer_data( + asset_pipe.add_temp_transfer_data( name=name, - owner=get_transfer_data_owner(td_type_key, use_default_owner), + owner=get_transfer_data_owner(asset_pipe, td_type_key, use_default_owner), type=td_type_key, obj=obj, use_default_owner=use_default_owner, @@ -689,6 +696,7 @@ def attribute_is_missing(transfer_data_item): def init_attributes(scene, obj, use_default_owner: bool): + asset_pipe = scene.asset_pipeline if obj.type != "MESH": return transfer_data = obj.transfer_data_ownership @@ -697,10 +705,10 @@ def init_attributes(scene, obj, use_default_owner: bool): # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) if len(matches) == 0: - scene.asset_pipeline.add_temp_transfer_data( + asset_pipe.add_temp_transfer_data( name=atttribute.name, owner=get_transfer_data_owner( - td_type_key, use_default_owner, atttribute.name + asset_pipe, td_type_key, use_default_owner, atttribute.name ), type=td_type_key, obj=obj, @@ -761,6 +769,7 @@ def parent_is_missing(transfer_data_item): def init_parent(scene, obj, use_default_owner: bool): + asset_pipe = scene.asset_pipeline td_type_key = constants.PARENT_KEY name = constants.PARENT_TRANSFER_DATA_ITEM_NAME transfer_data = obj.transfer_data_ownership @@ -771,9 +780,9 @@ def init_parent(scene, obj, use_default_owner: bool): matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner if len(matches) == 0: - scene.asset_pipeline.add_temp_transfer_data( + asset_pipe.add_temp_transfer_data( name=name, - owner=get_transfer_data_owner(td_type_key, use_default_owner), + owner=get_transfer_data_owner(asset_pipe, td_type_key, use_default_owner), type=td_type_key, obj=obj, use_default_owner=use_default_owner, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index b1c16eb5..fe4c1674 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -1,6 +1,6 @@ import bpy from ... import constants -from ..task_layer import draw_task_layer_selection, get_local_task_layers +from ..task_layer import draw_task_layer_selection def draw_transfer_data_type( @@ -30,7 +30,9 @@ def draw_transfer_data_type( if transfer_data_item.get("surrender"): enabled = ( - False if transfer_data_item.owner in get_local_task_layers() else True + False + if transfer_data_item.owner in asset_pipe.get_local_task_layers() + else True ) row.operator( "assetpipe.update_surrendered_transfer_data" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index d03fe30d..4dd1a04c 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -104,15 +104,18 @@ def transfer_data_item_init( data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers td_type_key (str): Key for the transfer data type """ + asset_pipe = scene.asset_pipeline transfer_data = obj.transfer_data_ownership for item in data_list: # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, item.name, td_type_key) if len(matches) == 0: - scene.asset_pipeline.add_temp_transfer_data( + asset_pipe.add_temp_transfer_data( name=item.name, - owner=get_transfer_data_owner(td_type_key, use_default_owner), + owner=get_transfer_data_owner( + asset_pipe, td_type_key, use_default_owner + ), type=td_type_key, obj=obj, use_default_owner=use_default_owner, diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index bc43cccf..7b311262 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -5,7 +5,6 @@ from pathlib import Path from .merge.naming import task_layer_prefix_transfer_data_update from .merge.task_layer import ( draw_task_layer_selection, - get_local_task_layers, ) from .merge.publish import get_next_published_file, find_all_published from .images import save_images @@ -692,6 +691,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): return [item for item in unfiltered_list if self.name_filter in item.name] def _get_transfer_data_to_update(self, context): + asset_pipe = context.scene.asset_pipeline objs = self._get_objects(context) transfer_data_items_to_update = [] if self.data_type == "TRANSFER_DATA": @@ -709,7 +709,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): return [ item for item in transfer_data_items_to_update - if item.owner in get_local_task_layers() + if item.owner in asset_pipe.get_local_task_layers() ] return transfer_data_items_to_update @@ -721,12 +721,13 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): return asset_objs if self.data_source == "ALL" else selected_asset_objs def _get_filtered_objects(self, context): + asset_pipe = context.scene.asset_pipeline objs = self._get_objects(context) if self.filter_owners == "LOCAL" and self.data_type == "OBJECT": return [ item for item in self._filter_by_name(context, objs) - if item.asset_id_owner in get_local_task_layers() + if item.asset_id_owner in asset_pipe.get_local_task_layers() ] if self.filter_owners == "OWNED" and self.data_type == "OBJECT": return [ @@ -803,6 +804,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): bottom_label_split.label(text=f"Change Ownership on {length} {data_type_name}") def execute(self, context: bpy.types.Context): + asset_pipe = context.scene.asset_pipeline objs = self._get_filtered_objects(context) if self.data_type == "OBJECT": @@ -813,7 +815,10 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): for transfer_data_item_to_update in transfer_data_items_to_update: if self.surrender_selection: - if transfer_data_item_to_update.owner in get_local_task_layers(): + if ( + transfer_data_item_to_update.owner + in asset_pipe.get_local_task_layers() + ): transfer_data_item_to_update.surrender = True continue transfer_data_item_to_update.owner = self.owner_selection diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 6ac979aa..df89e349 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -1,7 +1,6 @@ import bpy from typing import List from . import constants -from .merge.task_layer import get_local_task_layers from .config import get_task_layer_presets_path from pathlib import Path from .prefs import get_addon_prefs @@ -130,6 +129,9 @@ class AssetPipeline(bpy.types.PropertyGroup): new_local_task_layer = self.local_task_layers.add() new_local_task_layer.name = task_layer.name + def get_local_task_layers(self): + return [task_layer.name for task_layer in self.local_task_layers] + # UI BOOLS: used to show/hide transfer data elements # The names are also hard coded in constants.py under TRANSFER_DATA_TYPES # any changes will need to be reflected both here and in that enum diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 95da9903..9ac3a889 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -15,7 +15,6 @@ from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.shared_ids import get_shared_id_icon from . import constants from . import config -from .merge.task_layer import get_local_task_layers def sync_poll(cls, context): @@ -33,7 +32,8 @@ def sync_invoke(self, context): self._temp_transfer_data.clear() self._invalid_objs.clear() - local_col = context.scene.asset_pipeline.asset_collection + asset_pipe = context.scene.asset_pipeline + local_col = asset_pipe.asset_collection if not local_col: self.report({'ERROR'}, "Top level collection could not be found") return {'CANCELLED'} @@ -45,7 +45,7 @@ def sync_invoke(self, context): ownership_get(local_col, context.scene) - self._invalid_objs = get_invalid_objects(local_col) + self._invalid_objs = get_invalid_objects(asset_pipe, local_col) self._shared_ids = init_shared_ids(context.scene) @@ -95,9 +95,10 @@ def sync_execute_update_ownership(self, context): def sync_execute_prepare_sync(self, context): + asset_pipe = context.scene.asset_pipeline self._current_file = Path(bpy.data.filepath) self._temp_dir = Path(bpy.app.tempdir).parent - self._task_layer_keys = get_local_task_layers() + self._task_layer_keys = asset_pipe.get_local_task_layers() # TODO Check if file contains a valid task layer # if self._task_layer_key == "NONE": # self.report({'ERROR'}, "Current File Name doesn't contain valid task layer") diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index a77c1542..51a2074c 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -5,7 +5,6 @@ from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.task_layer import draw_task_layer_selection from .config import verify_json_data from . import constants -from .merge.task_layer import get_local_task_layers class ASSETPIPE_PT_sync(bpy.types.Panel): @@ -131,7 +130,11 @@ class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): row.label(text=f"{obj.name}: ", icon="OBJECT_DATA") if obj.get("asset_id_surrender"): - enabled = False if obj.asset_id_owner in get_local_task_layers() else True + enabled = ( + False + if obj.asset_id_owner in asset_pipe.get_local_task_layers() + else True + ) row.operator("assetpipe.update_surrendered_object") row.enabled = enabled else: -- 2.30.2 From c25f4d9d9008dfdad73002175e093ab2bdfdb0e5 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 17 Nov 2023 14:05:22 -0500 Subject: [PATCH 389/429] Asset Pipe: Add README file --- .../addons/asset_pipeline_2/README.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 scripts-blender/addons/asset_pipeline_2/README.md diff --git a/scripts-blender/addons/asset_pipeline_2/README.md b/scripts-blender/addons/asset_pipeline_2/README.md new file mode 100644 index 00000000..d6ea3dd8 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/README.md @@ -0,0 +1,110 @@ +# Asset Pipeline + +## Introduction +This Add-On was designed to allow multiple artists to collaborate while contributing to a common Asset. It enables simultaneous work on the same asset by multiple artists. The add-on works by tracking what data each artist contributes to the asset and merges the assets together into a final "Published" asset. This published asset is marked to be discovered by Blender's asset manager. +## Table of Contents + + +- [Asset Pipeline](#asset-pipeline) + - [Introduction](#introduction) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Key Concepts](#key-concepts) + - [Creating New Assets](#creating-new-assets) + - [Current File Mode](#current-file-mode) + - [Blank File Mode](#blank-file-mode) + - [Push/Pull](#pushpull) + - [Updating Ownership](#updating-ownership) + - [Save File](#save-file) + - [Merge with Published File](#merge-with-published-file) + - [Surrendering Ownership](#surrendering-ownership) + - [Publish New Version](#publish-new-version) + - [Active](#active) + - [Staged](#staged) + - [Review](#review) + + + +## Installation +1. Download [latest release](../addons/overview) +2. Launch Blender, navigate to `Edit > Preferences` select `Addons` and then `Install` +3. Navigate to the downloaded add-on and select `Install Add-on` + + +## Key Concepts +**Task Layers** Task Layers are defined in a JSON file that describes the number of layers used to manage the asset. Typically each task layer is given it's own file, artists can optionally house multiple task layers inside the same file if required. Each task layer is used to describe a step in the asset making process (e.g."Modeling", "Rigging", "Shading"). The number and content of a task layer is fully customizable by the artist. + +**Ownership** Each piece of data in the Asset Pipeline is owned by a task layer, this includes Objects, Task Layer Collections and Transfer Data. The owner of data is the only person who can contribute to that piece of data, including modifying or removing that data. Objects implicitly will own the meshes and other types of object data, multiple objects referencing the same mesh is not supported. + +**Asset Collection** Is the top-level collection for a given asset, all relevant objects/sub-collections for the asset are contained within this collection. + +**Task Layer Collection** These collections are children of the asset collection, each Task Layer collection is owned by a specific task layer, they are all top-level child collections to the Asset Collection. Children of the Task Layer collections are owned by the Task Layer collection's owner. + +**Transfer Data** Is data that a part or associated with an object or mesh, but can be explicitly owned and updated. This is the key concept that allows multiple artists to contribute to an asset. During the Push/Pull process transfer data is applied on top of each object, allowing artist A to own an object but artist B to own the vertex groups on that object for example. + +Transfer Data Types: + - Vertex Groups + - Modifiers + - Constraints + - Materials (including slots, index and the material IDs) + - ShapeKeys + - Attributes + - Parent Relationships + + **Shared IDs** Shared IDs are data-blocks that can be owned by many 'users'. This data type is limited to Geometry Node Groups and Images, these are pieces of data that need to be explicitly owned by a task layer, and only that task layer may update this data-block, but other task layers may reference this data-block. + + +## Creating New Assets +Once the add-on is installed you will be greeted by a new sidebar in the 3D View, titled 'Asset Pipeline'. Under the panel 'Asset Management' you will find a UI to set up a new Asset. The New Asset UI has two modes "Current File" and "Blank File". + +### Current File Mode +"Current File" mode will retain you current .blend file's data and allow you to use it's current directory to setup a new Asset. +To setup an asset using "Current File" mode, please open the file you would like to setup as an asset, then select "Current File" mode in the asset pipeline side panel in the 3D View. + + 1. Select the "Task Layer Preset" you would like to use. + 2. Select the collection to be the Asset Collection, this is the top level collection for your asset. + 3. Select 'Create New Asset' + 4. In the operator pop-up select which task layers will be local to your file, typically artists only select one. + 5. Ensure 'Create Files for Unselected Task Layers' is enabled, otherwise the add-on will not automatically create files for the other task layers. + 6. Press OK to set-up current file/folder as an Asset. The add-on will automatically create a published file, this file will be empty until you push to it. + + +### Blank File Mode +"Blank File" mode will create a new blank asset in a new directory named after your asset. + + 1. Select the "Task Layer Preset" you would like to use. + 2. Enter the name and prefix desired for your asset + 3. Select 'Create New Asset' + 4. In the operator pop-up select which task layers will be local to your file, typically artists only select one. + 5. Ensure 'Create Files for Unselected Task Layers' is enabled, otherwise the add-on will not automatically create files for the other task layers. + 6. Press OK to set-up current file/folder as an Asset. The add-on will automatically create a published file, this file will be empty until you push to it. + + +## Push/Pull +The Push/Pull process happens in three steps. + +### 1. Updating Ownership +When you Push/Pull a file, you will be greeted with an operator dialogue. This dialogue will list any new data that it has found in your file. Pressing OK will assign these new pieces of data to your local task layer, if you have multiple local task layers, you will be able to select which one is the owner of each piece of data. Once completed this information will be used to ensure your work is merged properly with the published file. + +### 2. Save File +The add-on will optionally save your current file plus any unsaved/unpacked images will be saved in a directory relative to your asset (configurable in the add-on preferences). It will always create a back-up of your current file, in the case where the merge process fails, you will be prompted to revert your file back to it's pre-merge status. + +### 3. Merge with Published File +Push and Pull are merging operations done to/from the published file. When you want to share your updated work to the rest of the team select "Push to Publish" to update the published file with your changes. Push will update any Transfer data you edited, and update any objects/collections you own with the version in your current file. Transfer Data owned by other artists will be re-applied to your objects. + +If another artist then uses the "Pull to Publish" operator the same process will occur, keeping all objects, collections and transfer data that is local to their file, and importing any data that was owned externally by other task layers. + +## Surrendering Ownership +In the ownership inspector each Object/Transfer Data item has an option to "surrender" that piece of data. When surrendering this piece of data is now "up for grabs" to all other task layers. After surrendering artists will need to push this update to the published file. The surrendered item's ownership indicator will be replaced by an "Update Surrendered" operator, this operator is available to all task layers except the one that surrendered that data. When another task layer pulls in from the publish, they will be able to run the "Update Surrendered" operator to claim it assigning it to that task layer. + +## Publish New Version +To Publish a new version of an asset select the "Publish New Version" operator. The operator dialogue will require an input on which publish type to create. Publish types are as follows. + +### Active +An active publish is a publish that can be referenced by the production into shot files, multiple version can be published if some shots require an older version of the current asset, but only a single asset will be updated with changes from the push/pull target. + +### Staged +A staged asset, is an publish that cannot be referenced by the production, only one staged asset can exist at a time. If a staged publish exists it will replace the active publish as the push/pull target. The staged area exists so artists can collaborate on a new version of an asset that is not ready to be used in production. + +### Review +A review publish is simple a way to test out the final published version of your asset. You can create as many review publishes as you want to check your work and ensure the merge process produces results that are expected. Review publish is never used as a push/pull target and is for testing only. \ No newline at end of file -- 2.30.2 From c3c251ff9d84e3218f14ef3fa26f7170fc78435b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 17 Nov 2023 14:18:03 -0500 Subject: [PATCH 390/429] Asset Pipe: Add Task Layer Selection for Shared IDs --- scripts-blender/addons/asset_pipeline_2/sync.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 9ac3a889..09f8ce6f 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -15,6 +15,7 @@ from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.shared_ids import get_shared_id_icon from . import constants from . import config +from .merge.task_layer import draw_task_layer_selection def sync_poll(cls, context): @@ -64,7 +65,12 @@ def sync_draw(self, context): box = layout.box() box.label(text="New 'Shared IDs' found") for id in self._shared_ids: - box.label(text=id.name, icon=get_shared_id_icon(id)) + row = box.row() + row.label(text=id.name, icon=get_shared_id_icon(id)) + draw_task_layer_selection( + layout=row, + data=id, + ) if len(self._temp_transfer_data) == 0: layout.label(text="No new local Transfer Data found") -- 2.30.2 From d791d920d434b4aa587ab9933ce54dfc8a9c60ca Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 13:13:23 -0500 Subject: [PATCH 391/429] Asset Pipe: Improvements to `_gen_transfer_data_map()` --- .../addons/asset_pipeline_2/merge/asset_mapping.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index 2f397d22..e512c9a5 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -223,13 +223,12 @@ class AssetTransferMapping: return None def _gen_transfer_data_map(self): - context = bpy.context + context = bpy.context # Bruh. transfer_data_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data temp_transfer_data.clear() - for source_obj in self.object_map: - target_obj = self.object_map[source_obj] - objs = [source_obj, target_obj] + for objs in self.object_map.items(): + source_obj, target_obj = objs for obj in objs: for transfer_data_item in obj.transfer_data_ownership: self._check_transfer_data_conflict(obj, transfer_data_item) -- 2.30.2 From bc2a5470acc4c254819989318aba57b3f5000028 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 14:11:08 -0500 Subject: [PATCH 392/429] Asset Pipe: Rename "Update Surrenderd" to "Claim Surrendered" --- scripts-blender/addons/asset_pipeline_2/ops.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 7b311262..f25e8ecc 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -538,8 +538,8 @@ class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): class ASSETPIPE_OT_update_surrendered_object(bpy.types.Operator): bl_idname = "assetpipe.update_surrendered_object" - bl_label = "Update Surrendered" - bl_description = """Update Surrended Object Owner""" + bl_label = "Claim Surrendered" + bl_description = """Claim Surrended Object Owner""" _obj = None _old_onwer = "" @@ -573,8 +573,8 @@ class ASSETPIPE_OT_update_surrendered_object(bpy.types.Operator): class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): bl_idname = "assetpipe.update_surrendered_transfer_data" - bl_label = "Update Surrendered" - bl_description = """Update Surrended Transfer Data Owner""" + bl_label = "Claim Surrendered" + bl_description = """Claim Surrended Transfer Data Owner""" transfer_data_item_name: bpy.props.StringProperty(name="Transfer Data Item Name") -- 2.30.2 From ff46c67d0e61ca6d7c1afd5490fa9dcad65c0abc Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 14:12:25 -0500 Subject: [PATCH 393/429] Asset Pipe: Surrende UI Updates - Keep Ownership UI if Surrendered - Replace Surrendered Bool with Heart Icon --- .../merge/transfer_data/transfer_ui.py | 31 +++++++++++++------ scripts-blender/addons/asset_pipeline_2/ui.py | 15 ++++++--- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index fe4c1674..fafbdc66 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -23,7 +23,6 @@ def draw_transfer_data_type( if not bool(asset_pipe.get(ui_bool)): return - scene = bpy.context.scene for transfer_data_item in transfer_data: row = box.row() row.label(text=f"{transfer_data_item.name}: ") @@ -34,17 +33,29 @@ def draw_transfer_data_type( if transfer_data_item.owner in asset_pipe.get_local_task_layers() else True ) - row.operator( + # Disable entire row if the item is surrender (prevents user from un-surrendering) + row.enabled = enabled + col = row.column() + col.operator( "assetpipe.update_surrendered_transfer_data" ).transfer_data_item_name = transfer_data_item.name - row.enabled = enabled - else: - row.prop(transfer_data_item, "surrender", text="Surrender") - draw_task_layer_selection( - layout=row, - data=transfer_data_item, - show_all_task_layers=transfer_data_item.get("use_default_owner"), - ) + + # New Row inside a column because draw_task_layer_selection() will enable/disable the entire row + # Only need this to affect itself and the "surrender" property + col = row.column() + task_layer_row = col.row() + + draw_task_layer_selection( + layout=task_layer_row, + data=transfer_data_item, + show_all_task_layers=transfer_data_item.get("use_default_owner"), + ) + surrender_icon = ( + "ORPHAN_DATA" if transfer_data_item.get("surrender") else "HEART" + ) + task_layer_row.prop( + transfer_data_item, "surrender", text="", icon=surrender_icon + ) def draw_transfer_data( diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 51a2074c..8b5f217a 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -135,11 +135,18 @@ class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): if obj.asset_id_owner in asset_pipe.get_local_task_layers() else True ) - row.operator("assetpipe.update_surrendered_object") row.enabled = enabled - else: - row.prop(obj, "asset_id_surrender", text="Surrender") - draw_task_layer_selection(layout=row, data=obj) + col = row.column() + col.operator("assetpipe.update_surrendered_object") + + # New Row inside a column because draw_task_layer_selection() will enable/disable the entire row + # Only need this to affect itself and the "surrender" property + col = row.column() + task_layer_row = col.row() + + draw_task_layer_selection(layout=task_layer_row, data=obj) + surrender_icon = "ORPHAN_DATA" if obj.get("asset_id_surrender") else "HEART" + task_layer_row.prop(obj, "asset_id_surrender", text="", icon=surrender_icon) draw_transfer_data(transfer_data, layout) -- 2.30.2 From 5fe1bc16458b938e616d8d960ae8cd430d0e9061 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 16:56:45 -0500 Subject: [PATCH 394/429] Asset Pipe: Add Auto-Surrender to Task Layer JSON --- .../asset_pipeline_2/merge/task_layer.py | 5 +- .../task_layer_configs/Character.json | 51 ++++++++++++++----- .../task_layer_configs/Set.json | 49 ++++++++++++++---- 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 908d38a4..cf06fa1c 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -7,8 +7,9 @@ from .. import config def get_default_task_layer(td_type: str, name=""): if td_type == constants.ATTRIBUTE_KEY: if name in config.DEFAULT_OWNERSHIP_ATTRIBUTES: - return config.DEFAULT_OWNERSHIP_ATTRIBUTES[name] - return config.DEFAULT_OWNERSHIP[td_type] + return config.DEFAULT_OWNERSHIP_ATTRIBUTES[name]['default_owner'] + print(config.DEFAULT_OWNERSHIP[td_type]['default_owner']) + return config.DEFAULT_OWNERSHIP[td_type]['default_owner'] def get_transfer_data_owner( diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json index 74e70542..1daa82c1 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json @@ -1,20 +1,47 @@ { "TASK_LAYER_TYPES": { - "Modeling":"MOD", - "Rigging":"RIG", - "Shading":"SHD" + "Modeling": "MOD", + "Rigging": "RIG", + "Shading": "SHD" }, "DEFAULT_OWNERSHIP": { - "GROUP_VERTEX": "Rigging", - "MODIFIER": "Rigging", - "CONSTRAINT": "Rigging", - "MATERIAL": "Shading", - "SHAPE_KEY": "Modeling", - "ATTRIBUTE": "Rigging", - "PARENT": "Rigging" + "GROUP_VERTEX": { + "default_owner": "Rigging", + "auto_surrender": false + }, + "MODIFIER": { + "default_owner": "Rigging", + "auto_surrender": false + }, + "CONSTRAINT": { + "default_owner": "Rigging", + "auto_surrender": false + }, + "MATERIAL": { + "default_owner": "Shading", + "auto_surrender": false + }, + "SHAPE_KEY": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "ATTRIBUTE": { + "default_owner": "Rigging", + "auto_surrender": false + }, + "PARENT": { + "default_owner": "Rigging", + "auto_surrender": false + } }, "DEFAULT_OWNERSHIP_ATTRIBUTES": { - "sharp_face": "Modeling", - "UVMap": "Shading" + "sharp_face": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "UVMap": { + "default_owner": "Shading", + "auto_surrender": false + } } } \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json index 8c755dad..63c4596d 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json @@ -1,19 +1,46 @@ { "TASK_LAYER_TYPES": { - "Modeling":"MOD", - "Shading":"SHD" + "Modeling": "MOD", + "Shading": "SHD" }, "DEFAULT_OWNERSHIP": { - "GROUP_VERTEX": "Modeling", - "MODIFIER": "Modeling", - "CONSTRAINT": "Modeling", - "MATERIAL": "Shading", - "SHAPE_KEY": "Modeling", - "ATTRIBUTE": "Modeling", - "PARENT": "Modeling" + "GROUP_VERTEX": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "MODIFIER": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "CONSTRAINT": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "MATERIAL": { + "default_owner": "Shading", + "auto_surrender": false + }, + "SHAPE_KEY": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "ATTRIBUTE": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "PARENT": { + "default_owner": "Modeling", + "auto_surrender": false + } }, "DEFAULT_OWNERSHIP_ATTRIBUTES": { - "sharp_face": "Modeling", - "UVMap": "Shading" + "sharp_face": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "UVMap": { + "default_owner": "Shading", + "auto_surrender": false + } } } \ No newline at end of file -- 2.30.2 From f9c8a22b32876dc352dd925fba0949418a537781 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 17:05:31 -0500 Subject: [PATCH 395/429] Asset Pipe: Remove Default Ownership Property --- .../addons/asset_pipeline_2/merge/core.py | 3 -- .../asset_pipeline_2/merge/task_layer.py | 8 ++--- .../merge/transfer_data/transfer_core.py | 35 ++++--------------- .../merge/transfer_data/transfer_functions.py | 35 ++++++++----------- .../merge/transfer_data/transfer_ui.py | 1 - .../merge/transfer_data/transfer_util.py | 5 ++- .../addons/asset_pipeline_2/ops.py | 3 -- .../addons/asset_pipeline_2/props.py | 8 +---- 8 files changed, 26 insertions(+), 72 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 9f132c87..e1482df8 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -3,7 +3,6 @@ from ..merge.naming import task_layer_prefix_transfer_data_update from .asset_mapping import AssetTransferMapping from .transfer_data.transfer_core import ( init_transfer_data, - init_transfer_data_with_defaults, transfer_data_is_missing, apply_transfer_data, transfer_data_clean, @@ -81,8 +80,6 @@ def ownership_get( if obj.asset_id_owner == "NONE" and obj in task_layer_objs: obj.asset_id_owner = default_task_layer # obj.name = asset_prefix_name_get(obj.name) - init_transfer_data_with_defaults(scene, obj) - continue # Skip items that have no owner if obj.asset_id_owner == "NONE": continue diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index cf06fa1c..b70b03c7 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -15,17 +15,13 @@ def get_default_task_layer(td_type: str, name=""): def get_transfer_data_owner( asset_pipe: 'bpy.types.AssetPipeline', td_type_key: str, - use_default_owner: bool, name="", ): default_tl = get_default_task_layer(td_type_key, name) - if use_default_owner: + if default_tl in asset_pipe.get_local_task_layers(): return default_tl else: - if default_tl in asset_pipe.get_local_task_layers(): - return default_tl - else: - return asset_pipe.get_local_task_layers()[0] + return asset_pipe.get_local_task_layers()[0] def draw_task_layer_selection( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 4f948429..5af482a1 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -76,34 +76,13 @@ def init_transfer_data( task_layer_name (str): Name of task layer temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP """ - transfer_functions.init_vertex_groups(scene, obj, False) - transfer_functions.init_material_slots(scene, obj, False) - transfer_functions.init_modifiers(scene, obj, False) - transfer_functions.init_constraints(scene, obj, False) - transfer_functions.init_shape_keys(scene, obj, False) - transfer_functions.init_attributes(scene, obj, False) - transfer_functions.init_parent(scene, obj, False) - - -def init_transfer_data_with_defaults( - scene: bpy.types.Scene, - obj: bpy.types.Object, -): - """Collect Transfer Data Items on a given object with default ownership data, - this can only be run on new objects - - Args: - obj (bpy.types.Object): Target object for transfer data - task_layer_name (str): Name of task layer - temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP - """ - transfer_functions.init_vertex_groups(scene, obj, True) - transfer_functions.init_material_slots(scene, obj, True) - transfer_functions.init_modifiers(scene, obj, True) - transfer_functions.init_constraints(scene, obj, True) - transfer_functions.init_shape_keys(scene, obj, True) - transfer_functions.init_attributes(scene, obj, True) - transfer_functions.init_parent(scene, obj, True) + transfer_functions.init_vertex_groups(scene, obj) + transfer_functions.init_material_slots(scene, obj) + transfer_functions.init_modifiers(scene, obj) + transfer_functions.init_constraints(scene, obj) + transfer_functions.init_shape_keys(scene, obj) + transfer_functions.init_attributes(scene, obj) + transfer_functions.init_parent(scene, obj) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 13e34232..41bd1f8f 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -35,13 +35,12 @@ def vertex_group_is_missing(transfer_data_item): ) -def init_vertex_groups(scene, obj, use_default_owner: bool): +def init_vertex_groups(scene, obj): transfer_data_item_init( scene=scene, obj=obj, data_list=obj.vertex_groups, td_type_key=constants.VERTEX_GROUP_KEY, - use_default_owner=use_default_owner, ) @@ -221,12 +220,13 @@ def modifier_is_missing(transfer_data_item): ) -def init_modifiers(scene, obj, use_default_owner: bool): +def init_modifiers(scene, obj): asset_pipe = scene.asset_pipeline td_type_key = constants.MODIFIER_KEY transfer_data = obj.transfer_data_ownership task_layer_owner = get_transfer_data_owner( - asset_pipe, td_type_key, use_default_owner + asset_pipe, + td_type_key, ) for mod in obj.modifiers: mod.name = task_layer_prefix_name_get(mod.name, task_layer_owner) @@ -238,7 +238,6 @@ def init_modifiers(scene, obj, use_default_owner: bool): owner=task_layer_owner, type=td_type_key, obj=obj, - use_default_owner=use_default_owner, ) @@ -321,12 +320,13 @@ def constraint_is_missing(transfer_data_item): ) -def init_constraints(scene, obj, use_default_owner: bool): +def init_constraints(scene, obj): td_type_key = constants.CONSTRAINT_KEY transfer_data = obj.transfer_data_ownership asset_pipe = scene.asset_pipeline task_layer_owner = get_transfer_data_owner( - asset_pipe, td_type_key, use_default_owner + asset_pipe, + td_type_key, ) for const in obj.constraints: const.name = task_layer_prefix_name_get(const.name, task_layer_owner) @@ -338,7 +338,6 @@ def init_constraints(scene, obj, use_default_owner: bool): owner=task_layer_owner, type=td_type_key, obj=obj, - use_default_owner=use_default_owner, ) @@ -417,7 +416,7 @@ def material_slots_is_missing(transfer_data_item): return True -def init_material_slots(scene, obj, use_default_owner: bool): +def init_material_slots(scene, obj): asset_pipe = scene.asset_pipeline td_type_key = constants.MATERIAL_SLOT_KEY name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME @@ -441,10 +440,9 @@ def init_material_slots(scene, obj, use_default_owner: bool): if len(matches) == 0: asset_pipe.add_temp_transfer_data( name=name, - owner=get_transfer_data_owner(asset_pipe, td_type_key, use_default_owner), + owner=get_transfer_data_owner(asset_pipe, td_type_key), type=td_type_key, obj=obj, - use_default_owner=use_default_owner, ) @@ -543,7 +541,7 @@ def shape_key_is_missing(transfer_data_item): ) -def init_shape_keys(scene, obj, use_default_owner: bool): +def init_shape_keys(scene, obj): if obj.type != "MESH" or obj.data.shape_keys is None: return @@ -562,7 +560,6 @@ def init_shape_keys(scene, obj, use_default_owner: bool): obj=obj, data_list=obj.data.shape_keys.key_blocks, td_type_key=constants.SHAPE_KEY_KEY, - use_default_owner=use_default_owner, ) @@ -695,7 +692,7 @@ def attribute_is_missing(transfer_data_item): return True -def init_attributes(scene, obj, use_default_owner: bool): +def init_attributes(scene, obj): asset_pipe = scene.asset_pipeline if obj.type != "MESH": return @@ -707,12 +704,9 @@ def init_attributes(scene, obj, use_default_owner: bool): if len(matches) == 0: asset_pipe.add_temp_transfer_data( name=atttribute.name, - owner=get_transfer_data_owner( - asset_pipe, td_type_key, use_default_owner, atttribute.name - ), + owner=get_transfer_data_owner(asset_pipe, td_type_key, atttribute.name), type=td_type_key, obj=obj, - use_default_owner=use_default_owner, ) @@ -768,7 +762,7 @@ def parent_is_missing(transfer_data_item): return True -def init_parent(scene, obj, use_default_owner: bool): +def init_parent(scene, obj): asset_pipe = scene.asset_pipeline td_type_key = constants.PARENT_KEY name = constants.PARENT_TRANSFER_DATA_ITEM_NAME @@ -782,10 +776,9 @@ def init_parent(scene, obj, use_default_owner: bool): if len(matches) == 0: asset_pipe.add_temp_transfer_data( name=name, - owner=get_transfer_data_owner(asset_pipe, td_type_key, use_default_owner), + owner=get_transfer_data_owner(asset_pipe, td_type_key), type=td_type_key, obj=obj, - use_default_owner=use_default_owner, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index fafbdc66..464ae10e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -48,7 +48,6 @@ def draw_transfer_data_type( draw_task_layer_selection( layout=task_layer_row, data=transfer_data_item, - show_all_task_layers=transfer_data_item.get("use_default_owner"), ) surrender_icon = ( "ORPHAN_DATA" if transfer_data_item.get("surrender") else "HEART" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index 4dd1a04c..0fb7e34b 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -94,7 +94,6 @@ def transfer_data_item_init( obj: bpy.types.Object, data_list: bpy.types.CollectionProperty, td_type_key: str, - use_default_owner=bool, ): """_summary_ @@ -114,9 +113,9 @@ def transfer_data_item_init( asset_pipe.add_temp_transfer_data( name=item.name, owner=get_transfer_data_owner( - asset_pipe, td_type_key, use_default_owner + asset_pipe, + td_type_key, ), type=td_type_key, obj=obj, - use_default_owner=use_default_owner, ) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index f25e8ecc..3b886a93 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -597,9 +597,6 @@ class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): draw_task_layer_selection( layout=row, data=self._surrendered_transfer_data, - show_all_task_layers=self._surrendered_transfer_data.get( - "use_default_owner" - ), show_local_task_layers=True, ) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index df89e349..f6e09878 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -47,11 +47,6 @@ class AssetTransferDataTemp(bpy.types.PropertyGroup): ) surrender: bpy.props.BoolProperty(name="Surrender Ownership", default=False) obj: bpy.props.PointerProperty(type=bpy.types.Object) - use_default_owner: bpy.props.BoolProperty( - name="Defaults Ownership", - description="Default ownership has been used when initializing these properties", - default=False, - ) class TaskLayerSettings(bpy.types.PropertyGroup): @@ -79,14 +74,13 @@ class AssetPipeline(bpy.types.PropertyGroup): temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) - def add_temp_transfer_data(self, name, owner, type, obj, use_default_owner=False): + def add_temp_transfer_data(self, name, owner, type, obj): 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 = obj - transfer_data_item.use_default_owner = use_default_owner ## NEW FILE -- 2.30.2 From 39c40f059535193907154e67e60218f776b43311 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 17:34:18 -0500 Subject: [PATCH 396/429] Asset Pipe: Add Logic for Auto Surrender --- .../asset_pipeline_2/merge/task_layer.py | 20 +++++++++----- .../merge/transfer_data/transfer_functions.py | 27 +++++++++++++++---- .../merge/transfer_data/transfer_util.py | 10 ++++--- .../addons/asset_pipeline_2/props.py | 3 ++- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index b70b03c7..8384c7df 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -4,12 +4,18 @@ from .. import constants from .. import config -def get_default_task_layer(td_type: str, name=""): +def get_default_task_layer_owner(td_type: str, name="") -> [str, bool]: if td_type == constants.ATTRIBUTE_KEY: if name in config.DEFAULT_OWNERSHIP_ATTRIBUTES: - return config.DEFAULT_OWNERSHIP_ATTRIBUTES[name]['default_owner'] + return ( + config.DEFAULT_OWNERSHIP_ATTRIBUTES[name]['default_owner'], + config.DEFAULT_OWNERSHIP_ATTRIBUTES[name]['auto_surrender'], + ) print(config.DEFAULT_OWNERSHIP[td_type]['default_owner']) - return config.DEFAULT_OWNERSHIP[td_type]['default_owner'] + return ( + config.DEFAULT_OWNERSHIP[td_type]['default_owner'], + config.DEFAULT_OWNERSHIP[td_type]['auto_surrender'], + ) def get_transfer_data_owner( @@ -17,11 +23,13 @@ def get_transfer_data_owner( td_type_key: str, name="", ): - default_tl = get_default_task_layer(td_type_key, name) + default_tl, auto_surrender = get_default_task_layer_owner(td_type_key, name) if default_tl in asset_pipe.get_local_task_layers(): - return default_tl + # If the default owner is local to the file, don't use auto_surrender + return default_tl, False else: - return asset_pipe.get_local_task_layers()[0] + # If the default owner is not local, pass auto surrender value + return asset_pipe.get_local_task_layers()[0], auto_surrender def draw_task_layer_selection( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 41bd1f8f..d54692fa 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -224,10 +224,11 @@ def init_modifiers(scene, obj): asset_pipe = scene.asset_pipeline td_type_key = constants.MODIFIER_KEY transfer_data = obj.transfer_data_ownership - task_layer_owner = get_transfer_data_owner( + task_layer_owner, auto_surrender = get_transfer_data_owner( asset_pipe, td_type_key, ) + for mod in obj.modifiers: mod.name = task_layer_prefix_name_get(mod.name, task_layer_owner) # Only add new ownership transfer_data_item if vertex group doesn't have an owner @@ -238,6 +239,7 @@ def init_modifiers(scene, obj): owner=task_layer_owner, type=td_type_key, obj=obj, + surrender=auto_surrender, ) @@ -324,7 +326,7 @@ def init_constraints(scene, obj): td_type_key = constants.CONSTRAINT_KEY transfer_data = obj.transfer_data_ownership asset_pipe = scene.asset_pipeline - task_layer_owner = get_transfer_data_owner( + task_layer_owner, auto_surrender = get_transfer_data_owner( asset_pipe, td_type_key, ) @@ -338,6 +340,7 @@ def init_constraints(scene, obj): owner=task_layer_owner, type=td_type_key, obj=obj, + surrender=auto_surrender, ) @@ -438,11 +441,16 @@ def init_material_slots(scene, obj): matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner if len(matches) == 0: + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, + td_type_key, + ) asset_pipe.add_temp_transfer_data( name=name, - owner=get_transfer_data_owner(asset_pipe, td_type_key), + owner=task_layer_owner, type=td_type_key, obj=obj, + surrender=auto_surrender, ) @@ -702,11 +710,15 @@ def init_attributes(scene, obj): # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) if len(matches) == 0: + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, td_type_key, atttribute.name + ) asset_pipe.add_temp_transfer_data( name=atttribute.name, - owner=get_transfer_data_owner(asset_pipe, td_type_key, atttribute.name), + owner=task_layer_owner, type=td_type_key, obj=obj, + surrender=auto_surrender, ) @@ -774,11 +786,16 @@ def init_parent(scene, obj): matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner if len(matches) == 0: + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, + td_type_key, + ) asset_pipe.add_temp_transfer_data( name=name, - owner=get_transfer_data_owner(asset_pipe, td_type_key), + owner=task_layer_owner, type=td_type_key, obj=obj, + surrender=auto_surrender, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index 0fb7e34b..627b0571 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -110,12 +110,14 @@ def transfer_data_item_init( # Only add new ownership transfer_data_item if vertex group doesn't have an owner matches = check_transfer_data_entry(transfer_data, item.name, td_type_key) if len(matches) == 0: + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, + td_type_key, + ) asset_pipe.add_temp_transfer_data( name=item.name, - owner=get_transfer_data_owner( - asset_pipe, - td_type_key, - ), + owner=task_layer_owner, type=td_type_key, obj=obj, + surrender=auto_surrender, ) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index f6e09878..f0dc3cd7 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -74,13 +74,14 @@ class AssetPipeline(bpy.types.PropertyGroup): temp_transfer_data: bpy.props.CollectionProperty(type=AssetTransferDataTemp) - def add_temp_transfer_data(self, name, owner, type, obj): + def add_temp_transfer_data(self, name, owner, type, obj, surrender): 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 = obj + transfer_data_item.surrender = surrender ## NEW FILE -- 2.30.2 From 5be23e4fcd16cdda5b176e20bf19d723a7724817 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 17:41:35 -0500 Subject: [PATCH 397/429] Asset Pipe: Rename Task Layer Defaults Dict Keys --- scripts-blender/addons/asset_pipeline_2/config.py | 12 ++++++------ .../addons/asset_pipeline_2/merge/task_layer.py | 12 ++++++------ .../task_layer_configs/Character.json | 4 ++-- .../asset_pipeline_2/task_layer_configs/Set.json | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/config.py b/scripts-blender/addons/asset_pipeline_2/config.py index 119835e7..b5cb0a0e 100644 --- a/scripts-blender/addons/asset_pipeline_2/config.py +++ b/scripts-blender/addons/asset_pipeline_2/config.py @@ -4,8 +4,8 @@ import json from . import constants TASK_LAYER_TYPES = {} -DEFAULT_OWNERSHIP = {} -DEFAULT_OWNERSHIP_ATTRIBUTES = {} +TASK_LAYER_DEFAULTS = {} +ATTRIBUTE_DEFAULTS = {} def get_json_file(): @@ -22,8 +22,8 @@ def get_task_layer_presets_path(): def verify_json_data(json_file_path=""): global TASK_LAYER_TYPES - global DEFAULT_OWNERSHIP - global DEFAULT_OWNERSHIP_ATTRIBUTES + global TASK_LAYER_DEFAULTS + global ATTRIBUTE_DEFAULTS directory = Path(bpy.data.filepath).parent if json_file_path == "": json_file_path = directory.joinpath(constants.TASK_LAYER_CONFIG_NAME) @@ -33,8 +33,8 @@ def verify_json_data(json_file_path=""): json_content = json.load(json_file) try: TASK_LAYER_TYPES = json_content["TASK_LAYER_TYPES"] - DEFAULT_OWNERSHIP = json_content["DEFAULT_OWNERSHIP"] - DEFAULT_OWNERSHIP_ATTRIBUTES = json_content["DEFAULT_OWNERSHIP_ATTRIBUTES"] + TASK_LAYER_DEFAULTS = json_content["TASK_LAYER_DEFAULTS"] + ATTRIBUTE_DEFAULTS = json_content["ATTRIBUTE_DEFAULTS"] return True except KeyError: return diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 8384c7df..93bf41ab 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -6,15 +6,15 @@ from .. import config def get_default_task_layer_owner(td_type: str, name="") -> [str, bool]: if td_type == constants.ATTRIBUTE_KEY: - if name in config.DEFAULT_OWNERSHIP_ATTRIBUTES: + if name in config.ATTRIBUTE_DEFAULTS: return ( - config.DEFAULT_OWNERSHIP_ATTRIBUTES[name]['default_owner'], - config.DEFAULT_OWNERSHIP_ATTRIBUTES[name]['auto_surrender'], + config.ATTRIBUTE_DEFAULTS[name]['default_owner'], + config.ATTRIBUTE_DEFAULTS[name]['auto_surrender'], ) - print(config.DEFAULT_OWNERSHIP[td_type]['default_owner']) + print(config.TASK_LAYER_DEFAULTS[td_type]['default_owner']) return ( - config.DEFAULT_OWNERSHIP[td_type]['default_owner'], - config.DEFAULT_OWNERSHIP[td_type]['auto_surrender'], + config.TASK_LAYER_DEFAULTS[td_type]['default_owner'], + config.TASK_LAYER_DEFAULTS[td_type]['auto_surrender'], ) diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json index 1daa82c1..1a2d4d24 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json @@ -4,7 +4,7 @@ "Rigging": "RIG", "Shading": "SHD" }, - "DEFAULT_OWNERSHIP": { + "TASK_LAYER_DEFAULTS": { "GROUP_VERTEX": { "default_owner": "Rigging", "auto_surrender": false @@ -34,7 +34,7 @@ "auto_surrender": false } }, - "DEFAULT_OWNERSHIP_ATTRIBUTES": { + "ATTRIBUTE_DEFAULTS": { "sharp_face": { "default_owner": "Modeling", "auto_surrender": false diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json index 63c4596d..ffccc7fd 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json @@ -3,7 +3,7 @@ "Modeling": "MOD", "Shading": "SHD" }, - "DEFAULT_OWNERSHIP": { + "TASK_LAYER_DEFAULTS": { "GROUP_VERTEX": { "default_owner": "Modeling", "auto_surrender": false @@ -33,7 +33,7 @@ "auto_surrender": false } }, - "DEFAULT_OWNERSHIP_ATTRIBUTES": { + "ATTRIBUTE_DEFAULTS": { "sharp_face": { "default_owner": "Modeling", "auto_surrender": false -- 2.30.2 From 705e0e56dce73da171acbbabb23dce1b455ca821 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 17:42:09 -0500 Subject: [PATCH 398/429] Asset Pipe: Remove No-Op Code --- scripts-blender/addons/asset_pipeline_2/merge/task_layer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 93bf41ab..28f6673f 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -11,7 +11,6 @@ def get_default_task_layer_owner(td_type: str, name="") -> [str, bool]: config.ATTRIBUTE_DEFAULTS[name]['default_owner'], config.ATTRIBUTE_DEFAULTS[name]['auto_surrender'], ) - print(config.TASK_LAYER_DEFAULTS[td_type]['default_owner']) return ( config.TASK_LAYER_DEFAULTS[td_type]['default_owner'], config.TASK_LAYER_DEFAULTS[td_type]['auto_surrender'], -- 2.30.2 From 78e2ae34f8cccf4b43ccf6b30971c7bfbd30a987 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 17:53:55 -0500 Subject: [PATCH 399/429] Asset Pipe: Enable Auto Surrender in default JSON Files --- .../asset_pipeline_2/task_layer_configs/Character.json | 6 +++--- .../addons/asset_pipeline_2/task_layer_configs/Set.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json index 1a2d4d24..bf9a8a63 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json @@ -19,7 +19,7 @@ }, "MATERIAL": { "default_owner": "Shading", - "auto_surrender": false + "auto_surrender": true }, "SHAPE_KEY": { "default_owner": "Modeling", @@ -37,11 +37,11 @@ "ATTRIBUTE_DEFAULTS": { "sharp_face": { "default_owner": "Modeling", - "auto_surrender": false + "auto_surrender": true }, "UVMap": { "default_owner": "Shading", - "auto_surrender": false + "auto_surrender": true } } } \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json index ffccc7fd..afa63097 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json @@ -18,7 +18,7 @@ }, "MATERIAL": { "default_owner": "Shading", - "auto_surrender": false + "auto_surrender": true }, "SHAPE_KEY": { "default_owner": "Modeling", @@ -36,11 +36,11 @@ "ATTRIBUTE_DEFAULTS": { "sharp_face": { "default_owner": "Modeling", - "auto_surrender": false + "auto_surrender": true }, "UVMap": { "default_owner": "Shading", - "auto_surrender": false + "auto_surrender": true } } } \ No newline at end of file -- 2.30.2 From d17791f17492fd1b6c168f846750fd178d0f5197 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 18:25:57 -0500 Subject: [PATCH 400/429] Asset Pipe: Fix Rename Task Layer Defaults Dict Keys --- scripts-blender/addons/asset_pipeline_2/config.py | 6 +++--- scripts-blender/addons/asset_pipeline_2/merge/task_layer.py | 4 ++-- scripts-blender/addons/asset_pipeline_2/props.py | 2 +- .../asset_pipeline_2/task_layer_configs/Character.json | 2 +- .../addons/asset_pipeline_2/task_layer_configs/Set.json | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/config.py b/scripts-blender/addons/asset_pipeline_2/config.py index b5cb0a0e..82d7ca9f 100644 --- a/scripts-blender/addons/asset_pipeline_2/config.py +++ b/scripts-blender/addons/asset_pipeline_2/config.py @@ -4,7 +4,7 @@ import json from . import constants TASK_LAYER_TYPES = {} -TASK_LAYER_DEFAULTS = {} +TRANSFER_DATA_DEFAULTS = {} ATTRIBUTE_DEFAULTS = {} @@ -22,7 +22,7 @@ def get_task_layer_presets_path(): def verify_json_data(json_file_path=""): global TASK_LAYER_TYPES - global TASK_LAYER_DEFAULTS + global TRANSFER_DATA_DEFAULTS global ATTRIBUTE_DEFAULTS directory = Path(bpy.data.filepath).parent if json_file_path == "": @@ -33,7 +33,7 @@ def verify_json_data(json_file_path=""): json_content = json.load(json_file) try: TASK_LAYER_TYPES = json_content["TASK_LAYER_TYPES"] - TASK_LAYER_DEFAULTS = json_content["TASK_LAYER_DEFAULTS"] + TRANSFER_DATA_DEFAULTS = json_content["TRANSFER_DATA_DEFAULTS"] ATTRIBUTE_DEFAULTS = json_content["ATTRIBUTE_DEFAULTS"] return True except KeyError: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 28f6673f..5c244df5 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -12,8 +12,8 @@ def get_default_task_layer_owner(td_type: str, name="") -> [str, bool]: config.ATTRIBUTE_DEFAULTS[name]['auto_surrender'], ) return ( - config.TASK_LAYER_DEFAULTS[td_type]['default_owner'], - config.TASK_LAYER_DEFAULTS[td_type]['auto_surrender'], + config.TRANSFER_DATA_DEFAULTS[td_type]['default_owner'], + config.TRANSFER_DATA_DEFAULTS[td_type]['auto_surrender'], ) diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index f0dc3cd7..343389b6 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -6,7 +6,7 @@ from pathlib import Path from .prefs import get_addon_prefs """ NOTE Items in these properties groups should be generated by a function that finds the -avaliable task layers from the task_layer_defaults.json file that needs to be created. +avaliable task layers from the task_layer.json file that needs to be created. """ diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json index bf9a8a63..9951df62 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json @@ -4,7 +4,7 @@ "Rigging": "RIG", "Shading": "SHD" }, - "TASK_LAYER_DEFAULTS": { + "TRANSFER_DATA_DEFAULTS": { "GROUP_VERTEX": { "default_owner": "Rigging", "auto_surrender": false diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json index afa63097..3507b7bc 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json @@ -3,7 +3,7 @@ "Modeling": "MOD", "Shading": "SHD" }, - "TASK_LAYER_DEFAULTS": { + "TRANSFER_DATA_DEFAULTS": { "GROUP_VERTEX": { "default_owner": "Modeling", "auto_surrender": false -- 2.30.2 From c9ad42332d2d6252114e7712c36cac1398f73f3e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Mon, 20 Nov 2023 18:28:13 -0500 Subject: [PATCH 401/429] Asset Pipe: Fix Typo --- scripts-blender/addons/asset_pipeline_2/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 3b886a93..46a28c23 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -231,7 +231,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_idname = "assetpipe.update_ownership" - bl_label = "Update Onwership" + bl_label = "Update Ownership" bl_description = """Update the Ownership of Objects and Transfer Data""" _temp_transfer_data = None -- 2.30.2 From 39c1f4f0a9d90bb6f081c62e3efda18569278af2 Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Tue, 21 Nov 2023 12:52:14 +0100 Subject: [PATCH 402/429] AssetPipe: Fix error on Empties --- scripts-blender/addons/asset_pipeline_2/merge/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index e1482df8..8b8fc231 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -196,7 +196,7 @@ def merge_task_layer( # Remove all transfer data from target objects for source_obj in map.object_map: - if source_obj.data.users > 1: + if source_obj.data and source_obj.data.users > 1: error_msg += f"Object {source_obj.name} contains multi-user datablock'\n" return error_msg target_obj = map.object_map[source_obj] @@ -205,10 +205,10 @@ def merge_task_layer( apply_transfer_data(context, map.transfer_data_map) for source_obj in map.object_map: - if target_obj.data.users > 1: - error_msg += f"Object {source_obj.name} contains multi-user datablock'\n" - return error_msg target_obj = map.object_map[source_obj] + if target_obj.data and target_obj.data.users > 1: + error_msg += f"Object {target_obj.name} contains multi-user datablock'\n" + return error_msg remap_user(source_obj, target_obj) transfer_data_clean(target_obj) -- 2.30.2 From 898b66f69f81ef0b2b72acdb7ebae0033bed16bf Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Tue, 21 Nov 2023 15:10:54 -0500 Subject: [PATCH 403/429] Asset Pipe: Raise Exception if Object Parent outside of Asset Collection --- .../asset_pipeline_2/merge/transfer_data/transfer_functions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index d54692fa..9d48350e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -783,6 +783,8 @@ def init_parent(scene, obj): # Only Execute if Material Slots exist on object if obj.parent == None: return + if obj.parent not in list(asset_pipe.asset_collection.all_objects): + raise Exception("Object parent cannot be outside of asset collection") matches = check_transfer_data_entry(transfer_data, name, td_type_key) # Only add new ownership transfer_data_item if vertex group doesn't have an owner if len(matches) == 0: -- 2.30.2 From 93e3f75886f8d473d40a1dcb7f165de86d8ebdd4 Mon Sep 17 00:00:00 2001 From: "demeterdzadik@gmail.com" Date: Tue, 21 Nov 2023 13:04:55 +0100 Subject: [PATCH 404/429] AssetPipe: Add RIG prefix in Set task layer config --- .../addons/asset_pipeline_2/task_layer_configs/Set.json | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json index 3507b7bc..27657d38 100644 --- a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json +++ b/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json @@ -1,6 +1,7 @@ { "TASK_LAYER_TYPES": { "Modeling": "MOD", + "Rigging": "RIG", "Shading": "SHD" }, "TRANSFER_DATA_DEFAULTS": { -- 2.30.2 From de611d4012683f48394519d7ec29f2e19ab5fe71 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 22 Nov 2023 16:21:30 -0500 Subject: [PATCH 405/429] Asset Pipe: Add Basic Proximity Transfer for Attributes --- .../merge/transfer_data/transfer_functions.py | 353 +++++++++++++++++- 1 file changed, 339 insertions(+), 14 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py index 9d48350e..884b4b9e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py @@ -478,20 +478,14 @@ def transfer_material_slots(target_obj: bpy.types.Object, source_obj): # SHAPE KEYS -def shape_key_set_active(obj, shape_key_name): - for index, shape_key in enumerate(obj.data.shape_keys.key_blocks): - if shape_key.name == shape_key_name: - obj.active_shape_key_index = index - - -def shape_key_closest_face_to_point(bm_source, p_target, bvh_tree=None): +def closest_face_to_point(bm_source, p_target, bvh_tree=None): if not bvh_tree: bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) (loc, norm, index, distance) = bvh_tree.find_nearest(p_target) return bm_source.faces[index] -def shape_key_tris_per_face(bm_source): +def tris_per_face(bm_source): tris_source = bm_source.calc_loop_triangles() tris_dict = dict() for face in bm_source.faces: @@ -503,7 +497,7 @@ def shape_key_tris_per_face(bm_source): return tris_dict -def shape_key_closest_tri_on_face(tris_dict, face, p): +def closest_tri_on_face(tris_dict, face, p): points = [] dist = [] tris = [] @@ -520,6 +514,203 @@ def shape_key_closest_tri_on_face(tris_dict, face, p): return (tri, point) +def closest_edge_on_face_to_line(face, p1, p2, skip_edges=None): + """Returns edge of a face which is closest to line.""" + for edge in face.edges: + if skip_edges: + if edge in skip_edges: + continue + res = mathutils.geometry.intersect_line_line( + p1, p2, *[edge.verts[i].co for i in range(2)] + ) + if not res: + continue + (p_traversal, p_edge) = res + frac_1 = (edge.verts[1].co - edge.verts[0].co).dot( + p_edge - edge.verts[0].co + ) / (edge.verts[1].co - edge.verts[0].co).length ** 2.0 + frac_2 = (p2 - p1).dot(p_traversal - p1) / (p2 - p1).length ** 2.0 + if (frac_1 >= 0 and frac_1 <= 1) and (frac_2 >= 0 and frac_2 <= 1): + return edge + return None + + +def edge_data_split(edge, data_layer, data_suffix: str): + for vert in edge.verts: + vals = [] + for loop in vert.link_loops: + loops_edge_vert = set([loop for f in edge.link_faces for loop in f.loops]) + if loop not in loops_edge_vert: + continue + dat = data_layer[loop.index] + element = list(getattr(dat, data_suffix)) + if not vals: + vals.append(element) + elif not vals[0] == element: + vals.append(element) + if len(vals) > 1: + return True + return False + + +def interpolate_data_from_face( + bm_source, tris_dict, face, p, data_layer_source, data_suffix='' +): + """Returns interpolated value of a data layer within a face closest to a point.""" + + (tri, point) = closest_tri_on_face(tris_dict, face, p) + if not tri: + return None + weights = mathutils.interpolate.poly_3d_calc( + [tri[i].vert.co for i in range(3)], point + ) + + if not data_suffix: + cols_weighted = [ + weights[i] * np.array(data_layer_source[tri[i].index]) for i in range(3) + ] + col = sum(np.array(cols_weighted)) + else: + cols_weighted = [ + weights[i] * np.array(getattr(data_layer_source[tri[i].index], data_suffix)) + for i in range(3) + ] + col = sum(np.array(cols_weighted)) + return col + + +def transfer_corner_data( + obj_source, obj_target, data_layer_source, data_layer_target, data_suffix='' +): + """ + Transfers interpolated face corner data from data layer of a source object to data layer of a + target object, while approximately preserving data seams (e.g. necessary for UV Maps). + The transfer is face interpolated per target corner within the source face that is closest + to the target corner point and does not have any data seams on the way back to the + source face that is closest to the target face's center. + """ + + bm_source = bmesh.new() + bm_source.from_mesh(obj_source.data) + bm_source.faces.ensure_lookup_table() + bm_target = bmesh.new() + bm_target.from_mesh(obj_target.data) + bm_target.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + + tris_dict = tris_per_face(bm_source) + + for face_target in bm_target.faces: + face_target_center = face_target.calc_center_median() + + face_source = closest_face_to_point(bm_source, face_target_center, bvh_tree) + + for corner_target in face_target.loops: + # find nearest face on target compared to face that loop belongs to + p = corner_target.vert.co + + face_source_closest = closest_face_to_point(bm_source, p, bvh_tree) + enclosed = face_source_closest is face_source + face_source_int = face_source + if not enclosed: + # traverse faces between point and face center + traversed_faces = set() + traversed_edges = set() + while face_source_int is not face_source_closest: + traversed_faces.add(face_source_int) + edge = closest_edge_on_face_to_line( + face_source_int, + face_target_center, + p, + skip_edges=traversed_edges, + ) + if edge == None: + break + if len(edge.link_faces) != 2: + break + traversed_edges.add(edge) + + split = edge_data_split(edge, data_layer_source, data_suffix) + if split: + break + + # set new source face to other face belonging to edge + face_source_int = ( + edge.link_faces[1] + if edge.link_faces[1] is not face_source_int + else edge.link_faces[0] + ) + + # avoid looping behaviour + if face_source_int in traversed_faces: + face_source_int = face_source + break + + # interpolate data from selected face + col = interpolate_data_from_face( + bm_source, tris_dict, face_source_int, p, data_layer_source, data_suffix + ) + if col is None: + continue + if not data_suffix: + data_layer_target.data[corner_target.index] = col + else: + setattr(data_layer_target[corner_target.index], data_suffix, list(col)) + return + + +def is_mesh_identical(mesh_a, mesh_b) -> bool: + if len(mesh_a.vertices) != len(mesh_b.vertices): + return False + if len(mesh_a.edges) != len(mesh_b.edges): + return False + if len(mesh_a.polygons) != len(mesh_b.polygons): + return False + for e1, e2 in zip(mesh_a.edges, mesh_b.edges): + for v1, v2 in zip(e1.vertices, e2.vertices): + if v1 != v2: + return False + + return True + + +def is_curve_identical(curve_a: bpy.types.Curve, curve_b: bpy.types.Curve) -> bool: + if len(curve_a.splines) != len(curve_b.splines): + return False + for spline1, spline2 in zip(curve_a.splines, curve_b.splines): + if len(spline1.points) != len(spline2.points): + return False + return True + + +def is_obdata_identical( + a: bpy.types.Object or bpy.types.Mesh, b: bpy.types.Object or bpy.types.Mesh +) -> bool: + """Checks if two objects have matching topology (efficiency over exactness)""" + if type(a) == bpy.types.Object: + a = a.data + if type(b) == bpy.types.Object: + b = b.data + + if type(a) != type(b): + return False + + if type(a) == bpy.types.Mesh: + return is_mesh_identical(a, b) + elif type(a) == bpy.types.Curve: + return is_curve_identical(a, b) + else: + # TODO: Support geometry types other than mesh or curve. + return + + +def shape_key_set_active(obj, shape_key_name): + for index, shape_key in enumerate(obj.data.shape_keys.key_blocks): + if shape_key.name == shape_key_name: + obj.active_shape_key_index = index + + def shape_keys_clean(obj): if obj.type != "MESH" or obj.data.shape_keys is None: return @@ -618,12 +809,12 @@ def transfer_shape_key( bm_source.faces.ensure_lookup_table() bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - tris_dict = shape_key_tris_per_face(bm_source) + tris_dict = tris_per_face(bm_source) for i, vert in enumerate(target_obj.data.vertices): p = vert.co - face = shape_key_closest_face_to_point(bm_source, p, bvh_tree) + face = closest_face_to_point(bm_source, p, bvh_tree) - (tri, point) = shape_key_closest_tri_on_face(tris_dict, face, p) + (tri, point) = closest_tri_on_face(tris_dict, face, p) if not tri: continue weights = mathutils.interpolate.poly_3d_calc( @@ -730,8 +921,8 @@ def transfer_attribute( source_attributes = source_obj.data.attributes target_attributes = target_obj.data.attributes source_attribute = source_attributes.get(attribute_name) - target_attribute = target_attributes.get(attribute_name) + if target_attribute: target_attributes.remove(target_attribute) @@ -740,7 +931,13 @@ def transfer_attribute( type=source_attribute.data_type, domain=source_attribute.domain, ) - # print(f"Transfering Attribute {attribute_name}") + + if not is_obdata_identical(source_obj, target_obj): + proximity_transfer_single_attribute( + source_obj, target_obj, source_attribute, target_attribute + ) + return + for source_data_item in source_attribute.data.items(): index = source_data_item[0] source_data = source_data_item[1] @@ -752,6 +949,134 @@ def transfer_attribute( setattr(target_data, key, getattr(source_data, key)) +def proximity_transfer_single_attribute( + source_obj: bpy.types.Object, + target_obj: bpy.types.Object, + source_attribute: bpy.types.Attribute, + target_attribute: bpy.types.Attribute, +): + # src_dat = source_obj.data + # tgt_dat = target_obj.data + # if type(src_dat) is not type(tgt_dat) or not (src_dat or tgt_dat): + # return False + # if type(tgt_dat) is not bpy.types.Mesh: # TODO: support more types + # return False + + # If target attribute already exists, remove it. + # tgt_attr = tgt_dat.attributes.get(source_attribute.name) + # if tgt_attr is not None: + # try: + # tgt_dat.attributes.remove(tgt_attr) + # except RuntimeError: + # # Built-ins like "position" cannot be removed, and should be skipped. + # return + + # Create target attribute. + # target_attribute = tgt_dat.attributes.new( + # source_attribute.name, source_attribute.data_type, source_attribute.domain + # ) + + data_sfx = { + 'INT8': 'value', + 'INT': 'value', + 'FLOAT': 'value', + 'FLOAT2': 'vector', + 'BOOLEAN': 'value', + 'STRING': 'value', + 'BYTE_COLOR': 'color', + 'FLOAT_COLOR': 'color', + 'FLOAT_VECTOR': 'vector', + } + + data_sfx = data_sfx[source_attribute.data_type] + + # if topo_match: + # # TODO: optimize using foreach_get/set rather than loop + # for i in range(len(source_attribute.data)): + # setattr(tgt_attr.data[i], data_sfx, getattr(source_attribute.data[i], data_sfx)) + # return + + # proximity fallback + if source_attribute.data_type == 'STRING': + # TODO: add NEAREST transfer fallback for attributes without interpolation + print( + f'Proximity based transfer for generic attributes of type STRING not supported yet. Skipping attribute {source_attribute.name} on {target_obj}.' + ) + return + + domain = source_attribute.domain + if ( + domain == 'POINT' + ): # TODO: deduplicate interpolated point domain proximity transfer + bm_source = bmesh.new() + bm_source.from_mesh(source_obj.data) + bm_source.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + + tris_dict = tris_per_face(bm_source) + + for i, vert in enumerate(target_obj.data.vertices): + p = vert.co + face = closest_face_to_point(bm_source, p, bvh_tree) + + (tri, point) = closest_tri_on_face(tris_dict, face, p) + if not tri: + continue + weights = mathutils.interpolate.poly_3d_calc( + [tri[i].vert.co for i in range(3)], point + ) + + if data_sfx in ['color']: + vals_weighted = [ + weights[i] + * ( + np.array( + getattr(source_attribute.data[tri[i].vert.index], data_sfx) + ) + ) + for i in range(3) + ] + else: + vals_weighted = [ + weights[i] + * (getattr(source_attribute.data[tri[i].vert.index], data_sfx)) + for i in range(3) + ] + setattr(target_attribute.data[i], data_sfx, sum(np.array(vals_weighted))) + return + elif domain == 'EDGE': + # TODO support proximity fallback for generic edge attributes + print( + f'Proximity based transfer of generic edge attributes not supported yet. Skipping attribute {source_attribute.name} on {target_obj}.' + ) + return + elif domain == 'FACE': + bm_source = bmesh.new() + bm_source.from_mesh(source_obj.data) + bm_source.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + for i, face in enumerate(target_obj.data.polygons): + p_target = face.center + closest_face = closest_face_to_point(bm_source, p_target, bvh_tree) + setattr( + target_attribute.data[i], + data_sfx, + getattr(source_attribute.data[closest_face.index], data_sfx), + ) + return + elif domain == 'CORNER': + transfer_corner_data( + source_obj, + target_obj, + source_attribute.data, + target_attribute.data, + data_suffix=data_sfx, + ) + return + + def parent_clean(obj): matches = check_transfer_data_entry( obj.transfer_data_ownership, -- 2.30.2 From 8d58308c7da9cfa4635caa89524f0936d141941b Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 22 Nov 2023 17:19:52 -0500 Subject: [PATCH 406/429] Asset Pipe: Organize Transfer Functions into Seperate Files --- .../merge/transfer_data/transfer_core.py | 64 +- .../merge/transfer_data/transfer_functions.py | 1131 ----------------- .../transfer_functions/attributes.py | 247 ++++ .../transfer_functions/constraints.py | 96 ++ .../transfer_functions/materials.py | 86 ++ .../transfer_functions/modifers.py | 112 ++ .../transfer_functions/parent.py | 59 + .../transfer_functions/proximity_core.py | 231 ++++ .../transfer_functions/shape_keys.py | 152 +++ .../transfer_functions/vertex_groups.py | 193 +++ 10 files changed, 1211 insertions(+), 1160 deletions(-) delete mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/proximity_core.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py create mode 100644 scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/vertex_groups.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 5af482a1..4bfb3df2 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -1,6 +1,14 @@ import bpy -from . import transfer_functions +from .transfer_functions import ( + attributes, + constraints, + modifers, + parent, + shape_keys, + vertex_groups, + materials, +) from ... import constants @@ -36,13 +44,12 @@ def copy_transfer_data_ownership( def transfer_data_clean(obj): - transfer_functions.vertex_groups_clean(obj) - transfer_functions.modifiers_clean(obj) - transfer_functions.constraints_clean(obj) - # transfer_functions.material_slots_clean(obj) - transfer_functions.shape_keys_clean(obj) - transfer_functions.attribute_clean(obj) - transfer_functions.parent_clean(obj) + vertex_groups.vertex_groups_clean(obj) + modifers.modifiers_clean(obj) + constraints.constraints_clean(obj) + shape_keys.shape_keys_clean(obj) + attributes.attribute_clean(obj) + parent.parent_clean(obj) def transfer_data_is_missing(transfer_data_item) -> bool: @@ -55,13 +62,12 @@ def transfer_data_is_missing(transfer_data_item) -> bool: bool: bool if item is missing """ return bool( - transfer_functions.vertex_group_is_missing(transfer_data_item) - or transfer_functions.modifier_is_missing(transfer_data_item) - # or transfer_functions.material_slots_is_missing(transfer_data_item) - or transfer_functions.constraint_is_missing(transfer_data_item) - or transfer_functions.shape_key_is_missing(transfer_data_item) - or transfer_functions.attribute_is_missing(transfer_data_item) - or transfer_functions.parent_is_missing(transfer_data_item) + vertex_groups.vertex_group_is_missing(transfer_data_item) + or modifers.modifier_is_missing(transfer_data_item) + or constraints.constraint_is_missing(transfer_data_item) + or shape_keys.shape_key_is_missing(transfer_data_item) + or attributes.attribute_is_missing(transfer_data_item) + or parent.parent_is_missing(transfer_data_item) ) @@ -76,13 +82,13 @@ def init_transfer_data( task_layer_name (str): Name of task layer temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP """ - transfer_functions.init_vertex_groups(scene, obj) - transfer_functions.init_material_slots(scene, obj) - transfer_functions.init_modifiers(scene, obj) - transfer_functions.init_constraints(scene, obj) - transfer_functions.init_shape_keys(scene, obj) - transfer_functions.init_attributes(scene, obj) - transfer_functions.init_parent(scene, obj) + vertex_groups.init_vertex_groups(scene, obj) + materials.init_materials(scene, obj) + modifers.init_modifiers(scene, obj) + constraints.init_constraints(scene, obj) + shape_keys.init_shape_keys(scene, obj) + attributes.init_attributes(scene, obj) + parent.init_parent(scene, obj) def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: @@ -113,7 +119,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: if source_obj != target_obj: if transfer_data_item.type == constants.VERTEX_GROUP_KEY: print(f"Transfering Data {constants.VERTEX_GROUP_KEY}: {name}") - transfer_functions.transfer_vertex_group( + vertex_groups.transfer_vertex_group( context=context, vertex_group_name=transfer_data_item.name, target_obj=target_obj, @@ -121,38 +127,38 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: ) if transfer_data_item.type == constants.MODIFIER_KEY: print(f"Transfering Data {constants.MODIFIER_KEY}: {name}") - transfer_functions.transfer_modifier( + modifers.transfer_modifier( modifier_name=transfer_data_item.name, target_obj=target_obj, source_obj=source_obj, ) if transfer_data_item.type == constants.CONSTRAINT_KEY: - transfer_functions.transfer_constraint( + constraints.transfer_constraint( constraint_name=transfer_data_item.name, target_obj=target_obj, source_obj=source_obj, ) if transfer_data_item.type == constants.MATERIAL_SLOT_KEY: print(f"Transfering Data {constants.MATERIAL_SLOT_KEY}: {name}") - transfer_functions.transfer_material_slots( + materials.transfer_materials( target_obj=target_obj, source_obj=source_obj, ) if transfer_data_item.type == constants.SHAPE_KEY_KEY: - transfer_functions.transfer_shape_key( + shape_keys.transfer_shape_key( context=context, target_obj=target_obj, source_obj=source_obj, shape_key_name=transfer_data_item.name, ) if transfer_data_item.type == constants.ATTRIBUTE_KEY: - transfer_functions.transfer_attribute( + attributes.transfer_attribute( target_obj=target_obj, source_obj=source_obj, attribute_name=transfer_data_item.name, ) if transfer_data_item.type == constants.PARENT_KEY: - transfer_functions.transfer_parent( + parent.transfer_parent( target_obj=target_obj, source_obj=source_obj, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py deleted file mode 100644 index 884b4b9e..00000000 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions.py +++ /dev/null @@ -1,1131 +0,0 @@ -import bpy -from typing import Dict, Tuple, List -from ..naming import get_basename, task_layer_prefix_name_get -from ..drivers import find_drivers, copy_driver -from ..visibility import override_obj_visability -from .transfer_util import ( - transfer_data_clean, - transfer_data_item_is_missing, - transfer_data_item_init, - check_transfer_data_entry, -) - -from ..task_layer import get_transfer_data_owner -from ... import constants -import mathutils -import bmesh -import numpy as np -from mathutils import Vector, kdtree - -## FUNCTIONS SPECFIC TO TRANSFER DATA TYPES - - -# VERTEX GROUPS -def vertex_groups_clean(obj): - transfer_data_clean( - obj=obj, data_list=obj.vertex_groups, td_type_key=constants.VERTEX_GROUP_KEY - ) - - -def vertex_group_is_missing(transfer_data_item): - return transfer_data_item_is_missing( - transfer_data_item=transfer_data_item, - td_type_key=constants.VERTEX_GROUP_KEY, - data_list=transfer_data_item.id_data.vertex_groups, - ) - - -def init_vertex_groups(scene, obj): - transfer_data_item_init( - scene=scene, - obj=obj, - data_list=obj.vertex_groups, - td_type_key=constants.VERTEX_GROUP_KEY, - ) - - -def transfer_vertex_group( - context, - vertex_group_name: str, - target_obj: bpy.types.Object, - source_obj: bpy.types.Object, -): - if target_obj == source_obj: - return - - if not source_obj.vertex_groups.get(vertex_group_name): - print(f"ERROR Vertex Group {vertex_group_name} not found in {source_obj.name}") - return - - precalc_and_transfer_single_group( - source_obj, target_obj, vertex_group_name, expand=2 - ) - - -def precalc_and_transfer_single_group(source_obj, target_obj, vgroup_name, expand=2): - """Convenience function to transfer a single group. For transferring multiple groups, - this is very inefficient and shouldn't be used. - - Instead, you should: - - build_kd_tree ONCE per source mesh. - - build_vert_influence_map and transfer_vertex_groups ONCE per object pair. - """ - - # Remove group from the target obj if it already exists. - tgt_vg = target_obj.vertex_groups.get(vgroup_name) - if tgt_vg: - target_obj.vertex_groups.remove(tgt_vg) - - kd_tree = build_kdtree(source_obj.data) - vert_influence_map = build_vert_influence_map( - source_obj, target_obj, kd_tree, expand - ) - transfer_vertex_groups( - source_obj, - target_obj, - vert_influence_map, - [source_obj.vertex_groups[vgroup_name]], - ) - - -def build_kdtree(mesh): - kd = kdtree.KDTree(len(mesh.vertices)) - for i, v in enumerate(mesh.vertices): - kd.insert(v.co, i) - kd.balance() - return kd - - -def build_vert_influence_map(obj_from, obj_to, kd_tree, expand=2): - verts_of_edge = { - i: (e.vertices[0], e.vertices[1]) for i, e in enumerate(obj_from.data.edges) - } - - edges_of_vert: Dict[int, List[int]] = {} - for edge_idx, edge in enumerate(obj_from.data.edges): - for vert_idx in edge.vertices: - if vert_idx not in edges_of_vert: - edges_of_vert[vert_idx] = [] - edges_of_vert[vert_idx].append(edge_idx) - - # A mapping from target vertex index to a list of source vertex indicies and - # their influence. - # This can be pre-calculated once per object pair, to minimize re-calculations - # of subsequent transferring of individual vertex groups. - vert_influence_map: List[int, List[Tuple[int, float]]] = {} - for i, dest_vert in enumerate(obj_to.data.vertices): - vert_influence_map[i] = get_source_vert_influences( - dest_vert, obj_from, kd_tree, expand, edges_of_vert, verts_of_edge - ) - - return vert_influence_map - - -def get_source_vert_influences( - target_vert, obj_from, kd_tree, expand=2, edges_of_vert={}, verts_of_edge={} -) -> List[Tuple[int, float]]: - _coord, idx, dist = get_nearest_vert(target_vert.co, kd_tree) - source_vert_indices = [idx] - - if dist == 0: - # If the vertex position is a perfect match, just use that one vertex with max influence. - return [(idx, 1)] - - for i in range(0, expand): - new_indices = [] - for vert_idx in source_vert_indices: - for edge in edges_of_vert[vert_idx]: - vert_other = other_vert_of_edge(edge, vert_idx, verts_of_edge) - if vert_other not in source_vert_indices: - new_indices.append(vert_other) - source_vert_indices.extend(new_indices) - - distances: List[Tuple[int, float]] = [] - distance_total = 0 - for src_vert_idx in source_vert_indices: - distance = (target_vert.co - obj_from.data.vertices[src_vert_idx].co).length - distance_total += distance - distances.append((src_vert_idx, distance)) - - # Calculate influences such that the total of all influences adds up to 1.0, - # and the influence is inversely correlated with the distance. - parts = [1 / (dist / distance_total) for idx, dist in distances] - parts_sum = sum(parts) - - influences = [ - (idx, 1 if dist == 0 else part / parts_sum) - for part, dist in zip(parts, distances) - ] - - return influences - - -def get_nearest_vert( - coords: Vector, kd_tree: kdtree.KDTree -) -> Tuple[Vector, int, float]: - """Return coordinate, index, and distance of nearest vert to coords in kd_tree.""" - return kd_tree.find(coords) - - -def other_vert_of_edge( - edge: int, vert: int, verts_of_edge: Dict[int, Tuple[int, int]] -) -> int: - verts = verts_of_edge[edge] - assert vert in verts, f"Vert {vert} not part of edge {edge}." - return verts[0] if vert == verts[1] else verts[1] - - -def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups): - """Transfer src_vgroups in obj_from to obj_to using a pre-calculated vert_influence_map.""" - - for src_vg in src_vgroups: - target_vg = obj_to.vertex_groups.get(src_vg.name) - if target_vg == None: - target_vg = obj_to.vertex_groups.new(name=src_vg.name) - - for i, dest_vert in enumerate(obj_to.data.vertices): - source_verts = vert_influence_map[i] - - # Vertex Group Name : Weight - vgroup_weights = {} - - for src_vert_idx, influence in source_verts: - for group in obj_from.data.vertices[src_vert_idx].groups: - group_idx = group.group - vg = obj_from.vertex_groups[group_idx] - if vg not in src_vgroups: - continue - if vg.name not in vgroup_weights: - vgroup_weights[vg.name] = 0 - vgroup_weights[vg.name] += vg.weight(src_vert_idx) * influence - - # Assign final weights of this vertex in the vertex groups. - for vg_name in vgroup_weights.keys(): - target_vg = obj_to.vertex_groups.get(vg_name) - target_vg.add([dest_vert.index], vgroup_weights[vg_name], 'REPLACE') - - -# MODIFIERS -def modifiers_clean(obj): - transfer_data_clean( - obj=obj, data_list=obj.modifiers, td_type_key=constants.MODIFIER_KEY - ) - - -def modifier_is_missing(transfer_data_item): - return transfer_data_item_is_missing( - transfer_data_item=transfer_data_item, - td_type_key=constants.MODIFIER_KEY, - data_list=transfer_data_item.id_data.modifiers, - ) - - -def init_modifiers(scene, obj): - asset_pipe = scene.asset_pipeline - td_type_key = constants.MODIFIER_KEY - transfer_data = obj.transfer_data_ownership - task_layer_owner, auto_surrender = get_transfer_data_owner( - asset_pipe, - td_type_key, - ) - - for mod in obj.modifiers: - mod.name = task_layer_prefix_name_get(mod.name, task_layer_owner) - # Only add new ownership transfer_data_item if vertex group doesn't have an owner - matches = check_transfer_data_entry(transfer_data, mod.name, td_type_key) - if len(matches) == 0: - asset_pipe.add_temp_transfer_data( - name=mod.name, - owner=task_layer_owner, - type=td_type_key, - obj=obj, - surrender=auto_surrender, - ) - - -def transfer_modifier(modifier_name, target_obj, source_obj): - # remove old and sync existing modifiers - context = bpy.context - scene = context.scene - old_mod = target_obj.modifiers.get(modifier_name) - if old_mod: - target_obj.modifiers.remove(old_mod) - - # transfer new modifiers - for i, mod in enumerate(source_obj.modifiers): - if mod.name == modifier_name: - mod_new = target_obj.modifiers.new(mod.name, mod.type) - # sort new modifier at correct index (default to beginning of the stack) - idx = 0 - if i > 0: - name_prev = source_obj.modifiers[i - 1].name - for target_mod_i, target_mod in enumerate(target_obj.modifiers): - if target_mod.name == name_prev: - idx = target_mod_i + 1 - with override_obj_visability(obj=target_obj, scene=scene): - with context.temp_override(object=target_obj): - bpy.ops.object.modifier_move_to_index( - modifier=mod_new.name, index=idx - ) - mod_target = target_obj.modifiers.get(mod.name) - props = [p.identifier for p in mod.bl_rna.properties if not p.is_readonly] - for prop in props: - value = getattr(mod, prop) - setattr(mod_target, prop, value) - - # rebind modifiers (corr. smooth, surf. deform, mesh deform) - for mod in target_obj.modifiers: - if mod.type == 'SURFACE_DEFORM': - if not mod.is_bound: - continue - for i in range(2): - with override_obj_visability(obj=target_obj, scene=scene): - with context.temp_override( - object=target_obj, active_object=target_obj - ): - bpy.ops.object.surfacedeform_bind(modifier=mod.name) - elif mod.type == 'MESH_DEFORM': - if not mod.is_bound: - continue - for i in range(2): - with override_obj_visability(obj=target_obj, scene=scene): - with context.temp_override( - object=target_obj, active_object=target_obj - ): - bpy.ops.object.meshdeform_bind(modifier=mod.name) - elif mod.type == 'CORRECTIVE_SMOOTH': - if not mod.is_bind: - continue - for i in range(2): - with override_obj_visability(obj=target_obj, scene=scene): - with context.temp_override( - object=target_obj, active_object=target_obj - ): - bpy.ops.object.correctivesmooth_bind(modifier=mod.name) - fcurves = find_drivers(source_obj, 'modifiers', modifier_name) - for fcurve in fcurves: - copy_driver(from_fcurve=fcurve, target=target_obj) - - -# CONSTRAINTS -def constraints_clean(obj): - transfer_data_clean( - obj=obj, data_list=obj.constraints, td_type_key=constants.CONSTRAINT_KEY - ) - - -def constraint_is_missing(transfer_data_item): - return transfer_data_item_is_missing( - transfer_data_item=transfer_data_item, - td_type_key=constants.CONSTRAINT_KEY, - data_list=transfer_data_item.id_data.constraints, - ) - - -def init_constraints(scene, obj): - td_type_key = constants.CONSTRAINT_KEY - transfer_data = obj.transfer_data_ownership - asset_pipe = scene.asset_pipeline - task_layer_owner, auto_surrender = get_transfer_data_owner( - asset_pipe, - td_type_key, - ) - for const in obj.constraints: - const.name = task_layer_prefix_name_get(const.name, task_layer_owner) - # Only add new ownership transfer_data_item if vertex group doesn't have an owner - matches = check_transfer_data_entry(transfer_data, const.name, td_type_key) - if len(matches) == 0: - asset_pipe.add_temp_transfer_data( - name=const.name, - owner=task_layer_owner, - type=td_type_key, - obj=obj, - surrender=auto_surrender, - ) - - -def transfer_constraint(constraint_name, target_obj, source_obj): - context = bpy.context - # remove old and sync existing modifiers - old_mod = target_obj.constraints.get(constraint_name) - if old_mod: - target_obj.constraints.remove(old_mod) - - # transfer new modifiers - for i, constraint in enumerate(source_obj.constraints): - if constraint.name == constraint_name: - constraint_new = target_obj.constraints.new(constraint.type) - constraint_new.name = constraint.name - # sort new modifier at correct index (default to beginning of the stack) - idx = 0 - if i > 0: - name_prev = source_obj.constraints[i - 1].name - for target_mod_i, target_constraint in enumerate( - target_obj.constraints - ): - if target_constraint.name == name_prev: - idx = target_mod_i + 1 - - if idx != i: - with override_obj_visability(obj=target_obj, scene=context.scene): - with context.temp_override(object=target_obj): - bpy.ops.constraint.move_to_index( - constraint=constraint_new.name, index=idx - ) - constraint_target = target_obj.constraints.get(constraint.name) - props = [ - p.identifier for p in constraint.bl_rna.properties if not p.is_readonly - ] - for prop in props: - value = getattr(constraint, prop) - setattr(constraint_target, prop, value) - - # HACK to cover edge case of armature constraints - if constraint.type == "ARMATURE": - for target_item in constraint.targets: - new_target = constraint_new.targets.new() - new_target.target = target_item.target - new_target.subtarget = target_item.subtarget - - fcurves = find_drivers(source_obj, 'constraints', constraint_name) - - for fcurve in fcurves: - copy_driver(from_fcurve=fcurve, target=target_obj) - - -# MATERIAL SLOT -def material_slots_clean(obj): - # Material slots cannot use generic transfer_data_clean() function - - matches = check_transfer_data_entry( - obj.transfer_data_ownership, - constants.MATERIAL_TRANSFER_DATA_ITEM_NAME, - constants.MATERIAL_SLOT_KEY, - ) - - # Clear Materials if No Transfer Data is Found - if len(matches) != 0: - return - - if obj.data and hasattr(obj.data, 'materials'): - obj.data.materials.clear() - - -def material_slots_is_missing(transfer_data_item): - if ( - transfer_data_item.type == constants.MATERIAL_SLOT_KEY - and len(transfer_data_item.id_data.material_slots) == 0 - ): - return True - - -def init_material_slots(scene, obj): - asset_pipe = scene.asset_pipeline - td_type_key = constants.MATERIAL_SLOT_KEY - name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME - transfer_data = obj.transfer_data_ownership - - material_objects = [ - 'CURVE', - 'GPENCIL', - 'META', - 'MESH', - 'SURFACE', - 'FONT', - 'VOLUME', - ] - - # Only Execute if Material Slots exist on object - if obj.type not in material_objects: - return - matches = check_transfer_data_entry(transfer_data, name, td_type_key) - # Only add new ownership transfer_data_item if vertex group doesn't have an owner - if len(matches) == 0: - task_layer_owner, auto_surrender = get_transfer_data_owner( - asset_pipe, - td_type_key, - ) - asset_pipe.add_temp_transfer_data( - name=name, - owner=task_layer_owner, - type=td_type_key, - obj=obj, - surrender=auto_surrender, - ) - - -def transfer_material_slots(target_obj: bpy.types.Object, source_obj): - # Delete all material slots of target object. - target_obj.data.materials.clear() - - # Transfer material slots - for idx in range(len(source_obj.material_slots)): - target_obj.data.materials.append(source_obj.material_slots[idx].material) - target_obj.material_slots[idx].link = source_obj.material_slots[idx].link - - # Transfer active material slot index - target_obj.active_material_index = source_obj.active_material_index - - # Transfer material slot assignments for curve - if target_obj.type == "CURVE": - for spl_to, spl_from in zip(target_obj.data.splines, source_obj.data.splines): - spl_to.material_index = spl_from.material_index - - if source_obj.data.attributes.get(constants.MATERIAL_ATTRIBUTE_NAME): - transfer_attribute(constants.MATERIAL_ATTRIBUTE_NAME, target_obj, source_obj) - - -# SHAPE KEYS - - -def closest_face_to_point(bm_source, p_target, bvh_tree=None): - if not bvh_tree: - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - (loc, norm, index, distance) = bvh_tree.find_nearest(p_target) - return bm_source.faces[index] - - -def tris_per_face(bm_source): - tris_source = bm_source.calc_loop_triangles() - tris_dict = dict() - for face in bm_source.faces: - tris_face = [] - for i in range(len(tris_source))[::-1]: - if tris_source[i][0] in face.loops: - tris_face.append(tris_source.pop(i)) - tris_dict[face] = tris_face - return tris_dict - - -def closest_tri_on_face(tris_dict, face, p): - points = [] - dist = [] - tris = [] - for tri in tris_dict[face]: - point = mathutils.geometry.closest_point_on_tri( - p, *[tri[i].vert.co for i in range(3)] - ) - tris.append(tri) - points.append(point) - dist.append((point - p).length) - min_idx = np.argmin(np.array(dist)) - point = points[min_idx] - tri = tris[min_idx] - return (tri, point) - - -def closest_edge_on_face_to_line(face, p1, p2, skip_edges=None): - """Returns edge of a face which is closest to line.""" - for edge in face.edges: - if skip_edges: - if edge in skip_edges: - continue - res = mathutils.geometry.intersect_line_line( - p1, p2, *[edge.verts[i].co for i in range(2)] - ) - if not res: - continue - (p_traversal, p_edge) = res - frac_1 = (edge.verts[1].co - edge.verts[0].co).dot( - p_edge - edge.verts[0].co - ) / (edge.verts[1].co - edge.verts[0].co).length ** 2.0 - frac_2 = (p2 - p1).dot(p_traversal - p1) / (p2 - p1).length ** 2.0 - if (frac_1 >= 0 and frac_1 <= 1) and (frac_2 >= 0 and frac_2 <= 1): - return edge - return None - - -def edge_data_split(edge, data_layer, data_suffix: str): - for vert in edge.verts: - vals = [] - for loop in vert.link_loops: - loops_edge_vert = set([loop for f in edge.link_faces for loop in f.loops]) - if loop not in loops_edge_vert: - continue - dat = data_layer[loop.index] - element = list(getattr(dat, data_suffix)) - if not vals: - vals.append(element) - elif not vals[0] == element: - vals.append(element) - if len(vals) > 1: - return True - return False - - -def interpolate_data_from_face( - bm_source, tris_dict, face, p, data_layer_source, data_suffix='' -): - """Returns interpolated value of a data layer within a face closest to a point.""" - - (tri, point) = closest_tri_on_face(tris_dict, face, p) - if not tri: - return None - weights = mathutils.interpolate.poly_3d_calc( - [tri[i].vert.co for i in range(3)], point - ) - - if not data_suffix: - cols_weighted = [ - weights[i] * np.array(data_layer_source[tri[i].index]) for i in range(3) - ] - col = sum(np.array(cols_weighted)) - else: - cols_weighted = [ - weights[i] * np.array(getattr(data_layer_source[tri[i].index], data_suffix)) - for i in range(3) - ] - col = sum(np.array(cols_weighted)) - return col - - -def transfer_corner_data( - obj_source, obj_target, data_layer_source, data_layer_target, data_suffix='' -): - """ - Transfers interpolated face corner data from data layer of a source object to data layer of a - target object, while approximately preserving data seams (e.g. necessary for UV Maps). - The transfer is face interpolated per target corner within the source face that is closest - to the target corner point and does not have any data seams on the way back to the - source face that is closest to the target face's center. - """ - - bm_source = bmesh.new() - bm_source.from_mesh(obj_source.data) - bm_source.faces.ensure_lookup_table() - bm_target = bmesh.new() - bm_target.from_mesh(obj_target.data) - bm_target.faces.ensure_lookup_table() - - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - - tris_dict = tris_per_face(bm_source) - - for face_target in bm_target.faces: - face_target_center = face_target.calc_center_median() - - face_source = closest_face_to_point(bm_source, face_target_center, bvh_tree) - - for corner_target in face_target.loops: - # find nearest face on target compared to face that loop belongs to - p = corner_target.vert.co - - face_source_closest = closest_face_to_point(bm_source, p, bvh_tree) - enclosed = face_source_closest is face_source - face_source_int = face_source - if not enclosed: - # traverse faces between point and face center - traversed_faces = set() - traversed_edges = set() - while face_source_int is not face_source_closest: - traversed_faces.add(face_source_int) - edge = closest_edge_on_face_to_line( - face_source_int, - face_target_center, - p, - skip_edges=traversed_edges, - ) - if edge == None: - break - if len(edge.link_faces) != 2: - break - traversed_edges.add(edge) - - split = edge_data_split(edge, data_layer_source, data_suffix) - if split: - break - - # set new source face to other face belonging to edge - face_source_int = ( - edge.link_faces[1] - if edge.link_faces[1] is not face_source_int - else edge.link_faces[0] - ) - - # avoid looping behaviour - if face_source_int in traversed_faces: - face_source_int = face_source - break - - # interpolate data from selected face - col = interpolate_data_from_face( - bm_source, tris_dict, face_source_int, p, data_layer_source, data_suffix - ) - if col is None: - continue - if not data_suffix: - data_layer_target.data[corner_target.index] = col - else: - setattr(data_layer_target[corner_target.index], data_suffix, list(col)) - return - - -def is_mesh_identical(mesh_a, mesh_b) -> bool: - if len(mesh_a.vertices) != len(mesh_b.vertices): - return False - if len(mesh_a.edges) != len(mesh_b.edges): - return False - if len(mesh_a.polygons) != len(mesh_b.polygons): - return False - for e1, e2 in zip(mesh_a.edges, mesh_b.edges): - for v1, v2 in zip(e1.vertices, e2.vertices): - if v1 != v2: - return False - - return True - - -def is_curve_identical(curve_a: bpy.types.Curve, curve_b: bpy.types.Curve) -> bool: - if len(curve_a.splines) != len(curve_b.splines): - return False - for spline1, spline2 in zip(curve_a.splines, curve_b.splines): - if len(spline1.points) != len(spline2.points): - return False - return True - - -def is_obdata_identical( - a: bpy.types.Object or bpy.types.Mesh, b: bpy.types.Object or bpy.types.Mesh -) -> bool: - """Checks if two objects have matching topology (efficiency over exactness)""" - if type(a) == bpy.types.Object: - a = a.data - if type(b) == bpy.types.Object: - b = b.data - - if type(a) != type(b): - return False - - if type(a) == bpy.types.Mesh: - return is_mesh_identical(a, b) - elif type(a) == bpy.types.Curve: - return is_curve_identical(a, b) - else: - # TODO: Support geometry types other than mesh or curve. - return - - -def shape_key_set_active(obj, shape_key_name): - for index, shape_key in enumerate(obj.data.shape_keys.key_blocks): - if shape_key.name == shape_key_name: - obj.active_shape_key_index = index - - -def shape_keys_clean(obj): - if obj.type != "MESH" or obj.data.shape_keys is None: - return - - for shape_key in obj.data.shape_keys.key_blocks: - matches = check_transfer_data_entry( - obj.transfer_data_ownership, - get_basename(shape_key.name), - constants.SHAPE_KEY_KEY, - ) - if len(matches) == 0: - obj.shape_key_remove(shape_key) - - -def shape_key_is_missing(transfer_data_item): - if not transfer_data_item.type == constants.SHAPE_KEY_KEY: - return - obj = transfer_data_item.id_data - if obj.type != 'MESH': - return - if not obj.data.shape_keys: - return True - return transfer_data_item_is_missing( - transfer_data_item=transfer_data_item, - td_type_key=constants.SHAPE_KEY_KEY, - data_list=obj.data.shape_keys.key_blocks, - ) - - -def init_shape_keys(scene, obj): - if obj.type != "MESH" or obj.data.shape_keys is None: - return - - # Check that the order is legal. - # Key Blocks must be ordered after the key they are Relative To. - for i, kb in enumerate(obj.data.shape_keys.key_blocks): - if kb.relative_key: - base_shape_idx = obj.data.shape_keys.key_blocks.find(kb.relative_key.name) - if base_shape_idx > i: - raise Exception( - f'Shape Key "{kb.name}" must be ordered after its base shape "{kb.relative_key.name}" on object "{obj.name}".' - ) - - transfer_data_item_init( - scene=scene, - obj=obj, - data_list=obj.data.shape_keys.key_blocks, - td_type_key=constants.SHAPE_KEY_KEY, - ) - - -def transfer_shape_key( - context: bpy.types.Context, - shape_key_name: str, - target_obj: bpy.types.Object, - source_obj: bpy.types.Object, -): - if not source_obj.data.shape_keys: - return - sk_source = source_obj.data.shape_keys.key_blocks.get(shape_key_name) - assert sk_source - - sk_target = None - if not target_obj.data.shape_keys: - sk_target = target_obj.shape_key_add() - if not sk_target: - sk_target = target_obj.data.shape_keys.key_blocks.get(shape_key_name) - if not sk_target: - sk_target = target_obj.shape_key_add() - - sk_target.name = sk_source.name - sk_target.vertex_group = sk_source.vertex_group - if sk_source.relative_key != sk_source: - relative_key = None - if target_obj.data.shape_keys: - relative_key = target_obj.data.shape_keys.key_blocks.get( - sk_source.relative_key.name - ) - if relative_key: - sk_target.relative_key = relative_key - else: - # If the base shape of one of our shapes was removed by another task layer, - # the result will probably be pretty bad, but it's not a catastrophic failure. - # Proceed with a warning. - print( - f'Warning: Base shape "{sk_source.relative_key.name}" of Key "{sk_source.name}" was removed from "{target_obj.name}"' - ) - - sk_target.slider_min = sk_source.slider_min - sk_target.slider_max = sk_source.slider_max - sk_target.value = sk_source.value - sk_target.mute = sk_source.mute - - bm_source = bmesh.new() - bm_source.from_mesh(source_obj.data) - bm_source.faces.ensure_lookup_table() - - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - tris_dict = tris_per_face(bm_source) - for i, vert in enumerate(target_obj.data.vertices): - p = vert.co - face = closest_face_to_point(bm_source, p, bvh_tree) - - (tri, point) = closest_tri_on_face(tris_dict, face, p) - if not tri: - continue - weights = mathutils.interpolate.poly_3d_calc( - [tri[i].vert.co for i in range(3)], point - ) - - vals_weighted = [ - weights[i] - * ( - sk_source.data[tri[i].vert.index].co - - source_obj.data.vertices[tri[i].vert.index].co - ) - for i in range(3) - ] - val = mathutils.Vector(sum(np.array(vals_weighted))) - sk_target.data[i].co = vert.co + val - - if source_obj.data.shape_keys is None: - return - - fcurves = find_drivers( - source_obj.data.shape_keys, - 'key_blocks', - shape_key_name, - ) - for fcurve in fcurves: - copy_driver(from_fcurve=fcurve, target=target_obj.data.shape_keys) - - -# ATTRIBUTE -def attributes_get_editable(attributes): - return [ - attribute - for attribute in attributes - if not ( - attribute.is_internal - or attribute.is_required - # Material Index is part of material transfer and should be skipped - or attribute.name == 'material_index' - ) - ] - - -def attribute_clean(obj): - if obj.type != "MESH": - return - attributes = attributes_get_editable(obj.data.attributes) - attributes_to_remove = [] - for attribute in attributes: - matches = check_transfer_data_entry( - obj.transfer_data_ownership, - get_basename(attribute.name), - constants.ATTRIBUTE_KEY, - ) - if len(matches) == 0: - attributes_to_remove.append(attribute.name) - - for attribute_name_to_remove in reversed(attributes_to_remove): - attribute_to_remove = obj.data.attributes.get(attribute_name_to_remove) - print(f"Cleaning attribute {attribute.name}") - obj.data.attributes.remove(attribute_to_remove) - - -def attribute_is_missing(transfer_data_item): - obj = transfer_data_item.id_data - if obj.type != "MESH": - return - attributes = attributes_get_editable(obj.data.attributes) - attribute_names = [attribute.name for attribute in attributes] - if ( - transfer_data_item.type == constants.ATTRIBUTE_KEY - and not transfer_data_item["name"] in attribute_names - ): - return True - - -def init_attributes(scene, obj): - asset_pipe = scene.asset_pipeline - if obj.type != "MESH": - return - transfer_data = obj.transfer_data_ownership - td_type_key = constants.ATTRIBUTE_KEY - for atttribute in attributes_get_editable(obj.data.attributes): - # Only add new ownership transfer_data_item if vertex group doesn't have an owner - matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) - if len(matches) == 0: - task_layer_owner, auto_surrender = get_transfer_data_owner( - asset_pipe, td_type_key, atttribute.name - ) - asset_pipe.add_temp_transfer_data( - name=atttribute.name, - owner=task_layer_owner, - type=td_type_key, - obj=obj, - surrender=auto_surrender, - ) - - -def transfer_attribute( - attribute_name: str, - target_obj: bpy.types.Object, - source_obj: bpy.types.Object, -): - source_attributes = source_obj.data.attributes - target_attributes = target_obj.data.attributes - source_attribute = source_attributes.get(attribute_name) - target_attribute = target_attributes.get(attribute_name) - - if target_attribute: - target_attributes.remove(target_attribute) - - target_attribute = target_attributes.new( - name=attribute_name, - type=source_attribute.data_type, - domain=source_attribute.domain, - ) - - if not is_obdata_identical(source_obj, target_obj): - proximity_transfer_single_attribute( - source_obj, target_obj, source_attribute, target_attribute - ) - return - - for source_data_item in source_attribute.data.items(): - index = source_data_item[0] - source_data = source_data_item[1] - keys = set(source_data.bl_rna.properties.keys()) - set( - bpy.types.Attribute.bl_rna.properties.keys() - ) - for key in list(keys): - target_data = target_attribute.data[index] - setattr(target_data, key, getattr(source_data, key)) - - -def proximity_transfer_single_attribute( - source_obj: bpy.types.Object, - target_obj: bpy.types.Object, - source_attribute: bpy.types.Attribute, - target_attribute: bpy.types.Attribute, -): - # src_dat = source_obj.data - # tgt_dat = target_obj.data - # if type(src_dat) is not type(tgt_dat) or not (src_dat or tgt_dat): - # return False - # if type(tgt_dat) is not bpy.types.Mesh: # TODO: support more types - # return False - - # If target attribute already exists, remove it. - # tgt_attr = tgt_dat.attributes.get(source_attribute.name) - # if tgt_attr is not None: - # try: - # tgt_dat.attributes.remove(tgt_attr) - # except RuntimeError: - # # Built-ins like "position" cannot be removed, and should be skipped. - # return - - # Create target attribute. - # target_attribute = tgt_dat.attributes.new( - # source_attribute.name, source_attribute.data_type, source_attribute.domain - # ) - - data_sfx = { - 'INT8': 'value', - 'INT': 'value', - 'FLOAT': 'value', - 'FLOAT2': 'vector', - 'BOOLEAN': 'value', - 'STRING': 'value', - 'BYTE_COLOR': 'color', - 'FLOAT_COLOR': 'color', - 'FLOAT_VECTOR': 'vector', - } - - data_sfx = data_sfx[source_attribute.data_type] - - # if topo_match: - # # TODO: optimize using foreach_get/set rather than loop - # for i in range(len(source_attribute.data)): - # setattr(tgt_attr.data[i], data_sfx, getattr(source_attribute.data[i], data_sfx)) - # return - - # proximity fallback - if source_attribute.data_type == 'STRING': - # TODO: add NEAREST transfer fallback for attributes without interpolation - print( - f'Proximity based transfer for generic attributes of type STRING not supported yet. Skipping attribute {source_attribute.name} on {target_obj}.' - ) - return - - domain = source_attribute.domain - if ( - domain == 'POINT' - ): # TODO: deduplicate interpolated point domain proximity transfer - bm_source = bmesh.new() - bm_source.from_mesh(source_obj.data) - bm_source.faces.ensure_lookup_table() - - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - - tris_dict = tris_per_face(bm_source) - - for i, vert in enumerate(target_obj.data.vertices): - p = vert.co - face = closest_face_to_point(bm_source, p, bvh_tree) - - (tri, point) = closest_tri_on_face(tris_dict, face, p) - if not tri: - continue - weights = mathutils.interpolate.poly_3d_calc( - [tri[i].vert.co for i in range(3)], point - ) - - if data_sfx in ['color']: - vals_weighted = [ - weights[i] - * ( - np.array( - getattr(source_attribute.data[tri[i].vert.index], data_sfx) - ) - ) - for i in range(3) - ] - else: - vals_weighted = [ - weights[i] - * (getattr(source_attribute.data[tri[i].vert.index], data_sfx)) - for i in range(3) - ] - setattr(target_attribute.data[i], data_sfx, sum(np.array(vals_weighted))) - return - elif domain == 'EDGE': - # TODO support proximity fallback for generic edge attributes - print( - f'Proximity based transfer of generic edge attributes not supported yet. Skipping attribute {source_attribute.name} on {target_obj}.' - ) - return - elif domain == 'FACE': - bm_source = bmesh.new() - bm_source.from_mesh(source_obj.data) - bm_source.faces.ensure_lookup_table() - - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - for i, face in enumerate(target_obj.data.polygons): - p_target = face.center - closest_face = closest_face_to_point(bm_source, p_target, bvh_tree) - setattr( - target_attribute.data[i], - data_sfx, - getattr(source_attribute.data[closest_face.index], data_sfx), - ) - return - elif domain == 'CORNER': - transfer_corner_data( - source_obj, - target_obj, - source_attribute.data, - target_attribute.data, - data_suffix=data_sfx, - ) - return - - -def parent_clean(obj): - matches = check_transfer_data_entry( - obj.transfer_data_ownership, - get_basename(constants.PARENT_TRANSFER_DATA_ITEM_NAME), - constants.PARENT_KEY, - ) - - if len(matches) != 0: - return - - obj.parent = None - print("Cleaning Parent Relationship") - - -def parent_is_missing(transfer_data_item): - if ( - transfer_data_item.type == constants.PARENT_KEY - and transfer_data_item.id_data.parent == None - ): - return True - - -def init_parent(scene, obj): - asset_pipe = scene.asset_pipeline - td_type_key = constants.PARENT_KEY - name = constants.PARENT_TRANSFER_DATA_ITEM_NAME - transfer_data = obj.transfer_data_ownership - - # Only Execute if Material Slots exist on object - if obj.parent == None: - return - if obj.parent not in list(asset_pipe.asset_collection.all_objects): - raise Exception("Object parent cannot be outside of asset collection") - matches = check_transfer_data_entry(transfer_data, name, td_type_key) - # Only add new ownership transfer_data_item if vertex group doesn't have an owner - if len(matches) == 0: - task_layer_owner, auto_surrender = get_transfer_data_owner( - asset_pipe, - td_type_key, - ) - asset_pipe.add_temp_transfer_data( - name=name, - owner=task_layer_owner, - type=td_type_key, - obj=obj, - surrender=auto_surrender, - ) - - -def transfer_parent(target_obj, source_obj): - target_obj.parent = source_obj.parent - target_obj.matrix_parent_inverse = source_obj.parent.matrix_world.inverted() diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py new file mode 100644 index 00000000..802b8af7 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py @@ -0,0 +1,247 @@ +import bpy +import mathutils +import bmesh +import numpy as np +from .proximity_core import ( + tris_per_face, + closest_face_to_point, + closest_tri_on_face, + is_obdata_identical, + transfer_corner_data, +) +from ..transfer_util import check_transfer_data_entry +from ...naming import get_basename +from ...task_layer import get_transfer_data_owner +from .... import constants + + +def attributes_get_editable(attributes): + return [ + attribute + for attribute in attributes + if not ( + attribute.is_internal + or attribute.is_required + # Material Index is part of material transfer and should be skipped + or attribute.name == 'material_index' + ) + ] + + +def attribute_clean(obj): + if obj.type != "MESH": + return + attributes = attributes_get_editable(obj.data.attributes) + attributes_to_remove = [] + for attribute in attributes: + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + get_basename(attribute.name), + constants.ATTRIBUTE_KEY, + ) + if len(matches) == 0: + attributes_to_remove.append(attribute.name) + + for attribute_name_to_remove in reversed(attributes_to_remove): + attribute_to_remove = obj.data.attributes.get(attribute_name_to_remove) + print(f"Cleaning attribute {attribute.name}") + obj.data.attributes.remove(attribute_to_remove) + + +def attribute_is_missing(transfer_data_item): + obj = transfer_data_item.id_data + if obj.type != "MESH": + return + attributes = attributes_get_editable(obj.data.attributes) + attribute_names = [attribute.name for attribute in attributes] + if ( + transfer_data_item.type == constants.ATTRIBUTE_KEY + and not transfer_data_item["name"] in attribute_names + ): + return True + + +def init_attributes(scene, obj): + asset_pipe = scene.asset_pipeline + if obj.type != "MESH": + return + transfer_data = obj.transfer_data_ownership + td_type_key = constants.ATTRIBUTE_KEY + for atttribute in attributes_get_editable(obj.data.attributes): + # Only add new ownership transfer_data_item if vertex group doesn't have an owner + matches = check_transfer_data_entry(transfer_data, atttribute.name, td_type_key) + if len(matches) == 0: + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, td_type_key, atttribute.name + ) + asset_pipe.add_temp_transfer_data( + name=atttribute.name, + owner=task_layer_owner, + type=td_type_key, + obj=obj, + surrender=auto_surrender, + ) + + +def transfer_attribute( + attribute_name: str, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, +): + source_attributes = source_obj.data.attributes + target_attributes = target_obj.data.attributes + source_attribute = source_attributes.get(attribute_name) + target_attribute = target_attributes.get(attribute_name) + + if target_attribute: + target_attributes.remove(target_attribute) + + target_attribute = target_attributes.new( + name=attribute_name, + type=source_attribute.data_type, + domain=source_attribute.domain, + ) + + if not is_obdata_identical(source_obj, target_obj): + proximity_transfer_single_attribute( + source_obj, target_obj, source_attribute, target_attribute + ) + return + + for source_data_item in source_attribute.data.items(): + index = source_data_item[0] + source_data = source_data_item[1] + keys = set(source_data.bl_rna.properties.keys()) - set( + bpy.types.Attribute.bl_rna.properties.keys() + ) + for key in list(keys): + target_data = target_attribute.data[index] + setattr(target_data, key, getattr(source_data, key)) + + +def proximity_transfer_single_attribute( + source_obj: bpy.types.Object, + target_obj: bpy.types.Object, + source_attribute: bpy.types.Attribute, + target_attribute: bpy.types.Attribute, +): + # src_dat = source_obj.data + # tgt_dat = target_obj.data + # if type(src_dat) is not type(tgt_dat) or not (src_dat or tgt_dat): + # return False + # if type(tgt_dat) is not bpy.types.Mesh: # TODO: support more types + # return False + + # If target attribute already exists, remove it. + # tgt_attr = tgt_dat.attributes.get(source_attribute.name) + # if tgt_attr is not None: + # try: + # tgt_dat.attributes.remove(tgt_attr) + # except RuntimeError: + # # Built-ins like "position" cannot be removed, and should be skipped. + # return + + # Create target attribute. + # target_attribute = tgt_dat.attributes.new( + # source_attribute.name, source_attribute.data_type, source_attribute.domain + # ) + + data_sfx = { + 'INT8': 'value', + 'INT': 'value', + 'FLOAT': 'value', + 'FLOAT2': 'vector', + 'BOOLEAN': 'value', + 'STRING': 'value', + 'BYTE_COLOR': 'color', + 'FLOAT_COLOR': 'color', + 'FLOAT_VECTOR': 'vector', + } + + data_sfx = data_sfx[source_attribute.data_type] + + # if topo_match: + # # TODO: optimize using foreach_get/set rather than loop + # for i in range(len(source_attribute.data)): + # setattr(tgt_attr.data[i], data_sfx, getattr(source_attribute.data[i], data_sfx)) + # return + + # proximity fallback + if source_attribute.data_type == 'STRING': + # TODO: add NEAREST transfer fallback for attributes without interpolation + print( + f'Proximity based transfer for generic attributes of type STRING not supported yet. Skipping attribute {source_attribute.name} on {target_obj}.' + ) + return + + domain = source_attribute.domain + if ( + domain == 'POINT' + ): # TODO: deduplicate interpolated point domain proximity transfer + bm_source = bmesh.new() + bm_source.from_mesh(source_obj.data) + bm_source.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + + tris_dict = tris_per_face(bm_source) + + for i, vert in enumerate(target_obj.data.vertices): + p = vert.co + face = closest_face_to_point(bm_source, p, bvh_tree) + + (tri, point) = closest_tri_on_face(tris_dict, face, p) + if not tri: + continue + weights = mathutils.interpolate.poly_3d_calc( + [tri[i].vert.co for i in range(3)], point + ) + + if data_sfx in ['color']: + vals_weighted = [ + weights[i] + * ( + np.array( + getattr(source_attribute.data[tri[i].vert.index], data_sfx) + ) + ) + for i in range(3) + ] + else: + vals_weighted = [ + weights[i] + * (getattr(source_attribute.data[tri[i].vert.index], data_sfx)) + for i in range(3) + ] + setattr(target_attribute.data[i], data_sfx, sum(np.array(vals_weighted))) + return + elif domain == 'EDGE': + # TODO support proximity fallback for generic edge attributes + print( + f'Proximity based transfer of generic edge attributes not supported yet. Skipping attribute {source_attribute.name} on {target_obj}.' + ) + return + elif domain == 'FACE': + bm_source = bmesh.new() + bm_source.from_mesh(source_obj.data) + bm_source.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + for i, face in enumerate(target_obj.data.polygons): + p_target = face.center + closest_face = closest_face_to_point(bm_source, p_target, bvh_tree) + setattr( + target_attribute.data[i], + data_sfx, + getattr(source_attribute.data[closest_face.index], data_sfx), + ) + return + elif domain == 'CORNER': + transfer_corner_data( + source_obj, + target_obj, + source_attribute.data, + target_attribute.data, + data_suffix=data_sfx, + ) + return diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py new file mode 100644 index 00000000..078425df --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py @@ -0,0 +1,96 @@ +import bpy +from ..transfer_util import ( + transfer_data_clean, + transfer_data_item_is_missing, + check_transfer_data_entry, +) +from ...naming import task_layer_prefix_name_get +from ...drivers import find_drivers, copy_driver +from ...visibility import override_obj_visability +from ...task_layer import get_transfer_data_owner +from .... import constants + + +def constraints_clean(obj): + transfer_data_clean( + obj=obj, data_list=obj.constraints, td_type_key=constants.CONSTRAINT_KEY + ) + + +def constraint_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, + td_type_key=constants.CONSTRAINT_KEY, + data_list=transfer_data_item.id_data.constraints, + ) + + +def init_constraints(scene, obj): + td_type_key = constants.CONSTRAINT_KEY + transfer_data = obj.transfer_data_ownership + asset_pipe = scene.asset_pipeline + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, + td_type_key, + ) + for const in obj.constraints: + const.name = task_layer_prefix_name_get(const.name, task_layer_owner) + # Only add new ownership transfer_data_item if vertex group doesn't have an owner + matches = check_transfer_data_entry(transfer_data, const.name, td_type_key) + if len(matches) == 0: + asset_pipe.add_temp_transfer_data( + name=const.name, + owner=task_layer_owner, + type=td_type_key, + obj=obj, + surrender=auto_surrender, + ) + + +def transfer_constraint(constraint_name, target_obj, source_obj): + context = bpy.context + # remove old and sync existing modifiers + old_mod = target_obj.constraints.get(constraint_name) + if old_mod: + target_obj.constraints.remove(old_mod) + + # transfer new modifiers + for i, constraint in enumerate(source_obj.constraints): + if constraint.name == constraint_name: + constraint_new = target_obj.constraints.new(constraint.type) + constraint_new.name = constraint.name + # sort new modifier at correct index (default to beginning of the stack) + idx = 0 + if i > 0: + name_prev = source_obj.constraints[i - 1].name + for target_mod_i, target_constraint in enumerate( + target_obj.constraints + ): + if target_constraint.name == name_prev: + idx = target_mod_i + 1 + + if idx != i: + with override_obj_visability(obj=target_obj, scene=context.scene): + with context.temp_override(object=target_obj): + bpy.ops.constraint.move_to_index( + constraint=constraint_new.name, index=idx + ) + constraint_target = target_obj.constraints.get(constraint.name) + props = [ + p.identifier for p in constraint.bl_rna.properties if not p.is_readonly + ] + for prop in props: + value = getattr(constraint, prop) + setattr(constraint_target, prop, value) + + # HACK to cover edge case of armature constraints + if constraint.type == "ARMATURE": + for target_item in constraint.targets: + new_target = constraint_new.targets.new() + new_target.target = target_item.target + new_target.subtarget = target_item.subtarget + + fcurves = find_drivers(source_obj, 'constraints', constraint_name) + + for fcurve in fcurves: + copy_driver(from_fcurve=fcurve, target=target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py new file mode 100644 index 00000000..f1d894fc --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py @@ -0,0 +1,86 @@ +import bpy +from .attributes import transfer_attribute +from ..transfer_util import check_transfer_data_entry +from ...task_layer import get_transfer_data_owner +from .... import constants + + +def materials_clean(obj): + # Material slots cannot use generic transfer_data_clean() function + + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + constants.MATERIAL_TRANSFER_DATA_ITEM_NAME, + constants.MATERIAL_SLOT_KEY, + ) + + # Clear Materials if No Transfer Data is Found + if len(matches) != 0: + return + + if obj.data and hasattr(obj.data, 'materials'): + obj.data.materials.clear() + + +def materials_is_missing(transfer_data_item): + if ( + transfer_data_item.type == constants.MATERIAL_SLOT_KEY + and len(transfer_data_item.id_data.material_slots) == 0 + ): + return True + + +def init_materials(scene, obj): + asset_pipe = scene.asset_pipeline + td_type_key = constants.MATERIAL_SLOT_KEY + name = constants.MATERIAL_TRANSFER_DATA_ITEM_NAME + transfer_data = obj.transfer_data_ownership + + material_objects = [ + 'CURVE', + 'GPENCIL', + 'META', + 'MESH', + 'SURFACE', + 'FONT', + 'VOLUME', + ] + + # Only Execute if Material Slots exist on object + if obj.type not in material_objects: + return + matches = check_transfer_data_entry(transfer_data, name, td_type_key) + # Only add new ownership transfer_data_item if vertex group doesn't have an owner + if len(matches) == 0: + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, + td_type_key, + ) + asset_pipe.add_temp_transfer_data( + name=name, + owner=task_layer_owner, + type=td_type_key, + obj=obj, + surrender=auto_surrender, + ) + + +def transfer_materials(target_obj: bpy.types.Object, source_obj): + # Delete all material slots of target object. + target_obj.data.materials.clear() + + # Transfer material slots + for idx in range(len(source_obj.material_slots)): + target_obj.data.materials.append(source_obj.material_slots[idx].material) + target_obj.material_slots[idx].link = source_obj.material_slots[idx].link + + # Transfer active material slot index + target_obj.active_material_index = source_obj.active_material_index + + # Transfer material slot assignments for curve + if target_obj.type == "CURVE": + for spl_to, spl_from in zip(target_obj.data.splines, source_obj.data.splines): + spl_to.material_index = spl_from.material_index + + if source_obj.data.attributes.get(constants.MATERIAL_ATTRIBUTE_NAME): + transfer_attribute(constants.MATERIAL_ATTRIBUTE_NAME, target_obj, source_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py new file mode 100644 index 00000000..4e985902 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py @@ -0,0 +1,112 @@ +import bpy +from ..transfer_util import ( + transfer_data_clean, + transfer_data_item_is_missing, + check_transfer_data_entry, +) +from ...naming import task_layer_prefix_name_get +from ...drivers import find_drivers, copy_driver +from ...visibility import override_obj_visability +from ...task_layer import get_transfer_data_owner +from .... import constants + + +def modifiers_clean(obj): + transfer_data_clean( + obj=obj, data_list=obj.modifiers, td_type_key=constants.MODIFIER_KEY + ) + + +def modifier_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, + td_type_key=constants.MODIFIER_KEY, + data_list=transfer_data_item.id_data.modifiers, + ) + + +def init_modifiers(scene, obj): + asset_pipe = scene.asset_pipeline + td_type_key = constants.MODIFIER_KEY + transfer_data = obj.transfer_data_ownership + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, + td_type_key, + ) + + for mod in obj.modifiers: + mod.name = task_layer_prefix_name_get(mod.name, task_layer_owner) + # Only add new ownership transfer_data_item if vertex group doesn't have an owner + matches = check_transfer_data_entry(transfer_data, mod.name, td_type_key) + if len(matches) == 0: + asset_pipe.add_temp_transfer_data( + name=mod.name, + owner=task_layer_owner, + type=td_type_key, + obj=obj, + surrender=auto_surrender, + ) + + +def transfer_modifier(modifier_name, target_obj, source_obj): + # remove old and sync existing modifiers + context = bpy.context + scene = context.scene + old_mod = target_obj.modifiers.get(modifier_name) + if old_mod: + target_obj.modifiers.remove(old_mod) + + # transfer new modifiers + for i, mod in enumerate(source_obj.modifiers): + if mod.name == modifier_name: + mod_new = target_obj.modifiers.new(mod.name, mod.type) + # sort new modifier at correct index (default to beginning of the stack) + idx = 0 + if i > 0: + name_prev = source_obj.modifiers[i - 1].name + for target_mod_i, target_mod in enumerate(target_obj.modifiers): + if target_mod.name == name_prev: + idx = target_mod_i + 1 + with override_obj_visability(obj=target_obj, scene=scene): + with context.temp_override(object=target_obj): + bpy.ops.object.modifier_move_to_index( + modifier=mod_new.name, index=idx + ) + mod_target = target_obj.modifiers.get(mod.name) + props = [p.identifier for p in mod.bl_rna.properties if not p.is_readonly] + for prop in props: + value = getattr(mod, prop) + setattr(mod_target, prop, value) + + # rebind modifiers (corr. smooth, surf. deform, mesh deform) + for mod in target_obj.modifiers: + if mod.type == 'SURFACE_DEFORM': + if not mod.is_bound: + continue + for i in range(2): + with override_obj_visability(obj=target_obj, scene=scene): + with context.temp_override( + object=target_obj, active_object=target_obj + ): + bpy.ops.object.surfacedeform_bind(modifier=mod.name) + elif mod.type == 'MESH_DEFORM': + if not mod.is_bound: + continue + for i in range(2): + with override_obj_visability(obj=target_obj, scene=scene): + with context.temp_override( + object=target_obj, active_object=target_obj + ): + bpy.ops.object.meshdeform_bind(modifier=mod.name) + elif mod.type == 'CORRECTIVE_SMOOTH': + if not mod.is_bind: + continue + for i in range(2): + with override_obj_visability(obj=target_obj, scene=scene): + with context.temp_override( + object=target_obj, active_object=target_obj + ): + bpy.ops.object.correctivesmooth_bind(modifier=mod.name) + fcurves = find_drivers(source_obj, 'modifiers', modifier_name) + for fcurve in fcurves: + copy_driver(from_fcurve=fcurve, target=target_obj) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py new file mode 100644 index 00000000..254f9434 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py @@ -0,0 +1,59 @@ +import bpy +from ..transfer_util import check_transfer_data_entry +from ...task_layer import get_transfer_data_owner +from ...naming import get_basename +from .... import constants + + +def parent_clean(obj): + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + get_basename(constants.PARENT_TRANSFER_DATA_ITEM_NAME), + constants.PARENT_KEY, + ) + + if len(matches) != 0: + return + + obj.parent = None + print("Cleaning Parent Relationship") + + +def parent_is_missing(transfer_data_item): + if ( + transfer_data_item.type == constants.PARENT_KEY + and transfer_data_item.id_data.parent == None + ): + return True + + +def init_parent(scene, obj): + asset_pipe = scene.asset_pipeline + td_type_key = constants.PARENT_KEY + name = constants.PARENT_TRANSFER_DATA_ITEM_NAME + transfer_data = obj.transfer_data_ownership + + # Only Execute if Material Slots exist on object + if obj.parent == None: + return + if obj.parent not in list(asset_pipe.asset_collection.all_objects): + raise Exception("Object parent cannot be outside of asset collection") + matches = check_transfer_data_entry(transfer_data, name, td_type_key) + # Only add new ownership transfer_data_item if vertex group doesn't have an owner + if len(matches) == 0: + task_layer_owner, auto_surrender = get_transfer_data_owner( + asset_pipe, + td_type_key, + ) + asset_pipe.add_temp_transfer_data( + name=name, + owner=task_layer_owner, + type=td_type_key, + obj=obj, + surrender=auto_surrender, + ) + + +def transfer_parent(target_obj, source_obj): + target_obj.parent = source_obj.parent + target_obj.matrix_parent_inverse = source_obj.parent.matrix_world.inverted() diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/proximity_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/proximity_core.py new file mode 100644 index 00000000..6dd6516e --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/proximity_core.py @@ -0,0 +1,231 @@ +import bpy +import mathutils +import bmesh +import numpy as np + + +def closest_face_to_point(bm_source, p_target, bvh_tree=None): + if not bvh_tree: + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + (loc, norm, index, distance) = bvh_tree.find_nearest(p_target) + return bm_source.faces[index] + + +def tris_per_face(bm_source): + tris_source = bm_source.calc_loop_triangles() + tris_dict = dict() + for face in bm_source.faces: + tris_face = [] + for i in range(len(tris_source))[::-1]: + if tris_source[i][0] in face.loops: + tris_face.append(tris_source.pop(i)) + tris_dict[face] = tris_face + return tris_dict + + +def closest_tri_on_face(tris_dict, face, p): + points = [] + dist = [] + tris = [] + for tri in tris_dict[face]: + point = mathutils.geometry.closest_point_on_tri( + p, *[tri[i].vert.co for i in range(3)] + ) + tris.append(tri) + points.append(point) + dist.append((point - p).length) + min_idx = np.argmin(np.array(dist)) + point = points[min_idx] + tri = tris[min_idx] + return (tri, point) + + +def closest_edge_on_face_to_line(face, p1, p2, skip_edges=None): + """Returns edge of a face which is closest to line.""" + for edge in face.edges: + if skip_edges: + if edge in skip_edges: + continue + res = mathutils.geometry.intersect_line_line( + p1, p2, *[edge.verts[i].co for i in range(2)] + ) + if not res: + continue + (p_traversal, p_edge) = res + frac_1 = (edge.verts[1].co - edge.verts[0].co).dot( + p_edge - edge.verts[0].co + ) / (edge.verts[1].co - edge.verts[0].co).length ** 2.0 + frac_2 = (p2 - p1).dot(p_traversal - p1) / (p2 - p1).length ** 2.0 + if (frac_1 >= 0 and frac_1 <= 1) and (frac_2 >= 0 and frac_2 <= 1): + return edge + return None + + +def edge_data_split(edge, data_layer, data_suffix: str): + for vert in edge.verts: + vals = [] + for loop in vert.link_loops: + loops_edge_vert = set([loop for f in edge.link_faces for loop in f.loops]) + if loop not in loops_edge_vert: + continue + dat = data_layer[loop.index] + element = list(getattr(dat, data_suffix)) + if not vals: + vals.append(element) + elif not vals[0] == element: + vals.append(element) + if len(vals) > 1: + return True + return False + + +def interpolate_data_from_face( + bm_source, tris_dict, face, p, data_layer_source, data_suffix='' +): + """Returns interpolated value of a data layer within a face closest to a point.""" + + (tri, point) = closest_tri_on_face(tris_dict, face, p) + if not tri: + return None + weights = mathutils.interpolate.poly_3d_calc( + [tri[i].vert.co for i in range(3)], point + ) + + if not data_suffix: + cols_weighted = [ + weights[i] * np.array(data_layer_source[tri[i].index]) for i in range(3) + ] + col = sum(np.array(cols_weighted)) + else: + cols_weighted = [ + weights[i] * np.array(getattr(data_layer_source[tri[i].index], data_suffix)) + for i in range(3) + ] + col = sum(np.array(cols_weighted)) + return col + + +def transfer_corner_data( + obj_source, obj_target, data_layer_source, data_layer_target, data_suffix='' +): + """ + Transfers interpolated face corner data from data layer of a source object to data layer of a + target object, while approximately preserving data seams (e.g. necessary for UV Maps). + The transfer is face interpolated per target corner within the source face that is closest + to the target corner point and does not have any data seams on the way back to the + source face that is closest to the target face's center. + """ + + bm_source = bmesh.new() + bm_source.from_mesh(obj_source.data) + bm_source.faces.ensure_lookup_table() + bm_target = bmesh.new() + bm_target.from_mesh(obj_target.data) + bm_target.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + + tris_dict = tris_per_face(bm_source) + + for face_target in bm_target.faces: + face_target_center = face_target.calc_center_median() + + face_source = closest_face_to_point(bm_source, face_target_center, bvh_tree) + + for corner_target in face_target.loops: + # find nearest face on target compared to face that loop belongs to + p = corner_target.vert.co + + face_source_closest = closest_face_to_point(bm_source, p, bvh_tree) + enclosed = face_source_closest is face_source + face_source_int = face_source + if not enclosed: + # traverse faces between point and face center + traversed_faces = set() + traversed_edges = set() + while face_source_int is not face_source_closest: + traversed_faces.add(face_source_int) + edge = closest_edge_on_face_to_line( + face_source_int, + face_target_center, + p, + skip_edges=traversed_edges, + ) + if edge == None: + break + if len(edge.link_faces) != 2: + break + traversed_edges.add(edge) + + split = edge_data_split(edge, data_layer_source, data_suffix) + if split: + break + + # set new source face to other face belonging to edge + face_source_int = ( + edge.link_faces[1] + if edge.link_faces[1] is not face_source_int + else edge.link_faces[0] + ) + + # avoid looping behaviour + if face_source_int in traversed_faces: + face_source_int = face_source + break + + # interpolate data from selected face + col = interpolate_data_from_face( + bm_source, tris_dict, face_source_int, p, data_layer_source, data_suffix + ) + if col is None: + continue + if not data_suffix: + data_layer_target.data[corner_target.index] = col + else: + setattr(data_layer_target[corner_target.index], data_suffix, list(col)) + return + + +def is_mesh_identical(mesh_a, mesh_b) -> bool: + if len(mesh_a.vertices) != len(mesh_b.vertices): + return False + if len(mesh_a.edges) != len(mesh_b.edges): + return False + if len(mesh_a.polygons) != len(mesh_b.polygons): + return False + for e1, e2 in zip(mesh_a.edges, mesh_b.edges): + for v1, v2 in zip(e1.vertices, e2.vertices): + if v1 != v2: + return False + + return True + + +def is_curve_identical(curve_a: bpy.types.Curve, curve_b: bpy.types.Curve) -> bool: + if len(curve_a.splines) != len(curve_b.splines): + return False + for spline1, spline2 in zip(curve_a.splines, curve_b.splines): + if len(spline1.points) != len(spline2.points): + return False + return True + + +def is_obdata_identical( + a: bpy.types.Object or bpy.types.Mesh, b: bpy.types.Object or bpy.types.Mesh +) -> bool: + """Checks if two objects have matching topology (efficiency over exactness)""" + if type(a) == bpy.types.Object: + a = a.data + if type(b) == bpy.types.Object: + b = b.data + + if type(a) != type(b): + return False + + if type(a) == bpy.types.Mesh: + return is_mesh_identical(a, b) + elif type(a) == bpy.types.Curve: + return is_curve_identical(a, b) + else: + # TODO: Support geometry types other than mesh or curve. + return diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py new file mode 100644 index 00000000..1820e637 --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py @@ -0,0 +1,152 @@ +import bpy +import mathutils +import bmesh +import numpy as np +from .proximity_core import tris_per_face, closest_face_to_point, closest_tri_on_face +from ..transfer_util import ( + transfer_data_item_is_missing, + transfer_data_item_init, + check_transfer_data_entry, +) +from ...naming import get_basename +from ...drivers import find_drivers, copy_driver +from .... import constants + + +def shape_key_set_active(obj, shape_key_name): + for index, shape_key in enumerate(obj.data.shape_keys.key_blocks): + if shape_key.name == shape_key_name: + obj.active_shape_key_index = index + + +def shape_keys_clean(obj): + if obj.type != "MESH" or obj.data.shape_keys is None: + return + + for shape_key in obj.data.shape_keys.key_blocks: + matches = check_transfer_data_entry( + obj.transfer_data_ownership, + get_basename(shape_key.name), + constants.SHAPE_KEY_KEY, + ) + if len(matches) == 0: + obj.shape_key_remove(shape_key) + + +def shape_key_is_missing(transfer_data_item): + if not transfer_data_item.type == constants.SHAPE_KEY_KEY: + return + obj = transfer_data_item.id_data + if obj.type != 'MESH': + return + if not obj.data.shape_keys: + return True + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, + td_type_key=constants.SHAPE_KEY_KEY, + data_list=obj.data.shape_keys.key_blocks, + ) + + +def init_shape_keys(scene, obj): + if obj.type != "MESH" or obj.data.shape_keys is None: + return + + # Check that the order is legal. + # Key Blocks must be ordered after the key they are Relative To. + for i, kb in enumerate(obj.data.shape_keys.key_blocks): + if kb.relative_key: + base_shape_idx = obj.data.shape_keys.key_blocks.find(kb.relative_key.name) + if base_shape_idx > i: + raise Exception( + f'Shape Key "{kb.name}" must be ordered after its base shape "{kb.relative_key.name}" on object "{obj.name}".' + ) + + transfer_data_item_init( + scene=scene, + obj=obj, + data_list=obj.data.shape_keys.key_blocks, + td_type_key=constants.SHAPE_KEY_KEY, + ) + + +def transfer_shape_key( + context: bpy.types.Context, + shape_key_name: str, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, +): + if not source_obj.data.shape_keys: + return + sk_source = source_obj.data.shape_keys.key_blocks.get(shape_key_name) + assert sk_source + + sk_target = None + if not target_obj.data.shape_keys: + sk_target = target_obj.shape_key_add() + if not sk_target: + sk_target = target_obj.data.shape_keys.key_blocks.get(shape_key_name) + if not sk_target: + sk_target = target_obj.shape_key_add() + + sk_target.name = sk_source.name + sk_target.vertex_group = sk_source.vertex_group + if sk_source.relative_key != sk_source: + relative_key = None + if target_obj.data.shape_keys: + relative_key = target_obj.data.shape_keys.key_blocks.get( + sk_source.relative_key.name + ) + if relative_key: + sk_target.relative_key = relative_key + else: + # If the base shape of one of our shapes was removed by another task layer, + # the result will probably be pretty bad, but it's not a catastrophic failure. + # Proceed with a warning. + print( + f'Warning: Base shape "{sk_source.relative_key.name}" of Key "{sk_source.name}" was removed from "{target_obj.name}"' + ) + + sk_target.slider_min = sk_source.slider_min + sk_target.slider_max = sk_source.slider_max + sk_target.value = sk_source.value + sk_target.mute = sk_source.mute + + bm_source = bmesh.new() + bm_source.from_mesh(source_obj.data) + bm_source.faces.ensure_lookup_table() + + bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) + tris_dict = tris_per_face(bm_source) + for i, vert in enumerate(target_obj.data.vertices): + p = vert.co + face = closest_face_to_point(bm_source, p, bvh_tree) + + (tri, point) = closest_tri_on_face(tris_dict, face, p) + if not tri: + continue + weights = mathutils.interpolate.poly_3d_calc( + [tri[i].vert.co for i in range(3)], point + ) + + vals_weighted = [ + weights[i] + * ( + sk_source.data[tri[i].vert.index].co + - source_obj.data.vertices[tri[i].vert.index].co + ) + for i in range(3) + ] + val = mathutils.Vector(sum(np.array(vals_weighted))) + sk_target.data[i].co = vert.co + val + + if source_obj.data.shape_keys is None: + return + + fcurves = find_drivers( + source_obj.data.shape_keys, + 'key_blocks', + shape_key_name, + ) + for fcurve in fcurves: + copy_driver(from_fcurve=fcurve, target=target_obj.data.shape_keys) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/vertex_groups.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/vertex_groups.py new file mode 100644 index 00000000..ed9bbd8f --- /dev/null +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/vertex_groups.py @@ -0,0 +1,193 @@ +import bpy +from mathutils import Vector, kdtree +from typing import Dict, Tuple, List +from ..transfer_util import ( + transfer_data_clean, + transfer_data_item_is_missing, + transfer_data_item_init, +) +from .... import constants + + +def vertex_groups_clean(obj): + transfer_data_clean( + obj=obj, data_list=obj.vertex_groups, td_type_key=constants.VERTEX_GROUP_KEY + ) + + +def vertex_group_is_missing(transfer_data_item): + return transfer_data_item_is_missing( + transfer_data_item=transfer_data_item, + td_type_key=constants.VERTEX_GROUP_KEY, + data_list=transfer_data_item.id_data.vertex_groups, + ) + + +def init_vertex_groups(scene, obj): + transfer_data_item_init( + scene=scene, + obj=obj, + data_list=obj.vertex_groups, + td_type_key=constants.VERTEX_GROUP_KEY, + ) + + +def transfer_vertex_group( + context, + vertex_group_name: str, + target_obj: bpy.types.Object, + source_obj: bpy.types.Object, +): + if target_obj == source_obj: + return + + if not source_obj.vertex_groups.get(vertex_group_name): + print(f"ERROR Vertex Group {vertex_group_name} not found in {source_obj.name}") + return + + precalc_and_transfer_single_group( + source_obj, target_obj, vertex_group_name, expand=2 + ) + + +def precalc_and_transfer_single_group(source_obj, target_obj, vgroup_name, expand=2): + """Convenience function to transfer a single group. For transferring multiple groups, + this is very inefficient and shouldn't be used. + + Instead, you should: + - build_kd_tree ONCE per source mesh. + - build_vert_influence_map and transfer_vertex_groups ONCE per object pair. + """ + + # Remove group from the target obj if it already exists. + tgt_vg = target_obj.vertex_groups.get(vgroup_name) + if tgt_vg: + target_obj.vertex_groups.remove(tgt_vg) + + kd_tree = build_kdtree(source_obj.data) + vert_influence_map = build_vert_influence_map( + source_obj, target_obj, kd_tree, expand + ) + transfer_vertex_groups( + source_obj, + target_obj, + vert_influence_map, + [source_obj.vertex_groups[vgroup_name]], + ) + + +def build_kdtree(mesh): + kd = kdtree.KDTree(len(mesh.vertices)) + for i, v in enumerate(mesh.vertices): + kd.insert(v.co, i) + kd.balance() + return kd + + +def build_vert_influence_map(obj_from, obj_to, kd_tree, expand=2): + verts_of_edge = { + i: (e.vertices[0], e.vertices[1]) for i, e in enumerate(obj_from.data.edges) + } + + edges_of_vert: Dict[int, List[int]] = {} + for edge_idx, edge in enumerate(obj_from.data.edges): + for vert_idx in edge.vertices: + if vert_idx not in edges_of_vert: + edges_of_vert[vert_idx] = [] + edges_of_vert[vert_idx].append(edge_idx) + + # A mapping from target vertex index to a list of source vertex indicies and + # their influence. + # This can be pre-calculated once per object pair, to minimize re-calculations + # of subsequent transferring of individual vertex groups. + vert_influence_map: List[int, List[Tuple[int, float]]] = {} + for i, dest_vert in enumerate(obj_to.data.vertices): + vert_influence_map[i] = get_source_vert_influences( + dest_vert, obj_from, kd_tree, expand, edges_of_vert, verts_of_edge + ) + + return vert_influence_map + + +def get_source_vert_influences( + target_vert, obj_from, kd_tree, expand=2, edges_of_vert={}, verts_of_edge={} +) -> List[Tuple[int, float]]: + _coord, idx, dist = get_nearest_vert(target_vert.co, kd_tree) + source_vert_indices = [idx] + + if dist == 0: + # If the vertex position is a perfect match, just use that one vertex with max influence. + return [(idx, 1)] + + for i in range(0, expand): + new_indices = [] + for vert_idx in source_vert_indices: + for edge in edges_of_vert[vert_idx]: + vert_other = other_vert_of_edge(edge, vert_idx, verts_of_edge) + if vert_other not in source_vert_indices: + new_indices.append(vert_other) + source_vert_indices.extend(new_indices) + + distances: List[Tuple[int, float]] = [] + distance_total = 0 + for src_vert_idx in source_vert_indices: + distance = (target_vert.co - obj_from.data.vertices[src_vert_idx].co).length + distance_total += distance + distances.append((src_vert_idx, distance)) + + # Calculate influences such that the total of all influences adds up to 1.0, + # and the influence is inversely correlated with the distance. + parts = [1 / (dist / distance_total) for idx, dist in distances] + parts_sum = sum(parts) + + influences = [ + (idx, 1 if dist == 0 else part / parts_sum) + for part, dist in zip(parts, distances) + ] + + return influences + + +def get_nearest_vert( + coords: Vector, kd_tree: kdtree.KDTree +) -> Tuple[Vector, int, float]: + """Return coordinate, index, and distance of nearest vert to coords in kd_tree.""" + return kd_tree.find(coords) + + +def other_vert_of_edge( + edge: int, vert: int, verts_of_edge: Dict[int, Tuple[int, int]] +) -> int: + verts = verts_of_edge[edge] + assert vert in verts, f"Vert {vert} not part of edge {edge}." + return verts[0] if vert == verts[1] else verts[1] + + +def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups): + """Transfer src_vgroups in obj_from to obj_to using a pre-calculated vert_influence_map.""" + + for src_vg in src_vgroups: + target_vg = obj_to.vertex_groups.get(src_vg.name) + if target_vg == None: + target_vg = obj_to.vertex_groups.new(name=src_vg.name) + + for i, dest_vert in enumerate(obj_to.data.vertices): + source_verts = vert_influence_map[i] + + # Vertex Group Name : Weight + vgroup_weights = {} + + for src_vert_idx, influence in source_verts: + for group in obj_from.data.vertices[src_vert_idx].groups: + group_idx = group.group + vg = obj_from.vertex_groups[group_idx] + if vg not in src_vgroups: + continue + if vg.name not in vgroup_weights: + vgroup_weights[vg.name] = 0 + vgroup_weights[vg.name] += vg.weight(src_vert_idx) * influence + + # Assign final weights of this vertex in the vertex groups. + for vg_name in vgroup_weights.keys(): + target_vg = obj_to.vertex_groups.get(vg_name) + target_vg.add([dest_vert.index], vgroup_weights[vg_name], 'REPLACE') -- 2.30.2 From d0ebd421a525fd7145e09d90eaeed5e8f6009ebc Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Wed, 22 Nov 2023 18:01:07 -0500 Subject: [PATCH 407/429] Asset Pipe: Add Transfer_Function_Utils folder --- .../merge/transfer_data/transfer_functions/attributes.py | 2 +- .../merge/transfer_data/transfer_functions/constraints.py | 4 ++-- .../merge/transfer_data/transfer_functions/modifers.py | 4 ++-- .../merge/transfer_data/transfer_functions/shape_keys.py | 8 ++++++-- .../transfer_functions/transfer_function_util}/drivers.py | 0 .../{ => transfer_function_util}/proximity_core.py | 0 .../transfer_function_util}/visibility.py | 0 7 files changed, 11 insertions(+), 7 deletions(-) rename scripts-blender/addons/asset_pipeline_2/merge/{ => transfer_data/transfer_functions/transfer_function_util}/drivers.py (100%) rename scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/{ => transfer_function_util}/proximity_core.py (100%) rename scripts-blender/addons/asset_pipeline_2/merge/{ => transfer_data/transfer_functions/transfer_function_util}/visibility.py (100%) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py index 802b8af7..edc6334d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py @@ -2,7 +2,7 @@ import bpy import mathutils import bmesh import numpy as np -from .proximity_core import ( +from .transfer_function_util.proximity_core import ( tris_per_face, closest_face_to_point, closest_tri_on_face, diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py index 078425df..3ea5a2c8 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py @@ -5,8 +5,8 @@ from ..transfer_util import ( check_transfer_data_entry, ) from ...naming import task_layer_prefix_name_get -from ...drivers import find_drivers, copy_driver -from ...visibility import override_obj_visability +from .transfer_function_util.drivers import find_drivers, copy_driver +from .transfer_function_util.visibility import override_obj_visability from ...task_layer import get_transfer_data_owner from .... import constants diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py index 4e985902..9e9296fe 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py @@ -1,12 +1,12 @@ import bpy +from .transfer_function_util.drivers import find_drivers, copy_driver +from .transfer_function_util.visibility import override_obj_visability from ..transfer_util import ( transfer_data_clean, transfer_data_item_is_missing, check_transfer_data_entry, ) from ...naming import task_layer_prefix_name_get -from ...drivers import find_drivers, copy_driver -from ...visibility import override_obj_visability from ...task_layer import get_transfer_data_owner from .... import constants diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py index 1820e637..be8f58f5 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py @@ -2,14 +2,18 @@ import bpy import mathutils import bmesh import numpy as np -from .proximity_core import tris_per_face, closest_face_to_point, closest_tri_on_face +from .transfer_function_util.proximity_core import ( + tris_per_face, + closest_face_to_point, + closest_tri_on_face, +) +from .transfer_function_util.drivers import find_drivers, copy_driver from ..transfer_util import ( transfer_data_item_is_missing, transfer_data_item_init, check_transfer_data_entry, ) from ...naming import get_basename -from ...drivers import find_drivers, copy_driver from .... import constants diff --git a/scripts-blender/addons/asset_pipeline_2/merge/drivers.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/drivers.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/drivers.py rename to scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/drivers.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/proximity_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/proximity_core.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/proximity_core.py rename to scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/proximity_core.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/visibility.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/visibility.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/visibility.py rename to scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/visibility.py -- 2.30.2 From bab990acb2bc5d1aec62082905410abb24894697 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 11:06:07 -0500 Subject: [PATCH 408/429] Asset Pipe: Move Update Ownership to Advanced Menu --- scripts-blender/addons/asset_pipeline_2/ui.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index 8b5f217a..e47cb557 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -54,7 +54,6 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): layout.prop(asset_pipe, "asset_collection") - layout.operator("assetpipe.update_ownership", text="Update Ownership") layout.operator("assetpipe.sync_push", text="Push to Publish", icon="TRIA_UP") layout.operator( "assetpipe.sync_pull", text="Pull from Publish", icon="TRIA_DOWN" @@ -80,9 +79,11 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): def draw(self, context: bpy.types.Context) -> None: layout = self.layout box = layout.box() + box.operator("assetpipe.update_ownership", text="Update Ownership") box.operator("assetpipe.reset_ownership", icon="LOOP_BACK") - box.operator("assetpipe.fix_prefixes", icon="CHECKMARK") box.operator("assetpipe.batch_ownership_change") + box = layout.box() + box.operator("assetpipe.fix_prefixes", icon="CHECKMARK") box.operator("assetpipe.revert_file", icon="FILE_TICK") # Task Layer Updater -- 2.30.2 From cb37e1acfae8f2af4832ba5af83fcf703a2747e9 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 11:22:37 -0500 Subject: [PATCH 409/429] Asset Pipe: Update Delimiter for Task Layer Prefix --- .../addons/asset_pipeline_2/constants.py | 2 ++ .../addons/asset_pipeline_2/merge/naming.py | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 513949b2..2705c1d0 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -8,6 +8,8 @@ ADDON_NAME = "asset_pipeline_2" TASK_LAYER_CONFIG_NAME = "task_layers.json" TASK_LAYER_CONFIG_DIR_NAME = "task_layer_configs" +DELIMITER = "-" + NONE_KEY = "NONE" VERTEX_GROUP_KEY = "GROUP_VERTEX" MODIFIER_KEY = "MODIFIER" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 02c213fd..e2a661bf 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -128,9 +128,9 @@ def asset_prefix_name_get(name: str) -> str: str: Returns name with prefix """ asset_pipe = bpy.context.scene.asset_pipeline - if name.startswith(asset_pipe.prefix + "."): + if name.startswith(asset_pipe.prefix + constants.DELIMITER): return name - prefix = asset_pipe.prefix + "." if asset_pipe.prefix != "" else "" + prefix = asset_pipe.prefix + constants.DELIMITER if asset_pipe.prefix != "" else "" return prefix + name @@ -147,10 +147,12 @@ def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: str: Returns name with prefix """ for task_layer_key in config.TASK_LAYER_TYPES: - if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): + if name.startswith( + config.TASK_LAYER_TYPES[task_layer_key] + constants.DELIMITER + ): return name prefix = config.TASK_LAYER_TYPES[task_layer_owner] - return prefix + "." + name + return prefix + constants.DELIMITER + name def task_layer_prefix_basename_get(name: str) -> str: @@ -165,8 +167,10 @@ def task_layer_prefix_basename_get(name: str) -> str: str: Returns name without task layer prefix """ for task_layer_key in config.TASK_LAYER_TYPES: - if name.startswith(config.TASK_LAYER_TYPES[task_layer_key] + "."): - return name.replace(name.split(".")[0], "")[1:] + if name.startswith( + config.TASK_LAYER_TYPES[task_layer_key] + constants.DELIMITER + ): + return name.replace(name.split(constants.DELIMITER)[0], "")[1:] return name @@ -192,7 +196,7 @@ def task_layer_prefix_transfer_data_update( td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) base_name = task_layer_prefix_basename_get(transfer_data_item.name) prefix = config.TASK_LAYER_TYPES[transfer_data_item.owner] - new_name = prefix + "." + base_name + new_name = prefix + constants.DELIMITER + base_name if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): return -- 2.30.2 From 61fb402dbb6fc48982c20c9b6a1de3697be52db2 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 11:59:16 -0500 Subject: [PATCH 410/429] Asset Pipe: Update naming for Merge Suffix Functions --- .../asset_pipeline_2/merge/asset_mapping.py | 20 +++++++++---------- .../addons/asset_pipeline_2/merge/core.py | 10 +++++----- .../addons/asset_pipeline_2/merge/naming.py | 15 +++++++------- .../transfer_functions/attributes.py | 4 ++-- .../transfer_functions/parent.py | 4 ++-- .../transfer_functions/shape_keys.py | 4 ++-- .../merge/transfer_data/transfer_util.py | 4 ++-- 7 files changed, 31 insertions(+), 30 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index e512c9a5..d61db631 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -1,7 +1,7 @@ import bpy from typing import Dict, Set from .naming import ( - get_target_name, + merge_get_target_name, task_layer_prefix_basename_get, ) from .util import get_storage_of_id @@ -53,7 +53,7 @@ class AssetTransferMapping: self.shared_id_map = self._gen_shared_id_map() def _get_external_object(self, local_obj): - external_obj_name = get_target_name( + external_obj_name = merge_get_target_name( local_obj.name, ) external_obj = self._external_col.all_objects.get(external_obj_name) @@ -113,7 +113,7 @@ class AssetTransferMapping: # Find new objects to add to local_col for external_obj in self._external_col.all_objects: local_col_objs = self._local_top_col.all_objects - obj = local_col_objs.get(get_target_name(external_obj.name)) + obj = local_col_objs.get(merge_get_target_name(external_obj.name)) if not obj and external_obj.asset_id_owner not in self._local_tls: self.external_obj_to_add.add(external_obj) return object_map @@ -128,7 +128,7 @@ class AssetTransferMapping: for local_task_layer_col in self._local_top_col.children: if local_task_layer_col.asset_id_owner not in self._local_tls: # Replace source object suffix with target suffix to get target object. - external_col_name = get_target_name(local_task_layer_col.name) + external_col_name = merge_get_target_name(local_task_layer_col.name) local_col = bpy.data.collections.get(external_col_name) if local_col: coll_map[local_task_layer_col] = local_col @@ -138,18 +138,18 @@ class AssetTransferMapping: ) self._no_match_source_colls.add(local_task_layer_col) - external_top_col_name = get_target_name(self._local_top_col.name) + external_top_col_name = merge_get_target_name(self._local_top_col.name) external_top_col = bpy.data.collections.get(external_top_col_name) # TODO Refactor for external_col in external_top_col.children: - local_col_name = get_target_name(external_col.name) + local_col_name = merge_get_target_name(external_col.name) local_col = bpy.data.collections.get(local_col_name) if not local_col and external_col.asset_id_owner not in self._local_tls: self.external_col_to_add.add(external_col) for local_col in self._local_top_col.children: - external_col_name = get_target_name(local_col.name) + external_col_name = merge_get_target_name(local_col.name) external_col = bpy.data.collections.get(external_col_name) if not external_col and local_col.asset_id_owner not in self._local_tls: self.external_col_to_remove.add(local_col) @@ -183,7 +183,7 @@ class AssetTransferMapping: return name, map_item def _check_transfer_data_conflict(self, obj, transfer_data_item): - other_obj = bpy.data.objects.get(get_target_name(obj.name)) + other_obj = bpy.data.objects.get(merge_get_target_name(obj.name)) check_transfer_data_item = None if not other_obj: return @@ -213,7 +213,7 @@ class AssetTransferMapping: def transfer_data_get_other(self, transfer_data_item): # THIS IS FOR WHEN SURRENDERED DATA HAS BEEN REPLACED obj = transfer_data_item.id_data - other_obj = bpy.data.objects.get(get_target_name(obj.name)) + other_obj = bpy.data.objects.get(merge_get_target_name(obj.name)) # Find Related Transfer Data Item on Target/Source Object for other_obj_transfer_data_item in other_obj.transfer_data_ownership: if task_layer_prefix_basename_get( @@ -272,7 +272,7 @@ class AssetTransferMapping: def _gen_shared_id_map(self): shared_id_map: Dict[bpy.types.ID, bpy.types.ID] = {} for local_id in get_shared_ids(self._local_top_col): - external_id_name = get_target_name(local_id.name) + external_id_name = merge_get_target_name(local_id.name) id_storage = get_storage_of_id(local_id) external_id = id_storage.get(external_id_name) if not external_id: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index 8b8fc231..d65c800b 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -10,8 +10,8 @@ from .transfer_data.transfer_core import ( from .transfer_data.transfer_util import transfer_data_add_entry from .naming import ( - add_suffix_to_hierarchy, - remove_suffix_from_hierarchy, + merge_add_suffix_to_hierarchy, + merge_remove_suffix_from_hierarchy, asset_prefix_name_get, get_id_type_name, ) @@ -171,10 +171,10 @@ def merge_task_layer( col_base_name = local_col.name local_suffix = constants.LOCAL_SUFFIX external_suffix = constants.EXTERNAL_SUFFIX - add_suffix_to_hierarchy(local_col, local_suffix) + merge_add_suffix_to_hierarchy(local_col, local_suffix) appended_col = import_data_from_lib(external_file, "collections", col_base_name) - add_suffix_to_hierarchy(appended_col, external_suffix) + merge_add_suffix_to_hierarchy(appended_col, external_suffix) local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] @@ -227,7 +227,7 @@ def merge_task_layer( bpy.ops.outliner.orphans_purge( do_local_ids=True, do_linked_ids=False, do_recursive=True ) - remove_suffix_from_hierarchy(local_col) + merge_remove_suffix_from_hierarchy(local_col) def import_data_from_lib( diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index e2a661bf..57d6a811 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -26,8 +26,7 @@ from .util import data_type_from_transfer_data_key DELIMITER = "." - -def get_target_suffix(suffix: str) -> str: +def merge_get_target_suffix(suffix: str) -> str: """Get the corrisponding suffix for a given suffix Args: @@ -42,7 +41,7 @@ def get_target_suffix(suffix: str) -> str: return constants.EXTERNAL_SUFFIX -def get_target_name(name: str) -> str: +def merge_get_target_name(name: str) -> str: """Get the corrisponding target name for a given datablock's suffix. Suffixes are set by the add_suffix_to_hierarchy() function prior to calling this function. @@ -54,12 +53,12 @@ def get_target_name(name: str) -> str: str: Returns datablock name with the opposite suffix """ old = name.split(DELIMITER)[-1] - new = get_target_suffix(old) + new = merge_get_target_suffix(old) li = name.rsplit(old, 1) return new.join(li) -def get_basename(name: str) -> str: +def merge_get_basename(name: str) -> str: """Returns the name of an asset without it's suffix""" if name.endswith(constants.LOCAL_SUFFIX) or name.endswith( constants.EXTERNAL_SUFFIX @@ -84,12 +83,14 @@ def remove_suffix_from_hierarchy(collection: bpy.types.Collection) -> None: # Don't rename linked datablocks. continue try: - db.name = get_basename(db.name) + db.name = merge_get_basename(db.name) except: pass -def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix_base: str) -> None: +def merge_add_suffix_to_hierarchy( + collection: bpy.types.Collection, suffix_base: str +) -> None: """Add a suffix to the names of all datablocks referenced by a collection, itself included. diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py index edc6334d..5bee5029 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py @@ -10,7 +10,7 @@ from .transfer_function_util.proximity_core import ( transfer_corner_data, ) from ..transfer_util import check_transfer_data_entry -from ...naming import get_basename +from ...naming import merge_get_basename from ...task_layer import get_transfer_data_owner from .... import constants @@ -36,7 +36,7 @@ def attribute_clean(obj): for attribute in attributes: matches = check_transfer_data_entry( obj.transfer_data_ownership, - get_basename(attribute.name), + merge_get_basename(attribute.name), constants.ATTRIBUTE_KEY, ) if len(matches) == 0: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py index 254f9434..10d3add0 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py @@ -1,14 +1,14 @@ import bpy from ..transfer_util import check_transfer_data_entry from ...task_layer import get_transfer_data_owner -from ...naming import get_basename +from ...naming import merge_get_basename from .... import constants def parent_clean(obj): matches = check_transfer_data_entry( obj.transfer_data_ownership, - get_basename(constants.PARENT_TRANSFER_DATA_ITEM_NAME), + merge_get_basename(constants.PARENT_TRANSFER_DATA_ITEM_NAME), constants.PARENT_KEY, ) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py index be8f58f5..758f5ce1 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py @@ -13,7 +13,7 @@ from ..transfer_util import ( transfer_data_item_init, check_transfer_data_entry, ) -from ...naming import get_basename +from ...naming import merge_get_basename from .... import constants @@ -30,7 +30,7 @@ def shape_keys_clean(obj): for shape_key in obj.data.shape_keys.key_blocks: matches = check_transfer_data_entry( obj.transfer_data_ownership, - get_basename(shape_key.name), + merge_get_basename(shape_key.name), constants.SHAPE_KEY_KEY, ) if len(matches) == 0: diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index 627b0571..e15caa68 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -1,5 +1,5 @@ import bpy -from ..naming import get_basename +from ..naming import merge_get_basename from ..task_layer import get_transfer_data_owner @@ -59,7 +59,7 @@ def transfer_data_clean( for item in data_list: matches = check_transfer_data_entry( obj.transfer_data_ownership, - get_basename(item.name), + merge_get_basename(item.name), td_type_key, ) if len(matches) == 0: -- 2.30.2 From 8efea36eaca00d2d9fd0a319cb9702e6a7ef3ca8 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 12:04:20 -0500 Subject: [PATCH 411/429] Asset Pipe: Improve Constants Documentation --- .../addons/asset_pipeline_2/constants.py | 70 ++++++++++++++----- .../addons/asset_pipeline_2/merge/naming.py | 29 ++++---- .../addons/asset_pipeline_2/ops.py | 9 ++- 3 files changed, 73 insertions(+), 35 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline_2/constants.py index 2705c1d0..b215f446 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline_2/constants.py @@ -1,15 +1,37 @@ -# Information about the list of task layers. -# There is no behaviour that is specific to a particular task layer. -# You could even choose to name your task layers after artists in your team. -# {Task Layer Key: Collection/UI name} - ADDON_NAME = "asset_pipeline_2" -TASK_LAYER_CONFIG_NAME = "task_layers.json" +# Delimiter used for naming data within Blender +NAME_DELIMITER = "-" + + +################### +# MERGE +################### + +# Delimiter used by suffixes in the merge process +MERGE_DELIMITER = "." + +# Suffixes used when naming items to merge +LOCAL_SUFFIX = "LOCAL" +EXTERNAL_SUFFIX = "EXTERNAL" + + +################### +# Task Layers +################### + +# Name of directory containing task layer prefixes internal to add-on TASK_LAYER_CONFIG_DIR_NAME = "task_layer_configs" -DELIMITER = "-" +# Name of task layer file found a the root of an asset +TASK_LAYER_CONFIG_NAME = "task_layers.json" + +################### +# Transferable Data +################### + +# Keys for transferable data NONE_KEY = "NONE" VERTEX_GROUP_KEY = "GROUP_VERTEX" MODIFIER_KEY = "MODIFIER" @@ -40,9 +62,31 @@ TRANSFER_DATA_TYPES_ENUM_ITEMS = [ for i, tup in enumerate(TRANSFER_DATA_TYPES.items()) ] + +# Name used in all material transferable data MATERIAL_TRANSFER_DATA_ITEM_NAME = "All Materials" + +# Name used in parent transferable data PARENT_TRANSFER_DATA_ITEM_NAME = "Parent Relationship" +MATERIAL_ATTRIBUTE_NAME = "material_index" + + +################### +# SHARED IDs +################### + +# SHARED ID Icons +GEO_NODE = "GEOMETRY_NODES" +IMAGE = "IMAGE_DATA" +BLANK = "BLANK1" + + +################### +# Publish +################### + +# List of different states used when Publishing a Final Asset PUBLISH_TYPES = [ ( "publish", @@ -64,15 +108,3 @@ PUBLISH_TYPES = [ PUBLISH_KEYS = [pub_type[0] for pub_type in PUBLISH_TYPES] ACTIVE_PUBLISH_KEY = PUBLISH_KEYS[0] STAGED_PUBLISH_KEY = PUBLISH_KEYS[1] - -LOCAL_SUFFIX = "LOCAL" -EXTERNAL_SUFFIX = "EXTERNAL" - - -MATERIAL_ATTRIBUTE_NAME = "material_index" - - -## SHARED ID Icons -GEO_NODE = "GEOMETRY_NODES" -IMAGE = "IMAGE_DATA" -BLANK = "BLANK1" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 57d6a811..172db873 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -24,7 +24,6 @@ from .util import get_storage_of_id from .. import constants, config from .util import data_type_from_transfer_data_key -DELIMITER = "." def merge_get_target_suffix(suffix: str) -> str: """Get the corrisponding suffix for a given suffix @@ -52,7 +51,7 @@ def merge_get_target_name(name: str) -> str: Returns: str: Returns datablock name with the opposite suffix """ - old = name.split(DELIMITER)[-1] + old = name.split(constants.MERGE_DELIMITER)[-1] new = merge_get_target_suffix(old) li = name.rsplit(old, 1) return new.join(li) @@ -63,11 +62,13 @@ def merge_get_basename(name: str) -> str: if name.endswith(constants.LOCAL_SUFFIX) or name.endswith( constants.EXTERNAL_SUFFIX ): - return DELIMITER.join(name.split(DELIMITER)[:-1]) + return constants.MERGE_DELIMITER.join( + name.split(constants.MERGE_DELIMITER)[:-1] + ) return name -def remove_suffix_from_hierarchy(collection: bpy.types.Collection) -> None: +def merge_remove_suffix_from_hierarchy(collection: bpy.types.Collection) -> None: """Removes the suffix after a set delimiter from all datablocks referenced by a collection, itself included @@ -99,7 +100,7 @@ def merge_add_suffix_to_hierarchy( suffix_base (str): Suffix to append to collection and items linked to collection """ - suffix = f"{DELIMITER}{suffix_base}" + suffix = f"{constants.MERGE_DELIMITER}{suffix_base}" ref_map = get_id_reference_map() datablocks = get_all_referenced_ids(collection, ref_map) @@ -110,7 +111,7 @@ def merge_add_suffix_to_hierarchy( continue collision_db = get_storage_of_id(db).get(db.name + suffix) if collision_db: - collision_db.name += f'{DELIMITER}OLD' + collision_db.name += f'{constants.MERGE_DELIMITER}OLD' try: db.name += suffix except: @@ -129,9 +130,11 @@ def asset_prefix_name_get(name: str) -> str: str: Returns name with prefix """ asset_pipe = bpy.context.scene.asset_pipeline - if name.startswith(asset_pipe.prefix + constants.DELIMITER): + if name.startswith(asset_pipe.prefix + constants.NAME_DELIMITER): return name - prefix = asset_pipe.prefix + constants.DELIMITER if asset_pipe.prefix != "" else "" + prefix = ( + asset_pipe.prefix + constants.NAME_DELIMITER if asset_pipe.prefix != "" else "" + ) return prefix + name @@ -149,11 +152,11 @@ def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: """ for task_layer_key in config.TASK_LAYER_TYPES: if name.startswith( - config.TASK_LAYER_TYPES[task_layer_key] + constants.DELIMITER + config.TASK_LAYER_TYPES[task_layer_key] + constants.NAME_DELIMITER ): return name prefix = config.TASK_LAYER_TYPES[task_layer_owner] - return prefix + constants.DELIMITER + name + return prefix + constants.NAME_DELIMITER + name def task_layer_prefix_basename_get(name: str) -> str: @@ -169,9 +172,9 @@ def task_layer_prefix_basename_get(name: str) -> str: """ for task_layer_key in config.TASK_LAYER_TYPES: if name.startswith( - config.TASK_LAYER_TYPES[task_layer_key] + constants.DELIMITER + config.TASK_LAYER_TYPES[task_layer_key] + constants.NAME_DELIMITER ): - return name.replace(name.split(constants.DELIMITER)[0], "")[1:] + return name.replace(name.split(constants.NAME_DELIMITER)[0], "")[1:] return name @@ -197,7 +200,7 @@ def task_layer_prefix_transfer_data_update( td_data = data_type_from_transfer_data_key(obj, transfer_data_item.type) base_name = task_layer_prefix_basename_get(transfer_data_item.name) prefix = config.TASK_LAYER_TYPES[transfer_data_item.owner] - new_name = prefix + constants.DELIMITER + base_name + new_name = prefix + constants.NAME_DELIMITER + base_name if new_name == transfer_data_item.name or not td_data.get(transfer_data_item.name): return diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 46a28c23..8f8f06e8 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -17,6 +17,7 @@ from .sync import ( sync_execute_pull, sync_execute_push, ) +from . import constants class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): @@ -77,11 +78,13 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): asset_col = self._asset_pipe.asset_collection name = ( asset_col.name - if "-" not in asset_col.name - else asset_col.name.split("-", 1)[1] + if constants.NAME_DELIMITER not in asset_col.name + else asset_col.name.split(constants.NAME_DELIMITER, 1)[1] ) prefix = ( - "" if "-" not in asset_col.name else asset_col.name.split("-", 1)[0] + "" + if constants.NAME_DELIMITER not in asset_col.name + else asset_col.name.split(constants.NAME_DELIMITER, 1)[0] ) else: -- 2.30.2 From 8fd8c1c36123a6c79f92ec66265d74572f81443f Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 12:05:07 -0500 Subject: [PATCH 412/429] Asset Pipe: Remove Testing Folder --- .../testing/resources/Modelling_Import.py | 77 -------- .../testing/resources/Rigging_Import.py | 174 ------------------ .../testing/resources/Shading_Import.py | 125 ------------- .../resources/sky_for_asset_test.blend | 3 - 4 files changed, 379 deletions(-) delete mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py delete mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py delete mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py delete mode 100644 scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py deleted file mode 100644 index 467e76d2..00000000 --- a/scripts-blender/addons/asset_pipeline_2/testing/resources/Modelling_Import.py +++ /dev/null @@ -1,77 +0,0 @@ -import bpy -from pathlib import Path - - -def import_data_from_lib( - libpath: Path, - data_category: str, - data_name: str, - link: bool = False, -) -> bpy.data: - """Appends/Links data from an external file into the current file. - - Args: - libpath (Path): path to .blend file that contains library - data_category (str): bpy.types, like object or collection - data_name (str): name of datablock to link/append - link (bool, optional): Set to link library otherwise append. Defaults to False. - - Returns: - bpy.data: returns whichever data_category/type that was linked/appended - """ - - noun = "Appended" - if link: - noun = "Linked" - - with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( - data_from, - data_to, - ): - if data_name not in eval(f"data_from.{data_category}"): - print( - f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", - ) - - # Check if datablock with same name already exists in blend file. - try: - eval(f"bpy.data.{data_category}['{data_name}']") - except KeyError: - pass - else: - print( - f"{data_name} already in bpy.data.{data_category} of this blendfile.", - ) - - # Append data block. - eval(f"data_to.{data_category}.append('{data_name}')") - print(f"{noun}:{data_name} from library: {libpath.as_posix()}") - - if link: - return eval( - f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" - ) - - return eval(f"bpy.data.{data_category}['{data_name}']") - - -## EXECUTION -task_layer_name = "Modeling" -external_file = ( - Path(bpy.data.filepath) - .parent.parent.parent.joinpath("resources") - .joinpath("sky_for_asset_test.blend") -) -appended_col = import_data_from_lib( - external_file, "collections", f"sky.{task_layer_name.lower()}" -) -asset_collection = bpy.context.scene.asset_pipeline.asset_collection -bpy.context.scene.collection.children.link(appended_col) - -task_layer_col = bpy.data.collections.new(task_layer_name) -asset_collection.children.link(task_layer_col) - -for obj in appended_col.objects: - task_layer_col.objects.link(obj) - -bpy.data.collections.remove(appended_col) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py deleted file mode 100644 index 20a6cfd1..00000000 --- a/scripts-blender/addons/asset_pipeline_2/testing/resources/Rigging_Import.py +++ /dev/null @@ -1,174 +0,0 @@ -import bpy -from pathlib import Path - - -def import_data_from_lib( - libpath: Path, - data_category: str, - data_name: str, - link: bool = False, -) -> bpy.data: - """Appends/Links data from an external file into the current file. - - Args: - libpath (Path): path to .blend file that contains library - data_category (str): bpy.types, like object or collection - data_name (str): name of datablock to link/append - link (bool, optional): Set to link library otherwise append. Defaults to False. - - Returns: - bpy.data: returns whichever data_category/type that was linked/appended - """ - - noun = "Appended" - if link: - noun = "Linked" - - with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( - data_from, - data_to, - ): - if data_name not in eval(f"data_from.{data_category}"): - print( - f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", - ) - - # Check if datablock with same name already exists in blend file. - try: - eval(f"bpy.data.{data_category}['{data_name}']") - except KeyError: - pass - else: - print( - f"{data_name} already in bpy.data.{data_category} of this blendfile.", - ) - - # Append data block. - eval(f"data_to.{data_category}.append('{data_name}')") - print(f"{noun}:{data_name} from library: {libpath.as_posix()}") - - if link: - return eval( - f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" - ) - - return eval(f"bpy.data.{data_category}['{data_name}']") - - -def transfer_constraint(constraint_name, target_obj, source_obj): - context = bpy.context - # remove old and sync existing modifiers - old_mod = target_obj.constraints.get(constraint_name) - if old_mod: - target_obj.constraints.remove(old_mod) - - # transfer new modifiers - for i, constraint in enumerate(source_obj.constraints): - if constraint.name == constraint_name: - constraint_new = target_obj.constraints.new(constraint.type) - constraint_new.name = constraint.name - # sort new modifier at correct index (default to beginning of the stack) - idx = 0 - if i > 0: - name_prev = source_obj.constraints[i - 1].name - for target_mod_i, target_constraint in enumerate( - target_obj.constraints - ): - if target_constraint.name == name_prev: - idx = target_mod_i + 1 - - if idx != i: - with override_obj_visability(obj=target_obj): - with context.temp_override(object=target_obj): - bpy.ops.constraint.move_to_index( - constraint=constraint_new.name, index=idx - ) - constraint_target = target_obj.constraints.get(constraint.name) - props = [ - p.identifier for p in constraint.bl_rna.properties if not p.is_readonly - ] - for prop in props: - value = getattr(constraint, prop) - setattr(constraint_target, prop, value) - - # HACK to cover edge case of armature constraints - if constraint.type == "ARMATURE": - for target_item in constraint.targets: - new_target = constraint_new.targets.new() - new_target.target = target_item.target - new_target.subtarget = target_item.subtarget - - -def transfer_vertex_groups(context, target_obj, source_obj): - with context.temp_override( - object=source_obj, selected_editable_objects=[target_obj, source_obj] - ): - bpy.ops.object.data_transfer( - data_type="VGROUP_WEIGHTS", - use_create=True, - vert_mapping='POLYINTERP_NEAREST', - layers_select_src="ALL", - layers_select_dst="NAME", - mix_mode="REPLACE", - ) - - -## EXECUTION -task_layer_name = "Rigging" -task_layer_col = bpy.data.collections.new(task_layer_name) -asset_collection = bpy.context.scene.asset_pipeline.asset_collection -asset_collection.children.link(task_layer_col) -external_file = ( - Path(bpy.data.filepath) - .parent.parent.parent.joinpath("resources") - .joinpath("sky_for_asset_test.blend") -) -appended_col = import_data_from_lib( - external_file, "collections", f"sky.{task_layer_name.lower()}" -) -bpy.context.scene.collection.children.link(appended_col) - -rig = None - - -# Link Armature into Scene -for obj in appended_col.objects: - if obj.type == "ARMATURE": - task_layer_col.objects.link(obj) - rig = obj - - -for obj in bpy.data.collections["Modeling"].objects: - source_obj = bpy.data.objects[f"{obj.name}.rigging"] - - ## Set Parent - obj.parent = rig - obj.matrix_parent_inverse = source_obj.parent.matrix_world.inverted() - - ## Transfer Vertex Groups - transfer_vertex_groups(bpy.context, obj, source_obj) - - ## Copy Constraints - for constraint in source_obj.constraints: - transfer_constraint(constraint.name, obj, source_obj) - - -main_body_obj = bpy.data.objects["GEO-Body"] -mod = main_body_obj.modifiers.new("Armature", type="ARMATURE") -mod.object = rig - - -for col in appended_col.children: - task_layer_col.children.link(col) - -for obj in task_layer_col.all_objects: - if obj.name.endswith(".rigging"): - obj.name = obj.name.replace(".rigging", "") - - -## REMOVE EVERYTHING ELSE -for obj in bpy.data.objects: - if obj.name.endswith(".rigging"): - bpy.data.objects.remove(obj) - -bpy.data.collections.remove(appended_col) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py b/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py deleted file mode 100644 index 8ff90490..00000000 --- a/scripts-blender/addons/asset_pipeline_2/testing/resources/Shading_Import.py +++ /dev/null @@ -1,125 +0,0 @@ -import bpy -from pathlib import Path - - -def import_data_from_lib( - libpath: Path, - data_category: str, - data_name: str, - link: bool = False, -) -> bpy.data: - """Appends/Links data from an external file into the current file. - - Args: - libpath (Path): path to .blend file that contains library - data_category (str): bpy.types, like object or collection - data_name (str): name of datablock to link/append - link (bool, optional): Set to link library otherwise append. Defaults to False. - - Returns: - bpy.data: returns whichever data_category/type that was linked/appended - """ - - noun = "Appended" - if link: - noun = "Linked" - - with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( - data_from, - data_to, - ): - if data_name not in eval(f"data_from.{data_category}"): - print( - f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", - ) - - # Check if datablock with same name already exists in blend file. - try: - eval(f"bpy.data.{data_category}['{data_name}']") - except KeyError: - pass - else: - print( - f"{data_name} already in bpy.data.{data_category} of this blendfile.", - ) - - # Append data block. - eval(f"data_to.{data_category}.append('{data_name}')") - print(f"{noun}:{data_name} from library: {libpath.as_posix()}") - - if link: - return eval( - f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" - ) - - return eval(f"bpy.data.{data_category}['{data_name}']") - - -def transfer_material_slots(target_obj: bpy.types.Object, source_obj): - # Delete all material slots of target object. - target_obj.data.materials.clear() - - # Transfer material slots - for idx in range(len(source_obj.material_slots)): - target_obj.data.materials.append(source_obj.material_slots[idx].material) - target_obj.material_slots[idx].link = source_obj.material_slots[idx].link - - -def transfer_attribute( - attribute_name: str, - target_obj: bpy.types.Object, - source_obj: bpy.types.Object, -): - source_attributes = source_obj.data.attributes - target_attributes = target_obj.data.attributes - source_attribute = source_attributes.get(attribute_name) - - target_attribute = target_attributes.get(attribute_name) - if target_attribute: - target_attributes.remove(target_attribute) - - target_attribute = target_attributes.new( - name=attribute_name, - type=source_attribute.data_type, - domain=source_attribute.domain, - ) - # print(f"Transfering Attribute {attribute_name}") - for source_data_item in source_attribute.data.items(): - index = source_data_item[0] - source_data = source_data_item[1] - keys = set(source_data.bl_rna.properties.keys()) - set( - bpy.types.Attribute.bl_rna.properties.keys() - ) - for key in list(keys): - target_data = target_attribute.data[index] - setattr(target_data, key, getattr(source_data, key)) - - -## EXECUTION" -task_layer_name = "Shading" -external_file = ( - Path(bpy.data.filepath) - .parent.parent.parent.joinpath("resources") - .joinpath("sky_for_asset_test.blend") -) -appended_col = import_data_from_lib( - external_file, "collections", f"sky.{task_layer_name.lower()}" -) -bpy.context.scene.collection.children.link(appended_col) - -source_body_obj = bpy.data.objects["GEO-Body.shading"] -target_body_obj = bpy.data.objects["GEO-Body"] - - -for obj in bpy.data.collections["Modeling"].objects: - source_obj = bpy.data.objects[f"{obj.name}.shading"] - transfer_material_slots(obj, source_obj) - -transfer_attribute( - attribute_name="material_index", - target_obj=target_body_obj, - source_obj=source_body_obj, -) - - -bpy.data.collections.remove(appended_col) diff --git a/scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend b/scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend deleted file mode 100644 index b745f940..00000000 --- a/scripts-blender/addons/asset_pipeline_2/testing/resources/sky_for_asset_test.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:221d07c3dc474f9c349b7ce0eaaa9892167b78c373f8418e9d863815b5bb6246 -size 2054192 -- 2.30.2 From abd9c3f956ab85d4c692d41ceab3806573822dd6 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 12:05:56 -0500 Subject: [PATCH 413/429] Asset Pipe: Remove testing .gitignore file --- scripts-blender/addons/asset_pipeline_2/.gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 scripts-blender/addons/asset_pipeline_2/.gitignore diff --git a/scripts-blender/addons/asset_pipeline_2/.gitignore b/scripts-blender/addons/asset_pipeline_2/.gitignore deleted file mode 100644 index a098b5c3..00000000 --- a/scripts-blender/addons/asset_pipeline_2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Temporary for Testing -testing/test_chr \ No newline at end of file -- 2.30.2 From f433baa3fba563cf3bd026f47d883742a61f5855 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 12:30:42 -0500 Subject: [PATCH 414/429] Asset Pipe: Use name Transferable Data in UI and Doc Strings --- .../addons/asset_pipeline_2/README.md | 12 +++---- .../asset_pipeline_2/merge/asset_mapping.py | 2 +- .../addons/asset_pipeline_2/merge/core.py | 18 +++++----- .../addons/asset_pipeline_2/merge/naming.py | 4 +-- .../asset_pipeline_2/merge/task_layer.py | 4 +-- .../merge/transfer_data/transfer_core.py | 18 +++++----- .../transfer_functions/materials.py | 2 +- .../merge/transfer_data/transfer_ui.py | 4 +-- .../merge/transfer_data/transfer_util.py | 36 +++++++++---------- .../addons/asset_pipeline_2/merge/util.py | 2 +- .../addons/asset_pipeline_2/ops.py | 34 +++++++++--------- .../addons/asset_pipeline_2/props.py | 6 ++-- .../addons/asset_pipeline_2/sync.py | 6 ++-- 13 files changed, 75 insertions(+), 73 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/README.md b/scripts-blender/addons/asset_pipeline_2/README.md index d6ea3dd8..9f8121ad 100644 --- a/scripts-blender/addons/asset_pipeline_2/README.md +++ b/scripts-blender/addons/asset_pipeline_2/README.md @@ -34,15 +34,15 @@ This Add-On was designed to allow multiple artists to collaborate while contribu ## Key Concepts **Task Layers** Task Layers are defined in a JSON file that describes the number of layers used to manage the asset. Typically each task layer is given it's own file, artists can optionally house multiple task layers inside the same file if required. Each task layer is used to describe a step in the asset making process (e.g."Modeling", "Rigging", "Shading"). The number and content of a task layer is fully customizable by the artist. -**Ownership** Each piece of data in the Asset Pipeline is owned by a task layer, this includes Objects, Task Layer Collections and Transfer Data. The owner of data is the only person who can contribute to that piece of data, including modifying or removing that data. Objects implicitly will own the meshes and other types of object data, multiple objects referencing the same mesh is not supported. +**Ownership** Each piece of data in the Asset Pipeline is owned by a task layer, this includes Objects, Task Layer Collections and Transferable Data. The owner of data is the only person who can contribute to that piece of data, including modifying or removing that data. Objects implicitly will own the meshes and other types of object data, multiple objects referencing the same mesh is not supported. **Asset Collection** Is the top-level collection for a given asset, all relevant objects/sub-collections for the asset are contained within this collection. **Task Layer Collection** These collections are children of the asset collection, each Task Layer collection is owned by a specific task layer, they are all top-level child collections to the Asset Collection. Children of the Task Layer collections are owned by the Task Layer collection's owner. -**Transfer Data** Is data that a part or associated with an object or mesh, but can be explicitly owned and updated. This is the key concept that allows multiple artists to contribute to an asset. During the Push/Pull process transfer data is applied on top of each object, allowing artist A to own an object but artist B to own the vertex groups on that object for example. +**Transferable Data** Is data that a part or associated with an object or mesh, but can be explicitly owned and updated. This is the key concept that allows multiple artists to contribute to an asset. During the Push/Pull process Transferable Data is applied on top of each object, allowing artist A to own an object but artist B to own the vertex groups on that object for example. -Transfer Data Types: +Transferable Data Types: - Vertex Groups - Modifiers - Constraints @@ -90,12 +90,12 @@ When you Push/Pull a file, you will be greeted with an operator dialogue. This d The add-on will optionally save your current file plus any unsaved/unpacked images will be saved in a directory relative to your asset (configurable in the add-on preferences). It will always create a back-up of your current file, in the case where the merge process fails, you will be prompted to revert your file back to it's pre-merge status. ### 3. Merge with Published File -Push and Pull are merging operations done to/from the published file. When you want to share your updated work to the rest of the team select "Push to Publish" to update the published file with your changes. Push will update any Transfer data you edited, and update any objects/collections you own with the version in your current file. Transfer Data owned by other artists will be re-applied to your objects. +Push and Pull are merging operations done to/from the published file. When you want to share your updated work to the rest of the team select "Push to Publish" to update the published file with your changes. Push will update any Transferable Data you edited, and update any objects/collections you own with the version in your current file. Transferable Data owned by other artists will be re-applied to your objects. -If another artist then uses the "Pull to Publish" operator the same process will occur, keeping all objects, collections and transfer data that is local to their file, and importing any data that was owned externally by other task layers. +If another artist then uses the "Pull to Publish" operator the same process will occur, keeping all objects, collections and Transferable Data that is local to their file, and importing any data that was owned externally by other task layers. ## Surrendering Ownership -In the ownership inspector each Object/Transfer Data item has an option to "surrender" that piece of data. When surrendering this piece of data is now "up for grabs" to all other task layers. After surrendering artists will need to push this update to the published file. The surrendered item's ownership indicator will be replaced by an "Update Surrendered" operator, this operator is available to all task layers except the one that surrendered that data. When another task layer pulls in from the publish, they will be able to run the "Update Surrendered" operator to claim it assigning it to that task layer. +In the ownership inspector each Object/Transferable Data item has an option to "surrender" that piece of data. When surrendering this piece of data is now "up for grabs" to all other task layers. After surrendering artists will need to push this update to the published file. The surrendered item's ownership indicator will be replaced by an "Update Surrendered" operator, this operator is available to all task layers except the one that surrendered that data. When another task layer pulls in from the publish, they will be able to run the "Update Surrendered" operator to claim it assigning it to that task layer. ## Publish New Version To Publish a new version of an asset select the "Publish New Version" operator. The operator dialogue will require an input on which publish type to create. Publish types are as follows. diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py index d61db631..d794554e 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py @@ -214,7 +214,7 @@ class AssetTransferMapping: # THIS IS FOR WHEN SURRENDERED DATA HAS BEEN REPLACED obj = transfer_data_item.id_data other_obj = bpy.data.objects.get(merge_get_target_name(obj.name)) - # Find Related Transfer Data Item on Target/Source Object + # Find Related Transferable Data Item on Target/Source Object for other_obj_transfer_data_item in other_obj.transfer_data_ownership: if task_layer_prefix_basename_get( other_obj_transfer_data_item.name diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline_2/merge/core.py index d65c800b..3046196d 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/core.py @@ -25,10 +25,10 @@ def ownership_transfer_data_cleanup( asset_pipe: 'bpy.types.AssetPipeline', obj: bpy.types.Object, ) -> None: - """Remove Transfer Data ownership items if the corrisponding data is missing + """Remove Transferable Data ownership items if the corrisponding data is missing Args: - obj (bpy.types.Object): Object that contains the transfer data + obj (bpy.types.Object): Object that contains the Transferable Data """ local_task_layer_keys = asset_pipe.get_local_task_layers() transfer_data = obj.transfer_data_ownership @@ -46,7 +46,7 @@ def ownership_get( local_col: bpy.types.Collection, scene: bpy.types.Scene, ) -> None: - """Find new transfer data owned by the local task layer. + """Find new Transferable Data owned by the local task layer. Marks items as owned by the local task layer if they are in the corrisponding task layer collection and have no owner. @@ -88,8 +88,8 @@ def ownership_get( def ownership_set(temp_transfer_data: bpy.types.CollectionProperty) -> None: - """Add new transfer data items on each object found in the - temp transfer data collection property + """Add new Transferable Data items on each object found in the + temp Transferable Data collection property Args: temp_transfer_data (bpy.types.CollectionProperty): Collection property containing newly found @@ -152,11 +152,11 @@ def merge_task_layer( ) -> None: """Combines data from an external task layer collection in the local task layer collection. By finding the owner of each collection, - object and transfer data item and keeping each layer of data via a copy + object and Transferable Data item and keeping each layer of data via a copy from it's respective owners. This ensures that objects owned by an external task layer will always be kept - linked into the scene, and any local transfer data like a modifier will be applied + linked into the scene, and any local Transferable Data like a modifier will be applied ontop of that external object of vice versa. Ownership is stored in an objects properties, and map is created to match each object to it's respective owner. @@ -183,7 +183,7 @@ def merge_task_layer( error_msg = '' if len(map.conflict_transfer_data) != 0: for conflict in map.conflict_transfer_data: - error_msg += f"Transfer Data conflict found for '{conflict.name}' on obj '{conflict.id_data.name}'\n" + error_msg += f"Transferable Data conflict found for '{conflict.name}' on obj '{conflict.id_data.name}'\n" return error_msg if len(map.conflict_ids) != 0: @@ -194,7 +194,7 @@ def merge_task_layer( ) return error_msg - # Remove all transfer data from target objects + # Remove all Transferable Data from target objects for source_obj in map.object_map: if source_obj.data and source_obj.data.users > 1: error_msg += f"Object {source_obj.name} contains multi-user datablock'\n" diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline_2/merge/naming.py index 172db873..d3e08f75 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/naming.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/naming.py @@ -161,7 +161,7 @@ def task_layer_prefix_name_get(name: str, task_layer_owner: str) -> str: def task_layer_prefix_basename_get(name: str) -> str: """Get the base of a name if it contains a task layer prefix. - This prefix is set on some transfer data items, this functions + This prefix is set on some Transferable Data items, this functions removes the prefixes and returns the basename Args: @@ -187,7 +187,7 @@ def task_layer_prefix_transfer_data_update( name of the actual data the transfer_data_item is referring to. Args: - transfer_data_item (bpy.types.CollectionProperty): Transfer Data Item that is named with prefix + transfer_data_item (bpy.types.CollectionProperty): Transferable Data Item that is named with prefix Returns: bool: Returns True if a change to the name was completed diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py index 5c244df5..4082f490 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py @@ -49,7 +49,7 @@ def draw_task_layer_selection( - When a user is overriding or the object is new (using default ownership): "Show All Task Layers" Args: layout (bpy.types.UILayout): Any UI Layout element like self.layout or row - data (bpy.types.CollectionProperty or bpy.types.ID): Object, Collection or Transfer Data Item + data (bpy.types.CollectionProperty or bpy.types.ID): Object, Collection or Transferable Data Item show_all_task_layers (bool, optional): Used when user is overriding or default ownership is set. Defaults to False. show_local_task_layers (bool, optional): Force Showing Local Task Layers Only. Defaults to False. text (str, optional): Title of prop search. Defaults to "". @@ -65,7 +65,7 @@ def draw_task_layer_selection( else: data_owner_name = "asset_id_owner" - # Get the current data owner from OBJ/COL or Transfer Data Item if it hasn't been passed + # Get the current data owner from OBJ/COL or Transferable Data Item if it hasn't been passed if current_data_owner is None: current_data_owner = data.get(data_owner_name) diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py index 4bfb3df2..c8786de8 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py @@ -21,11 +21,11 @@ from .transfer_util import ( def copy_transfer_data_ownership( transfer_data_item, target_obj: bpy.types.Object ) -> None: - """Copy transfer data item to object if non entry exists + """Copy Transferable Data item to object if non entry exists Args: transfer_data_item: Item of bpy.types.CollectionProperty from source object - target_obj (bpy.types.Object): Object to add transfer data item to + target_obj (bpy.types.Object): Object to add Transferable Data item to """ transfer_data = target_obj.transfer_data_ownership matches = check_transfer_data_entry( @@ -53,7 +53,7 @@ def transfer_data_clean(obj): def transfer_data_is_missing(transfer_data_item) -> bool: - """Check if Transfer Data item is missing + """Check if Transferable Data item is missing Args: transfer_data_item: Item of class ASSET_TRANSFER_DATA @@ -75,10 +75,10 @@ def init_transfer_data( scene: bpy.types.Scene, obj: bpy.types.Object, ): - """Collect Transfer Data Items on a given object + """Collect Transferable Data Items on a given object Args: - obj (bpy.types.Object): Target object for transfer data + obj (bpy.types.Object): Target object for Transferable Data task_layer_name (str): Name of task layer temp_transfer_data: Item of class ASSET_TRANSFER_DATA_TEMP """ @@ -92,9 +92,9 @@ def init_transfer_data( def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: - """Apply all transfer data from transfer data map onto objects. - Copies any transfer data owned by local layer onto objects owned by external layers. - Applies transfer data from external layers onto objects owned by local layers + """Apply all Transferable Data from Transferable Data map onto objects. + Copies any Transferable Data owned by local layer onto objects owned by external layers. + Applies Transferable Data from external layers onto objects owned by local layers Transfer_data_map is generated by class 'AssetTransferMapping' @@ -112,7 +112,7 @@ def apply_transfer_data(context: bpy.types.Context, transfer_data_map) -> None: target_obj = transfer_data.get('target_obj') source_obj = transfer_data.get('source_obj') if target_obj is None: - print(f"Failed to Transfer data for {transfer_data_item.id_data.name}") + print(f"Failed to Transferable Data for {transfer_data_item.id_data.name}") continue if transfer_data_item is None: continue diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py index f1d894fc..264452bb 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py @@ -14,7 +14,7 @@ def materials_clean(obj): constants.MATERIAL_SLOT_KEY, ) - # Clear Materials if No Transfer Data is Found + # Clear Materials if No Transferable Data is Found if len(matches) != 0: return diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py index 464ae10e..bda723e1 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py @@ -6,7 +6,7 @@ from ..task_layer import draw_task_layer_selection def draw_transfer_data_type( layout: bpy.types.UILayout, transfer_data: bpy.types.CollectionProperty ) -> None: - """Draw UI Element for items of a transfer data type""" + """Draw UI Element for items of a Transferable Data type""" asset_pipe = bpy.context.scene.asset_pipeline if transfer_data == []: return @@ -60,7 +60,7 @@ def draw_transfer_data_type( def draw_transfer_data( transfer_data: bpy.types.CollectionProperty, layout: bpy.types.UILayout ) -> None: - """Draw UI List of Transfer Data""" + """Draw UI List of Transferable Data""" vertex_groups = [] # vertex_colors = [] material_slots = [] diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py index e15caa68..a3ca1b8b 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py @@ -6,12 +6,12 @@ from ..task_layer import get_transfer_data_owner def check_transfer_data_entry( transfer_data: bpy.types.CollectionProperty, key: str, td_type_key: str ) -> set: - """Verifies if transfer data entry exists + """Verifies if Transferable Data entry exists Args: - ownership (bpy.types.CollectionProperty): Transfer Data of an object + ownership (bpy.types.CollectionProperty): Transferable Data of an object key (str): Name of item that is being verified - td_type_key (str): Type of transfer data + td_type_key (str): Type of Transferable Data Returns: set: Returns set of matches where name is found in ownership @@ -31,12 +31,12 @@ def transfer_data_add_entry( task_layer_name: str, surrender: bool, ): - """Add entry to transfer data ownership + """Add entry to Transferable Data ownership Args: - ownership (bpy.types.CollectionProperty): Transfer Data of an object - name (str): Name of new transfer data item - td_type_key (str): Type of transfer data + ownership (bpy.types.CollectionProperty): Transferable Data of an object + name (str): Name of new Transferable Data item + td_type_key (str): Type of Transferable Data task_layer_name (str): Name of current task layer """ transfer_data_item = transfer_data.add() @@ -52,9 +52,9 @@ def transfer_data_clean( ): """Removes data if a transfer_data_item doesn't exist but the data does exist Args: - obj (bpy.types.Object): Object containing transfer data - data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers - td_type_key (str): Key for the transfer data type + obj (bpy.types.Object): Object containing Transferable Data + data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible Transferable Data e.g. obj.modifiers + td_type_key (str): Key for the Transferable Data type """ for item in data_list: matches = check_transfer_data_entry( @@ -72,9 +72,9 @@ def transfer_data_item_is_missing( """Returns true if a transfer_data_item exists the data doesn't exist Args: - transfer_data_item (_type_): Item of Transfer Data - data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers - td_type_key (str): Key for the transfer data type + transfer_data_item (_type_): Item of Transferable Data + data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible Transferable Data e.g. obj.modifiers + td_type_key (str): Key for the Transferable Data type Returns: bool: Returns True if transfer_data_item is missing """ @@ -84,8 +84,8 @@ def transfer_data_item_is_missing( return True -"""Intilize transfer data to a temporary collection property, used - to draw a display of new transfer data to the user before merge process. +"""Intilize Transferable Data to a temporary collection property, used + to draw a display of new Transferable Data to the user before merge process. """ @@ -99,9 +99,9 @@ def transfer_data_item_init( Args: scene (bpy.types.Scene): Scene that contains a the file's asset - obj (bpy.types.Object): Object containing possible transfer data - data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible transfer data e.g. obj.modifiers - td_type_key (str): Key for the transfer data type + obj (bpy.types.Object): Object containing possible Transferable Data + data_list (bpy.types.CollectionProperty): Collection Property containing a type of possible Transferable Data e.g. obj.modifiers + td_type_key (str): Key for the Transferable Data type """ asset_pipe = scene.asset_pipeline transfer_data = obj.transfer_data_ownership diff --git a/scripts-blender/addons/asset_pipeline_2/merge/util.py b/scripts-blender/addons/asset_pipeline_2/merge/util.py index 0d62f0be..4aef3458 100644 --- a/scripts-blender/addons/asset_pipeline_2/merge/util.py +++ b/scripts-blender/addons/asset_pipeline_2/merge/util.py @@ -102,7 +102,7 @@ def traverse_collection_tree( def data_type_from_transfer_data_key(obj: bpy.types.Object, td_type: str): - """Returns the data on an object that is referred to by the transfer data type""" + """Returns the data on an object that is referred to by the Transferable Data type""" if td_type == constants.VERTEX_GROUP_KEY: return obj.vertex_groups if td_type == constants.MODIFIER_KEY: diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index 8f8f06e8..e66e7a0d 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -235,16 +235,16 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): class ASSETPIPE_OT_update_ownership(bpy.types.Operator): bl_idname = "assetpipe.update_ownership" bl_label = "Update Ownership" - bl_description = """Update the Ownership of Objects and Transfer Data""" + bl_description = """Update the Ownership of Objects and Transferable Data""" _temp_transfer_data = None _invalid_objs = [] _other_ids = [] expand: bpy.props.BoolProperty( - name="Show New Transfer Data", + name="Show New Transferable Data", default=False, - description="Show New Transfer Data", + description="Show New Transferable Data", ) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): @@ -273,9 +273,9 @@ class ASSETPIPE_OT_sync_pull(bpy.types.Operator): _sync_target: Path = None expand: bpy.props.BoolProperty( - name="Show New Transfer Data", + name="Show New Transferable Data", default=False, - description="Show New Transfer Data", + description="Show New Transferable Data", ) save: bpy.props.BoolProperty( name="Save File & Images", @@ -316,9 +316,9 @@ class ASSETPIPE_OT_sync_push(bpy.types.Operator): _sync_target: Path = None expand: bpy.props.BoolProperty( - name="Show New Transfer Data", + name="Show New Transferable Data", default=False, - description="Show New Transfer Data", + description="Show New Transferable Data", ) pull: bpy.props.BoolProperty( name="Pull before Pushing", @@ -416,7 +416,7 @@ class ASSETPIPE_OT_reset_ownership(bpy.types.Operator): bl_idname = "assetpipe.reset_ownership" bl_label = "Reset Ownership" bl_description = ( - """Reset the Object owner and transfer data on selected object(s)""" + """Reset the Object owner and Transferable Data on selected object(s)""" ) @classmethod @@ -507,7 +507,7 @@ class ASSETPIPE_OT_revert_file(bpy.types.Operator): class ASSETPIPE_OT_fix_prefixes(bpy.types.Operator): bl_idname = "assetpipe.fix_prefixes" bl_label = "Fix Prefixes" - bl_description = """Fix Prefixes for Modifiers and Constraints so they match Transfer Data Owner on selected object(s)""" + bl_description = """Fix Prefixes for Modifiers and Constraints so they match Transferable Data Owner on selected object(s)""" _updated_prefix = False @@ -577,9 +577,11 @@ class ASSETPIPE_OT_update_surrendered_object(bpy.types.Operator): class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): bl_idname = "assetpipe.update_surrendered_transfer_data" bl_label = "Claim Surrendered" - bl_description = """Claim Surrended Transfer Data Owner""" + bl_description = """Claim Surrended Transferable Data Owner""" - transfer_data_item_name: bpy.props.StringProperty(name="Transfer Data Item Name") + transfer_data_item_name: bpy.props.StringProperty( + name="Transferable Data Item Name" + ) _surrendered_transfer_data = None _old_onwer = "" @@ -607,7 +609,7 @@ class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): if self._surrendered_transfer_data.owner == self._old_onwer: self.report( {'ERROR'}, - f"Transfer Data Owner was not updated", + f"Transferable Data Owner was not updated", ) return {'CANCELLED'} self._surrendered_transfer_data.surrender = False @@ -626,7 +628,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): name_filter: bpy.props.StringProperty( name="Filter by Name", - description="Filter Object or Transfer Data items by name", + description="Filter Object or Transferable Data items by name", default="", ) @@ -649,7 +651,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): ( 'TRANSFER_DATA', "Transferable Data", - "Update Owner of Transfer Data within Objects", + "Update Owner of Transferable Data within Objects", ), ), ) @@ -682,7 +684,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): surrender_selection: bpy.props.BoolProperty( name="Set Surrender", default=False, - description="Surrender can only be set on objects/transfer data that are locally owned. Ownership cannot be changed while surrendering", + description="Surrender can only be set on objects/Transferable Data that are locally owned. Ownership cannot be changed while surrendering", ) def _filter_by_name(self, context, unfiltered_list: []): @@ -792,7 +794,7 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): length = len(objs) if objs else 0 else: transfer_data_items_to_update = self._get_transfer_data_to_update(context) - data_type_name = "Transfer Data Item(s)" + data_type_name = "Transferable Data Item(s)" length = ( len(transfer_data_items_to_update) if transfer_data_items_to_update diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline_2/props.py index 343389b6..1a5a1ffa 100644 --- a/scripts-blender/addons/asset_pipeline_2/props.py +++ b/scripts-blender/addons/asset_pipeline_2/props.py @@ -30,7 +30,7 @@ class AssetTransferData(bpy.types.PropertyGroup): owner: bpy.props.StringProperty(name="Owner", default="NONE") type: bpy.props.EnumProperty( - name="Transfer Data Type", + name="Transferable Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) surrender: bpy.props.BoolProperty(name="Surrender Ownership", default=False) @@ -42,7 +42,7 @@ class AssetTransferDataTemp(bpy.types.PropertyGroup): owner: bpy.props.StringProperty(name="OwneAr", default="NONE") type: bpy.props.EnumProperty( - name="Transfer Data Type", + name="Transferable Data Type", items=constants.TRANSFER_DATA_TYPES_ENUM_ITEMS, ) surrender: bpy.props.BoolProperty(name="Surrender Ownership", default=False) @@ -127,7 +127,7 @@ class AssetPipeline(bpy.types.PropertyGroup): def get_local_task_layers(self): return [task_layer.name for task_layer in self.local_task_layers] - # UI BOOLS: used to show/hide transfer data elements + # UI BOOLS: used to show/hide Transferable Data elements # The names are also hard coded in constants.py under TRANSFER_DATA_TYPES # any changes will need to be reflected both here and in that enum group_vertex_ui_bool: bpy.props.BoolProperty( diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline_2/sync.py index 09f8ce6f..c9c626e2 100644 --- a/scripts-blender/addons/asset_pipeline_2/sync.py +++ b/scripts-blender/addons/asset_pipeline_2/sync.py @@ -73,12 +73,12 @@ def sync_draw(self, context): ) if len(self._temp_transfer_data) == 0: - layout.label(text="No new local Transfer Data found") + layout.label(text="No new local Transferable Data found") else: - layout.label(text="New local Transfer Data will be Pushed to Publish") + layout.label(text="New local Transferable Data will be Pushed to Publish") row = layout.row() row.prop(self, "expand", text="", icon="COLLAPSEMENU", toggle=False) - row.label(text="Show New Transfer Data") + row.label(text="Show New Transferable Data") objs = [transfer_data_item.obj for transfer_data_item in self._temp_transfer_data] if not self.expand: -- 2.30.2 From 7a41c44394d1baab72dc972a9d1ab13c5153a2e1 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 13:21:30 -0500 Subject: [PATCH 415/429] Asset Pipe: Add Advanced Mode Preference --- scripts-blender/addons/asset_pipeline_2/prefs.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/prefs.py b/scripts-blender/addons/asset_pipeline_2/prefs.py index 160bd1b9..d13789f0 100644 --- a/scripts-blender/addons/asset_pipeline_2/prefs.py +++ b/scripts-blender/addons/asset_pipeline_2/prefs.py @@ -23,9 +23,16 @@ class ASSET_PIPELINE_addon_preferences(bpy.types.AddonPreferences): subtype="DIR_PATH", ) + is_advanced_mode: bpy.props.BoolProperty( + name="Advanced Mode", + description="Show Advanced Options in Asset Pipeline Panels", + default=False, + ) + def draw(self, context): self.layout.prop(self, "custom_task_layers_dir") self.layout.prop(self, "save_images_path") + self.layout.prop(self, "is_advanced_mode") classes = (ASSET_PIPELINE_addon_preferences,) -- 2.30.2 From 306b5d36bdc7148ae6426b9cb51fec1354927370 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 13:22:32 -0500 Subject: [PATCH 416/429] Asset Pipe: Move Pull before Push to Advanced Menu --- scripts-blender/addons/asset_pipeline_2/ops.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index e66e7a0d..efa79437 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -1,14 +1,15 @@ import bpy -from . import config import os from pathlib import Path +from . import constants +from . import config +from .prefs import get_addon_prefs from .merge.naming import task_layer_prefix_transfer_data_update from .merge.task_layer import ( draw_task_layer_selection, ) from .merge.publish import get_next_published_file, find_all_published from .images import save_images -from . import constants from .sync import ( sync_invoke, sync_draw, @@ -17,7 +18,6 @@ from .sync import ( sync_execute_pull, sync_execute_push, ) -from . import constants class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): @@ -337,7 +337,9 @@ class ASSETPIPE_OT_sync_push(bpy.types.Operator): return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context: bpy.types.Context): - self.layout.prop(self, "pull") + prefs = get_addon_prefs() + if prefs.is_advanced_mode: + self.layout.prop(self, "pull") self.layout.prop(self, "save") sync_draw(self, context) -- 2.30.2 From d14d14a2dff881ad5ec7ad96ca1afca6ac83a91a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 13:39:58 -0500 Subject: [PATCH 417/429] Asset Pipe: Hide Advanced Panel Behind Advanced Preference --- scripts-blender/addons/asset_pipeline_2/ui.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index e47cb557..eb2ef96a 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -4,6 +4,7 @@ from pathlib import Path from .merge.transfer_data.transfer_ui import draw_transfer_data from .merge.task_layer import draw_task_layer_selection from .config import verify_json_data +from .prefs import get_addon_prefs from . import constants @@ -76,6 +77,11 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): bl_parent_id = "ASSETPIPE_PT_sync" bl_options = {'DEFAULT_CLOSED'} + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + prefs = get_addon_prefs() + return prefs.is_advanced_mode + def draw(self, context: bpy.types.Context) -> None: layout = self.layout box = layout.box() -- 2.30.2 From 2d9b73aed22889ac9e1e758a3f6481773c149614 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 13:40:50 -0500 Subject: [PATCH 418/429] Asset Pipe: Remove No-Op Code --- scripts-blender/addons/asset_pipeline_2/ops.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index efa79437..e72d4d3f 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -619,10 +619,6 @@ class ASSETPIPE_OT_update_surrendered_transfer_data(bpy.types.Operator): return {'FINISHED'} -def get_task_layer_types(self, context): - return - - class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): bl_idname = "assetpipe.batch_ownership_change" bl_label = "Batch Set Ownership" -- 2.30.2 From dd796d812dcbafa495a37be4e0000c0405809611 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 13:47:32 -0500 Subject: [PATCH 419/429] Asset Pipe: Hide some options in Batch Operator behind Advanced Mode - Hide Owner Filter - Hide Avaliable Owner Selector - Reset Hidden Options to 'LOCAL' if not in advanced mode --- scripts-blender/addons/asset_pipeline_2/ops.py | 12 ++++++++++-- scripts-blender/addons/asset_pipeline_2/ui.py | 5 +++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline_2/ops.py index e72d4d3f..877da191 100644 --- a/scripts-blender/addons/asset_pipeline_2/ops.py +++ b/scripts-blender/addons/asset_pipeline_2/ops.py @@ -738,9 +738,14 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): return self._filter_by_name(context, objs) def invoke(self, context: bpy.types.Context, event: bpy.types.Event): + if not get_addon_prefs().is_advanced_mode: + self.filter_owners = 'LOCAL' + self.avaliable_owners = 'LOCAL' return context.window_manager.invoke_props_dialog(self, width=500) def draw(self, context: bpy.types.Context): + prefs = get_addon_prefs() + advanced_mode = prefs.is_advanced_mode grey_out = True if self.surrender_selection and self.data_type == "TRANSFER_DATA": grey_out = False @@ -754,7 +759,8 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): filter_owner_row = layout.row() filter_owner_row.enabled = grey_out - filter_owner_row.prop(self, "filter_owners") + if advanced_mode: + filter_owner_row.prop(self, "filter_owners") if self.data_type == "TRANSFER_DATA": layout.prop(self, "transfer_data_type") @@ -781,7 +787,9 @@ class ASSETPIPE_OT_batch_ownership_change(bpy.types.Operator): show_local_task_layers=show_local, text="Set To", ) - owner_row.prop(self, "avaliable_owners", text="") + + if advanced_mode: + owner_row.prop(self, "avaliable_owners", text="") if self.data_type == "TRANSFER_DATA": layout.prop(self, "surrender_selection", expand=True) diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline_2/ui.py index eb2ef96a..f887fe65 100644 --- a/scripts-blender/addons/asset_pipeline_2/ui.py +++ b/scripts-blender/addons/asset_pipeline_2/ui.py @@ -59,8 +59,10 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): layout.operator( "assetpipe.sync_pull", text="Pull from Publish", icon="TRIA_DOWN" ) + layout.separator() layout.operator("assetpipe.publish_new_version", icon="PLUS") - + layout.separator() + layout.operator("assetpipe.batch_ownership_change") # TODO Find new way to determine if we are in a published file more explicitly # if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE": # asset_pipe = context.scene.asset_pipeline @@ -87,7 +89,6 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): box = layout.box() box.operator("assetpipe.update_ownership", text="Update Ownership") box.operator("assetpipe.reset_ownership", icon="LOOP_BACK") - box.operator("assetpipe.batch_ownership_change") box = layout.box() box.operator("assetpipe.fix_prefixes", icon="CHECKMARK") box.operator("assetpipe.revert_file", icon="FILE_TICK") -- 2.30.2 From b2e6c429a28a945823239f300354de3ed86b2f1a Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 14:17:56 -0500 Subject: [PATCH 420/429] Asset Pipe: Remove Old Asset Pipeline Add-On --- .../addons/asset_pipeline/.gitignore | 115 --- .../addons/asset_pipeline/CHANGELOG.md | 13 - .../addons/asset_pipeline/README.md | 555 ------------ .../addons/asset_pipeline/TODO.txt | 84 -- .../addons/asset_pipeline/__init__.py | 80 -- .../addons/asset_pipeline/api/__init__.py | 34 - .../addons/asset_pipeline/asset_files.py | 282 ------ .../addons/asset_pipeline/asset_status.py | 37 - .../addons/asset_pipeline/builder/__init__.py | 53 -- .../asset_pipeline/builder/asset_builder.py | 612 ------------- .../asset_pipeline/builder/asset_importer.py | 317 ------- .../asset_pipeline/builder/asset_mapping.py | 356 -------- .../asset_pipeline/builder/asset_suffix.py | 69 -- .../asset_pipeline/builder/blstarter.py | 50 -- .../addons/asset_pipeline/builder/context.py | 682 -------------- .../addons/asset_pipeline/builder/hook.py | 161 ---- .../asset_pipeline/builder/lock_plan.py | 71 -- .../asset_pipeline/builder/meta_util.py | 71 -- .../addons/asset_pipeline/builder/metadata.py | 421 --------- .../addons/asset_pipeline/builder/ops.py | 811 ----------------- .../addons/asset_pipeline/builder/opsdata.py | 235 ----- .../asset_pipeline/builder/scripts/push.py | 157 ---- .../asset_pipeline/builder/task_layer.py | 380 -------- .../addons/asset_pipeline/builder/ui.py | 534 ----------- .../addons/asset_pipeline/builder/vis.py | 132 --- .../addons/asset_pipeline/constants.py | 32 - .../docs/production_config_example/hooks.py | 51 -- .../production_config_example/task_layers.py | 468 ---------- .../docs/production_config_heist/hooks.py | 110 --- .../production_config_heist/task_layers.py | 838 ------------------ .../addons/asset_pipeline/lib_util.py | 74 -- .../addons/asset_pipeline/prefs.py | 94 -- .../addons/asset_pipeline/prop_utils.py | 37 - .../addons/asset_pipeline/props.py | 445 ---------- .../addons/asset_pipeline/sys_utils.py | 68 -- .../addons/asset_pipeline/tests/__init__.py | 0 .../tests/test_blender_studio_pipeline.py | 5 - .../addons/asset_pipeline/updater/__init__.py | 49 - .../asset_pipeline/updater/asset_updater.py | 71 -- .../addons/asset_pipeline/updater/ops.py | 137 --- .../addons/asset_pipeline/updater/opsdata.py | 99 --- .../addons/asset_pipeline/updater/ui.py | 142 --- scripts-blender/addons/asset_pipeline/util.py | 188 ---- 43 files changed, 9220 deletions(-) delete mode 100644 scripts-blender/addons/asset_pipeline/.gitignore delete mode 100644 scripts-blender/addons/asset_pipeline/CHANGELOG.md delete mode 100644 scripts-blender/addons/asset_pipeline/README.md delete mode 100644 scripts-blender/addons/asset_pipeline/TODO.txt delete mode 100644 scripts-blender/addons/asset_pipeline/__init__.py delete mode 100644 scripts-blender/addons/asset_pipeline/api/__init__.py delete mode 100644 scripts-blender/addons/asset_pipeline/asset_files.py delete mode 100644 scripts-blender/addons/asset_pipeline/asset_status.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/__init__.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/asset_builder.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/asset_importer.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/asset_mapping.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/asset_suffix.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/blstarter.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/context.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/hook.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/lock_plan.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/meta_util.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/metadata.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/ops.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/opsdata.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/scripts/push.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/task_layer.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/ui.py delete mode 100644 scripts-blender/addons/asset_pipeline/builder/vis.py delete mode 100644 scripts-blender/addons/asset_pipeline/constants.py delete mode 100644 scripts-blender/addons/asset_pipeline/docs/production_config_example/hooks.py delete mode 100644 scripts-blender/addons/asset_pipeline/docs/production_config_example/task_layers.py delete mode 100644 scripts-blender/addons/asset_pipeline/docs/production_config_heist/hooks.py delete mode 100644 scripts-blender/addons/asset_pipeline/docs/production_config_heist/task_layers.py delete mode 100644 scripts-blender/addons/asset_pipeline/lib_util.py delete mode 100644 scripts-blender/addons/asset_pipeline/prefs.py delete mode 100644 scripts-blender/addons/asset_pipeline/prop_utils.py delete mode 100644 scripts-blender/addons/asset_pipeline/props.py delete mode 100644 scripts-blender/addons/asset_pipeline/sys_utils.py delete mode 100644 scripts-blender/addons/asset_pipeline/tests/__init__.py delete mode 100644 scripts-blender/addons/asset_pipeline/tests/test_blender_studio_pipeline.py delete mode 100644 scripts-blender/addons/asset_pipeline/updater/__init__.py delete mode 100644 scripts-blender/addons/asset_pipeline/updater/asset_updater.py delete mode 100644 scripts-blender/addons/asset_pipeline/updater/ops.py delete mode 100644 scripts-blender/addons/asset_pipeline/updater/opsdata.py delete mode 100644 scripts-blender/addons/asset_pipeline/updater/ui.py delete mode 100644 scripts-blender/addons/asset_pipeline/util.py diff --git a/scripts-blender/addons/asset_pipeline/.gitignore b/scripts-blender/addons/asset_pipeline/.gitignore deleted file mode 100644 index 245d94fc..00000000 --- a/scripts-blender/addons/asset_pipeline/.gitignore +++ /dev/null @@ -1,115 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# dotenv -.env - -# virtualenv -.venv -.venv* -venv/ -ENV/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# IDE settings -.vscode/ - -# utility bat files: -*jump_in_venv.bat - -#local tests -tests/local* - -# Production Config Dir. -production_config/* diff --git a/scripts-blender/addons/asset_pipeline/CHANGELOG.md b/scripts-blender/addons/asset_pipeline/CHANGELOG.md deleted file mode 100644 index 9ca5001b..00000000 --- a/scripts-blender/addons/asset_pipeline/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -## 0.1.2 - 2023-08-02 - -### FIXED -- Fix Changelog Rendering (#125) -- Fix line ends from DOS to UNIX (#68) - -## 0.1.1 - 2023-06-02 - -### FIXED -- Fix Addon Install Instructions -- Fix Addons Spelling and Links (#54) - - diff --git a/scripts-blender/addons/asset_pipeline/README.md b/scripts-blender/addons/asset_pipeline/README.md deleted file mode 100644 index eaf7e368..00000000 --- a/scripts-blender/addons/asset_pipeline/README.md +++ /dev/null @@ -1,555 +0,0 @@ -# Asset Pipeline -asset-pipeline is a Blender Add-on that manages the Asset Pipeline of the Blender Studio. It includes an Asset Builder and an Asset Updater. - -[Asset Pipeline Presentation](https://youtu.be/IBTEBhAouKc?t=527) - -## Table of Contents -- [Installation](#installation) -- [How to get started](#how-to-get-started) -- [Configuration](#configuration) - - [Task Layers](#task_layers.py) - - [Hooks](#hooks.py) -- [Getting Started as a Developer](#getting-started-as-a-developer) - - [Context](#context) - - [UI](#ui) - - [Asset Collection](#asset-collection) - - [Asset Files](#asset-files) - - [Metadata](#metadata) - - [Asset Importer](#asset-importer) - - [Asset Mapping](#asset-mapping) - - [Asset Builder](#asset-builder) - - [Asset Updater](#asset-updater) - - -## Installation -1. Download [latest release](../addons/overview) -2. Launch Blender, navigate to `Edit > Preferences` select `Addons` and then `Install`, -3. Navigate to the downloaded add-on and select `Install Add-on` - -> **_NOTE:_** This add-on depends on other add-ons that are in the [Blender Studio Tools](https://projects.blender.org/studio/blender-studio-pipeline). - -Make sure to also install: -- [**blender-kitsu**](/addons/blender_kitsu) - - -## How to get started - -After installing you need to setup the addon preferences to fit your environment. - -The asset-pipeline add-on can be configured with some config files. The idea is that for each project you can have a custom configuration. - -In the add-on preferences you need to setup the `Production Config Directory`. In this folder the add-on expects to find a file called `task_layers.py`. What exactly you need to define in this file is something you will learn in the [Configuration](#configuration) section. - -To understand the underlying concepts of the Asset Pipeline it is recommended to read [this](https://studio.blender.org/blog/asset-pipeline-update-2022/) article. - -## Configuration -The add-on can be configured on a per project basis, by pointing the the `Production Config Directory` property in the add-on preferences to a folder that contains the config files. - -The config files need to be named a certain way and contain certain content. - - - -### task_layers.py -In this file you can define the Task Layers and TransferSettings for this project. -For an example config check out: `docs/production_config_example/task_layers.py` - - ---- -**Defining Task Layers** - -To define a Task Layer import: - -``` -import bpy - -from asset_pipeline.api import ( - AssetTransferMapping, - TaskLayer, -) -``` - -And declare a TaskLayer class that Inherits from TaskLayer: - -``` -class RiggingTaskLayer(TaskLayer): - name = "Rigging" - order = 0 - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - pass - -``` - -The `class name` ("RiggingTaskLayer") will be the Identifier for that TaskLayer in the code. The `name` attribute will be used for display purposes in the UI. -There can be no TaskLayers with the same class name. - -The `order` attribute will be used to determine in which order the TaskLayers are processed. Processing a TaskLayer means calling the `transfer_data()` class method. - -> **_NOTE:_** The TaskLayer with the lowest order is a special TaskLayer. In the code it will be considered as the **base** TaskLayer. - -The `transfer_data()` function of the base TaskLayer should be empty as it provides the base for other task layers to transfer their data to. But it will still be called as there are cases where Users might need that functionality. - -When Users push one or multiple TaskLayers from an Asset Task to an Asset Publish or pull vice versa, we need a base on which we can transfer the data. - -During the transfer process there will be 3 Asset Collections: -- The Asset Collection of the Asset Task -- The Asset Collection of the Asset Publish -- The Target Asset Collection - -The Target Asset Collection is a duplicate of either the Task or Publish Asset Collection and is the base on which we transfer data to. The decision to duplicate the Publish or Task Collection depends on if the **base** Task Layer (Task Layer with lowers order) was enabled or not before the push or the pull. - -If we push from an Asset Task to an Asset Publish and the base TaskLayer is among the selection we take the Asset Collection from the Asset Task as a base. If it is not selected we take the Asset Collection od the Asset Publish as a base. - -If we pull from an Asset Publish to an Asset Task and the base TaskLayer is among the selection we take the Asset Collection from the Asset Publish as base. If it is not selected we take the Asset Collection of the Asset Task as a base. - -The `transfer_data()` function contains 4 parameters that are useful when writing the transfer instructions. - -``` - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - pass -``` - -- **context**: Regular bpy.context - -- **build_context**: Is an instance of type `asset_pipeline.builder.context.BuildContext`. It contains all information that the Asset Builder needs to process the transfer. You can for example query the selected TaskLayers with `build_context.asset_context.task_layer_assembly.get_used_task_layers()`. Or find out if the current operation was a `push` or a `pull` with `build_context.is_push`. - -- **transfer_mapping**: Will be an instance of type `AssetTransferMapping`. This is a mapping between source and target for: **objects**, **materials** and **collections**. The maps are just dictionaries where the key is the source and the value the target. Both key and target are actual Blender ID Datablocks. - -``` -transfer_mapping.object_map: Dict[bpy.types.Object, bpy.types.Object] - -transfer_mapping.collection_map: Dict[bpy.types.Collection, bpy.types.Collection] - -transfer_mapping.material_map: Dict[bpy.types.Material, bpy.types.Material] - -``` -This enables you to do things like this: -``` -for obj_source, obj_target in transfer_mapping.object_map.items(): - pass - -for mat_source, mat_target in transfer_mapping.material_map.items(): - pass - -... -``` -You can also access the root Asset source and Target Collection: -``` -transfer_mapping.source_coll: bpy.types.Collection -transfer_mapping.target_coll: bpy.types.Collection -``` - -Further than that you can access to objects which had no match. -``` -transfer_mapping.no_match_target_objs: Set[bpy.types.Object] (all Objects that exist in target but not in source) -transfer_mapping.no_match_source_objs: Set[bpy.types.Object] (vice versa) -``` -- **transfer_settings**: Is the `TransferSettings` PropertyGroup that was defined in the task_layer.py module. More to that in the next section. If the PropertyGroup was defined you can just query its values as you would regularly do it inside of Blender: `transfer_settings.my_value` - ---- -**Defining Transfer Settings** - -Transfer Settings are settings that Users can adjust inside of the Blender UI which can be queried in the `tranfer_data()` function and control certain aspects of the transfer. - -To define Transfer Setting you just have to add a class called `TranferSettings` that inherits from `bpy.props.PropertyGroup` in the task_layer.py file. - -``` -class TransferSettings(bpy.types.PropertyGroup): - transfer_mat: bpy.props.BoolProperty(name="Materials", default=True) - transfer_uvs: bpy.props.BoolProperty(name="UVs", default=True) - transfer_type: bpy.props.EnumProperty( - name="Transfer Type", - items=[("VERTEX_ORDER", "Vertex Order", ""), ("PROXIMITY", "Proximity", "")], - ) -``` -You can use every native Blender Property type. These properties are automatically exposed in the `Transfer Settings` tab UI in the Asset Pipeline Panel. - - -### hooks.py -The Asset Pipeline supports post transfer hooks that can be defined in the `hooks.py` file. Post Transfer hooks are simple Python functions that get executed **after** the successful transfer of all TaskLayers. - -> **_NOTE:_** Post Transfer Hooks are only executed on a push from Asset Task to Asset Publish. **Not** on a pull. - -These hooks could do anything but the the intent of a post merge hook is to bring the asset in the correct publish state. These are usually repetitive steps that the task artist has to do to prepare data for publishing (and often revert it again for working). - -For an example config check out: `docs/production_config_example/hooks.py` - -Start by importing these classes. - -``` -import bpy - -from asset_pipeline.api import hook, Wildcard, DoNotMatch - -``` - -An example definition of a hook can look like this: - -``` -@hook(match_asset="Generic Sprite") -def my_hook(asset_collection: bpy.types.Collection, **kwargs) -> None: - pass - -``` - -You define a regular python function and decorate it with the **hook()** decorator. -Note: The decorator needs to be executed. - -The hook decorator as well as the function itself can specify arguments. - -Let's first have a look at the hook decorator. -The Idea is that you can use the hook decorator to - -first: Let the asset-pipeline know that this is an actual hook it should register - -second: to filter under which conditions the hook gets executed. - -For filtering you can use these key word arguments inside of the hook decorator braces: -- `match_asset_type` -- `match_asset match_asset` -- `match_task_layers` - -For each of these keys you can supply these values: -* `str`: would perform an exact string match. -* `Iterator[str]`: would perform an exact string match with any of the given strings. -* `Type[Wildcard]`: would match any type for this parameter. This would be used so a hook - is called for any value. -* `Type[DoNotMatch]`: would ignore this hook when matching the hook parameter. This is the default - value for the matching criteria and would normally not be set directly in a - production configuration. - -With that in mind let's look at some more example hooks: - -``` -@hook() -def test_hook_A(**kwargs) -> None: - pass -``` -This hook has no filtering parameters so it is considered to be a **global** hook that always gets executed. - -``` -@hook(match_asset_type="Character") -def test_hook_B(**kwargs) -> None: - pass -``` - -This hook will only be executed if current Asset is of type "Character". - - -``` -@hook(match_task_layers="ShadingTaskLayer") -def test_hook_C(**kwargs) -> None: - pass -``` - -This hook will only be executed if the Task Layer: "ShadingTaskLayer" was amongst the Task Layers that were selected for this transfer operation. - -``` -@hook(match_asset="Ellie") -def test_hook_D(**kwargs) -> None: - pass -``` -This hook will only be executed if the asset "Ellie" is processed. - -``` -@hook( - match_asset="Generic Sprite", - match_task_layers=["RiggingTaskLayer", "ShadingTaskLayer], -) -def test_hook_E(**kwargs) -> None: - pass -``` -This hook will only be executed if the asset "Generic Sprite" is processed and the "RiggingTaskLayer" or -"ShadingTaskLayer" was amongst the Task Layers that were selected for this transfer operation. - - - -It is important to note that the asset-pipeline follows a certain order to execute the hooks. And that is exactly the one of the examples hook described above: - -1. Global hooks -2. Asset Type Hooks -3. Task Layer Hooks -4. Asset Hooks -5. Asset + TaskLayer specific Hooks - -The function itself should always have **\*\*kwargs** as a parameter. The asset-pipeline automatically passes a couple of useful keyword arguments to the function: -- `asset_collection`: bpy.types.Collection -- `context`: bpy.types.Context -- `asset_task`: asset_pipeline.asset_files.AssetTask -- `asset_dir`: asset_pipeline.asset_files.AssetDir - -By exposing these parameters in the hook function you can use them in your code: -``` -@hook() -def test_hook_F(context: bpy.types.Context, asset_collection: bpy.types.Collection, **kwargs) -> None: - print(asset_collection.name) -``` - - - -## Getting Started as a Developer - -The asset-pipeline contains two main packages. - -1. **builder**: The Asset Builder which contains most of the core definitions and logic of Task Layers, Asset publishing, pulling, the import process for that and metadata handling. - -2. **updater**: The Asset Updater is quite light weight. It handles detecting imported asset collections and fetching available asset publishes. It also handles the logic of the actual updating. - -Both packages share a couple of modules that you can find on the top level. - -Let's have a closer look at the **builder** package. - -The Pipeline of **publishing** an Asset looks roughly like the following: - -- Loading a .blend file -- Creating a Production Context -- Creating an Asset Context -- Selecting TaskLayers to publish -- Start publish: Create Build Context -- Fetch all asset publishes and their metadata -- Apply changes: Pushes the selected TaskLayer to the affected asset publishes, updates metadata (In separate Blender instances) -- Publish: Finalizes the publish process, commits changes to svn. - -The Pipeline of **pulling** TaskLayers from the latest asset publish goes down roughly like this: -- Loading a .blend file -- Creating a Production Context -- Creating an Asset Context -- Creating Build Context -- Selecting TaskLayers to pull -- Pull: Pulls the selected TaskLayers from the latest Asset Publish in to the current Asset Task and updates metadata. - ---- - -### Context - -The asset-pipeline strongly works with Context objects, that get populated with -information and are used by the AssetBuilder to perform the actual logic of -building an Asset. - -There are 3 types of contexts: - -- **ProductionContext**: Global production level context, gets loaded on startup, -processes all the config files. This context collects all the TaskLayers and -registers TransferSettings that are defined in the `task_layers.py` config file. -It also searches for the `hooks.py` file and collects all valid hooks. - -- **AssetContext**: Local Asset Context, gets loaded on each scene load. Stores -settings and information for active Asset. It holds all information that are -related to the current Asset. This includes the current Asset Collection, Asset -Task, available Asset Publishes, the Asset Directory, the configuration of Task -Layers (which ones are enabled and disabled) and the Transfer Settings values. - -- **BuildContext**: Gets loaded when starting a publish or a pull. Contains both the -ProductionContext and AssetContext as well as some other data. Is the actual -context that gets processed by the AssetBuilder. - -A key feature is that we need to be able to 'exchange' this information with -another blend file. As the 'push' or publish process requires to: - -Open another blend file -> load the build context there -> process it -> close it again. - -This can be achieved by using the -[pickle](https://docs.python.org/3/library/pickle.html) library and pickle the -Contexts. All the contexts are pickle-able. The **\_\_setstate\_\_**, -**\_\_getstate\_\_** functions ensure that. - - -### UI - -All of this information that hides in these Context Objects needs to be partially visible for -Users in the UI. In the `props.py` module there are a whole lot of PropertyGroups that can store this -information with native Blender Properties to display it in the UI. - -This requires some sort of sync process between the Context and the PropertyGroups. -This sync process happens in a couple of places: - -- On startup -- On scene load -- On start publish -- After push task layers -- After abort publish -- After pull task layers -- After publish -- After updating statuses (metadata) - -Which PropertyGroups get updated depends a little bit on the operations. In general the asset-pipeline -only tries to update the parts that were altered and are therefore outdated. - -Not only are PropertyGroups updated by the Context objects, sometimes it also goes the other way. -For example: The last selected TaskLayers are saved on Scene level. On load this selection is restored, -which also updates the AssetContext. - -### Asset Collection - -Per task file there is only **one** Asset Collection. The Asset Collection and all its children and -dependencies is the final data that is being worked with in the Asset Builder. - -An Asset Collection needs to be initialized which fills out a whole lot of properties that get fetched from Kitsu. - -The properties are saved on the Collection at: - -`collection.bsp_asset` - -as a PropertyGroup. Some properties you can access via Python Scripts are: - -``` -entity_parent_id: bpy.props.StringProperty(name="Asset Type ID") -entity_parent_name: bpy.props.StringProperty(name="Asset Type") -entity_name: bpy.props.StringProperty(name="Asset Name") -entity_id: bpy.props.StringProperty(name="Asset ID") -project_id: bpy.props.StringProperty(name="Project ID") -is_publish: bpy.props.BoolProperty( - name="Is Publish", - description="Controls if this Collection is an Asset Publish to distinguish it from a 'working' Collection", -) -version: bpy.props.StringProperty(name="Asset Version") -publish_path: bpy.props.StringProperty(name="Asset Publish") -rig: bpy.props.PointerProperty(type=bpy.types.Armature, name="Rig") -``` - -### Asset Files - -Often we have to interact with files on disk and do the same operations over and -over again. For this consider using the: **asset_file.py** module. It contains -the **AssetTask**, **AssetPublish** and -**AssetDir** classes that are very useful and an important part of the System. - -In fact every interaction with asset files happens via these classes as they automatically load -metadata, which is in integral part of the pipeline. - - -### Metadata - -An asset file is always paired with a metadata file. The metadata file contains various information -about that particular asset file. It saves all the TaskLayers that are contained in this file and where -they came from. It also holds all kinds of information that make the Asset clearly identifiable. - -The AssetFile Classes automatically load this metadata on creation. - -The file format of this metadata is `xmp`. For that the asset-pipeline uses the `xml.etree` library. -In the `metadata.py` file are Schemas that represent the different Metadata blocks. - -The idea here is to have Schemas in the form of Python `dataclasses` that can be converted to their equivalent as XML Element. That way we have a clear definition of what kind of field are expected and available. -Schemas can have nested Data Classes. The conversion from Data Class to XML Element happens in the `ElementMetadata` class and is automated. -Metadata Classes can also be generated from ElementClasses. This conversion is happening in the `from_element()` function. - -The code base should only work with Data Classes as they are much easier to handle. -That means it is forbidden to import `Element[]` classes from `metadata.py`. -The conversion from and to Data Classes is only handled in this module. - -That results in this logic: -A: Saving Metadata to file: - -> MetadataClass -> ElementClass -> XML File on Disk -B: Loading Metadata from file: - -> XML File on Disk -> ElementClass -> MetadataClass - -### Asset Importer - -The `AssetImporter` is responsible for importing the right collections from the right source file -so the data transfer can happen as expected. -The output is a `TransferCollectionTriplet` which holds a reference to the collection from the AssetTask, collection from the AssetPublish and the actual target Collection on which the data is transferred. - -The target Collection is either a duplicate of the the AssetTask Collection or the AssetPublish Collection. -Which it is depends on a number of factors. Is it pull or a push and which Task Layers are selected. The exact logic is described in the [configuration](#configuration) section. - -The important takeaway here is that during a transfer we always have these 3 Collections present and each TaskLayer is either transferred from the AssetTask or the AssetPublish Collection to the Target. - -The logic of figuring out what needs to be target is happening in the AssetImporter. To avoid naming collisions the AssetImporter uses a suffix system. Each of the collections, all their children (including materials and node trees) receive a suffix during import. - -### Asset Mapping - -To transfer data we need a source and a target. Users can describe what should happen during this transfer for each Task Layer in the: - -``` -@classmethod -def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, -) -> None: -``` - -method. Users can have access to this `source` and `target` via the `transfer_mapping`. The TransferMapping is a class that has a couple of properties, which hold dictionaries. - -In these dictionaries the key is the source and the value the target. -Both key and target are actual Blender ID Datablocks. -This makes it easy to write Merge Instructions. -With it you can do access things like: - -``` -transfer_mapping.object_map: Dict[bpy.types.Object, bpy.types.Object] -transfer_mapping.collection_map: Dict[bpy.types.Collection, bpy.types.Collection] -transfer_mapping.material_map: Dict[bpy.types.Material, bpy.types.Material] -``` - -This TransferMapping is created in the AssetBuilder in the `pull_from_task` and `pull_from_publish` functions. We always create **2** mappings: - -- asset_task -> target -- asset_publish -> target - -And when we finally loop through all the TaskLayers we decide for each TaskLayer which mapping to use (which will decide if we either transfer from the AssetTask Collection to the target Collection or AssetPublish Collection to target Collection). -And that is the mapping we pass to `TaskLayer.transfer_data()`. - -> **_NOTE:_** If Users are adjusting the Mapping in a `transfer_data()` function they have to be aware that they are working with **2** mappings. - -### Asset Builder - -The AssetBuilder class contains the actual logic that can process the BuildContext. - -That means that this ist the final place where we call the AssetImporter to import all the Collections and create the TransferCollectionTriplet. We also create the AssetTransferMappings here, we make sure that all objects are visible, we load the metadata, we loop through all the TaskLayers and call their `transfer_data()` functions and finally update the metadata. - - -The Asset Builder contains 3 functions: - -- `push` -- `pull_from_publish` -- `pull_from_task` - -You might wonder why we have one push function and two pulls? -This is because the push process requires us to start another Blender Instance that opens a .blend file. This Blender Instance then actually performs a pull, a `pull_from_task`. - -The `push` function prepares everything so the `pull_from_task` can be called in the new Blender Instance. -It does a couple of things: - -- pickles the `BuildContext` to disk -- starts a new Blender Instance with a Python script as `-P` argument - -(It does this for all the affected publishes) - -This Python script is inside of the repository: `asset_pipeline/builder/scripts/push.py` - -The scripts basically just restores the BuildContext and calls the `pull_from_task` function. - - -### Asset Updater - -The Asset Updater is very light weight compared to the Asset Builder. - -The main process how the Asset Updater collects its data goes like this: - -1. Scanning Scene for found Assets -2. For each Asset check the Asset Publish Directory for all versions (Ignore Assets Publishes in Review State) - -The most trickiest part here is to save this information nicely in native Blender Properties. Checkout the `props.py` module if you want to have a look. - -The update process itself is very straightforward: -Calling the `bpy.ops.wm.lib_relocate()` operator. - - - diff --git a/scripts-blender/addons/asset_pipeline/TODO.txt b/scripts-blender/addons/asset_pipeline/TODO.txt deleted file mode 100644 index a6438bfc..00000000 --- a/scripts-blender/addons/asset_pipeline/TODO.txt +++ /dev/null @@ -1,84 +0,0 @@ -Here are some ideas, bugs, and TODOs for the Asset Pipeline. - -High prio bugs: - - Crashes when pulling in dog.modeling.blend - - Seems to nukes face sets when pulling into modeling. - - Pulling into rigging, SurfaceDeform modifiers fall asleep. - - Pulling into rigging, GeoNodes modifiers lose some of their inputs until the same nodetree is re-assigned. - - Pulling into rigging and I think also on pushing, the Copy Location constraint targetting the zipper helper mesh ends up targetting the rig instead. I tried investigating this already but I just don't get it. - - Pulling into rigging after a mesh update, material assignments seem to break until pulling a 2nd time. - - -Low Prio: - Bugs: - - "Create production Context" (Refresh icon under Publish Manager panel) segfaults. - - If reloading the file mid-publish, Apply Changes button throws "StructRNA has been removed". - - If trying to push from an unsaved file, the changes since the last save won't be pushed. This is fine, but there should be an indication of it. - - I think all of these would be fixed by the "Sync" button idea. - - TODOs: - - Setting an asset to Deprecated should set all task layers to Locked. - - Asset status list seems to not show all versions until refresh button is pressed? - - We should update asset statuses as an early stage of the Publish process, to avoid potentially pushing into deprecated versions (if somebody else deprecates a version, we SVN update, but don't manually refresh or reload). - - Asset Updater: - - Don't fully ignore versions when their status is Review. Allow them to be manually selected at least. - - Also display the asset status(Review/Deprecated/Approved) in the version number enum drop-down. - - Is there a missing Purge at the end of update_asset()? - - Make the UI prettier and less confusing. - - Code quality: - - Looks like the generate_mapping() call could be moved out of task layers and into generic. - - De-duplicating pull_from_task and pull_from_publish would probably be pretty great. - - -## Idea: "Sync" instead of "Push/Pull": - Instead of the "push/pull" mental model that we currently have, I propose a "Sync" mental model. The "Sync" button would: - - Save a backup of the current file in the user's Autosave folder. - - Pull from Publish. - - Save the current file. - - Delete all collections and objects beside the asset collection. - - "Save As" to overwrite the publish. - - Open the original file. - - Benefits: - - No more opening a Blender subprocess in the background, which makes issues hard to troubleshoot. - - Files are always forced to stay in sync, because you can't push without pulling. - - Half the time spent on pushing and pulling, since it's only done once for two files. - - What you see is what you get: You can be confident that whatever lands in your asset collection is exactly what's in the publish as well. - - Downsides: - - Any "cleanup" operations done on the asset will now be done on the working file, such as un-assigning actions from rigs. (This could probably be accounted for at the cost of sacrificing the "Shat you see is what you get" benefit.) - - If the Asset Pipeline is broken, now your working file will be broken as well, instead of just the publish. (Hence the back-up as the first step) - - Hopefully this idea is still compatible with syncing multiple versions and accounting for locked task layers. - - -## Idea: Object ownership by Task Layer - A feature that was added after Paul left, was the ability for Task Layers to affect collection assingments. Relevant code is `transfer_collection_objects()`. The current behaviour and code are both crazy confusing; Any Task Layer can add objects to its collection (eg. Rigging can add objects to einar.rigging), but they can't remove them unless there's a special suffix in the colleciton name, ".FULLY_OWNED". This was obviously implemented in a rush, we needed it working on the day of, or we couldn't get the job done. - - All this code and behaviour can be thrown away in favor of something better. - - My proposal: - - Approach the whole system with an "override" mental model. - - An object is "owned" by the lowest-index task layer that it's assigned to. (rigging==0) - - If the object is assigned to other task layers, those task layers are "overriding" the aspects of the object that correspond to that task layer. - - This means that most objects will be assigned to most sub-collections, and that's okay! - - - A task layer can add and remove objects from its influence, but not add or remove objects from other task layers' influence. - - If an object is only assigned to a single task layer, don't transfer any data to it. - - If an object is in two task layer collections, determine which one is source and target, and transfer data accordingly. - - For example, if an object is assigned to two task layers(eg. rigging+shading), take the object from the task layer with lower index (rigging==0) and transfer the data of the higher index task layer to it. - - Although, I'm not sure how this will work if a task layer is locked. - -## Idea: Sanity Check panel - Would be cool (even as a separate addon) to add a "sanity check" button & panel that can warn about: - - Datablock in file but not referenced by current view layer - - Mesh/Armature datablock not named same as container object - - Datablock has .00x name ending - - Datablock has .TASK/.TARGET/etc ending - - Display a list of all datablocks per type, and show what other datablocks are referencing that one. Clicking on those sets the list filter to their datablock type and makes their entry the active one. - - Draw the User Remap operator or a masked version of it (since Objects might need to be removed from the View Layer before being user remapped) - - This would be quite similar to CloudRig's "Generation Log" list, that gets filled with warnings by the Generate button, with information about potential issues with a generated rig. diff --git a/scripts-blender/addons/asset_pipeline/__init__.py b/scripts-blender/addons/asset_pipeline/__init__.py deleted file mode 100644 index c39860f4..00000000 --- a/scripts-blender/addons/asset_pipeline/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -import bpy - -import importlib - -from . import prefs, util, props, api, builder, updater - -bl_info = { - "name": "Asset Pipeline", - "author": "Paul Golter", - "description": "Blender Studio Asset Pipeline Add-on", - "blender": (3, 1, 0), - "version": (0, 1, 2), - "location": "View3D", - "warning": "", - "doc_url": "", - "tracker_url": "", - "category": "Generic", -} - -logger = logging.getLogger("BSP") - - -def reload() -> None: - global util - global prefs - global props - global api - global builder - global updater - - importlib.reload(util) - importlib.reload(prefs) - importlib.reload(props) - importlib.reload(api) - - builder.reload() - updater.reload() - - -_need_reload = "prefs" in locals() -if _need_reload: - reload() - -# ----------------REGISTER--------------. - - -def register() -> None: - prefs.register() - props.register() - builder.register() - updater.register() - - -def unregister() -> None: - builder.unregister() - updater.unregister() - props.unregister() - prefs.unregister() diff --git a/scripts-blender/addons/asset_pipeline/api/__init__.py b/scripts-blender/addons/asset_pipeline/api/__init__.py deleted file mode 100644 index 079e2991..00000000 --- a/scripts-blender/addons/asset_pipeline/api/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -from ..builder.context import BuildContext -from ..builder.task_layer import TaskLayer -from ..builder.asset_mapping import AssetTransferMapping -from ..builder.hook import hook, Wildcard, DoNotMatch -from ..builder.vis import EnsureObjectVisibility, EnsureCollectionVisibility - -__all__ = ["TaskLayer", - "BuildContext", - "AssetTransferMapping", - "hook", - "Wildcard", - "DoNotMatch", - "EnsureObjectVisibility", - "EnsureCollectionVisibility", - ] diff --git a/scripts-blender/addons/asset_pipeline/asset_files.py b/scripts-blender/addons/asset_pipeline/asset_files.py deleted file mode 100644 index 59a672fc..00000000 --- a/scripts-blender/addons/asset_pipeline/asset_files.py +++ /dev/null @@ -1,282 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import re -import shutil -import logging - -from typing import List, Dict, Union, Any, Set, Optional -from pathlib import Path - -import bpy - -from . import constants -from .builder import metadata -from .builder.metadata import MetadataTreeAsset -from .asset_status import AssetStatus - -logger = logging.getLogger("BSP") - - -class FailedToIncrementLatestPublish(Exception): - pass - - -class FailedToLoadMetadata(Exception): - pass - - -class AssetFile: - def __init__(self, asset_path: Path): - self._path = asset_path - self._metadata_path = ( - asset_path.parent / f"{asset_path.stem}{constants.METADATA_EXT}" - ) - self._metadata: Optional[MetadataTreeAsset] = None - self._load_metadata() - - @property - def path(self) -> Path: - return self._path - - @property - def metadata_path(self) -> Path: - return self._metadata_path - - @property - def metadata(self) -> MetadataTreeAsset: - return self._metadata - - def write_metadata(self) -> None: - metadata.write_asset_metadata_tree_to_file(self.metadata_path, self.metadata) - - def reload_metadata(self) -> None: - if not self.metadata_path.exists(): - raise FailedToLoadMetadata( - f"Metadata file does not exist: {self.metadata_path.as_posix()}" - ) - self._load_metadata() - - @property - def pickle_path(self) -> Path: - return self.path.parent / f"{self.path.stem}.pickle" - - def __repr__(self) -> str: - return self._path.name - - def _load_metadata(self) -> None: - # Make AssetPublish initializeable even tough - # metadata file does not exist. - # Its handy to use this class for in the 'future' - # existing files, to query paths etc. - if not self.metadata_path.exists(): - logger.warning( - f"Metadata file does not exist: {self.metadata_path.as_posix()}" - ) - return - - self._metadata = metadata.load_asset_metadata_tree_from_file(self.metadata_path) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AssetFile): - raise NotImplementedError() - - return bool(self.path == other.path) - - def __hash__(self) -> int: - return hash(self.path) - - -class AssetTask(AssetFile): - """ - Represents a working file. - """ - - @property - def asset_dir(self) -> "AssetDir": - return AssetDir(self.path.parent) - - @property - def path_relative_to_asset_dir(self) -> Path: - return self._path.relative_to(self.asset_dir.path) - - -class AssetPublish(AssetFile): - """ - Represents a publish file. - """ - - def get_version(self, format: type = str) -> Optional[Union[str, int]]: - return get_file_version(self.path, format=format) - - def unlink(self) -> None: - """ - Caution: This will delete the file and the metadata file of this asset publish on disk. - """ - self.metadata_path.unlink() - self.path.unlink() - - @property - def asset_dir(self) -> "AssetDir": - return AssetDir(self.path.parent.parent) - - @property - def path_relative_to_asset_dir(self) -> Path: - return self._path.relative_to(self.asset_dir.path) - - -class AssetDir: - def __init__(self, path: Path): - self._path = path - # Directory name should match asset name - self._asset_disk_name = path.name - - @property - def path(self) -> Path: - return self._path - - @property - def asset_disk_name(self) -> str: - return self._asset_disk_name - - @property - def publish_dir(self) -> Path: - return self._path / "publish" - - def get_asset_publishes(self) -> List[AssetPublish]: - # Asset Naming Convention: {asset_name}.{asset_version}.{suffix} - # TODO: if asset_dir.name == asset.name we could use this logic here - if not self.publish_dir.exists(): - return [] - - blend_files = get_files_by_suffix(self.publish_dir, ".blend") - asset_publishes: List[AssetPublish] = [] - - for file in blend_files: - file_version = get_file_version(file) - if not file_version: - continue - - t = file.stem # Without suffix - t = t.replace(f".{file_version}", "") # Without version string - - # It it matches asset name now, it is an official publish. - if t != self._asset_disk_name: - continue - - asset_publishes.append(AssetPublish(file)) - - # Sort asset publishes after their 'version' ascending -> v001, v002, v003 - def get_publish_version(asset_publish: AssetPublish) -> int: - return asset_publish.get_version(format=int) - - asset_publishes.sort(key=get_publish_version) - return asset_publishes - - def increment_latest_publish(self) -> AssetPublish: - asset_publishes = self.get_asset_publishes() - if not asset_publishes: - raise FailedToIncrementLatestPublish( - f"No publishes available in: {self.publish_dir.as_posix()}" - ) - - latest_publish = asset_publishes[-1] - new_version = f"v{(latest_publish.get_version(format=int)+1):03}" - - # Duplicate blend and metadata file. - # Have metadata_path first so new_path is the one with .blend. - for path in [latest_publish.metadata_path, latest_publish.path]: - new_name = path.name.replace(latest_publish.get_version(), new_version) - new_path = latest_publish.path.parent / new_name - - if new_path.exists(): - raise FailedToIncrementLatestPublish( - f"Already exists: {new_path.as_posix()}" - ) - - shutil.copy(path, new_path) - logger.info(f"Copied: {path.name} to: {new_path.name}") - - new_publish = AssetPublish(new_path) - - # Update metadata. - new_publish.metadata.meta_asset.version = new_version - - # Set new status to review. - new_publish.metadata.meta_asset.status = AssetStatus.REVIEW.name - - # Set all task layers of new version to live. - for meta_tl in new_publish.metadata.meta_task_layers: - meta_tl.is_locked = False - - # Write metadata to disk. - new_publish.write_metadata() - - return new_publish - - def get_first_publish_path(self) -> Path: - filename = f"{self.asset_disk_name}.v001.blend" - return self.publish_dir / filename - - def __repr__(self) -> str: - publishes = ", ".join(str(a) for a in self.get_asset_publishes()) - return f"{self.asset_disk_name} (Publishes:{str(publishes)})" - - -def get_asset_disk_name(asset_name: str) -> str: - """ - Converts Asset Name that is stored on Kitsu to a - adequate name for the filesystem. Replaces spaces with underscore - and lowercases all. - """ - return asset_name.lower().replace(" ", "_") - - -def get_file_version(path: Path, format: type = str) -> Optional[Union[str, int]]: - """ - Detects if file has versioning pattern "v000" and returns that version. - Returns: - str: if file version exists - bool: False if no version was detected - """ - match = re.search("v(\d\d\d)", path.name) - if not match: - return None - - version = match.group(0) - - if format == str: - return version - - elif format == int: - return int(version.replace("v", "")) - - else: - raise ValueError(f"Unsupported format {format} expected: int, str.") - - -def get_files_by_suffix(dir_path: Path, suffix: str) -> List[Path]: - """ - Returns a list of paths that match the given ext in folder. - Args: - ext: String of file extensions eg. ".txt". - Returns: - List of Path() objects that match the ext. Returns empty list if no files were found. - """ - return [p for p in dir_path.iterdir() if p.is_file() and p.suffix == suffix] diff --git a/scripts-blender/addons/asset_pipeline/asset_status.py b/scripts-blender/addons/asset_pipeline/asset_status.py deleted file mode 100644 index 96dce91a..00000000 --- a/scripts-blender/addons/asset_pipeline/asset_status.py +++ /dev/null @@ -1,37 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from pathlib import Path -from enum import Enum, auto - -import bpy - - -class AssetStatus(Enum): - REVIEW = 0 - APPROVED = 1 - DEPRECATED = 2 - - -def get_asset_status_as_bl_enum( - self: bpy.types.Operator, context: bpy.types.Context -) -> List[Tuple[str, str, str]]: - return [(str(item.value), item.name.capitalize(), "") for item in AssetStatus] diff --git a/scripts-blender/addons/asset_pipeline/builder/__init__.py b/scripts-blender/addons/asset_pipeline/builder/__init__.py deleted file mode 100644 index 6ec73503..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import importlib - -from typing import List, Dict, Union, Any, Set, Optional - -from . import ops, ui -from .context import ProductionContext, AssetContext, BuildContext, UndoContext -from .asset_builder import AssetBuilder - -# Initialize building variables. -PROD_CONTEXT: Optional[ProductionContext] = None -ASSET_CONTEXT: Optional[AssetContext] = None -BUILD_CONTEXT: Optional[BuildContext] = None -ASSET_BUILDER: Optional[AssetBuilder] = None -UNDO_CONTEXT: Optional[UndoContext] = None - -# ----------------REGISTER--------------. - - -def reload() -> None: - global ops - global ui - - importlib.reload(ops) - importlib.reload(ui) - - -def register() -> None: - ops.register() - ui.register() - - -def unregister() -> None: - ui.unregister() - ops.unregister() diff --git a/scripts-blender/addons/asset_pipeline/builder/asset_builder.py b/scripts-blender/addons/asset_pipeline/builder/asset_builder.py deleted file mode 100644 index 06413d87..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/asset_builder.py +++ /dev/null @@ -1,612 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import pickle -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple, Callable -from pathlib import Path -from datetime import datetime - -import bpy - -from . import asset_suffix, metadata, meta_util -from .context import BuildContext -from .asset_importer import AssetImporter -from .asset_mapping import TransferCollectionTriplet, AssetTransferMapping -from .blstarter import BuilderBlenderStarter -from .metadata import MetadataTaskLayer, MetadataTreeAsset -from .hook import HookFunction - -from .. import constants, util -from ..asset_files import AssetPublish - -logger = logging.getLogger("BSP") - - -class AssetBuilderFailedToInitialize(Exception): - pass - - -class AssetBuilderFailedToPull(Exception): - pass - - -class AssetBuilderFailedToPublish(Exception): - pass - - -class AssetBuilder: - """ - The AssetBuilder contains the actual logic how to process the BuildContext. - It has 3 main functions: - - push: Starts process of opening a new Blender Instance and pickling the BuildContext. New Blender Instance - actually then loads the BuildContext and calls AssetBuilder.pull_from_task(). - - pull_from_publish: Pulls the selected TaskLayers from the AssetPublish in to the current AssetTask. - Does not require a new Blender Instance. - - pull_from_task: Pulls the selected TaskLayers from the AssetTask in to the current AssetPublish. - """ - - def __init__(self, build_context: BuildContext): - if not build_context: - raise AssetBuilderFailedToInitialize( - "Failed to initialize AssetBuilder. Build_context not valid." - ) - - self._build_context = build_context - self._asset_importer = AssetImporter(self._build_context) - self._transfer_settings = bpy.context.scene.bsp_asset_transfer_settings - - @property - def build_context(self) -> BuildContext: - return self._build_context - - @property - def asset_importer(self) -> AssetImporter: - return self._asset_importer - - @property - def transfer_settings(self) -> bpy.types.PropertyGroup: - return self._transfer_settings - - def push(self, context: bpy.types.Context) -> None: - """ - Starts process of opening a new Blender Instance and pickling the BuildContext. New Blender Instance - actually then loads the BuildContext and calls AssetBuilder.pull_from_task(). That means pickling the BuildContext - and restoring it in the other Blender Instance. - """ - - # No here it gets a little tricky. We cannot just simply - # perform a libraries.write() operation. The merge process - # requires additional operations to happen so we need to actually - # open the asset version blend file and perform them. - - # Now we already assembled this huge BuildContext, in which we have - # all the information we need for whatever needs to be done. - # The question is how can we share this info with the new Blender Instance - # that knows nothing about it. - - # A very effective and easy ways seems to be pickling the BuildContext - # and unpickling it in the new Blender Instance again. - # Some objects cannot be pickled (like the blender context or a collection) - # (We can add custom behavior to work around this please see: ./context.py) - - # Catch special case first version. - if not self.build_context.asset_publishes: - asset_publish = self._create_first_version() - - # Start pickling. - pickle_path = asset_publish.pickle_path - with open(pickle_path.as_posix(), "wb") as f: - pickle.dump(self.build_context, f) - - logger.info(f"Pickled to {pickle_path.as_posix()}") - - # Open new blender instance, with publish script. - # Publish script can detect a first version publish and performs - # a special set of operations. - BuilderBlenderStarter.start_publish( - asset_publish.path, - pickle_path, - ) - return - - # Normal publish process. - for process_pair in self.build_context.process_pairs: - - asset_publish = process_pair.asset_publish - - logger.info("Processing %s", asset_publish.path.as_posix()) - - # Start pickling. - pickle_path = ( - asset_publish.pickle_path - ) # TODO: Do we need a pickle for all of them? I think one would be enough. - with open(pickle_path.as_posix(), "wb") as f: - pickle.dump(self.build_context, f) - logger.info(f"Pickled to {pickle_path.as_posix()}") - - # Open new blender instance, with publish script. - popen = BuilderBlenderStarter.start_publish( - asset_publish.path, - pickle_path, - ) - return_code = popen.wait() - - # Update returncode property. This will be displayed - # as icon in the UI and shows Users if something went wrong - # during push. - asset_file = context.scene.bsp_asset.asset_publishes.get( - asset_publish.path.name - ) - asset_file.returncode_publish = return_code - print(f"Set {asset_file.path_str} to returncode {return_code}") - if return_code != 0: - logger.error( - "Push to %s exited with error code: %i", - asset_publish.path.name, - return_code, - ) - - def pull_from_publish( - self, - context: bpy.types.Context, - ) -> None: - - """ - Pulls the selected TaskLayers from the AssetPublish in to the current AssetTask. - """ - - # Here we don't need to open another blender instance. We can use the current - # one. We pull in the asset collection from the latest asset publish and - # perform the required data transfers depending on what was selected. - - # Set is_push attribute. - self.build_context.is_push = False - - # User does a pull. This code runs in AssetTask file. - # Check if there are any publishes. - if not self.build_context.asset_publishes: - raise AssetBuilderFailedToPull(f"Failed to pull. Found no asset publishes.") - - # We always want to pull from latest asset publish. - asset_publish = self.build_context.asset_publishes[-1] - - # Import Asset Collection form Asset Publish. - transfer_triplet: TransferCollectionTriplet = ( - self.asset_importer.import_asset_publish() - ) - - # The target collection (base) was already decided by ASSET_IMPORTER.import_asset_task() - # and is saved in transfer_triplet.target_coll. - mapping_task_target = AssetTransferMapping( - transfer_triplet.task_coll, transfer_triplet.target_coll - ) - mapping_publish_target = AssetTransferMapping( - transfer_triplet.publish_coll, transfer_triplet.target_coll - ) - - # Process only the TaskLayers that were ticked as 'use'. - used_task_layers = ( - self.build_context.asset_context.task_layer_assembly.get_used_task_layers() - ) - # Should be ordered, just in case. - prod_task_layers = self.build_context.prod_context.task_layers - prod_task_layers.sort(key=lambda tl: tl.order) - - transfer_triplet.reset_rigs() - # Apparently Blender does not evaluate objects or collections in the depsgraph - # in some cases if they are not visible. Ensure visibility here. - transfer_triplet.ensure_vis() - - # Perform Task Layer merging. - # Note: We always want to apply all TaskLayers except for the Task Layer with the lowest order - # aka 'Base Task Layer'. This Task Layer gives us the starting point on which to apply all other Task Layers - # on. The asset importer already handles this logic by supplying as with the right TARGET collection - # after import. That's why we could exclude the first task layer here in the loop. - # But people at the Studio pointed out it might still be useful sometimes to still let - # this task layer run the transfer() functions as there can be cases like: - # Prefixing modififers that are coming from a task layer with the task layer name. - logger.info(f"Using {prod_task_layers[0].name} as base.") - - # If metafile does not exist yet create it. - metadata_path = self.build_context.asset_task.metadata_path - if not metadata_path.exists(): - tree = self._create_asset_metadata_tree_from_collection() - metadata.write_asset_metadata_tree_to_file(metadata_path, tree) - logger.info("Created metadata file: %s", metadata_path.name) - del tree - - # Otherwise load it from disk. - meta_asset_tree = metadata.load_asset_metadata_tree_from_file(metadata_path) - - # Get time for later metadata update. - time = datetime.now() - - for task_layer in prod_task_layers: - - # Get metadata task layer for current task layer. - meta_tl = meta_asset_tree.get_metadata_task_layer(task_layer.get_id()) - - # Task Layer might not exist in metadata if it was added midway production - # if so add it here. - if not meta_tl: - logger.warning( - "Detected TaskLayer that was not in metadata file yet: %s. Will be added.", - task_layer.get_id(), - ) - meta_tl = meta_util.init_meta_task_layer(task_layer, asset_publish) - meta_asset_tree.add_metadata_task_layer(meta_tl) - - # Transfer selected task layers from Publish Coll -> Target Coll. - if task_layer in used_task_layers: - - logger.info( - f"Transferring {task_layer.name} from {transfer_triplet.publish_coll.name} to {transfer_triplet.target_coll.name}." - ) - task_layer.transfer( - context, self.build_context, mapping_publish_target, self.transfer_settings - ) - - # Update source meta task layer source path. - # Save path relative to asset directory, otherwise we have system paths in the start - # which might differ on various systems. - meta_tl.source_path = ( - asset_publish.path_relative_to_asset_dir.as_posix() - ) - meta_tl.updated_at = time.strftime(constants.TIME_FORMAT) - - # Transfer unselected task layers from Task Coll -> Target Coll. Retain them. - else: - logger.info( - f"Transferring {task_layer.name} from {transfer_triplet.task_coll.name} to {transfer_triplet.target_coll.name}." - ) - task_layer.transfer( - context, self.build_context, mapping_task_target, self.transfer_settings - ) - - # Here we don't want to update source path, we keep it as is, as we are just 'retaining' here. - - # Cleanup transfer. - self._clean_up_transfer(context, transfer_triplet) - - # Save updated metadata. - metadata.write_asset_metadata_tree_to_file(metadata_path, meta_asset_tree) - - def pull_from_task( - self, - context: bpy.types.Context, - ) -> None: - - """ - Pulls the selected TaskLayers from the AssetTask in to the current AssetPublish. - """ - # Set is_push attribute. - self.build_context.is_push = True - - # User does a publish/push. This code runs ins AssetPublish file. - # Import Asset Collection from Asset Task. - transfer_triplet: TransferCollectionTriplet = ( - self.asset_importer.import_asset_task() - ) - asset_publish = AssetPublish(Path(bpy.data.filepath)) - metadata_path = asset_publish.metadata_path - locked_task_layer_ids = asset_publish.metadata.get_locked_task_layer_ids() - meta_asset_tree = metadata.load_asset_metadata_tree_from_file(metadata_path) - - transfer_triplet.reset_rigs() - # Ensure visibility for depsgraph evaluation. - transfer_triplet.ensure_vis() - - # The target collection (base) was already decided by ASSET_IMPORTER.import_asset_task() - # and is saved in transfer_triplet.target_coll. - mapping_task_target = AssetTransferMapping( - transfer_triplet.task_coll, transfer_triplet.target_coll - ) - mapping_publish_target = AssetTransferMapping( - transfer_triplet.publish_coll, transfer_triplet.target_coll - ) - - # Process only the TaskLayers that were ticked as 'use'. - used_task_layers = ( - self.build_context.asset_context.task_layer_assembly.get_used_task_layers() - ) - # Should be ordered, just in case. - prod_task_layers = self.build_context.prod_context.task_layers - prod_task_layers.sort(key=lambda tl: tl.order) - - # Perform Task Layer merging. - - # Note: We always want to apply all TaskLayers except for the Task Layer with the lowest order - # aka 'Base Task Layer'. This Task Layer gives us the starting point on which to apply all other Task Layers - # on. The asset importer already handles this logic by supplying as with the right TARGET collection - # after import. That's why we could exclude the first task layer here in the loop. - # But people at the Studio pointed out it might still be useful sometimes to still let - # this task layer run the transfer() functions as there can be cases like: - # Prefixing modififers that are coming from a task layer with the task layer name. - logger.info(f"Using {prod_task_layers[0].name} as base.") - - # Get time for later metadata update. - time = datetime.now() - - for task_layer in prod_task_layers: - - # Get metadata task layer for current task layer. - meta_tl = meta_asset_tree.get_metadata_task_layer(task_layer.get_id()) - - # Task Layer might not exist in metadata if it was added midway production - # if so add it here. - if not meta_tl: - logger.warning( - "Detected TaskLayer that was not in metadata file yet: %s. Will be added.", - task_layer.get_id(), - ) - meta_tl = meta_util.init_meta_task_layer( - task_layer, self.build_context.asset_task - ) - meta_asset_tree.add_metadata_task_layer(meta_tl) - - # Transfer selected task layers from AssetTask Coll -> Target Coll. - # Skip any Task Layers that are locked in this AssetPublish. - # We have to do this check here because Users can push multiple Task Layer at - # the same time. Amongst the selected TaskLayers there could be some locked and some live - # in this asset publish. - if ( - task_layer in used_task_layers - and task_layer.get_id() not in locked_task_layer_ids - ): - logger.info( - f"Transferring {task_layer.name} from {transfer_triplet.task_coll.name} to {transfer_triplet.target_coll.name}." - ) - - task_layer.transfer( - context, self.build_context, mapping_task_target, self.transfer_settings - ) - - # Update source meta task layer source path. - # Save path relative to asset directory, otherwise we have system paths in the start - # which might differ on various systems. - meta_tl.source_path = ( - self.build_context.asset_task.path_relative_to_asset_dir.as_posix() - ) - meta_tl.updated_at = time.strftime(constants.TIME_FORMAT) - - else: - # Transfer unselected task layers from Publish Coll -> Target Coll. Retain them. - logger.info( - f"Transferring {task_layer.name} from {transfer_triplet.publish_coll.name} to {transfer_triplet.target_coll.name}." - ) - task_layer.transfer( - context, self.build_context, mapping_publish_target, self.transfer_settings - ) - - # Here we don't want to update source path, we keep it as is, as we are just 'retaining' here. - - # Cleanup transfer. - self._clean_up_transfer(context, transfer_triplet) - - # Save updated metadata. - metadata.write_asset_metadata_tree_to_file(metadata_path, meta_asset_tree) - - # Update asset collection properties. - context.scene.bsp_asset.asset_collection.bsp_asset.update_props_by_asset_publish( - asset_publish - ) - - # Run hook phase. - self._run_hooks(context) - - @staticmethod - def _remap_users(context): - """ - When objects inside the asset collection reference datablocks outside of - the asset collection or vice versa, some duplication can occur, as - outside objects end up with a .TASK suffix, and they end up referencing - objects that are no longer linked to the scene. - - Objects inside the asset collection correctly lose their suffix, but - also end up referencing outside objects without the suffix, which are - actually the wrong ones. - - So this function remaps references such that everything inside and outside - the asset collection reference each other once again, and removes - any leftover .TASK suffixes. - """ - - suf = constants.TASK_SUFFIX - for datablock in bpy.data.user_map(): - has_type = hasattr(datablock, 'type') - if has_type and datablock.type == 'OBJECT' \ - and datablock.name not in context.scene.objects: - # Objects that aren't in the scene have been replaced by the pull - # process, so we don't want to remap any references to them. - continue - storage = util.get_storage_of_id(datablock) - if not datablock.name.endswith(suf): - continue - - without_suffix = datablock.name.replace(suf, "") - other_db = storage.get(without_suffix) - if not other_db: - continue - - # print(f'REMAP USERS: "{other_db.name}" -> "{datablock.name}"') - other_db.user_remap(datablock) - # Rename the object to make its name available. - # This datablock should get purged soon, otherwise it's a bug. - other_db.name += "_Users_Remapped" - datablock.name = without_suffix - - # Since this process can leave unused datablocks behind, let's purge. - bpy.ops.outliner.orphans_purge(do_recursive=True) - - def _clean_up_transfer( - self, context: bpy.types.Context, transfer_triplet: TransferCollectionTriplet - ): - """ - Cleans up the transfer by removing the non target collection in the merge triplet, restoring - the visibilities as well as purging all orphan data. It also removes the suffixes from the target - collection and sets the asset collection. - """ - # Restore Visibility. - transfer_triplet.restore_vis() - - # Remove non TARGET collections. - for coll in [transfer_triplet.publish_coll, transfer_triplet.task_coll]: - util.del_collection(coll) - - # Purge orphan data. - # This is quite an important one, if this goes wrong we can end up with - # wrong data block names. - bpy.ops.outliner.orphans_purge(do_recursive=True) - - # Enable armature poses - for ob in transfer_triplet.target_coll.all_objects: - if ob.type != 'ARMATURE': - continue - ob.data.pose_position = 'POSE' - - # Remove suffix from TARGET Collection. - asset_suffix.remove_suffix_from_hierarchy(transfer_triplet.target_coll) - - self._remap_users(context) - - # Remove transfer suffix. - transfer_triplet.target_coll.bsp_asset.transfer_suffix = "" - - # Restore scenes asset collection. - context.scene.bsp_asset.asset_collection = transfer_triplet.target_coll - - def _run_hooks(self, context: bpy.types.Context) -> None: - - if not self.build_context.prod_context.hooks: - logger.info("No hooks to run") - return - - asset_coll = context.scene.bsp_asset.asset_collection - asset_data = asset_coll.bsp_asset - params = self.build_context.get_hook_kwargs(context) - hooks_to_run: Set[HookFunction] = set() - - # Collect global hooks first. - for hook in self.build_context.prod_context.hooks.filter(): - hooks_to_run.add(hook) - - # Collect asset type hooks. - for hook in self.build_context.prod_context.hooks.filter( - match_asset_type=asset_data.entity_parent_name, - ): - hooks_to_run.add(hook) - - # Collect Global Layer Hooks. - # We have to loop through each task layer here, can't give filter() function - # a list as one of the input parameters. - for ( - task_layer_id - ) in ( - self.build_context.asset_context.task_layer_assembly.get_used_task_layer_ids() - ): - for hook in self.build_context.prod_context.hooks.filter( - match_task_layers=task_layer_id, - ): - hooks_to_run.add(hook) - - # Collect asset hooks. - for hook in self.build_context.prod_context.hooks.filter( - match_asset=asset_data.entity_name, - ): - hooks_to_run.add(hook) - - # Collect asset + task layer specific hooks. - for ( - task_layer_id - ) in ( - self.build_context.asset_context.task_layer_assembly.get_used_task_layer_ids() - ): - for hook in self.build_context.prod_context.hooks.filter( - match_asset=asset_data.entity_name, - match_task_layers=task_layer_id, - ): - hooks_to_run.add(hook) - - # Run actual hooks. - for hook in hooks_to_run: - hook(**params) - - # Purge again. - bpy.ops.outliner.orphans_purge(do_recursive=True) - - def _create_first_version(self) -> AssetPublish: - first_publish = AssetPublish( - self._build_context.asset_dir.get_first_publish_path() - ) - asset_coll = self._build_context.asset_context.asset_collection - data_blocks = set((asset_coll,)) - - # Check if already exists. - if first_publish.path.exists(): - raise AssetBuilderFailedToPublish( - f"Failed to create first publish. Already exist: {first_publish.path.name}" - ) - - # Create asset meta tree. - asset_metadata_tree = self._create_asset_metadata_tree_from_collection() - - # Adjust version metadata. - asset_metadata_tree.meta_asset.version = first_publish.get_version() - - # Create directory if not exist. - first_publish.path.parent.mkdir(parents=True, exist_ok=True) - - # Save asset tree. - metadata.write_asset_metadata_tree_to_file( - first_publish.metadata_path, asset_metadata_tree - ) - - # Create blend file. - bpy.data.libraries.write( - first_publish.path.as_posix(), - data_blocks, - path_remap="RELATIVE_ALL", - fake_user=True, - ) - - logger.info("Created first asset version: %s", first_publish.path.as_posix()) - return first_publish - - def _create_asset_metadata_tree_from_collection(self) -> MetadataTreeAsset: - # Create asset meta tree. - meta_asset = ( - self.build_context.asset_context.asset_collection.bsp_asset.gen_metadata_class() - ) - meta_task_layers: List[MetadataTaskLayer] = [] - - for task_layer in self.build_context.prod_context.task_layers: - meta_tl = meta_util.init_meta_task_layer( - task_layer, self.build_context.asset_task - ) - meta_task_layers.append(meta_tl) - - meta_tree_asset = MetadataTreeAsset( - meta_asset=meta_asset, meta_task_layers=meta_task_layers - ) - return meta_tree_asset diff --git a/scripts-blender/addons/asset_pipeline/builder/asset_importer.py b/scripts-blender/addons/asset_pipeline/builder/asset_importer.py deleted file mode 100644 index ee21c9f0..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/asset_importer.py +++ /dev/null @@ -1,317 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import logging -import uuid -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from pathlib import Path - -import bpy - -from . import asset_suffix -from .context import BuildContext -from .asset_mapping import TransferCollectionTriplet - -from .. import constants -from ..asset_files import AssetPublish - -logger = logging.getLogger("BSP") - - -class FileExistsError(Exception): - pass - - -class ImportFailed(Exception): - pass - - -def import_data_from_lib( - libpath: Path, - data_category: str, - data_name: str, - link: bool = False, -) -> Any: - - noun = "Appended" - if link: - noun = "Linked" - - with bpy.data.libraries.load(libpath.as_posix(), relative=True, link=link) as ( - data_from, - data_to, - ): - - if data_name not in eval(f"data_from.{data_category}"): - raise ImportFailed( - f"Failed to import {data_category} {data_name} from {libpath.as_posix()}. Doesn't exist in file.", - ) - - # Check if datablock with same name already exists in blend file. - try: - eval(f"bpy.data.{data_category}['{data_name}']") - except KeyError: - pass - else: - raise ImportFailed( - f"{data_name} already in bpy.data.{data_category} of this blendfile.", - ) - - # Append data block. - eval(f"data_to.{data_category}.append('{data_name}')") - logger.info( - "%s: %s from library: %s", - noun, - data_name, - libpath.as_posix(), - ) - - if link: - return eval( - f"bpy.data.{data_category}['{data_name}', '{bpy.path.relpath(libpath.as_posix())}']" - ) - - return eval(f"bpy.data.{data_category}['{data_name}']") - - -class AssetImporter: - """ - Class that handles the creation of the TransferCollectionTriplet. - Depending on the operation (push/pull) and depending on the selected TaskLayers - we need to import and suffix the Asset Collections from the AssetTask and the AssetPublish - after a certain logic. - """ - - def __init__(self, build_context: BuildContext): - self._build_context = build_context - - @property - def build_context(self) -> BuildContext: - return self._build_context - - def _duplicate_tmp_blendfile(self) -> Path: - # Gen a UUID to minimize risk of overwriting an existing blend file. - id = uuid.uuid4() - filepath_tmp = Path(bpy.data.filepath) - filepath_tmp = filepath_tmp.parent / f"{filepath_tmp.stem}-{id}.blend" - - if filepath_tmp.exists(): - raise FileExistsError( - f"Failed to duplicate blend file. Path already exists: {filepath_tmp.as_posix()}" - ) - - # Duplicate blend file by saving it in filepath_tmp. - bpy.ops.wm.save_as_mainfile(filepath=filepath_tmp.as_posix(), copy=True) - - logger.debug("Created temporary duplicate: %s", filepath_tmp.name) - - return filepath_tmp - - def _import_coll_with_suffix( - self, libpath: Path, coll_name: str, coll_suffix: str - ) -> bpy.types.Collection: - - coll = import_data_from_lib(libpath, "collections", coll_name) - asset_suffix.add_suffix_to_hierarchy(coll, coll_suffix) - return coll - - def import_asset_task(self) -> TransferCollectionTriplet: - """ - Imports that asset task that is stored in BuildContext.asset_task. - Note: This function assumes it is run in an asset publish file. - """ - - # TODO: Add safety check to verify this function is not run in an - # asset task. Maybe built context could receive a flag that we can check here? - - asset_task = self.build_context.asset_task - asset_publish = AssetPublish(Path(bpy.data.filepath)) - - asset_coll_publish = self.build_context.asset_context.asset_collection - asset_coll_name = asset_coll_publish.name - - # We now need to either duplicate the asset task or publish collection - # depending on which one is going to be the base. To make this decision we should look - # at the enabled TaskLayers in the build context and then check the 'order' attribute of TaskLayers - # if the asset task collection contains a task layer with the lowest order we have to take that as - # a base. - orders_prod: List[int] = self.build_context.prod_context.get_task_layer_orders() - orders_asset_task: List[ - int - ] = self.build_context.asset_context.task_layer_assembly.get_task_layer_orders( - only_used=True - ) - - # If the smallest order of the asset task is equal the smallest order or prod orders - # We know that we need to take the collection of the asset task as a new base. - - # BASE --> ASSET_TASK COLLECTION - if min(orders_asset_task) == min(orders_prod): - - logger.info("Take Asset Task as Base: %s", asset_task.path.name) - - # Suffix asset_publish collection with .PUBLISH - asset_suffix.add_suffix_to_hierarchy( - asset_coll_publish, constants.PUBLISH_SUFFIX - ) - - # Import asset task collection with .TASK suffix. - asset_coll_task = self._import_coll_with_suffix( - asset_task.path, asset_coll_name, constants.TASK_SUFFIX - ) - - # Import asset_task collection again and suffix as .TARGET - asset_coll_target = self._import_coll_with_suffix( - asset_task.path, asset_coll_name, constants.TARGET_SUFFIX - ) - - # BASE --> ASSET_PUBLISH COLLECTION - else: - - logger.info("Take Asset Publish as Base: %s", asset_publish.path.name) - - # Make tmp blendfile. - # This is a little tricks that prevents us from having to duplicate the whole - # Collection hierarchy and deal with annoyin .001 suffixes. - # That way we can first suffix the asset publish collection and then import it again. - tmp_blendfile_path = self._duplicate_tmp_blendfile() - - # Suffix asset_publish collection with .PUBLISH. - asset_suffix.add_suffix_to_hierarchy( - asset_coll_publish, constants.PUBLISH_SUFFIX - ) - - # Import asset task collection with .TASK suffix. - asset_coll_task = self._import_coll_with_suffix( - asset_task.path, asset_coll_name, constants.TASK_SUFFIX - ) - - # Import asset_publish collection from tmp blend file and suffix as .TARGET - asset_coll_target = self._import_coll_with_suffix( - tmp_blendfile_path, asset_coll_name, constants.TARGET_SUFFIX - ) - - # Remove tmp blend file. - tmp_blendfile_path.unlink() - - # Link for debugging. - for coll in [asset_coll_publish, asset_coll_target, asset_coll_task]: - if coll in list(bpy.context.scene.collection.children): - continue - bpy.context.scene.collection.children.link(coll) - - # Set suffixes. - asset_coll_task.bsp_asset.transfer_suffix = constants.TASK_SUFFIX - asset_coll_publish.bsp_asset.transfer_suffix = constants.PUBLISH_SUFFIX - asset_coll_target.bsp_asset.transfer_suffix = constants.TARGET_SUFFIX - - return TransferCollectionTriplet( - asset_coll_task, asset_coll_publish, asset_coll_target - ) - - def import_asset_publish(self) -> TransferCollectionTriplet: - """ - Imports the latest asset publish. - """ - # TODO: shares a lot of the same code as import_asset_task(). Refactor it to make it DRY. - - # TODO: Add safety check to verify this function is not run in an - # asset publish. Maybe built context could receive a flag that we can check here? - # Get latest asset version. - asset_publish = self.build_context.asset_publishes[-1] - asset_task = self.build_context.asset_task - asset_coll_task = self.build_context.asset_context.asset_collection - asset_coll_name = asset_coll_task.name - - # We now need to either duplicate the asset task or publish collection - # depending on which one is going to be the base. To make this decision we should look - # at the enabled TaskLayers in the build context and then check the 'order' attribute of TaskLayers - # if the asset task collection contains a task layer with the lowest order we have to take that as - # a base. - orders_prod: List[int] = self.build_context.prod_context.get_task_layer_orders() - orders_asset_publish: List[ - int - ] = self.build_context.asset_context.task_layer_assembly.get_task_layer_orders( - only_used=True - ) - - # Remember in this scenario the orders_asset_task might be a little misleading - # because we have to turn it around. In this case the user selects which TaskLayers they want - # to pull from the ASSET PUBLISH. But the base logic stays the same: - - # If the smallest order of the asset publish is equal the smallest order or prod orders - # We know that we need to take the collection of the asset publish as a new base. - - # BASE --> ASSET_PUBLISH COLLECTION - if min(orders_asset_publish) == min(orders_prod): - logger.info("Take Asset Publish as Base: %s", asset_publish.path.name) - - # Suffix asset_task collection with .TASK - asset_suffix.add_suffix_to_hierarchy(asset_coll_task, constants.TASK_SUFFIX) - - # Import asset_publish collection with .PUBLISH suffix. - asset_coll_publish = self._import_coll_with_suffix( - asset_publish.path, asset_coll_name, constants.PUBLISH_SUFFIX - ) - - # Import asset_publish collection again and suffix as .TARGET - asset_coll_target = self._import_coll_with_suffix( - asset_publish.path, asset_coll_name, constants.TARGET_SUFFIX - ) - - # BASE --> ASSET_TASK COLLECTION - else: - logger.info("Take Asset Task as Base: %s", asset_task.path.name) - - # Make tmp blendfile. - # This is a little tricks that prevents us from having to duplicate the whole - # Collection hierarchy and deal with annoyin .001 suffixes. - # That way we can first suffix the asset publish collection and then import it again. - tmp_blendfile_path = self._duplicate_tmp_blendfile() - - # Suffix asset_task collection with .TASK. - asset_suffix.add_suffix_to_hierarchy(asset_coll_task, constants.TASK_SUFFIX) - - # Import asset publish collection with .PUBLISH suffix. - asset_coll_publish = self._import_coll_with_suffix( - asset_publish.path, asset_coll_name, constants.PUBLISH_SUFFIX - ) - - # Import asset_task collection from tmp blend file and suffix as .TARGET - asset_coll_target = self._import_coll_with_suffix( - tmp_blendfile_path, asset_coll_name, constants.TARGET_SUFFIX - ) - - # Remove tmp blend file. - tmp_blendfile_path.unlink() - - # Link for debugging. - for coll in [asset_coll_publish, asset_coll_target, asset_coll_task]: - if coll in list(bpy.context.scene.collection.children): - continue - bpy.context.scene.collection.children.link(coll) - - # Set suffixes. - asset_coll_task.bsp_asset.transfer_suffix = constants.TASK_SUFFIX - asset_coll_publish.bsp_asset.transfer_suffix = constants.PUBLISH_SUFFIX - asset_coll_target.bsp_asset.transfer_suffix = constants.TARGET_SUFFIX - - return TransferCollectionTriplet( - asset_coll_task, asset_coll_publish, asset_coll_target - ) diff --git a/scripts-blender/addons/asset_pipeline/builder/asset_mapping.py b/scripts-blender/addons/asset_pipeline/builder/asset_mapping.py deleted file mode 100644 index f14350ff..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/asset_mapping.py +++ /dev/null @@ -1,356 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple - -from pathlib import Path - -import bpy - -from .vis import EnsureCollectionVisibility - -from .. import util - -logger = logging.getLogger("BSP") - - -class TransferCollectionTriplet: - """ - This class holds the 3 collections that are needed for the merge process. Publish, Task and Target Collection. - During the merge we have to dynamically decide which Task Layer we take from the Publish Collection - and which we take from the Task Collection to apply on the target. - That's why we save these 3 Collections in a dedicated class, as we require them. - """ - - def __init__( - self, - task_coll: bpy.types.Collection, - publish_coll: bpy.types.Collection, - target_coll: bpy.types.Collection, - ): - self.publish_coll = publish_coll - self.task_coll = task_coll - self.target_coll = target_coll - self._vis_colls: List[EnsureCollectionVisibility] = [] - - def get_collections(self) -> List[bpy.types.Collection]: - return [self.task_coll, self.publish_coll, self.target_coll] - - def reset_rigs(self) -> None: - """To ensure correct data transferring, make sure all rigs are in their - default positions.""" - for main_coll in self.get_collections(): - for ob in main_coll.all_objects: - if ob.type != "ARMATURE": - continue - util.reset_armature_pose( - ob, - reset_properties=True, - reset_transforms=True, - ) - ob.data.pose_position = "REST" - - def ensure_vis(self) -> None: - # Apparently Blender does not evaluate objects or collections in the depsgraph - # in some cases if they are not visible. This is something Users should not have to take - # care about when writing their transfer data instructions. So we will make sure here - # that everything is visible and after the transfer the original state will be restored. - - # Catch mistake if someone calls this twice without restoring before. - if self._vis_colls: - self.restore_vis() - for main_coll in self.get_collections(): - self.recursive_ensure_vis(main_coll) - - def recursive_ensure_vis(self, coll): - self._vis_colls.append(EnsureCollectionVisibility(coll)) - for subcoll in coll.children: - self.recursive_ensure_vis(subcoll) - - def restore_vis(self) -> None: - for vis_coll in self._vis_colls: - vis_coll.restore() - - self._vis_colls.clear() - - -def rreplace(s: str, old: str, new: str, occurrence: int) -> str: - li = s.rsplit(old, occurrence) - return new.join(li) - - -class AssetTransferMapping: - """ - The AssetTranfserMapping class represents a mapping between a source and a target. - It contains an object mapping which connects each source object with a target - object as well as a collection mapping. - The mapping process relies heavily on suffixes, which is why we use - MergeCollections as input that store a suffix. - - Instances of this class will be pased TaskLayer data transfer function so Users - can easily write their merge instructions. - """ - - def __init__( - self, - source_coll: bpy.types.Collection, - target_coll: bpy.types.Collection, - ): - - self._source_coll = source_coll - self._target_coll = target_coll - - self._no_match_source_objs: Set[bpy.types.Object] = set() - self._no_match_target_objs: Set[bpy.types.Object] = set() - - self._no_match_source_colls: Set[bpy.types.Object] = set() - self._no_match_target_colls: Set[bpy.types.Object] = set() - - # TODO: gen_map functions almost have the same code, - # refactor it to one function with the right parameters. - self.generate_mapping() - - @property - def source_coll(self) -> bpy.types.Collection: - return self._source_coll - - @property - def target_coll(self) -> bpy.types.Collection: - return self._target_coll - - @property - def no_match_source_objs(self) -> Set[bpy.types.Object]: - """ - All objects that exist in source but not in target - """ - return self._no_match_source_objs - - @property - def no_match_target_objs(self) -> Set[bpy.types.Object]: - """ - All objects that exist in target but not in source - """ - return self._no_match_target_objs - - @property - def no_match_source_colls(self) -> Set[bpy.types.Object]: - """ - All collections that exist in source but not in target - """ - return self._no_match_source_colls - - @property - def no_match_target_colls(self) -> Set[bpy.types.Object]: - """ - All collections that exist in target but not in source - """ - return self._no_match_target_colls - - def generate_mapping(self) -> None: - self._object_map = self._gen_object_map() - self._collection_map = self._gen_collection_map() - self._material_map = self._gen_material_map() - - def _gen_object_map(self) -> Dict[bpy.types.Object, bpy.types.Object]: - - """ - Tries to link all objects in source collection to an object in - target collection. Uses suffixes to match them up. - """ - - object_map: Dict[bpy.types.Object, bpy.types.Object] = {} - - for source_obj in self.source_coll.all_objects: - - # assert source_obj.name.endswith(self._source_merge_coll.suffix) - - # Replace source object suffix with target suffix to get target object. - target_obj_name = rreplace( - source_obj.name, - self._source_coll.bsp_asset.transfer_suffix, - self._target_coll.bsp_asset.transfer_suffix, - 1, - ) - try: - target_obj = self._target_coll.all_objects[target_obj_name] - except KeyError: - logger.debug( - "Failed to find match obj %s for %s", - target_obj_name, - source_obj.name, - ) - self._no_match_source_objs.add(source_obj) - continue - else: - object_map[source_obj] = target_obj - # logger.debug( - # "Found match: source: %s target: %s", - # source_obj.name, - # target_obj.name, - # ) - - # Populate no match target set. - match_target_objs = set([obj for obj in object_map.values()]) - self._no_match_target_objs = ( - set(self.target_coll.all_objects) - match_target_objs - ) - - return object_map - - def _gen_collection_map(self) -> Dict[bpy.types.Collection, bpy.types.Collection]: - """ - Tries to link all source collections to a target collection. - Uses suffixes to match them up. - """ - coll_map: Dict[bpy.types.Collection, bpy.types.Collection] = {} - - # Link top most parents. - coll_map[self.source_coll] = self.target_coll - - # Link up all children. - for s_coll in util.traverse_collection_tree(self.source_coll): - - # assert source_obj.name.endswith(self._source_merge_coll.suffix) - - # Replace source object suffix with target suffix to get target object. - target_coll_name = rreplace( - s_coll.name, - self._source_coll.bsp_asset.transfer_suffix, - self._target_coll.bsp_asset.transfer_suffix, - 1, - ) - try: - t_coll = bpy.data.collections[target_coll_name] - except KeyError: - logger.debug( - "Failed to find match collection %s for %s", - s_coll.name, - target_coll_name, - ) - self._no_match_source_colls.add(s_coll) - continue - else: - coll_map[s_coll] = t_coll - # logger.debug( - # "Found match: source: %s target: %s", - # s_coll.name, - # t_coll.name, - # ) - - all_tgt_colls = set(self.target_coll.children_recursive) - all_tgt_colls.add(self.target_coll) - match_target_colls = set([coll for coll in coll_map.values()]) - self._no_match_target_colls = all_tgt_colls - match_target_colls - - return coll_map - - def _gen_material_map(self) -> Dict[bpy.types.Material, bpy.types.Material]: - material_map: Dict[bpy.types.Material, bpy.types.Material] = {} - - source_materials: List[bpy.types.Material] = self._get_all_materials_of_coll( - self.source_coll - ) - target_materials_dict: Dict[ - str, bpy.types.Material - ] = self._get_all_materials_of_coll(self.target_coll, as_dict=True) - - # Link up all children. - for s_mat in source_materials: - - # assert s_mat.name.endswith(self._source_merge_coll.suffix) - - # Replace source object suffix with target suffix to get target object. - target_mat_name = rreplace( - s_mat.name, - self._source_coll.bsp_asset.transfer_suffix, - self._target_coll.bsp_asset.transfer_suffix, - 1, - ) - try: - t_mat = target_materials_dict[target_mat_name] - except KeyError: - logger.debug( - "Failed to find match material %s for %s", - s_mat.name, - target_mat_name, - ) - continue - else: - material_map[s_mat] = t_mat - # logger.debug( - # "Found match: source: %s target: %s", - # s_mat.name, - # t_mat.name, - # ) - - return material_map - - def _get_all_materials_of_coll( - self, coll: bpy.types.Collection, as_dict: bool = False - ) -> Union[List[bpy.types.Material], Dict[str, bpy.types.Material]]: - materials: List[bpy.types.Material] = [] - for obj in coll.all_objects: - for ms in obj.material_slots: - m = ms.material - - # Material can be None. - if not m: - continue - - if m in materials: - continue - - materials.append(m) - - # Return list. - if not as_dict: - return materials - - # Return dict. - materials_dict = {} - for mat in materials: - materials_dict[mat.name] = mat - return materials_dict - - @property - def object_map(self) -> Dict[bpy.types.Object, bpy.types.Object]: - """ - Key: Source - Value: Target - """ - return self._object_map - - @property - def collection_map(self) -> Dict[bpy.types.Collection, bpy.types.Collection]: - """ - Key: Source - Value: Target - """ - return self._collection_map - - @property - def material_map(self) -> Dict[bpy.types.Material, bpy.types.Material]: - """ - Key: Source - Value: Target - """ - return self._material_map diff --git a/scripts-blender/addons/asset_pipeline/builder/asset_suffix.py b/scripts-blender/addons/asset_pipeline/builder/asset_suffix.py deleted file mode 100644 index c3afff4b..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/asset_suffix.py +++ /dev/null @@ -1,69 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator - -import bpy -from bpy_extras.id_map_utils import get_id_reference_map, get_all_referenced_ids - -from .. import constants -from ..util import get_storage_of_id - -logger = logging.getLogger("BSP") - - -def remove_suffix_from_hierarchy( - collection: bpy.types.Collection, delimiter: str = constants.DELIMITER -): - """Removes the suffix after a set delimiter from all datablocks - referenced by a collection, itself included""" - - ref_map = get_id_reference_map() - datablocks = get_all_referenced_ids(collection, ref_map) - datablocks.add(collection) - for db in datablocks: - if db.library: - # Don't rename linked datablocks. - continue - try: - db.name = delimiter.join(db.name.split(delimiter)[:-1]) - except: - pass - - -def add_suffix_to_hierarchy(collection: bpy.types.Collection, suffix: str): - """Add a suffix to the names of all datablocks referenced by a collection, - itself included.""" - - ref_map = get_id_reference_map() - datablocks = get_all_referenced_ids(collection, ref_map) - datablocks.add(collection) - for db in datablocks: - if db.library: - # Don't rename linked datablocks. - continue - collision_db = get_storage_of_id(db).get(db.name+suffix) - if collision_db: - collision_db.name += '.OLD' - try: - db.name += suffix - except: - pass diff --git a/scripts-blender/addons/asset_pipeline/builder/blstarter.py b/scripts-blender/addons/asset_pipeline/builder/blstarter.py deleted file mode 100644 index a37c179b..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/blstarter.py +++ /dev/null @@ -1,50 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# This file was made by Jeroen Bakker in the shot-builder repository: -# https://developer.blender.org/diffusion/BSTS/browse/master/shot-builder/shot_builder/sys_utils -import logging -import subprocess - -from pathlib import Path -from typing import List, Dict, Union, Any, Optional - -logger = logging.getLogger("BSP") - -import bpy - - -class BuilderBlenderStarter: - - path: Path = Path(bpy.app.binary_path) - publish_script: Path = Path(__file__).parent.joinpath("scripts/push.py") - - @classmethod - def start_publish(cls, filepath: Path, pickle_path: Path) -> subprocess.Popen: - cmd_str = ( - f'"{cls.path.as_posix()}" "{filepath.as_posix()}"' - ' -b' - # ' --factory-startup' - # f' --addons blender_kitsu,asset_pipeline' - f' -P "{cls.publish_script.as_posix()}"' - f' -- "{pickle_path.as_posix()}"' - ) - popen = subprocess.Popen(cmd_str, shell=True) - return popen diff --git a/scripts-blender/addons/asset_pipeline/builder/context.py b/scripts-blender/addons/asset_pipeline/builder/context.py deleted file mode 100644 index d3d086d1..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/context.py +++ /dev/null @@ -1,682 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -""" -The asset-pipeline works heavily with the concept of Contexts. -There are 3 types of contexts: - -ProductionContext: Global production level context, gets loaded on startup, processes all the config files. - -AssetContext: Local Asset Context, gets loaded on each scene load. Stores settings and information for active Asset. - -BuildContext: Gets loaded when starting a publish or a pull. Contains both the ProductionContext and AssetContext -as well as some other data. Is the actual context that gets processed by the AssetBuilder. - -A key feature is that we need to be able to 'exchange' this information with another blend file. As the actual -transfer process requires to: -open another blend file -> load the build context there -> process it -> close it again. -This can be achieved by using the `pickle` library and pickle the Contexts. All the contexts are pickleable. -""" - -import importlib -import logging - -from typing import List, Dict, Union, Any, Set, Optional -from types import ModuleType, FunctionType - -from pathlib import Path - -import bpy - -from .task_layer import TaskLayer, TaskLayerAssembly -from .hook import Hooks - -from .. import constants, prop_utils -from ..sys_utils import SystemPathInclude -from ..asset_files import AssetDir, AssetPublish, AssetTask - -logger = logging.getLogger("BSP") - - -class ProdContextFailedToInitialize(Exception): - pass - - -class AssetContextFailedToInitialize(Exception): - pass - - -class BuildContextFailedToInitialize(Exception): - pass - - -class InvalidTaskLayerDefinition(Exception): - pass - - -class ProcessPair: - """ - Simple Class that stores a logically connected target and a pull from path. - """ - - def __init__(self, asset_task: AssetTask, asset_publish: AssetPublish) -> None: - self.asset_task = asset_task - self.asset_publish = asset_publish - - def __eq__(self, other: object) -> bool: - if not isinstance(other, ProcessPair): - raise NotImplementedError() - - return bool( - self.asset_task == other.asset_task - and self.asset_publish == other.asset_publish - ) - - def __hash__(self) -> int: - return hash((self.asset_task, self.asset_publish)) - - -class ProductionContext: - - """ - A context that represents configuration on a Production Level. - Independent from Blender, no bpy access. This context mostly holds - the defined TaskLayers in the config files, the transfer settings and the hooks. - """ - - def __init__(self, config_folder: Path): - - if not config_folder or not config_folder.exists(): - raise ProdContextFailedToInitialize( - f"Failed to init ProductionContext. Invalid config folder: {config_folder}" - ) - - self._task_layers: List[type[TaskLayer]] = [] - self._transfer_settings: Optional[type[bpy.types.PropertyGroup]] = None - self._config_folder: Path = config_folder - self._module_of_task_layers: Optional[ModuleType] = None - self._module_of_hooks: Optional[ModuleType] = None - self._hooks = Hooks() - - # Load configs from config_folder. - self._collect_configs() - logger.debug("Initialized Production Context") - - @property - def config_folder(self) -> Path: - return self._config_folder - - @property - def task_layers(self) -> List[type[TaskLayer]]: - return self._task_layers - - def get_task_layer_orders(self) -> List[int]: - """ - Returns a list of all TaskLayers.order values. - """ - return [t.order for t in self.task_layers] - - def _collect_configs(self) -> None: - - # Add config folder temporarily to sys.path for convenient - # import. - - with SystemPathInclude([self._config_folder]): - - # Load Task Layers. - # TODO: information duplicated in add-on preferences - # Make it DRY - - # Check if task layers module was already imported. - # TODO: does not work perfectly, if we remove a TaskLayer from - # config file and then reload, it's still there. - # https://stackoverflow.com/questions/2918898/prevent-python-from-caching-the-imported-modules - if self._module_of_task_layers: - # Reload it so Users won't have to restart Blender. - self._module_of_task_layers = importlib.reload( - self._module_of_task_layers - ) - else: - import task_layers as prod_task_layers - - self._module_of_task_layers = prod_task_layers - - # Crawl module for TaskLayers. - self._collect_prod_task_layers() - self._collect_prod_transfer_settings() - - try: - import hooks - - except ModuleNotFoundError: - logger.debug( - "Found no 'hooks' module in: %s", self._config_folder.as_posix() - ) - self._module_of_hooks = None - - else: - self._module_of_hooks = hooks - self._collect_prod_hooks() - - def _collect_prod_task_layers(self) -> None: - - # Clear task layer list, otherwise we will add new but don't - # remove old. - self._task_layers.clear() - module = self._module_of_task_layers - - # Find all valid TaskLayer Classes. - for module_item_str in dir(module): - module_item = getattr(module, module_item_str) - - # This checks that the module item is a class definition - # and not e.G and instance of that class. - if module_item.__class__ != type: - continue - - if not issubclass(module_item, TaskLayer): - continue - - # We don't want to collect to Root TaskLayer class. - # Only classes that inherit from it. - if module_item == TaskLayer: - continue - - # Checks e.G that 'name' class attribute is set. - if not module_item.is_valid(): - if module_item.order < 0: - raise InvalidTaskLayerDefinition( - f"Invalid TaskLayer {str(module_item)} Order attribute not set.", - ) - if not module_item.name: - raise InvalidTaskLayerDefinition( - f"Invalid Task Layer {str(module_item)} Name attribute not set.", - ) - continue - - self._task_layers.append(module_item) - - # Check if any TaskLayers have the same order. - self._validate_task_layer_orders() - - # Sort TaskLayers after order attribute. - self._task_layers.sort(key=lambda tl: tl.order) - - if self.task_layers: - logger.info(f"Detected Production TaskLayers: {self.task_layers}") - - def _collect_prod_hooks(self) -> None: - - module = self._module_of_hooks - self._hooks = Hooks() - - for module_item_str in dir(module): - module_item = getattr(module, module_item_str) - # Skip non functions. - if not isinstance(module_item, FunctionType): - continue - # Skip functions of other modules. - if module_item.__module__ != module.__name__: - continue - # @hook() decorator adds this attribute which make a hook - # distinguishable from a regular function. - # Note: @hook() needs to be called otherwise this check - # will fail. - if not hasattr(module_item, constants.HOOK_ATTR_NAME): - continue - - self._hooks.register(module_item) - - if self._hooks: - logger.info(f"Detected Production Hooks: {self._hooks.callables}") - - def _collect_prod_transfer_settings(self) -> None: - """ - Here we search the task_layers.py module for a class that is - named as defined in constants.TRANSFER_SETTINGS_NAME. This is supposed to be - a regular Blender PropertyGroup. In this PropertyGroup Users can define - regular blender Properties that represent a setting to customize the - transfer data process. This PropertyGroup will be registered on scene level - and can then be easily queried in the transfer data function of the TaskLayer. - That way Users can provide themselves options to use in their code. - This options are also displayed in the Blender AssetPipeline Panel automatically. - """ - self._transfer_settings = None - module = self._module_of_task_layers - - try: - prop_group = getattr(module, constants.TRANSFER_SETTINGS_NAME) - except AttributeError: - logger.info( - "No Transfer Settings loaded. Failed to find %s variable.", - constants.TRANSFER_SETTINGS_NAME, - ) - else: - # Check if prop group is actually of type PropertyGroup. - if not issubclass(prop_group, bpy.types.PropertyGroup): - raise ProdContextFailedToInitialize( - f"{constants.TRANSFER_SETTINGS_NAME} must be subclass of bpy.types.PropertyGroup" - ) - self._transfer_settings = prop_group - try: - bpy.utils.unregister_class(prop_group) - except RuntimeError: - bpy.utils.register_class(prop_group) - # Scene Asset Pipeline Properties. - bpy.types.Scene.bsp_asset_transfer_settings = bpy.props.PointerProperty( - type=prop_group - ) - - logger.info(f"Detected Transfer Settings: {self._transfer_settings}") - logger.info( - f"Registered Transfer Settings: bpy.types.Scene.bsp_asset_transfer_settings" - ) - - def _validate_task_layer_orders(self) -> None: - for i in range(len(self._task_layers)): - tl = self._task_layers[i] - - for j in range(i + 1, len(self._task_layers)): - tl_comp = self._task_layers[j] - if tl.order == tl_comp.order: - raise InvalidTaskLayerDefinition( - f"Invalid Task Layer {str(tl)} has some 'order' as {str(tl_comp)}.", - ) - - @property - def hooks(self) -> Hooks: - return self._hooks - - def __repr__(self) -> str: - header = "\nPRODUCTION CONTEXT\n------------------------------------" - footer = "------------------------------------" - prod_task_layers = ( - f"Production Task Layers: {[t.name for t in self._task_layers]}" - ) - return "\n".join([header, prod_task_layers, footer]) - - def __getstate__(self) -> Dict[str, Any]: - # Pickle uses this function to generate a dictionary which it uses - # to pickle the instance. - # Here we can basically overwrite this dictionary, for example to - # delete some properties that pickle can't handle. - - # Pickle cannot store module objects. - state = self.__dict__.copy() - state["_module_of_task_layers"] = None - state["_module_of_hooks"] = None - return state - - def __setstate__(self, state: Dict[str, Any]) -> None: - # Pickle uses a state Dictionary to restore the instance attributes. - # In this function we can overwrite this behavior and restore - # data that pickle wasn't able to store - - self.__dict__.update(state) - - # Restore module object. - with SystemPathInclude([self.config_folder]): - import task_layers as prod_task_layers - - try: - import hooks - - except ModuleNotFoundError: - hooks = None - - self._module_of_task_layers = prod_task_layers - self._module_of_hooks = hooks - - -class AssetContext: - - """ - The Asset Context gets updated on each scene load. It holds all information that are related - to the current Asset. This includes the current Asset Collection, Asset Task, available Asset Publishes, - the Asset Directory, the configuration of Task Layers (which ones are enabled and disabled) - and the Transfer Settings. - """ - - def __init__(self, bl_context: bpy.types.Context, prod_context: ProductionContext): - - # Check if bl_context and config_folder are valid. - if not all([bl_context, bl_context.scene.bsp_asset.asset_collection]): - raise AssetContextFailedToInitialize( - "Failed to initialize AssetContext. Invalid blender_context or asset collection not set." - ) - # Check if file is saved. - if not bpy.data.filepath: - raise AssetContextFailedToInitialize( - "Failed to initialize AssetContext. File not saved" - ) - - self._bl_context: bpy.types.Context = bl_context - self._asset_collection: bpy.types.Collection = ( - bl_context.scene.bsp_asset.asset_collection - ) - self._task_layer_assembly = TaskLayerAssembly(prod_context._task_layers) - self._asset_dir = AssetDir(Path(bpy.data.filepath).parent) - self._asset_task = AssetTask(Path(bpy.data.filepath)) - self._asset_publishes: List[AssetPublish] = [] - - # Transfer settings are stored in a PropertyGroup on scene level. - # We cannot pickle those. So what we do is write them in a dictionary here - # before publish and restore the settings when we open the other blend file. - self._transfer_settings: Dict[str, Any] = {} - - # TODO: Load custom Task Layers. - self._custom_task_layers: List[Any] = [] - - self._collect_asset_publishes() - logger.debug("Initialized Asset Context") - - @property - def asset_collection(self) -> bpy.types.Collection: - return self._asset_collection - - @property - def asset_name(self) -> str: - return self.asset_collection.bsp_asset.entity_name - - @property - def asset_task(self) -> AssetTask: - return self._asset_task - - @property - def asset_dir(self) -> AssetDir: - return self._asset_dir - - @property - def asset_publishes(self) -> List[AssetPublish]: - return self._asset_publishes - - @property - def task_layer_assembly(self) -> TaskLayerAssembly: - return self._task_layer_assembly - - @property - def transfer_settings(self) -> Dict[str, Any]: - return self._transfer_settings - - def reload_asset_publishes(self) -> None: - self._collect_asset_publishes() - - def reload_asset_publishes_metadata(self) -> None: - for asset_publish in self.asset_publishes: - asset_publish.reload_metadata() - - def update_from_bl_context_pull(self, bl_context: bpy.types.Context) -> None: - self._bl_context = bl_context - self._asset_collection = bl_context.scene.bsp_asset.asset_collection - self._update_task_layer_assembly_from_context_pull(bl_context) - self._update_transfer_settings_from_context(bl_context) - - def update_from_bl_context_push(self, bl_context: bpy.types.Context) -> None: - self._bl_context = bl_context - self._asset_collection = bl_context.scene.bsp_asset.asset_collection - self._update_task_layer_assembly_from_context_push(bl_context) - self._update_transfer_settings_from_context(bl_context) - - def _collect_asset_publishes(self) -> None: - self._asset_publishes.clear() - self._asset_publishes.extend(self._asset_dir.get_asset_publishes()) - - def _update_task_layer_assembly_from_context_pull( - self, bl_context: bpy.types.Context - ) -> None: - # Update TaskLayerAssembly, to load the - # previously disabled and enabled TaskLayer States. - # They are stored in context.scene.bl_asset.task_layers - - # TODO: we should take in to account that in the meantime - # production TaskLayers could have been updated. - bsp = bl_context.scene.bsp_asset - for item in bsp.task_layers_pull: - task_layer_config = self.task_layer_assembly.get_task_layer_config( - item.task_layer_id - ) - task_layer_config.use = item.use - - def _update_task_layer_assembly_from_context_push( - self, bl_context: bpy.types.Context - ) -> None: - bsp = bl_context.scene.bsp_asset - for item in bsp.task_layers_push: - task_layer_config = self.task_layer_assembly.get_task_layer_config( - item.task_layer_id - ) - task_layer_config.use = item.use - - def _update_transfer_settings_from_context( - self, bl_context: bpy.types.Context - ) -> None: - for prop_name, prop in prop_utils.get_property_group_items( - bl_context.scene.bsp_asset_transfer_settings - ): - self._transfer_settings[prop_name] = getattr( - bl_context.scene.bsp_asset_transfer_settings, prop_name - ) - - def __repr__(self) -> str: - header = "\nASSET CONTEXT\n------------------------------------" - footer = "------------------------------------" - asset_info = f"Asset: {self.asset_collection.bsp_asset.entity_name}({self.asset_collection})" - task_layer_assembly = str(self.task_layer_assembly) - - return "\n".join( - [ - header, - asset_info, - task_layer_assembly, - footer, - ] - ) - - def __getstate__(self) -> Dict[str, Any]: - - # Pickle cannot pickle blender context or collection. - state = self.__dict__.copy() - state["_bl_context"] = None - state["_restore_asset_collection_name"] = self.asset_collection.name - state["_asset_collection"] = None - return state - - def __setstate__(self, state: Dict[str, Any]) -> None: - self.__dict__.update(state) - asset_coll_name = state["_restore_asset_collection_name"] - asset_coll = bpy.data.collections[asset_coll_name] - self._asset_collection = asset_coll - self._bl_context = bpy.context - - del self._restore_asset_collection_name - logger.info( - "Restored Asset Collection: %s, Context: %s", - str(self._asset_collection), - str(self._bl_context), - ) - - -class BuildContext: - - """ - Class that should function as Context for the asset build. - Here we want to store everything that is relevant for the build. - The Builder will process this context. - Should be updated on start publish/pull and only be relevant for publish/pull. - """ - - def __init__( - self, - prod_context: ProductionContext, - asset_context: AssetContext, - ): - if not all([prod_context, asset_context]): - raise BuildContextFailedToInitialize( - "Failed to initialize Build Context. Production or Asset Context not initialized." - ) - - self._prod_context: ProductionContext = prod_context - self._asset_context: AssetContext = asset_context - self._process_pairs: List[ProcessPair] = [] - self.is_push: bool = False # Only for TaskLayer.transfer_data() to know if its push or pull. - - self._collect_process_pairs() - - def _collect_process_pairs(self) -> None: - # Here we want to loop through all asset publishes and - # create a list of process pairs out of it. - # This is the place where we perform the logic of checking - # which task layers the user selected in self._asset_context.task_layer_assembly - # and then reading the metadata of each asset publish and check where the corresponding - # task layers are live. - # The result of this is a list of process pairs(target, pull_from) that - # the AssetBuilder needs to process - self._process_pairs.clear() - - process_pairs_set = set() - - tl_assembly = self._asset_context.task_layer_assembly - task_layers_enabled = tl_assembly.get_used_task_layers() - - for asset_publish in self.asset_publishes: - - # For this asset publish get all locked task layers IDs. - locked_task_layer_ids = asset_publish.metadata.get_locked_task_layer_ids() - - # Check if there is any enabled Task Layer ID that is not in the locked IDs. - for tl in task_layers_enabled: - if tl.get_id() not in locked_task_layer_ids: - process_pairs_set.add(ProcessPair(self.asset_task, asset_publish)) - - self._process_pairs.extend(list(process_pairs_set)) - self._process_pairs.sort(key=lambda x: x.asset_publish.path.name) - - @property - def prod_context(self) -> ProductionContext: - return self._prod_context - - @property - def asset_context(self) -> AssetContext: - return self._asset_context - - @property - def asset_task(self) -> AssetTask: - return self.asset_context.asset_task - - @property - def asset_dir(self) -> AssetDir: - return self.asset_context.asset_dir - - @property - def asset_publishes(self) -> List[AssetPublish]: - return self.asset_context.asset_publishes - - @property - def process_pairs(self) -> Set[ProcessPair]: - return self._process_pairs - - def __repr__(self) -> str: - header = "\nBUILD CONTEXT\n------------------------------------" - footer = "------------------------------------" - asset_task = f"Asset Task: {str(self.asset_task)}" - asset_disk_name = f"Asset Disk Name: {self.asset_dir.asset_disk_name}" - asset_dir = f"Asset Dir: {str(self.asset_dir)}" - return "\n".join( - [ - header, - asset_disk_name, - asset_task, - asset_dir, - str(self.prod_context), - str(self.asset_context), - footer, - ] - ) - - def get_hook_kwargs(self, context: bpy.types.Context) -> Dict[str, Any]: - return { - "asset_collection": context.scene.bsp_asset.asset_collection, - "context": context, - "asset_task": self.asset_task, - "asset_dir": self.asset_context.asset_dir, - } - - -class UndoContext: - """ - This should be a context that we can populate along the way of starting a publish and actually publishing. - The idea is that we can add 'undo' steps that we can then undo() if users aborts the publish. - The point of it is to mainly be able to revert the filesystem and other things that happen between starting - the publish and aborting it. - These steps will also be mirrored on the scene Property group so you can actually start a publish - open another scene and still abort it and it will undo the correct things. - """ - - def __init__(self): - self._asset_publishes: List[AssetPublish] = [] - - @property - def asset_publishes(self) -> List[AssetPublish]: - return self._asset_publishes - - def has_steps_files_create(self) -> bool: - return bool(self._asset_publishes) - - def add_step_publish_create( - self, bl_context: bpy.types.Context, asset_publish: AssetPublish - ) -> None: - # Add to self context. - self._asset_publishes.append(asset_publish) - - # Add to scene, to restore on load. - bl_context.scene.bsp_asset.undo_context.add_step_asset_publish_create( - asset_publish - ) - - logger.debug("Created file creation undo step: %s", asset_publish.path.name) - - def undo(self, bl_context: bpy.types.Context) -> None: - - # Delete files. - for asset_publish in self._asset_publishes: - if asset_publish.path.exists(): - logger.info( - "Undoing file creation. Delete: [%s, %s]", - asset_publish.path.name, - asset_publish.metadata_path.name, - ) - asset_publish.unlink() - - # Clear. - self.clear(bl_context) - - def update_from_bl_context(self, bl_context: bpy.types.Context) -> None: - - self._asset_publishes.clear() - - for item in bl_context.scene.bsp_asset.undo_context.files_created: - self._asset_publishes.append(AssetPublish(item.path)) - - def clear(self, bl_context: bpy.types.Context) -> None: - # Clear self steps. - self._asset_publishes.clear() - - # Clear scene. - bl_context.scene.bsp_asset.undo_context.clear() diff --git a/scripts-blender/addons/asset_pipeline/builder/hook.py b/scripts-blender/addons/asset_pipeline/builder/hook.py deleted file mode 100644 index 4de51c0e..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/hook.py +++ /dev/null @@ -1,161 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - - -# The Hook system for the Asset Builder is copied over from the shot-builder, developed by @Jeroen Bakker -# https://developer.blender.org/diffusion/BSTS/browse/master/shot-builder/ - -import logging -from typing import ( - Optional, - Any, - Set, - Tuple, - List, - Type, - Callable, - Dict, - cast, - Union, - Iterator, -) -from types import FunctionType, ModuleType -from pathlib import Path - -from .. import constants - -logger = logging.getLogger(name="BSP") - - -class Wildcard: - pass - - -class DoNotMatch: - pass - - -MatchCriteriaType = Union[str, List[str], Type[Wildcard], Type[DoNotMatch]] -""" -The MatchCriteriaType is a type definition for the parameters of the `hook` decorator. - -The matching parameters can use multiple types to detect how the matching criteria -would work. - -* `str`: would perform an exact string match. -* `Iterator[str]`: would perform an exact string match with any of the given strings. -* `Type[Wildcard]`: would match any type for this parameter. This would be used so a hook - is called for any value. -* `Type[DoNotMatch]`: would ignore this hook when matching the hook parameter. This is the default - value for the matching criteria and would normally not be set directly in a - production configuration. -""" - -MatchingRulesType = Dict[str, MatchCriteriaType] -""" -Hooks are stored as `constants.HOOK_ATTR_NAME' attribute on the function. -The MatchingRulesType is the type definition of the `constants.HOOK_ATTR_NAME` attribute. -""" - -HookFunction = Callable[[Any], None] - - -def _match_hook_parameter( - hook_criteria: MatchCriteriaType, match_query: Optional[str] -) -> bool: - - # print(f"hook_criteria: {hook_criteria} | match_query: {match_query}") - - if hook_criteria == DoNotMatch: - return match_query is None - - if hook_criteria == Wildcard: - return True - - if isinstance(hook_criteria, str): - return match_query == hook_criteria - - if isinstance(hook_criteria, list): - return match_query in hook_criteria - - logger.error(f"Incorrect matching criteria {hook_criteria}, {match_query}") - return False - - -class Hooks: - def __init__(self): - self._hooks: List[HookFunction] = [] - - def register(self, func: HookFunction) -> None: - # logger.info(f"Registering hook '{func.__name__}'") - self._hooks.append(func) - - @property - def callables(self) -> List[HookFunction]: - return self._hooks - - def matches( - self, - hook: HookFunction, - match_asset_type: Optional[str] = None, - match_asset: Optional[str] = None, - match_task_layers: Optional[str] = None, # Could be List[str] - **kwargs: Optional[str], - ) -> bool: - assert not kwargs - rules = cast(MatchingRulesType, getattr(hook, constants.HOOK_ATTR_NAME)) - return all( - ( - _match_hook_parameter(rules["match_asset_type"], match_asset_type), - _match_hook_parameter(rules["match_asset"], match_asset), - _match_hook_parameter(rules["match_task_layers"], match_task_layers), - ) - ) - - def filter(self, **kwargs: Optional[str]) -> Iterator[HookFunction]: - for hook in self._hooks: - if self.matches(hook=hook, **kwargs): - yield hook - - def __bool__(self) -> bool: - return bool(self._hooks) - - -def hook( - match_asset_type: MatchCriteriaType = DoNotMatch, - match_asset: MatchCriteriaType = DoNotMatch, - match_task_layers: MatchCriteriaType = DoNotMatch, -) -> Callable[[FunctionType], FunctionType]: - """ - Decorator to add custom logic when building a shot. - - Hooks are used to extend the configuration that would be not part of the core logic of the shot builder tool. - """ - rules = { - "match_asset_type": match_asset_type, - "match_asset": match_asset, - "match_task_layers": match_task_layers, - } - - def wrapper(func: FunctionType) -> FunctionType: - setattr(func, constants.HOOK_ATTR_NAME, rules) - return func - - return wrapper diff --git a/scripts-blender/addons/asset_pipeline/builder/lock_plan.py b/scripts-blender/addons/asset_pipeline/builder/lock_plan.py deleted file mode 100644 index 22645817..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/lock_plan.py +++ /dev/null @@ -1,71 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple - -from .task_layer import TaskLayer - -from ..asset_files import AssetPublish - -logger = logging.getLogger("BSP") - - -class TaskLayerLockPlan: - """ - When creating a new incrementation of an asset publish we need to somehow store - from which previous asset publishes which task layer will be locked. - This is automatically calculated, but this information should also be displayed in the UI. - This class helps with that. This class can also actually lock the task layers. - """ - - def __init__( - self, asset_publish: AssetPublish, task_layers_to_lock: List[TaskLayer] - ): - self._asset_publish = asset_publish - self._task_layers_to_lock = task_layers_to_lock - - @property - def asset_publish(self) -> AssetPublish: - return self._asset_publish - - @property - def task_layers_to_lock(self) -> List[TaskLayer]: - return self._task_layers_to_lock - - def get_task_layer_ids_to_lock(self) -> List[str]: - return [tl.get_id() for tl in self.task_layers_to_lock] - - def lock(self) -> None: - - """ - Sets the is_locked attribute of each TaskLayer to lock in writes - metadata to disk. - """ - for meta_task_layer in self.asset_publish.metadata.meta_task_layers: - - if ( - not meta_task_layer.is_locked - and meta_task_layer.id in self.get_task_layer_ids_to_lock() - ): - meta_task_layer.is_locked = True - - self.asset_publish.write_metadata() diff --git a/scripts-blender/addons/asset_pipeline/builder/meta_util.py b/scripts-blender/addons/asset_pipeline/builder/meta_util.py deleted file mode 100644 index d4e31ccd..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/meta_util.py +++ /dev/null @@ -1,71 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -import socket -from dataclasses import asdict -from datetime import datetime -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from pathlib import Path - -import bpy - -from .task_layer import TaskLayer -from .metadata import MetadataTaskLayer, MetadataUser -from ..asset_files import AssetTask, AssetPublish - -from .. import constants - -try: - from .util import is_addon_active - import blender_kitsu.cache - kitsu_available = True -except: - kitsu_available = False - -logger = logging.getLogger("BSP") - - -def init_meta_task_layer( - task_layer: type[TaskLayer], source_asset_file: Union[AssetTask, AssetPublish] -) -> MetadataTaskLayer: - - d: Dict[str, Any] = {} - time = datetime.now() - - d["id"] = task_layer.get_id() - d["name"] = task_layer.name - - d["source_revision"] = "" # TODO: - d["source_path"] = source_asset_file.path_relative_to_asset_dir.as_posix() - d["is_locked"] = False - - d["created_at"] = time.strftime(constants.TIME_FORMAT) - d["updated_at"] = time.strftime(constants.TIME_FORMAT) - d["software_hash"] = bpy.app.build_hash.decode() - d["hostname"] = socket.gethostname() - - user_dict = dict() - if kitsu_available and is_addon_active("blender_kitsu"): - user_dict = asdict(blender_kitsu.cache.user_active_get()) - d["author"] = MetadataUser.from_dict(user_dict) - - return MetadataTaskLayer.from_dict(d) diff --git a/scripts-blender/addons/asset_pipeline/builder/metadata.py b/scripts-blender/addons/asset_pipeline/builder/metadata.py deleted file mode 100644 index afddd65f..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/metadata.py +++ /dev/null @@ -1,421 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -""" -The idea here is to have Schemas in the form of Python `Dataclasses` that can be converted to their equivalent as XML Element. That way we have a clear definition of what kind of field are expected and available. -Schemas can have nested Dataclasses. The conversion from Dataclass to XML Element happens in the `ElementMetadata` class and is automated. -Metadata Classes can also be generated from ElementClasses. This conversion is happening in the `from_element()` function. - -The code base should only work with Dataclasses. -That means it is forbidden to import Element[] classes, the conversion from and to Dataclasses is only handled in this module. - -That results in this logic: -A: Saving Metadata to file: - -> MetadataClass -> ElementClass -> XML File on Disk -B: Loading Metadata from file: - -> XML File on Disk -> ElementClass -> MetadataClass - -""" - -import inspect -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple, TypeVar, Callable -from dataclasses import dataclass, asdict, field, fields -from pathlib import Path - -from xml.etree import ElementTree as ET -from xml.etree.ElementTree import Element, ElementTree -from xml.dom import minidom - -from ..asset_status import AssetStatus - -logger = logging.getLogger("BSP") - -M = TypeVar("M", bound="MetadataClass") -E = TypeVar("E", bound="ElementMetadata") - - -class FailedToInitAssetElementTree(Exception): - pass - - -class FailedToInitMetadataTaskLayer(Exception): - pass - - -def prettify(element: Element) -> str: - xmlstr = ET.tostring(element, "utf-8") - reparse: minidom.Document = minidom.parseString(xmlstr) - pretty_str: bytes = reparse.toprettyxml(indent=" ", encoding="utf-8") - return pretty_str.decode() - - -def write_element_tree_to_file(filepath: Path, tree: ElementTree) -> None: - xmlstr = prettify(tree.getroot()) - with open(filepath.as_posix(), "w") as f: - f.write(xmlstr) - # tree.write(filepath.as_posix()) - - -def write_asset_metadata_tree_to_file( - filepath: Path, asset_metadata_tree: "MetadataTreeAsset" -) -> None: - e_tree = ElementTreeAsset.from_metadata_cls(asset_metadata_tree) - write_element_tree_to_file(filepath, e_tree) - - -def load_from_file(filepath: Path) -> ElementTree: - return ET.parse(filepath.as_posix()) - - -def load_asset_metadata_tree_from_file(filepath: Path) -> "MetadataTreeAsset": - tree = load_from_file(filepath) - asset_tree = ElementTreeAsset(element=tree.getroot()) - return MetadataTreeAsset.from_element(asset_tree) - - -def convert_value_for_xml(value: Any) -> Any: - """ - Takes as input a value and converts it so it can - be saved by to the xml format. - """ - if type(value) == bool: - return str(value).lower() - - # TODO: XML does not support Lists, add some functionality to handle the conversion - # of lists in Metadata classes to elements. - elif type(value) == list: - return "" - - elif type(value) == Path: - return value.as_posix() - - elif type(value) == AssetStatus: - # If value is AssetStatus(Enum) - # save the name as str instead of value(int), so its - # more human readable - return value.name - - return value - - -def convert_value_from_xml(element: Element) -> Any: - """ - Takes as input an element and converts the element.text - to a value that works for the MetadataClasses. - """ - value = element.text - if value == "false": - return False - elif value == "true": - return True - elif element.tag == "status": - return getattr(AssetStatus, value) - return value - - -def convert_metadata_obj_to_elements( - root_element: Element, metadata_class: M -) -> Element: - """ - This function makes sure that the input MetadataClass - will be converted to an element tree. It also handles - nested MetadataClasses respectively. The resulting tree of elements - will be appended to the input root_element. - """ - # asdict() recursively converts all dataclasses to dicts. - # even nested ones. https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict - # That's why we need to do it this way, otherwise the issubclass() check for MetadataClass - # won't work. - d = dict( - (field.name, getattr(metadata_class, field.name)) - for field in fields(metadata_class) - ) - for key, value in d.items(): - - e = Element(key) - # print(f"Processing: {key}:{value}") - # print(type(value)) - if issubclass(type(value), MetadataClass): - convert_metadata_obj_to_elements(e, value) - else: - e.text = convert_value_for_xml(value) - - root_element.append(e) - - return root_element - - -# METADATA CLASSES -# ---------------------------------------------- - - -class MetadataClass: - @classmethod - def from_dict(cls: type[M], env: Dict[str, Any]) -> M: - return cls( - **{k: v for k, v in env.items() if k in inspect.signature(cls).parameters} - ) - - @classmethod - def from_element(cls: type[M], element: Element) -> M: - d = {} - # Take care to only take fist layer with './', otherwise we would take the - # e.G the 'id' attribute of author and overwrite it. - # cannot use e.iter(). - for sub_e in element.findall("./"): - d[sub_e.tag] = convert_value_from_xml(sub_e) - return cls.from_dict(d) - - -@dataclass -class MetadataUser(MetadataClass): - """ - Tries to mirror Kitsu Asset data structure as much as possible. - """ - - id: str = "00000000-0000-0000-0000-000000000000" - first_name: str = "Unknown" - last_name: str = "Unknown" - full_name: str = "Unknown" - - -@dataclass -class MetadataTaskLayer(MetadataClass): - id: str - name: str - - source_path: str - source_revision: str - is_locked: bool - - created_at: str - updated_at: str - author: MetadataUser - software_hash: str - hostname: str - - # Optional. - flags: List[str] = field(default_factory=list) - - @classmethod - def from_element(cls: type[M], element: Element) -> M: - # For nested Metadata Classes we need to re-implement this. - d = {} - # Take care to only take fist layer with './', otherwise we would take the - # e.G the 'id' attribute of author and overwrite it. - # cannot use e.iter(). - for sub_e in element.findall("./"): - if sub_e.tag == "author": - continue - d[sub_e.tag] = convert_value_from_xml(sub_e) - - # Convert Author element to MetadataUser. - author = element.find(".author") - if author == None: - raise FailedToInitMetadataTaskLayer( - "Expected to find 'author' element in input" - ) - d["author"] = MetadataUser.from_element(element.author) - return cls.from_dict(d) - - -@dataclass -class MetadataAsset(MetadataClass): - """ - Tries to mirror Kitsu Asset data structure as much as possible. - """ - - name: str - parent_id: str - parent_name: str - project_id: str - - version: str - status: AssetStatus - - id: str = "00000000-0000-0000-0000-000000000000" - - # Optional. - flags: List[str] = field(default_factory=list) - - # This is only placeholder and will be filled when creating - task_layers_production: List[MetadataTaskLayer] = field(default_factory=list) - - -@dataclass -class MetadataTreeAsset(MetadataClass): - meta_asset: MetadataAsset - meta_task_layers: List[MetadataTaskLayer] - - @classmethod - def from_element(cls: type[M], element: "ElementTreeAsset") -> M: - # For nested Metadata Classes we need to re-implement this. - d = {} - e_asset = element.asset_element - e_task_layers: List[ElementTaskLayer] = element.get_element_task_layers() - d["meta_asset"] = MetadataAsset.from_element(e_asset) - d["meta_task_layers"] = [] - for e_tl in e_task_layers: - m_tl = MetadataTaskLayer.from_element(e_tl) - d["meta_task_layers"].append(m_tl) - - return cls.from_dict(d) - - def get_metadata_task_layer(self, id: str) -> Optional[MetadataTaskLayer]: - """ - Id == TaskLayer.get_id() - """ - for tl in self.meta_task_layers: - if tl.id == id: - return tl - return None - - def get_locked_metadata_task_layer(self) -> List[MetadataTaskLayer]: - return [tl for tl in self.meta_task_layers if tl.is_locked] - - def get_locked_task_layer_ids(self) -> List[str]: - return [tl.id for tl in self.meta_task_layers if tl.is_locked] - - def get_task_layer_ids(self) -> List[str]: - return [tl.id for tl in self.meta_task_layers] - - def add_metadata_task_layer(self, meta_tl: MetadataTaskLayer) -> None: - if meta_tl.id in self.get_task_layer_ids(): - logger.warning("Will not add metadata task layer. %s already in list", meta_tl.id) - return - self.meta_task_layers.append(meta_tl) - -# ELEMENT CLASSES -# ---------------------------------------------- -class ElementMetadata(Element): - _tag: str = "" - - def __init__(self, element: Optional[Element] = None) -> None: - super().__init__(self._tag) - # If we initialize with an element, we basically want to - # copy the content of the element in to an instance of type - # ElementMetadata to benefit from additional functions. - if element: - for child in element: - self.append(child) - - @classmethod - def from_metadata_cls(cls, meta_class: M) -> E: - # If metaclass has an ID field - # Add a "id" attribute to the element for convenient - # querying. - instance = cls() - if hasattr(meta_class, "id") and meta_class.id: - instance.attrib.update({"id": meta_class.id}) - - # This function makes sure that the input MetadataClass - # will be converted to an element tree. It also handles - # nested MetadataClasses respectively. - convert_metadata_obj_to_elements(instance, meta_class) - return instance - - -class ElementUser(ElementMetadata): - _tag: str = "User" - - -class ElementAsset(ElementMetadata): - _tag: str = "Asset" - - @property - def task_layers_production(self) -> Element: - return self.find(".task_layers_production") - - -class ElementTaskLayer(ElementMetadata): - _tag: str = "TaskLayer" - - @classmethod - def from_metadata_cls(cls, meta_class: MetadataTaskLayer) -> "ElementTaskLayer": - - instance = super().from_metadata_cls(meta_class) - - # Update Author field. - e = instance.find(".author") - e.text = e.find(".full_name").text - return instance - - @property - def author(self) -> Optional[Element]: - return self.find(".author") - - -class ElementTreeAsset(ElementTree): - @classmethod - def from_metadata_cls( - cls, meta_tree_asset: MetadataTreeAsset - ) -> "ElementTreeAsset": - # Create Asset Element and append to root. - asset_element: ElementAsset = ElementAsset.from_metadata_cls( - meta_tree_asset.meta_asset - ) - - # Create ProductionTaskLayers Element - prod_task_layers = asset_element.task_layers_production - - # TODO: I DONT UNDERSTAND: - # For some reasons the task_layers_production entry will - # be duplicated if we just use - # prod_task_layers = asset_element.task_layers_production - # no idea why, we need to first delete it and add it again??? - for i in asset_element: - if i.tag == "task_layers_production": - asset_element.remove(i) - - prod_task_layers = Element("task_layers_production") - - # Need to check for None, if element empty it is falsy. - if prod_task_layers == None: - raise FailedToInitAssetElementTree( - f"Failed to find task_layers_production child in ElementAsset Class." - ) - - # Append all meta task layers to it. - for meta_tl in meta_tree_asset.meta_task_layers: - tl_element = ElementTaskLayer.from_metadata_cls(meta_tl) - prod_task_layers.append(tl_element) - - asset_element.append(prod_task_layers) - - return cls(asset_element) - - def get_element_task_layers(self) -> List[ElementTaskLayer]: - l: List[ElementTaskLayer] = [] - for e in self.findall(".//TaskLayer"): - # We need to pass e as ElementTree otherwise we won't receive - # a full tree copy of all childrens recursively. - e_tl = ElementTaskLayer(element=e) - l.append(e_tl) - - return l - - def get_task_layer(self, id: str) -> Optional[Element]: - return self.find(f".//TaskLayer[@id='{id}']") - - @property - def asset_element(self) -> Element: - return self.getroot() diff --git a/scripts-blender/addons/asset_pipeline/builder/ops.py b/scripts-blender/addons/asset_pipeline/builder/ops.py deleted file mode 100644 index 644b4fa1..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/ops.py +++ /dev/null @@ -1,811 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from pathlib import Path - -import bpy -from bpy.app.handlers import persistent - -from . import opsdata - -from .. import asset_status, util, builder, constants -from ..asset_status import AssetStatus - -logger = logging.getLogger("BSP") - - -class BSP_ASSET_initial_publish(bpy.types.Operator): - bl_idname = "bsp_asset.initial_publish" - bl_label = "Create First Publish" - bl_description = "Creates the first publish by exporting the asset collection" - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - asset_coll = context.scene.bsp_asset.asset_collection - return bool( - util.is_file_saved() - and asset_coll - and not context.scene.bsp_asset.is_publish_in_progress - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT - and not builder.ASSET_CONTEXT.asset_publishes - ) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Update Asset Context from context so BUILD_CONTEXT works with up to date data. - builder.ASSET_CONTEXT.update_from_bl_context_push(context) - - # Create Build Context. - builder.BUILD_CONTEXT = builder.BuildContext( - builder.PROD_CONTEXT, builder.ASSET_CONTEXT - ) - - # Create Asset Builder. - builder.ASSET_BUILDER = builder.AssetBuilder(builder.BUILD_CONTEXT) - - # Publish - builder.ASSET_BUILDER.push(context) - - # Update Asset Context publish files. - builder.ASSET_CONTEXT.reload_asset_publishes() - opsdata.populate_asset_publishes_by_asset_context( - context, builder.ASSET_CONTEXT - ) - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - -class BSP_ASSET_start_publish(bpy.types.Operator): - bl_idname = "bsp_asset.start_publish" - bl_label = "Start Publish" - bl_description = "Saves .blend file and starts publish of the Asset Collection" - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - asset_coll = context.scene.bsp_asset.asset_collection - return bool( - util.is_file_saved() - and asset_coll - and not context.scene.bsp_asset.is_publish_in_progress - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - and opsdata.are_any_task_layers_enabled_push(context) - ) - - def execute(self, context: bpy.types.Context) -> Set[str]: - # Save blend file. - bpy.ops.wm.save_mainfile() - - # Update Asset Context from context so BUILD_CONTEXT works with up to date data. - builder.ASSET_CONTEXT.update_from_bl_context_push(context) - - # Update the asset publishes again. - builder.ASSET_CONTEXT.reload_asset_publishes() - - # Create Build Context. - builder.BUILD_CONTEXT = builder.BuildContext( - builder.PROD_CONTEXT, builder.ASSET_CONTEXT - ) - - # That means that the selected TaskLayers were locked in all versions. - if not builder.BUILD_CONTEXT.process_pairs: - enabled_tl_ids = [ - tl.get_id() - for tl in builder.BUILD_CONTEXT.asset_context.task_layer_assembly.get_used_task_layers() - ] - self.report( - {"WARNING"}, - f"Task Layers: {','.join(enabled_tl_ids)} are locked in all asset publishes.", - ) - builder.BUILD_CONTEXT = None - return {"CANCELLED"} - - # Make sure that the blender property group gets updated as well. - opsdata.populate_asset_publishes_by_build_context( - context, builder.BUILD_CONTEXT - ) - - # Update properties. - context.scene.bsp_asset.is_publish_in_progress = True - - # print(builder.BUILD_CONTEXT) - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - -class BSP_ASSET_start_publish_new_version(bpy.types.Operator): - bl_idname = "bsp_asset.start_publish_new_version" - bl_label = "Start Publish New Version" - bl_description = ( - "Saves .blend file and starts publish of the Asset Collection as a new Version" - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - asset_coll = context.scene.bsp_asset.asset_collection - return bool( - util.is_file_saved() - and asset_coll - and not context.scene.bsp_asset.is_publish_in_progress - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - and context.window_manager.bsp_asset.new_asset_version - and opsdata.are_any_task_layers_enabled_push(context) - ) - - def execute(self, context: bpy.types.Context) -> Set[str]: - # Save blend file. - bpy.ops.wm.save_mainfile() - - # Update Asset Context from context so BUILD_CONTEXT works with up to date data. - builder.ASSET_CONTEXT.update_from_bl_context_push(context) - - # Copy latest asset publish and increment. - asset_publish = builder.ASSET_CONTEXT.asset_dir.increment_latest_publish() - - # Add file create step of new asset publish. - builder.UNDO_CONTEXT.add_step_publish_create(context, asset_publish) - - # Update the asset publishes again. - builder.ASSET_CONTEXT.reload_asset_publishes() - - # Get task layers that need be locked resulting of the creation of the new - # asset publish with the currently enabled task layers. - lock_plans = opsdata.get_task_layer_lock_plans(builder.ASSET_CONTEXT) - opsdata.populate_context_with_lock_plans(context, lock_plans) - - # Lock task layers. - for task_layer_lock_plan in lock_plans: - task_layer_lock_plan.lock() - logger.info( - "TaskLayers locked(%s): %s", - task_layer_lock_plan.asset_publish.path.name, - ",".join(task_layer_lock_plan.get_task_layer_ids_to_lock()), - ) - - # TODO: Create Undo Step for metadata adjustment. - - # Create Build Context. - builder.BUILD_CONTEXT = builder.BuildContext( - builder.PROD_CONTEXT, builder.ASSET_CONTEXT - ) - # print(builder.BUILD_CONTEXT) - - # Make sure that the blender property group gets updated as well. - # Note: By Build context as we only want to show the relevant - # asset publishes. - opsdata.populate_asset_publishes_by_build_context( - context, builder.BUILD_CONTEXT - ) - - # Update properties. - context.scene.bsp_asset.is_publish_in_progress = True - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - -class BSP_ASSET_abort_publish(bpy.types.Operator): - bl_idname = "bsp_asset.abort_publish" - bl_label = "Abort Publish" - bl_description = "Aborts publish of the Asset Collection" - - new_files_handeling: bpy.props.EnumProperty( - items=[ - ("DELETE", "Delete", "This will delete newly created files on abort"), - ("KEEP", "Keep", "This will keep newly created files on abort"), - ] - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - return bool(context.scene.bsp_asset.is_publish_in_progress) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Undo. - # This will undo all steps that were done between start publish and the call of this function. - if self.new_files_handeling == "DELETE": - builder.UNDO_CONTEXT.undo(context) - else: - builder.UNDO_CONTEXT.clear(context) - - # Update Asset context after undo. - builder.ASSET_CONTEXT.reload_asset_publishes() - - # Reset asset publishes to global list. - opsdata.populate_asset_publishes_by_asset_context( - context, builder.ASSET_CONTEXT - ) - - # Uninitialize Build Context. - builder.BUILD_CONTEXT = None - - # Update properties. - context.scene.bsp_asset.is_publish_in_progress = False - - opsdata.clear_task_layer_lock_plans(context) - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> Set[str]: - if builder.UNDO_CONTEXT.has_steps_files_create(): - return context.window_manager.invoke_props_dialog(self, width=400) - return self.execute(context) - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - # Target. - layout.row(align=True).label( - text="This Operation can delete files on disk", icon="ERROR" - ) - layout.row(align=True).separator() - - for asset_publish in builder.UNDO_CONTEXT.asset_publishes: - layout.row(align=True).label(text=f"- {asset_publish.path.name}") - - layout.row(align=True).separator() - layout.row(align=True).label(text="How do you want to proceed?") - - layout.row(align=True).prop(self, "new_files_handeling", expand=True) - - -class BSP_ASSET_push_task_layers(bpy.types.Operator): - bl_idname = "bsp_asset.push_task_layers" - bl_label = "Apply Changes" - bl_description = ( - "Calls the push function of the Asset Builder with the current Build Context" - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - return bool( - context.scene.bsp_asset.is_publish_in_progress - and util.is_file_saved() - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT, - ) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Create Asset Builder. - builder.ASSET_BUILDER = builder.AssetBuilder(builder.BUILD_CONTEXT) - - # That means that the selected TaskLayers were locked in all versions. - # This code shouldn't be running if all previous logic goes well. - # Just in case Users might change metadata manually, lets leave it here. - if not builder.BUILD_CONTEXT.process_pairs: - enabled_tl_ids = [ - tl.get_id() - for tl in builder.BUILD_CONTEXT.asset_context.task_layer_assembly.get_used_task_layers() - ] - self.report( - {"WARNING"}, - f"Task Layers: {','.join(enabled_tl_ids)} are locked in all asset publishes.", - ) - # Abort the publish. - bpy.ops.bsp_asset.abort_publish() - return {"CANCELLED"} - - # Publish. - builder.ASSET_BUILDER.push(context) - - # There can be a case where new task layers are added during production - # While the pushing will add the new task layer to the metadata file - # the task layer list for each asset publish does not update that change. - # This fixes that. - builder.BUILD_CONTEXT.asset_context.reload_asset_publishes_metadata() - opsdata.update_asset_publishes_by_build_context(context, builder.BUILD_CONTEXT) - - # TODO: Add undo step for metadata adjustment - # and task layer push to make it undoable on abort. - - # Update properties. - context.scene.bsp_asset.are_task_layers_pushed = True - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - -def draw_task_layers_list( - layout: bpy.types.UILayout, - context: bpy.types.Context, - prop_name: str, - disable: bool = False, -) -> bpy.types.UILayout: - """ - Draws context.bsp_asset.task_layers_owner. - `prop_name`: str has to be either: 'task_layer_pull' or 'task_layer_push' - """ - - row = layout.row(align=True) - - # Ui-list. - row.template_list( - "BSP_UL_task_layers", - f"{prop_name}_list", - context.scene.bsp_asset, - prop_name, - context.scene.bsp_asset, - f"{prop_name}_index", - rows=constants.DEFAULT_ROWS, - type="DEFAULT", - ) - if disable: - row.enabled = False - - return row - - -class BSP_ASSET_pull(bpy.types.Operator): - bl_idname = "bsp_asset.pull" - bl_label = "Pull Task Layers" - bl_description = ( - "Pull in data from a set of Task Layers. The initial set is those task layers which are not owned by this file" - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - return bool( - not context.scene.bsp_asset.is_publish_in_progress - and util.is_file_saved() - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT - ) - - def invoke(self, context, event): - bsp = context.scene.bsp_asset - - for tl_owned, tl_pull in zip(bsp.task_layers_push, bsp.task_layers_pull): - tl_pull.use = not tl_owned.use - - return context.window_manager.invoke_props_dialog(self, width=400) - - def draw(self, context): - draw_task_layers_list(self.layout, context, "task_layers_pull") - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Update Asset Context from context so BUILD_CONTEXT works with up to date data. - builder.ASSET_CONTEXT.update_from_bl_context_pull(context) - - # Update the asset publishes again. - # builder.ASSET_CONTEXT.update_asset_publishes() - - # Create Build Context. - builder.BUILD_CONTEXT = builder.BuildContext( - builder.PROD_CONTEXT, builder.ASSET_CONTEXT - ) - - # Create Asset Builder. - builder.ASSET_BUILDER = builder.AssetBuilder(builder.BUILD_CONTEXT) - - # Pull. - builder.ASSET_BUILDER.pull_from_publish(context) - - return {"FINISHED"} - - -class BSP_ASSET_publish(bpy.types.Operator): - bl_idname = "bsp_asset.publish" - bl_label = "Publish" - bl_description = "Publishes the pushed changes on SVN" - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - - return bool( - context.scene.bsp_asset.is_publish_in_progress - and util.is_file_saved() - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - and context.scene.bsp_asset.are_task_layers_pushed - ) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Placeholder - - # Commit to SVN. - - # Reset asset publishes to global list. - opsdata.populate_asset_publishes_by_asset_context( - context, builder.ASSET_CONTEXT - ) - opsdata.clear_task_layer_lock_plans(context) - - # Uninitialize Build Context. - builder.BUILD_CONTEXT = None - - # Update properties. - context.scene.bsp_asset.is_publish_in_progress = False - context.scene.bsp_asset.are_task_layers_pushed = False - - # Clear undo context. - builder.UNDO_CONTEXT.clear(context) - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - -class BSP_ASSET_create_prod_context(bpy.types.Operator): - bl_idname = "bsp_asset.create_prod_context" - bl_label = "Create Production Context" - bl_description = ( - "Process config files in production config folder. Loads all task layers." - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - addon_prefs = util.get_addon_prefs() - return bool(addon_prefs.is_prod_task_layers_module_path_valid()) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Initialize Asset Context. - addon_prefs = util.get_addon_prefs() - config_folder = Path(addon_prefs.prod_config_dir) - builder.PROD_CONTEXT = builder.ProductionContext(config_folder) - - # print(builder.PROD_CONTEXT) - - # When we run this operator to update the production context - # We also want the asset context to be updated. - if bpy.ops.bsp_asset.create_asset_context.poll(): - bpy.ops.bsp_asset.create_asset_context() - - return {"FINISHED"} - - -class BSP_ASSET_create_asset_context(bpy.types.Operator): - bl_idname = "bsp_asset.create_asset_context" - bl_label = "Create Asset Context" - bl_description = ( - "Initialize Asset Context from Production Context. " - "Try to restore Task Layer Settings for this Asset" - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - asset_coll: bpy.types.Collection = context.scene.bsp_asset.asset_collection - return bool(builder.PROD_CONTEXT and asset_coll and bpy.data.filepath) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Initialize Asset Context. - builder.ASSET_CONTEXT = builder.AssetContext(context, builder.PROD_CONTEXT) - - # Populate collection property with loaded task layers. - opsdata.populate_task_layers(context, builder.ASSET_CONTEXT) - - # Populate collection property with found asset publishes. - opsdata.populate_asset_publishes_by_asset_context( - context, builder.ASSET_CONTEXT - ) - - # Update Asset Context from bl context again, as populate - # task layers tries to restore previous task layer selection states. - builder.ASSET_CONTEXT.update_from_bl_context_push(context) - - # print(builder.ASSET_CONTEXT) - return {"FINISHED"} - - -class BSP_ASSET_set_task_layer_status(bpy.types.Operator): - bl_idname = "bsp_asset.set_task_layer_status" - bl_label = "Set Task Layer Status" - bl_description = "Sets the Status of a Task Layer of a specific Asset Publish, which controls the is_locked attribute" - - @staticmethod - def get_current_state(self: bpy.types.Operator) -> str: - # Get Metadata Task Layer. - asset_publish = opsdata.get_active_asset_publish(bpy.context) - m_tl = asset_publish.metadata.get_metadata_task_layer(self.task_layer) - return "locked" if m_tl.is_locked else "live" - - target: bpy.props.StringProperty(name="Target") # type: ignore - task_layer: bpy.props.EnumProperty( # type: ignore - items=opsdata.get_task_layers_for_bl_enum, - name="Task Layer", - description="Task Layer for which to change the Status", - ) - current_status: bpy.props.StringProperty( # type: ignore - name="Current Status", - description="Current State of selected Task Layer", - get=get_current_state.__func__, - ) - new_status: bpy.props.EnumProperty( # type: ignore - items=[("locked", "locked", ""), ("live", "live", "")], - name="New Status", - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - asset_coll = context.scene.bsp_asset.asset_collection - return bool( - util.is_file_saved() - and asset_coll - and not context.scene.bsp_asset.is_publish_in_progress - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - ) - - def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> Set[str]: - # Get selected asset publish. - self.asset_publish = opsdata.get_active_asset_publish(context) - - # Update target attribute. - self.target = self.asset_publish.path.name - - return context.window_manager.invoke_props_dialog(self, width=400) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Exit if no status change. - if self.new_status == self.current_status: - return {"CANCELLED"} - - # Update locked state. - is_locked = True if self.new_status == "locked" else False - self.asset_publish.metadata.get_metadata_task_layer( - self.task_layer - ).is_locked = is_locked - - # Write metadata to file. - self.asset_publish.write_metadata() - - # Log. - logger.info( - f"Set {self.asset_publish.path.name} {self.task_layer} Task Layer Status: {self.new_status}" - ) - - # Reset attributes. - del self.asset_publish - - # Reload asset publishes. - builder.ASSET_CONTEXT.reload_asset_publishes_metadata() - opsdata.populate_asset_publishes_by_asset_context( - context, builder.ASSET_CONTEXT - ) - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - # Target. - row = layout.row(align=True) - row.prop(self, "target") - row.enabled = False - - # Task Layer. - row = layout.row(align=True) - row.prop(self, "task_layer") - - # Current State. - row = layout.row(align=True) - row.prop(self, "current_status") - - layout.separator() - layout.separator() - - # New State. - row = layout.row(align=True) - row.prop(self, "new_status") - - -class BSP_ASSET_set_asset_status(bpy.types.Operator): - bl_idname = "bsp_asset.set_asset_status" - bl_label = "Set Asset Status" - bl_description = "Sets the Status of a specific Asset Publish" - - @staticmethod - def get_current_status(self: bpy.types.Operator) -> str: - # Get Metadata Task Layer. - asset_publish = opsdata.get_active_asset_publish(bpy.context) - return asset_publish.metadata.meta_asset.status.name.capitalize() - - target: bpy.props.StringProperty(name="Target") # type: ignore - - current_status: bpy.props.StringProperty( # type: ignore - name="Current Status", - description="Current State of selected Task Layer", - get=get_current_status.__func__, - ) - new_status: bpy.props.EnumProperty( # type: ignore - items=asset_status.get_asset_status_as_bl_enum, - name="New Status", - ) - - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - asset_coll = context.scene.bsp_asset.asset_collection - return bool( - util.is_file_saved() - and asset_coll - and not context.scene.bsp_asset.is_publish_in_progress - and builder.PROD_CONTEXT - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - ) - - def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> Set[str]: - # Get selected asset publish. - self.asset_publish = opsdata.get_active_asset_publish(context) - - # Update target attribute. - self.target = self.asset_publish.path.name - - return context.window_manager.invoke_props_dialog(self, width=400) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - status = AssetStatus(int(self.new_status)) - - # Current status is in in int, convert new status to it so - # we can compare. - # Exit if no status change. - if status.name == self.current_status.upper(): - return {"CANCELLED"} - - # Update Assset Status. - self.asset_publish.metadata.meta_asset.status = status - - # Write metadata to file. - self.asset_publish.write_metadata() - - # Log. - logger.info(f"Set {self.asset_publish.path.name} Asset Status: {status.name}") - - # Reset attributes. - del self.asset_publish - - # Reload asset publishes. - builder.ASSET_CONTEXT.reload_asset_publishes_metadata() - opsdata.populate_asset_publishes_by_asset_context( - context, builder.ASSET_CONTEXT - ) - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - # Target. - row = layout.row(align=True) - row.prop(self, "target") - row.enabled = False - - # Current State. - row = layout.row(align=True) - row.prop(self, "current_status") - - layout.separator() - layout.separator() - - # New State. - row = layout.row(align=True) - row.prop(self, "new_status") - - -@persistent -def create_undo_context(_): - builder.UNDO_CONTEXT = builder.UndoContext() - builder.UNDO_CONTEXT.update_from_bl_context(bpy.context) - - -@persistent -def create_asset_context(_): - # We want this to run on every scene load. - # As active assets might change after scene load. - if bpy.ops.bsp_asset.create_asset_context.poll(): - bpy.ops.bsp_asset.create_asset_context() - else: - # That means we load a scene with no asset collection - # assigned. Previous ASSET_CONTEXT should therefore - # be uninitialized. - logger.error( - "Failed to initialize Asset Context. bpy.ops.bsp_asset.create_asset_context.poll() failed." - ) - builder.ASSET_CONTEXT = None - opsdata.clear_asset_publishes(bpy.context) - opsdata.clear_task_layers(bpy.context) - - -@persistent -def create_prod_context(_): - - # Should only run once on startup. - if not builder.PROD_CONTEXT: - if bpy.ops.bsp_asset.create_prod_context.poll(): - bpy.ops.bsp_asset.create_prod_context() - else: - logger.error( - "Failed to initialize Production Context. bpy.ops.bsp_asset.create_prod_context.poll() failed." - ) - builder.PROD_CONTEXT = None - - -# ----------------REGISTER--------------. - -classes = [ - BSP_ASSET_create_prod_context, - BSP_ASSET_create_asset_context, - BSP_ASSET_initial_publish, - BSP_ASSET_start_publish, - BSP_ASSET_start_publish_new_version, - BSP_ASSET_abort_publish, - BSP_ASSET_push_task_layers, - BSP_ASSET_pull, - BSP_ASSET_publish, - BSP_ASSET_set_task_layer_status, - BSP_ASSET_set_asset_status, -] - - -def register() -> None: - for cls in classes: - bpy.utils.register_class(cls) - - # Handlers. - bpy.app.handlers.load_post.append(create_prod_context) - bpy.app.handlers.load_post.append(create_asset_context) - bpy.app.handlers.load_post.append(create_undo_context) - - -def unregister() -> None: - - # Handlers. - bpy.app.handlers.load_post.remove(create_undo_context) - bpy.app.handlers.load_post.remove(create_asset_context) - bpy.app.handlers.load_post.remove(create_prod_context) - - for cls in reversed(classes): - bpy.utils.unregister_class(cls) diff --git a/scripts-blender/addons/asset_pipeline/builder/opsdata.py b/scripts-blender/addons/asset_pipeline/builder/opsdata.py deleted file mode 100644 index 9957067f..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/opsdata.py +++ /dev/null @@ -1,235 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from pathlib import Path - -import bpy - -from .context import AssetContext, BuildContext -from .task_layer import TaskLayer -from .lock_plan import TaskLayerLockPlan - -from .. import builder -from ..asset_files import AssetPublish - -logger = logging.getLogger("BSP") - - -def populate_task_layers( - context: bpy.types.Context, asset_context: AssetContext -) -> None: - - for prop_group in [ - context.scene.bsp_asset.task_layers_push, - context.scene.bsp_asset.task_layers_pull, - ]: - # Make a backup to restore task layer settings as good as possible. - tmp_backup: Dict[str, Dict[str, Any]] = {} - for ( - task_layer_id, - task_layer_prop_group, - ) in prop_group.items(): - tmp_backup[task_layer_id] = task_layer_prop_group.as_dict() - - # Clear task layer collection property. - prop_group.clear() - - # Load Task Layers from Production Context, try to restore - # previous task layer settings - for ( - key, - task_layer_config, - ) in asset_context.task_layer_assembly.task_layer_config_dict.items(): - item = prop_group.add() - item.name = key - item.task_layer_id = key - item.task_layer_name = task_layer_config.task_layer.name - - # Restore previous settings. - if key in tmp_backup: - bkp = tmp_backup.get(key) - if not bkp: - continue - item.use = bkp["use"] - - # Update actual ASSET_CONTEXT, which will transfer the task layer settings, - # which we restored from scene level. - task_layer_config.use = bkp["use"] - - -def add_asset_publish_to_context( - context: bpy.types.Context, asset_publish: AssetPublish -) -> None: - - item = context.scene.bsp_asset.asset_publishes.add() - item.update_props_by_asset_publish(asset_publish) - - -def update_asset_publishes_by_build_context( - context: bpy.types.Context, build_context: BuildContext -) -> None: - - for asset_publish in build_context.asset_publishes: - item = context.scene.bsp_asset.asset_publishes.get(asset_publish.path.name) - if item: - item.update_props_by_asset_publish(asset_publish) - - -def populate_asset_publishes_by_asset_context( - context: bpy.types.Context, asset_context: AssetContext -) -> None: - - """ - This populates the context with asset publishes based on the asset context. - Meaning it will take all found asset publishes (asset_context.asset_publishes). - """ - - # Clear asset_publishes collection property. - clear_asset_publishes(context) - - # Load Asset Publishes from Asset Context. - for asset_publish in asset_context.asset_publishes: - add_asset_publish_to_context(context, asset_publish) - - -def populate_asset_publishes_by_build_context( - context: bpy.types.Context, build_context: BuildContext -) -> None: - """ - This populates the context with asset publishes based on the build context. - Meaning it will only take the asset publishes it will find in - build_context.process_pairs. - """ - - # Clear asset_publishes collection property. - clear_asset_publishes(context) - - # Load Asset Publishes from Asset Context. - for process_pair in build_context.process_pairs: - asset_publish = process_pair.asset_publish - add_asset_publish_to_context(context, asset_publish) - - -def clear_task_layers(context: bpy.types.Context) -> None: - context.scene.bsp_asset.task_layers_push.clear() - context.scene.bsp_asset.task_layers_pull.clear() - - -def clear_task_layer_lock_plans(context: bpy.types.Context) -> None: - context.scene.bsp_asset.task_layer_lock_plans.clear() - - -def clear_asset_publishes(context: bpy.types.Context) -> None: - context.scene.bsp_asset.asset_publishes.clear() - - -def get_active_asset_publish(context: bpy.types.Context) -> AssetPublish: - index = context.scene.bsp_asset.asset_publishes_index - asset_file = context.scene.bsp_asset.asset_publishes[index] - return AssetPublish(asset_file.path) - - -def get_task_layers_for_bl_enum( - self: bpy.types.Operator, context: bpy.types.Context -) -> List[Tuple[str, str, str]]: - if not builder.ASSET_CONTEXT: - return [] - return builder.ASSET_CONTEXT.task_layer_assembly.get_task_layers_for_bl_enum() - - -def get_task_layer_lock_plans(asset_context: AssetContext) -> List[TaskLayerLockPlan]: - - """ - This function should be called when you want to know which task layers of which asset publishes - need to be locked after creating a new asset publish with a selection of task layers. - This information will be returned in the form of a List of TaskLayerLockPlan classes. - """ - - task_layer_lock_plans: List[TaskLayerLockPlan] = [] - task_layers_to_push = asset_context.task_layer_assembly.get_used_task_layers() - - for asset_publish in asset_context.asset_publishes[:-1]: - - task_layers_to_lock: List[TaskLayer] = [] - - for task_layer in task_layers_to_push: - - # This is an interesting case, that means the task layer is not even in the assset publish - # metadata file. Could happen if there was a new production task layer added midway production. - if task_layer.get_id() not in asset_publish.metadata.get_task_layer_ids(): - # TODO: How to handle this case? - logger.warning( - "TaskLayer: %s does not exist in %s. Maybe added during production?", - task_layer.get_id(), - asset_publish.metadata_path.name, - ) - continue - - # Task Layer is already locked. - if ( - task_layer.get_id() - in asset_publish.metadata.get_locked_task_layer_ids() - ): - continue - - # Otherwise this Task Layer should be locked. - task_layers_to_lock.append(task_layer) - - # If task layers need to be locked - # Store that in TaskLayerLockPlan. - if task_layers_to_lock: - task_layer_lock_plans.append( - TaskLayerLockPlan(asset_publish, task_layers_to_lock) - ) - - return task_layer_lock_plans - - -def populate_context_with_lock_plans( - context: bpy.types.Context, lock_plan_list: List[TaskLayerLockPlan] -) -> None: - - context.scene.bsp_asset.task_layer_lock_plans.clear() - - # Add asset publishes. - for lock_plan in lock_plan_list: - item = context.scene.bsp_asset.task_layer_lock_plans.add() - item.path_str = lock_plan.asset_publish.path.as_posix() - - # Add task layers to lock for that asset publish. - for tl_to_lock in lock_plan.task_layers_to_lock: - tl_item = item.task_layers.add() - tl_item.name = tl_to_lock.get_id() - tl_item.task_layer_id = tl_to_lock.get_id() - tl_item.task_layer_name = tl_to_lock.name - - -def are_any_task_layers_enabled_push(context: bpy.types.Context) -> bool: - """ - Returns true if any task layers are selected in the task layer push list. - """ - bsp = context.scene.bsp_asset - enabled_task_layers = [ - tlg for tlg in bsp.task_layers_push.values() if tlg.use - ] - return bool(enabled_task_layers) \ No newline at end of file diff --git a/scripts-blender/addons/asset_pipeline/builder/scripts/push.py b/scripts-blender/addons/asset_pipeline/builder/scripts/push.py deleted file mode 100644 index f5c749ed..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/scripts/push.py +++ /dev/null @@ -1,157 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -""" -As the publish process requires a number of more complex operations, we need to actually have a Blender Instance -opening that file and then executing the operations. -This script can be passed as -P option when starting a blender exe. -It needs a pickle_path after -- . The pickle path contains a pickled BuildContext from the AssetTask. -This BuildContext will be unpickled in this script and processed, which means performing -the publish of the selected TaskLayers in the AssetTask. -""" -import traceback -import logging -import pickle -import sys - -from typing import List, Dict, Union, Any, Set, Optional -from pathlib import Path - -from asset_pipeline import prop_utils -from asset_pipeline.builder import AssetBuilder -from asset_pipeline.builder.context import BuildContext -from asset_pipeline.asset_files import AssetPublish - -import bpy - -logger = logging.getLogger("BSP") - -# Get cli input. -argv = sys.argv -# logger.info(argv) -argv = argv[argv.index("--") + 1 :] - -logger.info("\n" * 2) -logger.info(f"STARTING NEW BLENDER: {bpy.data.filepath}") -logger.info("RUNNING PUSH SCRIPT") -logger.info("------------------------------------") - -try: - argv[0] -except IndexError: - logger.error("Supply pickle path as first argument after '--'.") - sys.exit(1) - -# Check if pickle path is valid. -pickle_path: str = argv[0] - -if not pickle_path: - logger.error("Supply valid pickle path as first argument after '--'.") - sys.exit(1) - -pickle_path: Path = Path(pickle_path) - -if not pickle_path.exists(): - logger.error(f"Pickle path does not exist: {pickle_path.as_posix()}") - sys.exit(1) - - -def exception_handler(func): - def func_wrapper(*args, **kwargs): - try: - return func(*args, **kwargs) - - except Exception as error: - print("\n" *2) - print(f"------------------------EXCEPTION({Path(bpy.data.filepath).name})------------------------") - exc_info = sys.exc_info() - traceback.print_exception(*exc_info) - del exc_info - print("\n" *2) - sys.exit(1) # Enables us to show warning in UI - - return func_wrapper - - -@exception_handler -def run(): - # Load pickle - logger.info(f"LOADING PICKLE: %s", pickle_path.as_posix()) - with open(pickle_path.as_posix(), "rb") as f: - BUILD_CONTEXT: BuildContext = pickle.load(f) - - # If first publish, only link in asset collection and update properties. - if not BUILD_CONTEXT.asset_publishes: - asset_publish = AssetPublish(Path(bpy.data.filepath)) - asset_coll = BUILD_CONTEXT.asset_context.asset_collection - # Update scene asset collection. - bpy.context.scene.bsp_asset.asset_collection = asset_coll - - # Update asset collection properties. - asset_coll.bsp_asset.update_props_by_asset_publish(asset_publish) - - # Link in asset collection in scene. - bpy.context.scene.collection.children.link(asset_coll) - bpy.context.scene.bsp_asset.asset_collection = asset_coll - - # Delete pickle. - pickle_path.unlink() - logger.info("Deleted pickle: %s", pickle_path.name) - - # Shutdown Blender. - bpy.ops.wm.save_mainfile() - bpy.ops.wm.quit_blender() - sys.exit(0) - - logger.info("LOAD TRANSFER SETTINGS") - - # Fetch transfer settings from AssetContext and update scene settings - # as they are the ones that are used by the pull() process. - TRANSFER_SETTINGS = bpy.context.scene.bsp_asset_transfer_settings - for prop_name, prop in prop_utils.get_property_group_items(TRANSFER_SETTINGS): - try: - value = BUILD_CONTEXT.asset_context.transfer_settings[prop_name] - except KeyError: - continue - else: - setattr(TRANSFER_SETTINGS, prop_name, value) - logger.info("Loaded setting(%s: %s)", prop_name, str(value)) - - logger.info(BUILD_CONTEXT) - - logger.info( - f"IMPORTING ASSET COLLECTION FROM TASK: %s", - BUILD_CONTEXT.asset_task.path.as_posix(), - ) - - ASSET_BUILDER = AssetBuilder(BUILD_CONTEXT) - - ASSET_BUILDER.pull_from_task(bpy.context) - - # Delete pickle. - pickle_path.unlink() - logger.info("Deleted pickle: %s", pickle_path.name) - - # Shutdown Blender. - bpy.ops.wm.save_mainfile() - bpy.ops.wm.quit_blender() - sys.exit(0) - - -run() diff --git a/scripts-blender/addons/asset_pipeline/builder/task_layer.py b/scripts-blender/addons/asset_pipeline/builder/task_layer.py deleted file mode 100644 index 6dd6c629..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/task_layer.py +++ /dev/null @@ -1,380 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from types import ModuleType - -from pathlib import Path - -import bpy -from bpy_extras.id_map_utils import get_id_reference_map - -from .asset_mapping import AssetTransferMapping -from ..util import unlink_collections_recursive -from ..constants import FULLY_OWNED_SUFFIX - -logger = logging.getLogger("BSP") - - -# TODO: Do we maybe need a BaseTask Layer that has default order = 0 -# and has no transfer_data function? -# The Base Task Layer gives us the base data on which we apply all other -# TaskLayers. Merging this layer just means, take it as a starting point. -# Note: Right now the Asset Importer already handles this logic by checking if the -# asset task source has the TaskLayer with the lowest order enabled and creates a TARGET collection. - - -class TaskLayer: - """ - This class is more or less boilerplate so Users can easily write their TaskLayer - in the production config file. Users need to implement the transfer_data function - and fille out the class attributes. - """ - name: str = "" - description: str = "" - order: int = -1 - task_suffix: str = "" - - @classmethod - def get_id(cls) -> str: - """ - Used to uniquely identify a TaskLayer as we expect that there are not 2 TaskLayer Classes - That have the same name. - """ - return cls.__name__ - - @classmethod - def is_valid(cls) -> bool: - return bool(cls.name and cls.order >= 0) - - @classmethod - def transfer( - cls, - context: bpy.types.Context, - build_context: "BuildContext", # Otherwise get stupid circular import errors. - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - cls.transfer_collections(transfer_mapping) - cls.transfer_data(context, build_context, transfer_mapping, transfer_settings) - cls.assign_objects(transfer_mapping) - cls.fix_geonode_modifiers() - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: "BuildContext", # Otherwise get stupid circular import errors. - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - - # TODO: transfer_settings can be None if Users didn't provide a - # TransferSettings class in the task layer module. We should update this. - """ - The AssetTranfserMapping class represents a mapping between a source and a target. - It contains an object mapping which connects each source object with a target. - The maps are just dictionaries where the key is the source and the value the target. - Both key and target are actual Blender ID Datablocks. - This makes it easy to write Merge Instructions. - With it you can do access things like: - - transfer_mapping.object_map: Dict[bpy.types.Object, bpy.types.Object] - transfer_mapping.collection_map: Dict[bpy.types.Collection, bpy.types.Collection] - transfer_mapping.material_map: Dict[bpy.types.Material, bpy.types.Material] - - For all mappings: - Key: Source - Value: Target - - You can also access the root Asset source and Target Collection: - transfer_mapping.source_coll: bpy.types.Collection - transfer_mapping.target_coll: bpy.types.Collection - - Further than that you can access to objects which had no match. - transfer_mapping.no_match_target_objs: Set[bpy.types.Object] (all objs that exist in target but not in source) - transfer_mapping.no_match_source_objs: Set[bpy.types.Object] (vice versa) - - - Further then that Users can define custom transfer settings by defining a TransferSettings - Class which inherits from a PropertyGroup in the task_layer module. Users can query these settings - by checking the transfer_settings argument. - - transfer_settings.custom_option - """ - raise NotImplementedError - - @classmethod - def get_task_collections(cls, root_coll: bpy.types.Collection) -> Set[bpy.types.Collection]: - """Return direct children of an Asset Collection who have the suffix of this Task Layer (eg. "einar.rig"). - The root_coll that is the Asset Collection can be either the .TASK or .PUBLISH or .TARGET collection. - """ - transfer_suffix = root_coll.bsp_asset.transfer_suffix - task_collections = set() - - for c in root_coll.children: - if cls.task_suffix and c.name.replace(transfer_suffix, "").endswith(cls.task_suffix): - task_collections.add(c) - - return task_collections - - - @classmethod - def transfer_collections(cls, transfer_mapping: AssetTransferMapping): - source_root_coll = transfer_mapping.source_coll - - # Empty target collections that end in ".FULLY_OWNED". - fully_owned_colls = {c for c in transfer_mapping.target_coll.children_recursive if FULLY_OWNED_SUFFIX in c.name} - for fully_owned_coll in fully_owned_colls: - cls.recursive_clear_fully_owned_target_coll(fully_owned_coll, transfer_mapping) - - for src_coll in cls.get_task_collections(source_root_coll): - cls.transfer_collection_objects(transfer_mapping, src_coll, source_root_coll) - - # Unlink target collections that no longer exist in source. - for target_coll in transfer_mapping.target_coll.children: - if cls.task_suffix and cls.task_suffix in target_coll.name: - unlink_collections_recursive(target_coll, transfer_mapping.no_match_target_colls) - - @classmethod - def recursive_clear_fully_owned_target_coll(cls, coll, transfer_mapping): - if not cls.task_suffix or cls.task_suffix not in coll.name: - return - - for ob in coll.objects[:]: - coll.objects.unlink(ob) - # The object mapping also needs to be removed (this should be more effective than purging, I think) - for key in list(transfer_mapping.object_map.keys()): - if transfer_mapping.object_map[key] == ob: - del transfer_mapping._object_map[key] - - for subcoll in coll.children[:]: - coll.children.unlink(subcoll) - # # The collection mapping also needs to be removed. - for key in list(transfer_mapping.collection_map.keys()): - if transfer_mapping.collection_map[key] == subcoll: - del transfer_mapping._collection_map[key] - cls.recursive_clear_fully_owned_target_coll(subcoll, transfer_mapping) - bpy.data.collections.remove(subcoll) # Just to free up the name, so we avoid a .001 suffix when the target collection is (re-)created later. - - - @classmethod - def transfer_collection_objects(cls, - transfer_mapping: AssetTransferMapping, - src_coll: bpy.types.Collection, - src_parent_coll: bpy.types.Collection): - """ - Recursively transfer object assignments from source to target collections. - If an object ends up being un-assigned, it may get purged. - New collections will be created as necessary. - """ - - # Create target collection if necessary. - tgt_coll = transfer_mapping.collection_map.get(src_coll) - if not tgt_coll: - src_suffix = transfer_mapping.source_coll.bsp_asset.transfer_suffix - tgt_suffix = transfer_mapping.target_coll.bsp_asset.transfer_suffix - tgt_coll = bpy.data.collections.new(src_coll.name.replace(src_suffix, tgt_suffix)) - tgt_coll.hide_viewport = src_coll.hide_viewport - tgt_coll.hide_render = src_coll.hide_render - transfer_mapping._collection_map[src_coll] = tgt_coll - tgt_parent = transfer_mapping.collection_map.get(src_parent_coll) - assert tgt_parent, "The corresponding target parent collection should've been created in the previous recursion: " + src_coll.name - tgt_parent.children.link(tgt_coll) - - # Un-assigning everything from the target coll. - for o in tgt_coll.objects: - tgt_coll.objects.unlink(o) - - # Re-assign those objects which correspond to the ones in source coll. - for src_ob in src_coll.objects: - tgt_ob = transfer_mapping.object_map.get(src_ob) - if not tgt_ob: - # Allow task layers to add objects that didn't previously exist in target coll. - tgt_ob = src_ob - ref_map = get_id_reference_map() - if src_ob in ref_map: - for dependency in ref_map[src_ob]: - if type(dependency) == bpy.types.Object: - tgt_dependency = transfer_mapping.object_map.get(dependency) - if tgt_dependency: - dependency.user_remap(tgt_dependency) - tgt_coll.objects.link(tgt_ob) - - # Do the same recursively for child collections. - for child_coll in src_coll.children: - cls.transfer_collection_objects(transfer_mapping, child_coll, src_coll) - - @classmethod - def assign_objects(cls, - transfer_mapping: AssetTransferMapping): - """ - Unassign remaining source collections/objects and replace them with - target collections/objects for the whole file. - """ - # iterate through all collections in the file - for coll in list(bpy.data.collections): - collection_map = transfer_mapping.collection_map - transfer_collections = set().union(*[{k, v} for k, v in collection_map.items()]) - if coll in transfer_collections: - continue - for child_coll in coll.children: - if child_coll not in collection_map: - continue - if child_coll in {transfer_mapping.source_coll, transfer_mapping.target_coll}: - continue - tgt_coll = collection_map.get(child_coll) - if not tgt_coll: - continue - coll.children.unlink(child_coll) - coll.children.link(tgt_coll) - for ob in coll.objects: - if ob not in transfer_mapping.object_map: - continue - tgt_ob = transfer_mapping.object_map.get(ob) - if not tgt_ob: - continue - coll.objects.unlink(ob) - coll.objects.link(tgt_ob) - ob.user_remap(tgt_ob) - - @classmethod - def fix_geonode_modifiers(cls): - """Workaround to a weird issue where some GeoNode modifier inputs disappear...""" - for o in bpy.data.objects: - if o.type != 'MESH': - continue - for m in o.modifiers: - if m.type == 'NODES': - m.node_group = m.node_group - - def __repr__(self) -> str: - return f"TaskLayer{self.name}" - - -class TaskLayerConfig: - """ - This Class holds a TaskLayer and additional Information that - determine how this TaskLayer is handeled during build. - For example .use controls if TaskLayer should be used for build. - """ - - def __init__(self, task_layer: type[TaskLayer]): - self._task_layer = task_layer - self._use: bool = False - - @property - def task_layer(self) -> type[TaskLayer]: - return self._task_layer - - @property - def use(self) -> bool: - return self._use - - @use.setter - def use(self, value: bool) -> None: - self._use = value - - def reset(self) -> None: - self._use = False - - def __repr__(self) -> str: - return f"{self.task_layer.name}(use: {self.use})" - - -class TaskLayerAssembly: - - """ - This Class holds all TaskLayers relevant for the build. - Each TaskLayer is stored as a TaskLayerConfig object which provides - the additional information. - """ - - def __init__(self, task_layers: List[type[TaskLayer]]): - # Create a dictionary data structure here, so we can easily control - # from within Blender by string which TaskLayers to enable and disable for built. - # As key we will use the class.get_id() attribute of each TaskLayer. (Should be unique) - self._task_layer_config_dict: Dict[str, TaskLayerConfig] = {} - self._task_layers = task_layers - self._task_layer_configs: List[TaskLayerConfig] = [] - # For each TaskLayer create a TaskLayerConfig and add entry in - # dictionary. - for task_layer in task_layers: - - # Make sure that for whatever reason there are no 2 identical TaskLayer. - if task_layer.get_id() in self._task_layer_config_dict: - - self._task_layer_config_dict.clear() - raise Exception( - f"Detected 2 TaskLayers with the same Class name. [{task_layer.get_id()}]" - ) - tc = TaskLayerConfig(task_layer) - self._task_layer_configs.append(tc) - self._task_layer_config_dict[task_layer.get_id()] = tc - - # Sort lists. - self._task_layer_configs.sort(key=lambda tc: tc.task_layer.order) - self._task_layers.sort(key=lambda tl: tl.order) - - def get_task_layer_config(self, key: str) -> TaskLayerConfig: - return self._task_layer_config_dict[key] - - def get_used_task_layers(self) -> List[type[TaskLayer]]: - return [tc.task_layer for tc in self.task_layer_configs if tc.use] - - def get_used_task_layer_ids(self) -> List[str]: - return [t.get_id() for t in self.get_used_task_layers()] - - def get_task_layers_for_bl_enum(self) -> List[Tuple[str, str, str]]: - return [(tl.get_id(), tl.name, tl.description) for tl in self.task_layers] - - @property - def task_layer_config_dict(self) -> Dict[str, TaskLayerConfig]: - return self._task_layer_config_dict - - @property - def task_layer_configs(self) -> List[TaskLayerConfig]: - return self._task_layer_configs - - @property - def task_layers(self) -> List[type[TaskLayer]]: - return self._task_layers - - @property - def task_layer_names(self) -> List[str]: - return [l.name for l in self.task_layers] - - def get_task_layer_orders(self, only_used: bool = False) -> List[int]: - """ - Returns a list of all TaskLayers.order values. - """ - if not only_used: - return [t.order for t in self.task_layers] - else: - return [tc.task_layer.order for tc in self.task_layer_configs if tc.use] - - def __repr__(self) -> str: - body = f"{', '.join([str(t) for t in self.task_layer_configs])}" - return f"TaskLayerAssembly: ({body})" - - def __bool__(self) -> bool: - return bool(self._task_layer_config_dict) diff --git a/scripts-blender/addons/asset_pipeline/builder/ui.py b/scripts-blender/addons/asset_pipeline/builder/ui.py deleted file mode 100644 index 8e9316ea..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/ui.py +++ /dev/null @@ -1,534 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -from pathlib import Path -from typing import List, Dict, Union, Any, Set, Optional - -import bpy - -from .ops import ( - draw_task_layers_list, - BSP_ASSET_initial_publish, - BSP_ASSET_start_publish, - BSP_ASSET_start_publish_new_version, - BSP_ASSET_abort_publish, - BSP_ASSET_create_prod_context, - BSP_ASSET_create_asset_context, - BSP_ASSET_push_task_layers, - BSP_ASSET_pull, - BSP_ASSET_publish, - BSP_ASSET_set_task_layer_status, - BSP_ASSET_set_asset_status, -) -from .. import builder, constants, prop_utils, util - -try: - from .util import is_addon_active - import blender_kitsu.cache - kitsu_available = True -except: - kitsu_available = False - - -def poll_asset_collection_not_init(context: bpy.types.Context) -> bool: - return not bool(context.scene.bsp_asset.asset_collection) - - -def poll_error_invalid_task_layer_module_path() -> bool: - addon_prefs = util.get_addon_prefs() - return bool(not addon_prefs.is_prod_task_layers_module_path_valid()) - - -def poll_error_file_not_saved() -> bool: - return not bool(bpy.data.filepath) - - -def poll_error(context: bpy.types.Context) -> bool: - return ( - poll_asset_collection_not_init(context) - or poll_error_file_not_saved() - or poll_error_invalid_task_layer_module_path() - ) - - -def draw_error_invalid_task_layer_module_path( - box: bpy.types.UILayout, -) -> bpy.types.UILayout: - row = box.row(align=True) - row.label(text="Invalid Task Layer Module") - row.operator( - "preferences.addon_show", text="Open Addon Preferences" - ).module = "asset_pipeline" - - -def draw_error_file_not_saved( - box: bpy.types.UILayout, -) -> bpy.types.UILayout: - row = box.row(align=True) - row.label(text="File needs to be saved") - - -def draw_error_asset_collection_not_init( - box: bpy.types.UILayout, -) -> bpy.types.UILayout: - box.row().label(text="Initialize Asset Collection") - - -def draw_affected_asset_publishes_list( - self: bpy.types.Panel, - context: bpy.types.Context, - disable: bool = False, -) -> bpy.types.UILayout: - layout: bpy.types.UILayout = self.layout - - box = layout.box() - row = box.row(align=True) - row.label(text="Asset Publishes") - row.operator(BSP_ASSET_create_asset_context.bl_idname, icon="FILE_REFRESH", text="") - - # Ui-list. - row = box.row() - row.template_list( - "BSP_UL_affected_asset_publishes", - "affected_asset_publishes_list", - context.scene.bsp_asset, - "asset_publishes", - context.scene.bsp_asset, - "asset_publishes_index", - rows=constants.DEFAULT_ROWS, - type="DEFAULT", - ) - if disable: - row.enabled = False - - return box - - -def draw_task_layer_lock_plans_on_new_publish( - self: bpy.types.Panel, - context: bpy.types.Context, - disable: bool = False, -) -> bpy.types.UILayout: - layout: bpy.types.UILayout = self.layout - - box = layout.box() - row = box.row(align=True) - row.label(text="Locked Task Layers") - - # Ui-list. - row = box.row() - row.template_list( - "BSP_UL_task_layer_lock_plans", - "task_layer_lock_plans", - context.scene.bsp_asset, - "task_layer_lock_plans", - context.scene.bsp_asset, - "task_layer_lock_plans_index", - rows=constants.DEFAULT_ROWS, - type="DEFAULT", - ) - if disable: - row.enabled = False - - return box - - -# ----------------PANELS--------------. - - -class BSP_ASSET_main_panel: - bl_category = "Asset Pipeline" - bl_label = "Asset Pipeline" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - - -class BSP_ASSET_PT_vi3d_asset_pipeline(BSP_ASSET_main_panel, bpy.types.Panel): - def draw(self, context: bpy.types.Context) -> None: - - layout: bpy.types.UILayout = self.layout - bsp = context.scene.bsp_asset - - # Warning box. - if poll_error(context): - box = layout.box() - box.label(text="Warning", icon="ERROR") - - if poll_error_file_not_saved: - draw_error_file_not_saved(box) - - if poll_error_invalid_task_layer_module_path(): - draw_error_invalid_task_layer_module_path(box) - - if poll_asset_collection_not_init(context): - draw_error_asset_collection_not_init(box) - - -class BSP_ASSET_PT_vi3d_configure(BSP_ASSET_main_panel, bpy.types.Panel): - bl_label = "Configure" - bl_parent_id = "BSP_ASSET_PT_vi3d_asset_pipeline" - bl_options = {"DEFAULT_CLOSED"} - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - if not context.scene.bsp_asset.asset_collection: - if kitsu_available and is_addon_active("blender_kitsu", context) and not blender_kitsu.cache.asset_active_get(): - box = layout.box() - box.label(text="Warning", icon="ERROR") - box.row(align=True).label(text="Select Asset in Kitsu Context Browser") - - layout.row().prop_search(context.scene.bsp_asset, "asset_collection_name", bpy.data, 'collections') - layout.separator() - - # Draw Task Layer List. - row = layout.row() - row.label(text="Owned Task Layers") - row = row.row() - row.enabled = False # TODO: This operator is crashing Blender! - row.operator(BSP_ASSET_create_prod_context.bl_idname, icon="FILE_REFRESH", text="") - draw_task_layers_list(layout, context, "task_layers_push") - - -class BSP_ASSET_PT_vi3d_publish(BSP_ASSET_main_panel, bpy.types.Panel): - - bl_label = "Publish" - bl_parent_id = "BSP_ASSET_PT_vi3d_asset_pipeline" - bl_options = {"DEFAULT_CLOSED"} - - @classmethod - def poll(cls, context): - return bool(builder.ASSET_CONTEXT and context.scene.bsp_asset.asset_collection) - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - bsp = context.scene.bsp_asset - - # Show warning if blend file not saved. - if not bpy.data.filepath: - layout.row().label(text="Blend files needs to be saved", icon="ERROR") - return - - # Initial publish. - if not builder.ASSET_CONTEXT.asset_publishes: - layout.row().operator(BSP_ASSET_initial_publish.bl_idname, icon="ADD") - return - - # Publish is in progress. - # --------------------------------- - if bsp.is_publish_in_progress: - - # Draw abort button. - layout.row().operator(BSP_ASSET_abort_publish.bl_idname, icon='X') - - # Draw Task Layer List. - layout.label(text="Pushing Task Layers:") - draw_task_layers_list(layout, context, "task_layers_push", disable=True) - - # If new publish, draw task layer lock list. - if len(bsp.task_layer_lock_plans.items()) > 0: - draw_task_layer_lock_plans_on_new_publish(self, context) - - # Draw affected publishes list. - box = draw_affected_asset_publishes_list(self, context) - - # Draw push task layers operator inside of box. - row = box.row() - row.operator(BSP_ASSET_push_task_layers.bl_idname) - - # Draw publish operator. - row = layout.operator(BSP_ASSET_publish.bl_idname) - - return - - # No publish in progress. - # --------------------------------- - - # Production Context not loaded. - if not builder.PROD_CONTEXT: - layout.row().operator( - BSP_ASSET_create_prod_context.bl_idname, icon="FILE_REFRESH" - ) - return - - # Production Context is initialized. - row = layout.row(align=True) - if context.window_manager.bsp_asset.new_asset_version: - row.operator(BSP_ASSET_start_publish_new_version.bl_idname) - else: - row.operator(BSP_ASSET_start_publish.bl_idname) - - row.prop( - context.window_manager.bsp_asset, - "new_asset_version", - text="", - icon="ADD", - ) - return - - -class BSP_ASSET_PT_vi3d_pull(BSP_ASSET_main_panel, bpy.types.Panel): - - bl_label = "Pull" - bl_parent_id = "BSP_ASSET_PT_vi3d_asset_pipeline" - bl_options = {"DEFAULT_CLOSED"} - - @classmethod - def poll(cls, context): - return bool( - context.scene.bsp_asset.asset_collection - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - ) - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - # Show warning if blend file not saved. - if not bpy.data.filepath: - layout.row().label(text="Blend files needs to be saved", icon="ERROR") - return - - box = layout.box() - box.label(text="Pull") - - row = box.row(align=True) - row.prop(context.window_manager.bsp_asset, "asset_publish_source_path") - - row = box.row(align=True) - row.operator(BSP_ASSET_pull.bl_idname) - - -class BSP_ASSET_PT_vi3d_status(BSP_ASSET_main_panel, bpy.types.Panel): - - bl_label = "Status" - bl_parent_id = "BSP_ASSET_PT_vi3d_asset_pipeline" - bl_options = {"DEFAULT_CLOSED"} - - @classmethod - def poll(cls, context): - return bool( - context.scene.bsp_asset.asset_collection - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - ) - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - box = draw_affected_asset_publishes_list(self, context, disable=False) - - # Task Layer Status. - box = layout.box() - box.label(text="Task Layer Status") - row = box.row(align=True) - row.operator(BSP_ASSET_set_task_layer_status.bl_idname) - - # Asset Status. - box = layout.box() - box.label(text="Asset Status") - row = box.row(align=True) - row.operator(BSP_ASSET_set_asset_status.bl_idname) - - -class BSP_ASSET_PT_vi3d_transfer_settings(BSP_ASSET_main_panel, bpy.types.Panel): - - bl_label = "Transfer Settings" - bl_parent_id = "BSP_ASSET_PT_vi3d_asset_pipeline" - bl_options = {"DEFAULT_CLOSED"} - - @classmethod - def poll(cls, context): - return bool( - hasattr(context.scene, "bsp_asset_transfer_settings") - and context.scene.bsp_asset.asset_collection - and builder.ASSET_CONTEXT - and builder.ASSET_CONTEXT.asset_publishes - ) - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - for (pname, prop,) in prop_utils.get_property_group_items( - context.scene.bsp_asset_transfer_settings - ): - layout.row().prop(context.scene.bsp_asset_transfer_settings, pname) - - -class BSP_ASSET_PT_collection_asset_properties(bpy.types.Panel): - bl_label = "Asset Properties" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "collection" - - @classmethod - def poll(cls, context): - coll = context.collection - return ( - context.collection != context.scene.collection and coll.bsp_asset.is_asset - ) - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - coll = context.collection - - layout.row().prop(coll.bsp_asset, "displ_entity_name") - layout.row().prop(coll.bsp_asset, "displ_entity_id") - - layout.row().prop(coll.bsp_asset, "rig") - - # Display publish properties. - if coll.bsp_asset.is_publish: - box = layout.box() - box.row().label(text="Publish Properties") - box.row().prop(coll.bsp_asset, "displ_version") - box.row().prop(coll.bsp_asset, "displ_publish_path") - - -# ----------------UI-LISTS--------------. - - -class BSP_UL_task_layers(bpy.types.UIList): - def draw_item( - self, context, layout, data, item, icon, active_data, active_propname, index - ): - layout: bpy.types.UILayout = layout - - if self.layout_type in {"DEFAULT", "COMPACT"}: - layout.label(text=item.task_layer_name) - layout.prop(item, "use", text="") - - elif self.layout_type in {"GRID"}: - layout.alignment = "CENTER" - layout.label(text=item.task_layer_name) - - -class BSP_UL_affected_asset_publishes(bpy.types.UIList): - def draw_item( - self, context, layout, data, item, icon, active_data, active_propname, index - ): - layout: bpy.types.UILayout = layout - - if self.layout_type in {"DEFAULT", "COMPACT"}: - - # Di split for filename spacing. - row = layout.row(align=True) - row.alignment = "LEFT" - - # Draw filename with status in brackets. - base_split = row.split(factor=0.4) - - label_text = item.path.name - label_text += f"({item.status[:1]})".upper() - - # Calculate icon depending on the subprocess return code. - # This is a nice way to indicate User if something went wrong - # during push through UI. - icon = "NONE" - if context.scene.bsp_asset.is_publish_in_progress: - if item.returncode_publish == 0: - icon = "CHECKMARK" - elif item.returncode_publish == -1: - icon = "NONE" - else: - icon = "ERROR" - - base_split.label(text=label_text, icon=icon) - - # Draw each task layer. - for tl_item in item.task_layers: - - # Get locked state. - icon = "MESH_CIRCLE" - if tl_item.is_locked: - icon = "LOCKED" - - # Draw label that represents task layer with locked state as icon. - base_split.label(text=f"{tl_item.task_layer_id[:2]}".upper(), icon=icon) - - elif self.layout_type in {"GRID"}: - layout.alignment = "CENTER" - layout.label(text=item.path.name) - - -class BSP_UL_task_layer_lock_plans(bpy.types.UIList): - def draw_item( - self, context, layout, data, item, icon, active_data, active_propname, index - ): - layout: bpy.types.UILayout = layout - - if self.layout_type in {"DEFAULT", "COMPACT"}: - - # Di split for filename spacing. - row = layout.row(align=True) - row.alignment = "LEFT" - - # Draw filename with status in brackets. - base_split = row.split(factor=0.4) - - label_text = item.path.name - base_split.label(text=label_text) - - for tl_item in context.scene.bsp_asset.task_layers_push: - - # Draw label for each task layer to align spacing. - if tl_item.task_layer_id in [ - tl.task_layer_id for tl in item.task_layers - ]: - # Get locked state. - icon = "LOCKED" - - # Draw label that represents task layer with locked state as icon. - base_split.label( - text=f"{tl_item.task_layer_id[:2]}".upper(), icon=icon - ) - # If task layer was not locked just draw empty string but still draw it for - # alignment. - else: - base_split.label(text=f" ") - - elif self.layout_type in {"GRID"}: - layout.alignment = "CENTER" - layout.label(text=item.path.name) - - -# ----------------REGISTER--------------. - -classes = [ - BSP_ASSET_PT_collection_asset_properties, - BSP_UL_task_layers, - BSP_UL_affected_asset_publishes, - BSP_ASSET_PT_vi3d_asset_pipeline, - BSP_ASSET_PT_vi3d_configure, - BSP_ASSET_PT_vi3d_publish, - BSP_ASSET_PT_vi3d_pull, - BSP_ASSET_PT_vi3d_status, - BSP_ASSET_PT_vi3d_transfer_settings, - BSP_UL_task_layer_lock_plans, -] - - -def register() -> None: - for cls in classes: - bpy.utils.register_class(cls) - - -def unregister() -> None: - for cls in reversed(classes): - bpy.utils.unregister_class(cls) diff --git a/scripts-blender/addons/asset_pipeline/builder/vis.py b/scripts-blender/addons/asset_pipeline/builder/vis.py deleted file mode 100644 index ea91a378..00000000 --- a/scripts-blender/addons/asset_pipeline/builder/vis.py +++ /dev/null @@ -1,132 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -# This code is copied over from character-pipeline-assistant/utils.py file. -# https://gitlab.com/blender/character-pipeline-assistant -# Original Author of this code is: Unknown. - -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple - -from pathlib import Path - -import bpy - -from .. import util - -logger = logging.getLogger("BSP") - - -def get_layer_coll_from_coll( - collection: bpy.types.Collection, -) -> Optional[bpy.types.LayerCollection]: - - lcolls = util.traverse_collection_tree(bpy.context.view_layer.layer_collection) - for lcoll in lcolls: - if lcoll.name == collection.name: - return lcoll - - return None - - -def set_active_collection(collection: bpy.types.Collection) -> None: - layer_collection = get_layer_coll_from_coll(collection) - bpy.context.view_layer.active_layer_collection = layer_collection - - -class EnsureObjectVisibility: - def get_visibility_driver(self) -> Optional[bpy.types.FCurve]: - obj = bpy.data.objects.get(self.obj_name) - assert obj, "Object was renamed while its visibility was being ensured?" - if hasattr(obj, "animation_data") and obj.animation_data: - return obj.animation_data.drivers.find("hide_viewport") - - - def __init__(self, obj: bpy.types.Object): - self.obj_name = obj.name - - # Eye icon - self.hide = obj.hide_get() - obj.hide_set(False) - - # Screen icon driver - self.drv_mute = None - drv = self.get_visibility_driver() - if drv: - self.drv_mute = drv.mute - drv.mute = True - - # Screen icon - self.hide_viewport = obj.hide_viewport - obj.hide_viewport = False - - # Temporarily assign the object to the scene root collection, and - # take note of whether it was already assigned previously, or not. - self.assigned_to_scene_root = False - if obj.name not in bpy.context.scene.collection.objects: - self.assigned_to_scene_root = True - bpy.context.scene.collection.objects.link(obj) - - - def restore(self): - obj = bpy.data.objects.get(self.obj_name) - assert obj, f"Error: Object {self.obj_name} was renamed or removed before its visibility was restored!" - obj.hide_set(self.hide) - - if self.drv_mute != None: # We want to catch both True and False here. - drv = self.get_visibility_driver() - drv.mute = self.drv_mute - - obj.hide_viewport = self.hide_viewport - - if self.assigned_to_scene_root and obj.name in bpy.context.scene.collection.objects: - bpy.context.scene.collection.objects.unlink(obj) - - -class EnsureCollectionVisibility: - """Ensure all objects in a collection are visible. - The original visibility states can be restored using .restore(). - NOTE: Collection and Object names must not change until restore() is called!!! - """ - - def __init__(self, coll: bpy.types.Collection, do_objects=True): - self.coll_name = coll.name - - # Assign object to scene root to make sure it doesn't get hidden by collection - # settings. - # NOTE: Previously, we just messed with and then reset collection settings, - # but that stopped working in the background Blender process since D15885. - - # Objects - self.object_visibilities = [] - if do_objects: - for ob in coll.objects: - self.object_visibilities.append(EnsureObjectVisibility(ob)) - - def restore(self) -> None: - """Restore visibility settings to their original state.""" - coll = bpy.data.collections.get(self.coll_name) - if not coll: - return - - # Objects - for ob_vis in self.object_visibilities: - ob_vis.restore() diff --git a/scripts-blender/addons/asset_pipeline/constants.py b/scripts-blender/addons/asset_pipeline/constants.py deleted file mode 100644 index f5833e49..00000000 --- a/scripts-blender/addons/asset_pipeline/constants.py +++ /dev/null @@ -1,32 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -METADATA_EXT = ".xmp" -VERSION_PATTERN = r"v(\d\d\d)" -DELIMITER = "." -TARGET_SUFFIX = ".TARGET" -TASK_SUFFIX = ".TASK" -PUBLISH_SUFFIX = ".PUBLISH" -FULLY_OWNED_SUFFIX = ".FULLY_OWNED" -TRANSFER_SETTINGS_NAME = "TransferSettings" -DEFAULT_ROWS = 3 -TIME_FORMAT = r"%Y-%m-%dT%H:%M:%S" -DEFAULT_ASSET_STATUS = "REVIEW" -HOOK_ATTR_NAME = "_asset_builder_rules" diff --git a/scripts-blender/addons/asset_pipeline/docs/production_config_example/hooks.py b/scripts-blender/addons/asset_pipeline/docs/production_config_example/hooks.py deleted file mode 100644 index a1ee3528..00000000 --- a/scripts-blender/addons/asset_pipeline/docs/production_config_example/hooks.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import Any, Dict, List, Set, Union, Optional -import bpy - -from asset_pipeline.api import hook, Wildcard, DoNotMatch - -""" -Hooks can be matched with the following parameters. -As match input you can use str, list, WildCard, DoNotMatch - -Examples: -- Global Hooks (No match filter): @hook() -- Hooks for an asset type only: @hook(match_asset_type="Character") -- Hooks for a specific asset: @hook(match_asset: "Sprite") -- Hooks for a task layer only @hook(match_task_layers: ["ShadingTaskLayer", "RiggingTaskLayer"] -- Hooks for an asset and a task layer combination: @hook(macth_asset: "Sprite", match_task_layers: "ShadingTaskLayer") -Note: the decorator needs to be executed. - -It is important to note that the asset-pipeline follows a certain order to execute the hooks. And that is exactly the one of the examples hook described above: - -1. Global hooks -2. Asset Type Hooks -3. Task Layer Hooks -4. Asset Hooks -5. Asset + TaskLayer specific Hooks - - -The function itself should always have **\*\*kwargs** as a parameter. The asset-pipeline automatically passes a couple of useful keyword arguments to the function: -- `asset_collection`: bpy.types.Collection -- `context`: bpy.types.Context -- `asset_task`: asset_pipeline.asset_files.AssetTask -- `asset_dir`: asset_pipeline.asset_files.AssetDir - -By exposing these parameters in the hook function you can use them in your code. -""" - -@hook() -def test_hook_A(asset_collection: bpy.types.Collection, **kwargs) -> None: - print("Test Hook A running!") - - -@hook(match_asset="Test") -def test_hook_B(**kwargs) -> None: - print("Test Hook B running!") - - -@hook( - match_asset="Generic Sprite", - match_task_layers="ShadingTaskLayer", -) -def test_hook_sprite(asset_collection: bpy.types.Collection, **kwargs) -> None: - print(f"Test Hook Sprite {asset_collection} is running!") diff --git a/scripts-blender/addons/asset_pipeline/docs/production_config_example/task_layers.py b/scripts-blender/addons/asset_pipeline/docs/production_config_example/task_layers.py deleted file mode 100644 index d7a16260..00000000 --- a/scripts-blender/addons/asset_pipeline/docs/production_config_example/task_layers.py +++ /dev/null @@ -1,468 +0,0 @@ -from typing import Any, Dict, List, Set, Union, Optional - -import bpy -from asset_pipeline.api import ( - AssetTransferMapping, - TaskLayer, - BuildContext -) - -""" -The AssetTranfserMapping class represents a mapping between a source and a target. -It contains an object mapping which connects each source object with a target. -The maps are just dictionaries where the key is the source and the value the target. -Both key and target are actual Blender ID Datablocks. -This makes it easy to write Merge Instructions. -With it you can do access things like: - -transfer_mapping.object_map: Dict[bpy.types.Object, bpy.types.Object] -transfer_mapping.collection_map: Dict[bpy.types.Collection, bpy.types.Collection] -transfer_mapping.material_map: Dict[bpy.types.Material, bpy.types.Material] - -For all mappings: -Key: Source -Value: Target - -You can also access the root Asset source and Target Collection: -transfer_mapping.source_coll: bpy.types.Collection -transfer_mapping.target_coll: bpy.types.Collection - -Further than that you can access to objects which had no match. -transfer_mapping.no_match_target_objs: Set[bpy.types.Object] (all objs that exist in target but not in source) -transfer_mapping.no_match_source_objs: Set[bpy.types.Object] (vice versa) - - -Further then that Users can define custom transfer settings by defining a TransferSettings -Class which inherits from a PropertyGroup in the task_layer module. Users can query these settings -by checking the transfer_settings argument. - -transfer_settings.custom_option -""" - -class TransferSettings(bpy.types.PropertyGroup): - imp_mat: bpy.props.BoolProperty(name="Materials", default=True) # type: ignore - imp_uv: bpy.props.BoolProperty(name="UVs", default=True) # type: ignore - imp_vcol: bpy.props.BoolProperty(name="Vertex Colors", default=True) # type: ignore - transfer_type: bpy.props.EnumProperty( # type: ignore - name="Transfer Type", - items=[("VERTEX_ORDER", "Vertex Order", ""), ("PROXIMITY", "Proximity", "")], - ) - -class RiggingTaskLayer(TaskLayer): - name = "Rigging" - order = 0 - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - print(f"Processing data from TaskLayer {cls.__name__}") - -# Not allowed: 2 TaskLayer Classes with the same ClassName (Note: note 'name' attribute) -class ShadingTaskLayer(TaskLayer): - name = "Shading" - order = 2 - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - print(f"Processing data from TaskLayer {cls.__name__}") - - settings = transfer_settings - - for obj_source, obj_target in transfer_mapping.object_map.items(): - - if not obj_target.type in ["MESH", "CURVE"]: - continue - - if obj_target.name.startswith("WGT-"): - while obj_target.material_slots: - obj_target.active_material_index = 0 - bpy.ops.object.material_slot_remove({"object": obj_target}) - continue - - if obj_target.type != obj_source.type: - print(f"Warning: {obj_target.name} of incompatible object type") - continue - - # Transfer material slot assignments. - # Delete all material slots of target object. - while len(obj_target.material_slots) > len(obj_source.material_slots): - obj_target.active_material_index = len(obj_source.material_slots) - bpy.ops.object.material_slot_remove({"object": obj_target}) - - # Transfer material slots - for idx in range(len(obj_source.material_slots)): - if idx >= len(obj_target.material_slots): - bpy.ops.object.material_slot_add({"object": obj_target}) - obj_target.material_slots[idx].link = obj_source.material_slots[ - idx - ].link - obj_target.material_slots[idx].material = obj_source.material_slots[ - idx - ].material - - # Transfer active material slot index - obj_target.active_material_index = obj_source.active_material_index - - # Transfer material slot assignments for curve - if obj_target.type == "CURVE": - for spl_to, spl_from in zip( - obj_target.data.splines, obj_source.data.splines - ): - spl_to.material_index = spl_from.material_index - - # Rest of the loop applies only to meshes. - if obj_target.type != "MESH": - continue - - # Transfer material slot assignments for mesh - for pol_to, pol_from in zip( - obj_target.data.polygons, obj_source.data.polygons - ): - pol_to.material_index = pol_from.material_index - pol_to.use_smooth = pol_from.use_smooth - - # Transfer UV Seams - if settings.imp_uv: - if settings.transfer_type == "VERTEX_ORDER" and len( - obj_source.data.edges - ) == len(obj_target.data.edges): - for edge_from, edge_to in zip( - obj_source.data.edges, obj_target.data.edges - ): - edge_to.use_seam = edge_from.use_seam - else: - bpy.ops.object.data_transfer( - { - "object": obj_source, - "selected_editable_objects": [obj_target], - }, - data_type="SEAM", - edge_mapping="NEAREST", - mix_mode="REPLACE", - ) - - # Transfer UV layers - if settings.imp_uv: - while len(obj_target.data.uv_layers) > 0: - rem = obj_target.data.uv_layers[0] - obj_target.data.uv_layers.remove(rem) - if settings.transfer_type == "VERTEX_ORDER": - for uv_from in obj_source.data.uv_layers: - uv_to = obj_target.data.uv_layers.new( - name=uv_from.name, do_init=False - ) - for loop in obj_target.data.loops: - try: - uv_to.data[loop.index].uv = uv_from.data[loop.index].uv - except: - print( - f"no UVs transferred for {obj_target.name}. Probably mismatching vertex count: {len(obj_source.data.vertices)} vs {len(obj_target.data.vertices)}" - ) - break - elif settings.transfer_type == "PROXIMITY": - bpy.ops.object.data_transfer( - { - "object": obj_source, - "selected_editable_objects": [obj_target], - }, - data_type="UV", - use_create=True, - loop_mapping="NEAREST_POLYNOR", - poly_mapping="NEAREST", - layers_select_src="ALL", - layers_select_dst="NAME", - mix_mode="REPLACE", - ) - # Make sure correct layer is set to active - for uv_l in obj_source.data.uv_layers: - if uv_l.active_render: - obj_target.data.uv_layers[uv_l.name].active_render = True - break - - # Transfer Vertex Colors - if settings.imp_vcol: - while len(obj_target.data.vertex_colors) > 0: - rem = obj_target.data.vertex_colors[0] - obj_target.data.vertex_colors.remove(rem) - if settings.transfer_type == "VERTEX_ORDER": - for vcol_from in obj_source.data.vertex_colors: - vcol_to = obj_target.data.vertex_colors.new( - name=vcol_from.name, do_init=False - ) - for loop in obj_target.data.loops: - try: - vcol_to.data[loop.index].color = vcol_from.data[ - loop.index - ].color - except: - print( - f"no Vertex Colors transferred for {obj_target.name}. Probably mismatching vertex count: {len(obj_source.data.vertices)} vs {len(obj_target.data.vertices)}" - ) - elif settings.transfer_type == "PROXIMITY": - bpy.ops.object.data_transfer( - { - "object": obj_source, - "selected_editable_objects": [obj_target], - }, - data_type="VCOL", - use_create=True, - loop_mapping="NEAREST_POLYNOR", - layers_select_src="ALL", - layers_select_dst="NAME", - mix_mode="REPLACE", - ) - - # Set 'PREVIEW' vertex color layer as active - for idx, vcol in enumerate(obj_target.data.vertex_colors): - if vcol.name == "PREVIEW": - obj_target.data.vertex_colors.active_index = idx - break - - # Set 'Baking' or 'UVMap' uv layer as active - for idx, uvlayer in enumerate(obj_target.data.uv_layers): - if uvlayer.name == "Baking": - obj_target.data.uv_layers.active_index = idx - break - if uvlayer.name == "UVMap": - obj_target.data.uv_layers.active_index = idx - - # Select preview texture as active if found - for mslot in obj_target.material_slots: - if not mslot.material or not mslot.material.node_tree: - continue - for node in mslot.material.node_tree.nodes: - if not node.type == "TEX_IMAGE": - continue - if not node.image: - continue - if "preview" in node.image.name: - mslot.material.node_tree.nodes.active = node - break - - -### Object utilities -def copy_parenting(source_ob: bpy.types.Object, target_ob: bpy.types.Object) -> None: - """Copy parenting data from one object to another.""" - target_ob.parent = source_ob.parent - target_ob.parent_type = source_ob.parent_type - target_ob.parent_bone = source_ob.parent_bone - target_ob.matrix_parent_inverse = source_ob.matrix_parent_inverse.copy() - - -def copy_attributes(a: Any, b: Any) -> None: - keys = dir(a) - for key in keys: - if ( - not key.startswith("_") - and not key.startswith("error_") - and key != "group" - and key != "is_valid" - and key != "rna_type" - and key != "bl_rna" - ): - try: - setattr(b, key, getattr(a, key)) - except AttributeError: - pass - - -def copy_driver( - source_fcurve: bpy.types.FCurve, - target_obj: bpy.types.Object, - data_path: Optional[str] = None, - index: Optional[int] = None, -) -> bpy.types.FCurve: - if not data_path: - data_path = source_fcurve.data_path - - new_fc = None - try: - if index: - new_fc = target_obj.driver_add(data_path, index) - else: - new_fc = target_obj.driver_add(data_path) - except: - print(f"Couldn't copy driver {source_fcurve.data_path} to {target_obj.name}") - return - - copy_attributes(source_fcurve, new_fc) - copy_attributes(source_fcurve.driver, new_fc.driver) - - # Remove default modifiers, variables, etc. - for m in new_fc.modifiers: - new_fc.modifiers.remove(m) - for v in new_fc.driver.variables: - new_fc.driver.variables.remove(v) - - # Copy modifiers - for m1 in source_fcurve.modifiers: - m2 = new_fc.modifiers.new(type=m1.type) - copy_attributes(m1, m2) - - # Copy variables - for v1 in source_fcurve.driver.variables: - v2 = new_fc.driver.variables.new() - copy_attributes(v1, v2) - for i in range(len(v1.targets)): - copy_attributes(v1.targets[i], v2.targets[i]) - - return new_fc - - -def copy_drivers(source_ob: bpy.types.Object, target_ob: bpy.types.Object) -> None: - """Copy all drivers from one object to another.""" - if not hasattr(source_ob, "animation_data") or not source_ob.animation_data: - return - - for fc in source_ob.animation_data.drivers: - copy_driver(fc, target_ob) - - -def copy_rigging_object_data( - source_ob: bpy.types.Object, target_ob: bpy.types.Object -) -> None: - """Copy all object data that could be relevant to rigging.""" - # TODO: Object constraints, if needed. - copy_drivers(source_ob, target_ob) - copy_parenting(source_ob, target_ob) - # HACK: For some reason Armature constraints on grooming objects lose their target when updating? Very strange... - for c in target_ob.constraints: - if c.type == "ARMATURE": - for t in c.targets: - if t.target == None: - t.target = target_ob.parent - - -class GroomingTaskLayer(TaskLayer): - name = "Grooming" - order = 1 - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - - print(f"Processing data from TaskLayer {cls.__name__}") - coll_source = transfer_mapping.source_coll - coll_target = transfer_mapping.target_coll - for obj_source, obj_target in transfer_mapping.object_map.items(): - if not "PARTICLE_SYSTEM" in [mod.type for mod in obj_source.modifiers]: - continue - l = [] - for mod in obj_source.modifiers: - if not mod.type == "PARTICLE_SYSTEM": - l += [mod.show_viewport] - mod.show_viewport = False - - bpy.ops.particle.copy_particle_systems( - {"object": obj_source, "selected_editable_objects": [obj_target]} - ) - - c = 0 - for mod in obj_source.modifiers: - if mod.type == "PARTICLE_SYSTEM": - continue - mod.show_viewport = l[c] - c += 1 - - # TODO: handle cases where collections with exact naming convention cannot be found - try: - coll_from_hair = next( - c for name, c in coll_source.children.items() if ".hair" in name - ) - coll_from_part = next( - c - for name, c in coll_from_hair.children.items() - if ".hair.particles" in name - ) - coll_from_part_proxy = next( - c - for name, c in coll_from_part.children.items() - if ".hair.particles.proxy" in name - ) - except: - print( - "Warning: Could not find existing particle hair collection. Make sure you are following the exact naming and structuring convention!" - ) - return - - # link 'from' hair.particles collection in 'to' - try: - coll_to_hair = next( - c for name, c in coll_target.children.items() if ".hair" in name - ) - except: - coll_target.children.link(coll_from_hair) - return - - coll_to_hair.children.link(coll_from_part) - try: - coll_to_part = next( - c - for name, c in coll_to_hair.children.items() - if ".hair.particles" in name - ) - except: - print( - "Warning: Failed to find particle hair collections in target collection" - ) - coll_to_part.user_clear() - bpy.data.collections.remove(coll_to_part) - return - - # transfer shading - # transfer_dict = map_objects_by_name(coll_to_part, coll_from_part) - # transfer_shading_data(context, transfer_dict) - ShadingTaskLayer.transfer_data(context, transfer_mapping, transfer_settings) - - # transfer modifers - for obj_source, obj_target in transfer_mapping.object_map.items(): - if not "PARTICLE_SYSTEM" in [m.type for m in obj_target.modifiers]: - bpy.ops.object.make_links_data( - {"object": obj_source, "selected_editable_objects": [obj_target]}, - type="MODIFIERS", - ) - - # We want to rig the hair base mesh with an Armature modifier, so transfer vertex groups by proximity. - bpy.ops.object.data_transfer( - {"object": obj_source, "selected_editable_objects": [obj_target]}, - data_type="VGROUP_WEIGHTS", - use_create=True, - vert_mapping="NEAREST", - layers_select_src="ALL", - layers_select_dst="NAME", - mix_mode="REPLACE", - ) - - # We used to want to rig the auto-generated hair particle proxy meshes with Surface Deform, so re-bind those. - # NOTE: Surface Deform probably won't be used for final rigging - for mod in obj_target.modifiers: - if mod.type == "SURFACE_DEFORM" and mod.is_bound: - for i in range(2): - bpy.ops.object.surfacedeform_bind( - {"object": obj_target}, modifier=mod.name - ) - - copy_rigging_object_data(obj_source, obj_target) - # remove 'to' hair.particles collection - coll_to_part.user_clear() - bpy.data.collections.remove(coll_to_part) - - return - - diff --git a/scripts-blender/addons/asset_pipeline/docs/production_config_heist/hooks.py b/scripts-blender/addons/asset_pipeline/docs/production_config_heist/hooks.py deleted file mode 100644 index a8e0ccd0..00000000 --- a/scripts-blender/addons/asset_pipeline/docs/production_config_heist/hooks.py +++ /dev/null @@ -1,110 +0,0 @@ -from typing import Any, Dict, List, Set, Union, Optional -import bpy - -from asset_pipeline.api import hook, Wildcard, DoNotMatch - -""" -Hooks can be matched with the following parameters. -As match input you can use str, list, WildCard, DoNotMatch - -Examples: -- Global Hooks (No match filter): @hook() -- Hooks for an asset type only: @hook(match_asset_type="Character") -- Hooks for a specific asset: @hook(match_asset: "Sprite") -- Hooks for a task layer only @hook(match_task_layers: ["ShadingTaskLayer", "RiggingTaskLayer"] -- Hooks for an asset and a task layer combination: @hook(macth_asset: "Sprite", match_task_layers: "ShadingTaskLayer") -Note: the decorator needs to be executed. - -It is important to note that the asset-pipeline follows a certain order to execute the hooks. And that is exactly the one of the examples hook described above: - -1. Global hooks -2. Asset Type Hooks -3. Task Layer Hooks -4. Asset Hooks -5. Asset + TaskLayer specific Hooks - - -The function itself should always have **\*\*kwargs** as a parameter. The asset-pipeline automatically passes a couple of useful keyword arguments to the function: -- `asset_collection`: bpy.types.Collection -- `context`: bpy.types.Context -- `asset_task`: asset_pipeline.asset_files.AssetTask -- `asset_dir`: asset_pipeline.asset_files.AssetDir - -By exposing these parameters in the hook function you can use them in your code. -""" - -@hook( - match_task_layers="ModelingTaskLayer", -) -def geometry_cleanup(context: bpy.types.Context, asset_collection: bpy.types.Collection, **kwargs) -> None: - for ob in asset_collection.all_objects: - if not ob.data: - continue - if not ob.type == 'MESH': # TODO: Support other object types - continue - # make meshes single user - if ob.data.users > 1: - ob.data = ob.data.copy() - - # check for modifiers to apply - if not [mod for mod in ob.modifiers if mod.name.split('-')[0]=='APL']: - continue - - # remember modifier visibility - mod_vis = [] - for i, mod in enumerate(ob.modifiers): - if mod.name.split('-')[0] != 'APL': - if mod.show_viewport: - mod_vis += [i] - mod.show_viewport = False - - # apply modifiers - depsgraph = context.evaluated_depsgraph_get() - old_mesh = ob.data - ob.data = bpy.data.meshes.new_from_object(ob.evaluated_get(depsgraph)) - ob.data.name = old_mesh.name - bpy.data.meshes.remove(old_mesh) - - for i in mod_vis[::-1]: - ob.modifiers[i].show_viewport = True - for mod in ob.modifiers: - if mod.name.split('-')[0] == 'APL': - ob.modifiers.remove(mod) - - -@hook( - match_task_layers="ShadingTaskLayer", -) -def set_preview_shading(context: bpy.types.Context, asset_collection: bpy.types.Collection, **kwargs) -> None: - for ob in asset_collection.all_objects: - if not ob.data: - continue - if not ob.type == 'MESH': - continue - - # Set 'PREVIEW' vertex color layer as active - for idx, vcol in enumerate(ob.data.vertex_colors): - if vcol.name == "PREVIEW": - ob.data.vertex_colors.active_index = idx - break - - # Set 'Baking' or 'UVMap' uv layer as active - for idx, uvlayer in enumerate(ob.data.uv_layers): - if uvlayer.name == "Baking": - ob.data.uv_layers.active_index = idx - break - if uvlayer.name == "UVMap": - ob.data.uv_layers.active_index = idx - - # Select preview texture as active if found - for mslot in ob.material_slots: - if not mslot.material or not mslot.material.node_tree: - continue - for node in mslot.material.node_tree.nodes: - if not node.type == "TEX_IMAGE": - continue - if not node.image: - continue - if "preview" in node.image.name: - mslot.material.node_tree.nodes.active = node - break diff --git a/scripts-blender/addons/asset_pipeline/docs/production_config_heist/task_layers.py b/scripts-blender/addons/asset_pipeline/docs/production_config_heist/task_layers.py deleted file mode 100644 index cba71bd6..00000000 --- a/scripts-blender/addons/asset_pipeline/docs/production_config_heist/task_layers.py +++ /dev/null @@ -1,838 +0,0 @@ -from typing import Any, Dict, List, Set, Union, Optional - -import bpy -import mathutils -import bmesh -import numpy as np -from asset_pipeline.api import ( - AssetTransferMapping, - TaskLayer, - BuildContext, -) - -class TransferSettings(bpy.types.PropertyGroup): - pass - #imp_mat: bpy.props.BoolProperty(name="Materials", default=True) # type: ignore - #imp_uv: bpy.props.BoolProperty(name="UVs", default=True) # type: ignore - #imp_vcol: bpy.props.BoolProperty(name="Vertex Colors", default=True) # type: ignore - #transfer_type: bpy.props.EnumProperty( # type: ignore - # name="Transfer Type", - # items=[("VERTEX_ORDER", "Vertex Order", ""), ("PROXIMITY", "Proximity", "")], - #) - -class RiggingTaskLayer(TaskLayer): - name = "Rigging" - order = 0 - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - print(f"\n\033[1mProcessing data from {cls.__name__}...\033[0m") - - settings = transfer_settings - - depsgraph = context.evaluated_depsgraph_get() - transfer_mapping.generate_mapping() - - # add prefixes to existing modifiers - for obj_source, obj_target in transfer_mapping.object_map.items(): - prefix_modifiers(obj_target, 0) - - -class ModelingTaskLayer(TaskLayer): - name = "Modeling" - order = 1 - ''' - Only affects objects of the target inside collections ending with '.geometry'. New objects can be created anywhere. - New modifiers are automatically prefixed with 'GEO-'. Any modifier that is given the prefix 'APL-' will be automatically applied after push. - The order of the modifier stack is generally owned by the rigging task layer. Newly created modifiers in the modeling task layer are an exception. - ''' - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - - ) -> None: - print(f"\n\033[1mProcessing data from {cls.__name__}...\033[0m") - - settings = transfer_settings - - depsgraph = context.evaluated_depsgraph_get() - transfer_mapping.generate_mapping() - - # identify geometry collections in source and target - geometry_colls_source = [] - for coll in transfer_mapping.collection_map.keys(): - if coll.name.split('.')[-2] == 'geometry': - geometry_colls_source += [coll] - geometry_objs_source = {ob for coll in geometry_colls_source for ob in list(coll.all_objects)} - - geometry_colls_target = [] - for coll in transfer_mapping.collection_map.keys(): - if coll.name.split('.')[-2] == 'geometry': - geometry_colls_target += [transfer_mapping.collection_map[coll]] - geometry_objs_target = {ob for coll in geometry_colls_target for ob in list(coll.all_objects)} - - # handle new objects - for ob in transfer_mapping.no_match_source_objs: - # link new object to target parent collection - for coll_source in transfer_mapping.collection_map.keys(): - if ob in set(coll_source.objects): - transfer_mapping.collection_map[coll_source].objects.link(ob) - - # (replace object dependencies) - pass - - # handle removed objects - for ob in transfer_mapping.no_match_target_objs: - # delete objects inside the target .geometry collections - if ob in geometry_objs_target: - print(info_text(f"DELETING {ob.name}")) - bpy.data.objects.remove(ob) - - # transfer data between object geometries - for obj_source, obj_target in transfer_mapping.object_map.items(): - if obj_source not in geometry_objs_source: - continue - - # transfer object transformation (world space) - con_vis = [] - for con in obj_target.constraints: - con_vis += [con.enabled] - con.enabled = False - for con in obj_source.constraints: - con.enabled = False - depsgraph = context.evaluated_depsgraph_get() - - obj_target.matrix_world = obj_source.matrix_world - for con, vis in zip(obj_target.constraints, con_vis): - con.enabled = vis - - # TODO: support object type change - if obj_source.type != obj_target.type: - print(warning_text(f"Mismatching object type. Skipping {obj_target.name}.")) - continue - - # check for topology match (vertex, edge, loop count) (mesh, curve separately) - topo_match = match_topology(obj_source, obj_target) - if topo_match is None: # TODO: support geometry types other than mesh or curve - continue - - # if topology matches: transfer position attribute (keeping shapekeys intact) - if topo_match: - if obj_target.type == 'MESH': - if len(obj_target.data.vertices)==0: - print(warning_text(f"Mesh object '{obj_target.name}' has empty object data")) - continue - offset = [obj_source.data.vertices[i].co - obj_target.data.vertices[i].co for i in range(len(obj_source.data.vertices))] - - offset_sum = 0 - for x in offset: - offset_sum += x.length - offset_avg = offset_sum/len(offset) - if offset_avg>0.1: - print(warning_text(f"Average Vertex offset is {offset_avg} for {obj_target.name}")) - - for i, vec in enumerate(offset): - obj_target.data.vertices[i].co += vec - - # update shapekeys - if obj_target.data.shape_keys: - for key in obj_target.data.shape_keys.key_blocks: - for i, point in enumerate([dat.co for dat in key.data]): - key.data[i].co = point + offset[i] - elif obj_target.type == 'CURVE': # TODO: proper geometry transfer for curves - obj_target.data = obj_source.data - else: - pass - - # if topology does not match replace geometry (throw warning) -> TODO: handle data transfer onto mesh for simple cases (trivial topological changes: e.g. added separate mesh island, added span) - else: - # replace the object data and do proximity transfer of all rigging data - - # generate new transfer source object from mesh data - obj_target_original = bpy.data.objects.new(f"{obj_target.name}.original", obj_target.data) - if obj_target.data.shape_keys: - sk_original = obj_target.data.shape_keys.copy() - else: sk_original = None - context.scene.collection.objects.link(obj_target_original) - - print(warning_text(f"Topology Mismatch! Replacing object data and transferring with potential data loss on '{obj_target.name}'")) - obj_target.data = obj_source.data - - # transfer weights - bpy.ops.object.data_transfer( - { - "object": obj_target_original, - "active_object": obj_target_original, - "selected_editable_objects": [obj_target], - }, - data_type="VGROUP_WEIGHTS", - use_create=True, - vert_mapping='POLYINTERP_NEAREST', - layers_select_src="ALL", - layers_select_dst="NAME", - mix_mode="REPLACE", - ) - - # transfer shapekeys - transfer_shapekeys_proximity(obj_target_original, obj_target) - - # transfer drivers - copy_drivers(sk_original, obj_target.data.shape_keys) - - del sk_original - bpy.data.objects.remove(obj_target_original) - - # sync modifier stack (those without prefix on the source are added and prefixed, those with matching/other prefix are synced/ignored based on their prefix) - # add prefix to existing modifiers - prefix_modifiers(obj_source, 1) - # remove old and sync existing modifiers TODO: Stack position and parameters - for mod in obj_target.modifiers: - if mod.name.split('-')[0] not in ['GEO', 'APL']: - continue - if mod.name not in [m.name for m in obj_source.modifiers]: - print(info_text(f"Removing modifier {mod.name}")) - obj_target.modifiers.remove(mod) - - # transfer new modifiers - for i, mod in enumerate(obj_source.modifiers): - if mod.name.split('-')[0] not in ['GEO', 'APL']: - continue - if mod.name in [m.name for m in obj_target.modifiers]: - continue - mod_new = obj_target.modifiers.new(mod.name, mod.type) - # sort new modifier at correct index (default to beginning of the stack) - idx = 0 - if i>0: - name_prev = obj_source.modifiers[i-1].name - for target_mod_i, target_mod in enumerate(obj_target.modifiers): - if target_mod.name == name_prev: - idx = target_mod_i+1 - bpy.ops.object.modifier_move_to_index({'object': obj_target}, modifier=mod_new.name, index=idx) - - # sync modifier settings - for i, mod_source in enumerate(obj_source.modifiers): - mod_target = obj_target.modifiers.get(mod_source.name) - if not mod_target: - continue - if mod_source.name.split('-')[0] not in ['GEO', 'APL']: - continue - for prop in [p.identifier for p in mod_source.bl_rna.properties if not p.is_readonly]: - value = getattr(mod_source, prop) - if type(value) == bpy.types.Object and value in transfer_mapping.object_map: - # If a modifier is referencing a .TASK object, - # remap that reference to a .TARGET object. - # (Eg. modeling Mirror modifier with a mirror object) - value = transfer_mapping.object_map[value] - setattr(mod_target, prop, value) - - # rebind modifiers (corr. smooth, surf. deform, mesh deform) - for mod in obj_target.modifiers: - if mod.type == 'SURFACE_DEFORM': - if not mod.is_bound: - continue - for i in range(2): - bpy.ops.object.surfacedeform_bind({"object": obj_target,"active_object": obj_target}, modifier=mod.name) - elif mod.type == 'MESH_DEFORM': - if not mod.is_bound: - continue - for i in range(2): - bpy.ops.object.meshdeform_bind({"object": obj_target,"active_object": obj_target}, modifier=mod.name) - elif mod.type == 'CORRECTIVE_SMOOTH': - if not mod.is_bind: - continue - for i in range(2): - bpy.ops.object.correctivesmooth_bind({"object": obj_target,"active_object": obj_target}, modifier=mod.name) - - - # restore multiusers - if not (build_context.is_push or type(cls) in build_context.asset_context.task_layer_assembly.get_used_task_layers()): - meshes_dict = dict() - for ob in transfer_mapping.object_map.keys(): - if not ob.data: - continue - if ob.type not in ['MESH', 'CURVE']: - continue - if ob.data not in meshes_dict.keys(): - meshes_dict[ob.data] = [ob] - else: - meshes_dict[ob.data] += [ob] - for mesh, objects in meshes_dict.items(): - main_mesh = transfer_mapping.object_map[objects[0]].data - for ob in objects: - transfer_mapping.object_map[ob].data = main_mesh - -def prefix_modifiers(obj: bpy.types.Object, idx: int, delimiter = '-') -> None: - prefixes = ['RIG', 'GEO', 'APL'] - for mod in obj.modifiers: - if not mod.name.split(delimiter)[0] in prefixes: - mod.name = f'{prefixes[idx]}{delimiter}{mod.name}' - -# Not allowed: 2 TaskLayer Classes with the same ClassName (Note: note 'name' attribute) -class ShadingTaskLayer(TaskLayer): - name = "Shading" - order = 3 - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - print(f"\n\033[1mProcessing data from {cls.__name__}...\033[0m") - - settings = transfer_settings - - depsgraph = context.evaluated_depsgraph_get() - transfer_mapping.generate_mapping() - - for obj_source, obj_target in transfer_mapping.object_map.items(): - - if not obj_target.type in ["MESH", "CURVE"]: - continue - - if obj_target.name.startswith("WGT-"): - while obj_target.material_slots: - obj_target.active_material_index = 0 - bpy.ops.object.material_slot_remove({"object": obj_target}) - continue - - # TODO: support object type change - if obj_source.type != obj_target.type: - print(warning_text(f"Mismatching object type. Skipping {obj_target.name}.")) - continue - - # Transfer material slot assignments. - # Delete all material slots of target object. - while len(obj_target.material_slots) > len(obj_source.material_slots): - obj_target.active_material_index = len(obj_source.material_slots) - bpy.ops.object.material_slot_remove({"object": obj_target}) - - # Transfer material slots - for idx in range(len(obj_source.material_slots)): - if idx >= len(obj_target.material_slots): - bpy.ops.object.material_slot_add({"object": obj_target}) - obj_target.material_slots[idx].link = obj_source.material_slots[idx].link - obj_target.material_slots[idx].material = obj_source.material_slots[idx].material - - # Transfer active material slot index - obj_target.active_material_index = obj_source.active_material_index - - # Transfer material slot assignments for curve - if obj_target.type == "CURVE": - if len(obj_target.data.splines)==0: - print(warning_text(f"Curve object '{obj_target.name}' has empty object data")) - continue - for spl_to, spl_from in zip(obj_target.data.splines, obj_source.data.splines): - spl_to.material_index = spl_from.material_index - - # Rest of the loop applies only to meshes. - if obj_target.type != "MESH": - continue - - if len(obj_target.data.vertices)==0: - print(warning_text(f"Mesh object '{obj_target.name}' has empty object data")) - continue - - topo_match = match_topology(obj_source, obj_target) - if not topo_match: # TODO: Support trivial topology changes in more solid way than proximity transfer - print(warning_text(f"Mismatch in topology, falling back to proximity transfer. (Object '{obj_target.name}')")) - - # generate new transfer source object from mesh data - obj_source_original = bpy.data.objects.new(f"{obj_source.name}.original", obj_source.data) - context.scene.collection.objects.link(obj_source_original) - - # Transfer face data - if topo_match: - for pol_to, pol_from in zip(obj_target.data.polygons, obj_source.data.polygons): - pol_to.material_index = pol_from.material_index - pol_to.use_smooth = pol_from.use_smooth - else: - obj_source_eval = obj_source.evaluated_get(depsgraph) - for pol_target in obj_target.data.polygons: - (hit, loc, norm, face_index) = obj_source_eval.closest_point_on_mesh(pol_target.center) - pol_source = obj_source_eval.data.polygons[face_index] - pol_target.material_index = pol_source.material_index - pol_target.use_smooth = pol_source.use_smooth - - # Transfer UV Seams - if topo_match: - for edge_from, edge_to in zip(obj_source.data.edges, obj_target.data.edges): - edge_to.use_seam = edge_from.use_seam - else: - bpy.ops.object.data_transfer( - { - "object": obj_source_original, - "active_object": obj_source_original, - "selected_editable_objects": [obj_target], - }, - data_type="SEAM", - edge_mapping="NEAREST", - mix_mode="REPLACE", - ) - - # Transfer UV layers - while len(obj_target.data.uv_layers) > 0: - rem = obj_target.data.uv_layers[0] - obj_target.data.uv_layers.remove(rem) - if topo_match: - for uv_from in obj_source.data.uv_layers: - uv_to = obj_target.data.uv_layers.new(name=uv_from.name, do_init=False) - for loop in obj_target.data.loops: - uv_to.data[loop.index].uv = uv_from.data[loop.index].uv - else: - for uv_from in obj_source.data.uv_layers: - uv_to = obj_target.data.uv_layers.new(name=uv_from.name, do_init=False) - transfer_corner_data(obj_source, obj_target, uv_from.data, uv_to.data, data_suffix = 'uv') - - # Make sure correct layer is set to active - for uv_l in obj_source.data.uv_layers: - if uv_l.active_render: - obj_target.data.uv_layers[uv_l.name].active_render = True - break - - # Transfer Vertex Colors - while len(obj_target.data.vertex_colors) > 0: - rem = obj_target.data.vertex_colors[0] - obj_target.data.vertex_colors.remove(rem) - if topo_match: - for vcol_from in obj_source.data.vertex_colors: - vcol_to = obj_target.data.vertex_colors.new(name=vcol_from.name, do_init=False) - for loop in obj_target.data.loops: - vcol_to.data[loop.index].color = vcol_from.data[loop.index].color - else: - for vcol_from in obj_source.data.vertex_colors: - vcol_to = obj_target.data.vertex_colors.new(name=vcol_from.name, do_init=False) - transfer_corner_data(obj_source, obj_target, vcol_from.data, vcol_to.data, data_suffix = 'color') - bpy.data.objects.remove(obj_source_original) - - -### Utilities - -def info_text(text: str) -> str: - return f"\t\033[1mInfo\033[0m\t: "+text - -def warning_text(text: str) -> str: - return f"\t\033[1m\033[93mWarning\033[0m\t: "+text - -def error_text(text: str) -> str: - return f"\t\033[1m\033[91mError\033[0m\t: "+text - -def match_topology(a: bpy.types.Object, b: bpy.types.Object) -> bool: - """Checks if two objects have matching topology (efficiency over exactness)""" - if a.type != b.type: - return False - if a.type == 'MESH': - if len(a.data.vertices) != len(b.data.vertices): - return False - if len(a.data.edges) != len(b.data.edges): - return False - if len(a.data.polygons) != len(b.data.polygons): - return False - for e1, e2 in zip(a.data.edges, b.data.edges): - for v1, v2 in zip(e1.vertices, e2.vertices): - if v1 != v2: - return False - return True - elif a.type == 'CURVE': - if len(a.data.splines) != len(b.data.splines): - return False - for spline1, spline2 in zip(a.data.splines, b.data.splines): - if len(spline1.points) != len(spline2.points): - return False - return True - return None - -def copy_parenting(source_ob: bpy.types.Object, target_ob: bpy.types.Object) -> None: - """Copy parenting data from one object to another.""" - target_ob.parent = source_ob.parent - target_ob.parent_type = source_ob.parent_type - target_ob.parent_bone = source_ob.parent_bone - target_ob.matrix_parent_inverse = source_ob.matrix_parent_inverse.copy() - - -def copy_attributes(a: Any, b: Any) -> None: - keys = dir(a) - for key in keys: - if ( - not key.startswith("_") - and not key.startswith("error_") - and key != "group" - and key != "is_valid" - and key != "rna_type" - and key != "bl_rna" - ): - try: - setattr(b, key, getattr(a, key)) - except AttributeError: - pass - - -def copy_driver( - source_fcurve: bpy.types.FCurve, - target_obj: bpy.types.Object, - data_path: Optional[str] = None, - index: Optional[int] = None, -) -> bpy.types.FCurve: - if not data_path: - data_path = source_fcurve.data_path - - new_fc = None - try: - if index: - new_fc = target_obj.driver_add(data_path, index) - else: - new_fc = target_obj.driver_add(data_path) - except: - print(warning_text(f"Couldn't copy driver {source_fcurve.data_path} to {target_obj.name}")) - return - - copy_attributes(source_fcurve, new_fc) - copy_attributes(source_fcurve.driver, new_fc.driver) - - # Remove default modifiers, variables, etc. - for m in new_fc.modifiers: - new_fc.modifiers.remove(m) - for v in new_fc.driver.variables: - new_fc.driver.variables.remove(v) - - # Copy modifiers - for m1 in source_fcurve.modifiers: - m2 = new_fc.modifiers.new(type=m1.type) - copy_attributes(m1, m2) - - # Copy variables - for v1 in source_fcurve.driver.variables: - v2 = new_fc.driver.variables.new() - copy_attributes(v1, v2) - for i in range(len(v1.targets)): - copy_attributes(v1.targets[i], v2.targets[i]) - - return new_fc - - -def copy_drivers(source_ob: bpy.types.Object, target_ob: bpy.types.Object) -> None: - """Copy all drivers from one object to another.""" - if not hasattr(source_ob, "animation_data") or not source_ob.animation_data: - return - - for fc in source_ob.animation_data.drivers: - copy_driver(fc, target_ob) - - -def copy_rigging_object_data( - source_ob: bpy.types.Object, target_ob: bpy.types.Object -) -> None: - """Copy all object data that could be relevant to rigging.""" - # TODO: Object constraints, if needed. - copy_drivers(source_ob, target_ob) - copy_parenting(source_ob, target_ob) - # HACK: For some reason Armature constraints on grooming objects lose their target when updating? Very strange... - for c in target_ob.constraints: - if c.type == "ARMATURE": - for t in c.targets: - if t.target == None: - t.target = target_ob.parent - -# mesh interpolation utilities -def edge_data_split(edge, data_layer, data_suffix: str): - for vert in edge.verts: - vals = [] - for loop in vert.link_loops: - loops_edge_vert = set([loop for f in edge.link_faces for loop in f.loops]) - if loop not in loops_edge_vert: - continue - dat = data_layer[loop.index] - element = list(getattr(dat,data_suffix)) - if not vals: - vals.append(element) - elif not vals[0] == element: - vals.append(element) - if len(vals) > 1: - return True - return False - -def closest_edge_on_face_to_line(face, p1, p2, skip_edges=None): - ''' Returns edge of a face which is closest to line.''' - for edge in face.edges: - if skip_edges: - if edge in skip_edges: - continue - res = mathutils.geometry.intersect_line_line(p1, p2, *[edge.verts[i].co for i in range(2)]) - if not res: - continue - (p_traversal, p_edge) = res - frac_1 = (edge.verts[1].co-edge.verts[0].co).dot(p_edge-edge.verts[0].co)/(edge.verts[1].co-edge.verts[0].co).length**2. - frac_2 = (p2-p1).dot(p_traversal-p1)/(p2-p1).length**2. - if (frac_1 >= 0 and frac_1 <= 1) and (frac_2 >= 0 and frac_2 <= 1): - return edge - return None - -def interpolate_data_from_face(bm_source, tris_dict, face, p, data_layer_source, data_suffix = ''): - ''' Returns interpolated value of a data layer within a face closest to a point.''' - - (tri, point) = closest_tri_on_face(tris_dict, face, p) - if not tri: - return None - weights = mathutils.interpolate.poly_3d_calc([tri[i].vert.co for i in range(3)], point) - - if not data_suffix: - cols_weighted = [weights[i]*np.array(data_layer_source[tri[i].index]) for i in range(3)] - col = sum(np.array(cols_weighted)) - else: - cols_weighted = [weights[i]*np.array(getattr(data_layer_source[tri[i].index], data_suffix)) for i in range(3)] - col = sum(np.array(cols_weighted)) - return col - -def closest_face_to_point(bm_source, p_target, bvh_tree = None): - if not bvh_tree: - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - (loc, norm, index, distance) = bvh_tree.find_nearest(p_target) - return bm_source.faces[index] - -def tris_per_face(bm_source): - tris_source = bm_source.calc_loop_triangles() - tris_dict = dict() - for face in bm_source.faces: - tris_face = [] - for i in range(len(tris_source))[::-1]: - if tris_source[i][0] in face.loops: - tris_face.append(tris_source.pop(i)) - tris_dict[face] = tris_face - return tris_dict - -def closest_tri_on_face(tris_dict, face, p): - points = [] - dist = [] - tris = [] - for tri in tris_dict[face]: - point = mathutils.geometry.closest_point_on_tri(p, *[tri[i].vert.co for i in range(3)]) - tris.append(tri) - points.append(point) - dist.append((point-p).length) - min_idx = np.argmin(np.array(dist)) - point = points[min_idx] - tri = tris[min_idx] - return (tri, point) - -def transfer_corner_data(obj_source, obj_target, data_layer_source, data_layer_target, data_suffix = ''): - ''' - Transfers interpolated face corner data from data layer of a source object to data layer of a - target object, while approximately preserving data seams (e.g. necessary for UV Maps). - The transfer is face interpolated per target corner within the source face that is closest - to the target corner point and does not have any data seams on the way back to the - source face that is closest to the target face's center. - ''' - bm_source = bmesh.new() - bm_source.from_mesh(obj_source.data) - bm_source.faces.ensure_lookup_table() - bm_target = bmesh.new() - bm_target.from_mesh(obj_target.data) - bm_target.faces.ensure_lookup_table() - - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - - tris_dict = tris_per_face(bm_source) - - for face_target in bm_target.faces: - face_target_center = face_target.calc_center_median() - - face_source = closest_face_to_point(bm_source, face_target_center, bvh_tree) - - for corner_target in face_target.loops: - #find nearest face on target compared to face that loop belongs to - p = corner_target.vert.co - - face_source_closest = closest_face_to_point(bm_source, p, bvh_tree) - enclosed = face_source_closest is face_source - face_source_int = face_source - if not enclosed: - # traverse faces between point and face center - traversed_faces = set() - traversed_edges = set() - while(face_source_int is not face_source_closest): - traversed_faces.add(face_source_int) - edge = closest_edge_on_face_to_line(face_source_int, face_target_center, p, skip_edges = traversed_edges) - if edge == None: - break - if len(edge.link_faces)!=2: - break - traversed_edges.add(edge) - - split = edge_data_split(edge, data_layer_source, data_suffix) - if split: - break - - # set new source face to other face belonging to edge - face_source_int = edge.link_faces[1] if edge.link_faces[1] is not face_source_int else edge.link_faces[0] - - # avoid looping behaviour - if face_source_int in traversed_faces: - face_source_int = face_source - break - - # interpolate data from selected face - col = interpolate_data_from_face(bm_source, tris_dict, face_source_int, p, data_layer_source, data_suffix) - if col is None: - continue - if not data_suffix: - data_layer_target.data[corner_target.index] = col - else: - setattr(data_layer_target[corner_target.index], data_suffix, list(col)) - return - -def transfer_shapekeys_proximity(obj_source, obj_target) -> None: - ''' - Transfers shapekeys from one object to another - based on the mesh proximity with face interpolation. - ''' - # copy shapekey layout - if not obj_source.data.shape_keys: - return - for sk_source in obj_source.data.shape_keys.key_blocks: - if obj_target.data.shape_keys: - sk_target = obj_target.data.shape_keys.key_blocks.get(sk_source.name) - if sk_target: - continue - sk_target = obj_target.shape_key_add() - sk_target.name = sk_source.name - for sk_target in obj_target.data.shape_keys.key_blocks: - sk_source = obj_source.data.shape_keys.key_blocks[sk_target.name] - sk_target.vertex_group = sk_source.vertex_group - sk_target.relative_key = obj_target.data.shape_keys.key_blocks[sk_source.relative_key.name] - - bm_source = bmesh.new() - bm_source.from_mesh(obj_source.data) - bm_source.faces.ensure_lookup_table() - - bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(bm_source) - - tris_dict = tris_per_face(bm_source) - - for i, vert in enumerate(obj_target.data.vertices): - p = vert.co - face = closest_face_to_point(bm_source, p, bvh_tree) - - (tri, point) = closest_tri_on_face(tris_dict, face, p) - if not tri: - continue - weights = mathutils.interpolate.poly_3d_calc([tri[i].vert.co for i in range(3)], point) - - for sk_target in obj_target.data.shape_keys.key_blocks: - sk_source = obj_source.data.shape_keys.key_blocks.get(sk_target.name) - - vals_weighted = [weights[i]*(sk_source.data[tri[i].vert.index].co-obj_source.data.vertices[tri[i].vert.index].co) for i in range(3)] - val = mathutils.Vector(sum(np.array(vals_weighted))) - sk_target.data[i].co = vert.co+val - -class GroomingTaskLayer(TaskLayer): - name = "Grooming" - order = 2 - - @classmethod - def transfer_data( - cls, - context: bpy.types.Context, - build_context: BuildContext, - transfer_mapping: AssetTransferMapping, - transfer_settings: bpy.types.PropertyGroup, - ) -> None: - - print(f"\n\033[1mProcessing data from {cls.__name__}...\033[0m") - coll_source = transfer_mapping.source_coll - coll_target = transfer_mapping.target_coll - for obj_source, obj_target in transfer_mapping.object_map.items(): - if not "PARTICLE_SYSTEM" in [mod.type for mod in obj_source.modifiers]: - continue - l = [] - for mod in obj_source.modifiers: - if not mod.type == "PARTICLE_SYSTEM": - l += [mod.show_viewport] - mod.show_viewport = False - - bpy.ops.particle.copy_particle_systems( - {"object": obj_source, "selected_editable_objects": [obj_target]} - ) - - c = 0 - for mod in obj_source.modifiers: - if mod.type == "PARTICLE_SYSTEM": - continue - mod.show_viewport = l[c] - c += 1 - - # TODO: handle cases where collections with exact naming convention cannot be found - try: - coll_from_hair = next(c for name, c in coll_source.children.items() if ".hair" in name) - coll_from_part = next(c for name, c in coll_from_hair.children.items() if ".hair.particles" in name) - coll_from_part_proxy = next(c for name, c in coll_from_part.children.items() if ".hair.particles.proxy" in name) - except: - print(warning_text(f"Could not find existing particle hair collection. Make sure you are following the exact naming and structuring convention!")) - return - - # link 'from' hair.particles collection in 'to' - try: - coll_to_hair = next(c for name, c in coll_target.children.items() if ".hair" in name) - except: - coll_target.children.link(coll_from_hair) - return - - coll_to_hair.children.link(coll_from_part) - try: - coll_to_part = next(c for name, c in coll_to_hair.children.items() if ".hair.particles" in name) - except: - print(warning_text(f"Failed to find particle hair collections in target collection")) - coll_to_part.user_clear() - bpy.data.collections.remove(coll_to_part) - return - - # transfer shading - # transfer_dict = map_objects_by_name(coll_to_part, coll_from_part) - # transfer_shading_data(context, transfer_dict) - ShadingTaskLayer.transfer_data(context, transfer_mapping, transfer_settings) - - # transfer modifers - for obj_source, obj_target in transfer_mapping.object_map.items(): - if not "PARTICLE_SYSTEM" in [m.type for m in obj_target.modifiers]: - bpy.ops.object.make_links_data( - {"object": obj_source, "selected_editable_objects": [obj_target]}, - type="MODIFIERS", - ) - - # We want to rig the hair base mesh with an Armature modifier, so transfer vertex groups by proximity. - bpy.ops.object.data_transfer( - {"object": obj_source, "selected_editable_objects": [obj_target]}, - data_type="VGROUP_WEIGHTS", - use_create=True, - vert_mapping="NEAREST", - layers_select_src="ALL", - layers_select_dst="NAME", - mix_mode="REPLACE", - ) - - # We used to want to rig the auto-generated hair particle proxy meshes with Surface Deform, so re-bind those. - # NOTE: Surface Deform probably won't be used for final rigging - for mod in obj_target.modifiers: - if mod.type == "SURFACE_DEFORM" and mod.is_bound: - for i in range(2): - bpy.ops.object.surfacedeform_bind( - {"object": obj_target}, modifier=mod.name - ) - - copy_rigging_object_data(obj_source, obj_target) - # remove 'to' hair.particles collection - coll_to_part.user_clear() - bpy.data.collections.remove(coll_to_part) - return diff --git a/scripts-blender/addons/asset_pipeline/lib_util.py b/scripts-blender/addons/asset_pipeline/lib_util.py deleted file mode 100644 index 87e3e1f8..00000000 --- a/scripts-blender/addons/asset_pipeline/lib_util.py +++ /dev/null @@ -1,74 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import os -import logging -from typing import Optional, Any, Set, Tuple, List, Union -from pathlib import Path - -import bpy - - -class ItemIsLocal(Exception): - pass - - -def is_item_local( - item: Union[bpy.types.Collection, bpy.types.Object, bpy.types.Camera] -) -> bool: - # Local collection of blend file. - if not item.override_library and not item.library: - return True - return False - - -def is_item_lib_override( - item: Union[bpy.types.Collection, bpy.types.Object, bpy.types.Camera] -) -> bool: - # Collection from libfile and overwritten. - if item.override_library and not item.library: - return True - return False - - -def is_item_lib_source( - item: Union[bpy.types.Collection, bpy.types.Object, bpy.types.Camera] -) -> bool: - # Source collection from libfile not overwritten. - if not item.override_library and item.library: - return True - return False - - -def get_item_lib( - item: Union[bpy.types.Collection, bpy.types.Object, bpy.types.Camera] -) -> bpy.types.Library: - if is_item_local(item): - # Local collection - raise ItemIsLocal(f"{item} is local to this blend file. Cannot get lib.") - - if is_item_lib_source(item): - # Source collection not overwritten. - return item.library - - if is_item_lib_override(item): - # Overwritten collection. - return item.override_library.reference.library - - raise RuntimeError(f"Failed to get libfile for {item}") diff --git a/scripts-blender/addons/asset_pipeline/prefs.py b/scripts-blender/addons/asset_pipeline/prefs.py deleted file mode 100644 index 1ee2699e..00000000 --- a/scripts-blender/addons/asset_pipeline/prefs.py +++ /dev/null @@ -1,94 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import logging -from typing import Optional, Any, Set, Tuple, List -from pathlib import Path - -import bpy - - -logger = logging.getLogger(name="BSP") - - -class BSP_addon_preferences(bpy.types.AddonPreferences): - - bl_idname = __package__ - - def get_prod_task_layers_module_path(self) -> str: - if not self.prod_config_dir: - return "" - - return Path(self.prod_config_dir).joinpath("task_layers.py").as_posix() - - prod_config_dir: bpy.props.StringProperty( # type: ignore - name="Production Config Directory", - default="", - subtype="DIR_PATH", - ) - - prod_task_layers_module: bpy.props.StringProperty( # type: ignore - name="Production Task Layers Module", - default="", - get=get_prod_task_layers_module_path, - ) - - def is_prod_task_layers_module_path_valid(self) -> bool: - path = self.get_prod_task_layers_module_path() - if not path: - return False - - if not Path(path).exists(): - return False - return True - - def draw(self, context: bpy.types.Context) -> None: - layout: bpy.types.UILayout = self.layout - - # Production Settings. - box = layout.box() - box.label(text="Production", icon="FILEBROWSER") - - # Production Config Dir. - row = box.row(align=True) - row.prop(self, "prod_config_dir") - - # Production Task Layers Module. - icon = "NONE" - row = box.row(align=True) - - if not self.is_prod_task_layers_module_path_valid(): - icon = "ERROR" - - row.prop(self, "prod_task_layers_module", icon=icon) - - -# ----------------REGISTER--------------. - -classes = [BSP_addon_preferences] - - -def register() -> None: - for cls in classes: - bpy.utils.register_class(cls) - - -def unregister() -> None: - for cls in reversed(classes): - bpy.utils.unregister_class(cls) diff --git a/scripts-blender/addons/asset_pipeline/prop_utils.py b/scripts-blender/addons/asset_pipeline/prop_utils.py deleted file mode 100644 index 482e3540..00000000 --- a/scripts-blender/addons/asset_pipeline/prop_utils.py +++ /dev/null @@ -1,37 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -from typing import List, Dict, Union, Any, Optional, Tuple, Generator - -import bpy - - -def get_property_group_items( - property_group: bpy.types.PropertyGroup, -) -> Generator[Tuple[str, bpy.types.Property], None, None]: - - for i in range(len(property_group.bl_rna.properties.items())): - item = property_group.bl_rna.properties.items()[i] - iname, iprop = item - - if iname in ["rna_type", "bl_rna", "name"]: - continue - - yield item diff --git a/scripts-blender/addons/asset_pipeline/props.py b/scripts-blender/addons/asset_pipeline/props.py deleted file mode 100644 index 60f24870..00000000 --- a/scripts-blender/addons/asset_pipeline/props.py +++ /dev/null @@ -1,445 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import os -from typing import Optional, Dict, Any, List, Tuple - -from pathlib import Path - -import bpy - -try: - from .util import is_addon_active - import blender_kitsu.cache - kitsu_available = True -except: - kitsu_available = False -from . import constants, builder, asset_files, lib_util -from .builder.metadata import MetadataAsset, MetadataTaskLayer -from .asset_files import AssetPublish - -import logging - -logger = logging.getLogger("BSP") - - -class FailedToGetAssetPublish(Exception): - pass - - -class BSP_ASSET_asset_collection(bpy.types.PropertyGroup): - """ - Collection Properties for Blender Studio Asset Collections - """ - - # Global is asset identifier. - is_asset: bpy.props.BoolProperty( # type: ignore - name="Is Asset", - default=False, - description="Controls if this Collection is recognized as an official Asset", - ) - - # Asset identification properties. - # We use entity_ prefix as blender uses .id as built in attribute already, which - # might be confusing. - entity_parent_id: bpy.props.StringProperty(name="Asset Type ID") # type: ignore - entity_parent_name: bpy.props.StringProperty(name="Asset Type") # type: ignore - entity_name: bpy.props.StringProperty(name="Asset Name") # type: ignore - entity_id: bpy.props.StringProperty(name="Asset ID") # type: ignore - project_id: bpy.props.StringProperty(name="Project ID") # type: ignore - - # For Asset Publish. - is_publish: bpy.props.BoolProperty( # type: ignore - name="Is Publish", - description="Controls if this Collection is an Asset Publish to distinguish it from a 'working' Collection", - ) - version: bpy.props.StringProperty(name="Asset Version") # type: ignore - publish_path: bpy.props.StringProperty(name="Asset Publish") # type: ignore - - # Other properties, useful for external scripts. - rig: bpy.props.PointerProperty(type=bpy.types.Armature, name="Rig") # type: ignore - - # Metadata for Asset Builder. - transfer_suffix: bpy.props.StringProperty(name="Transfer Suffix") # type: ignore - - # Display properties that can't be set by User in UI. - displ_entity_name: bpy.props.StringProperty(name="Asset Name", get=lambda self: self.entity_name) # type: ignore - displ_entity_id: bpy.props.StringProperty(name="Asset ID", get=lambda self: self.entity_id) # type: ignore - - displ_is_publish: bpy.props.BoolProperty(name="Is Publish", get=lambda self: self.is_publish) # type: ignore - displ_version: bpy.props.StringProperty(name="Asset Version", get=lambda self: self.version) # type: ignore - displ_publish_path: bpy.props.StringProperty(name="Asset Path", get=lambda self: self.publish_path) # type: ignore - - def clear(self) -> None: - """ - Gets called when uninitializing an Asset Collection for example. - """ - - self.is_asset = False - - self.entity_parent_id = "" - self.entity_parent_name = "" - self.entity_name = "" - self.entity_id = "" - self.project_id = "" - - self.is_publish = False - self.version = "" - - self.rig = None - - self.transfer_suffix = "" - - def gen_metadata_class(self) -> MetadataAsset: - # These keys represent all mandatory arguments for the data class metadata.MetaAsset - # The idea is, to be able to construct a MetaAsst from this dict. - # Note: This function will most likely only be called when creating the first asset version - # to get some data to start with. - keys = [ - "entity_name", - "entity_id", - "entity_parent_id", - "entity_parent_name", - "project_id", - "version", - ] - d = {} - for key in keys: - - # MetaAsset tries to mirror Kitsu data structure as much as possible. - # Remove entity_ prefix. - if key.startswith("entity_"): - d[key.replace("entity_", "")] = getattr(self, key) - else: - d[key] = getattr(self, key) - - # Set status to default asset status. - d["status"] = constants.DEFAULT_ASSET_STATUS - return MetadataAsset.from_dict(d) - - def update_props_by_asset_publish(self, asset_publish: AssetPublish) -> None: - self.is_publish = True - self.version = asset_publish.get_version() - self.status = asset_publish.metadata.meta_asset.status.name - - def get_asset_publish(self) -> AssetPublish: - if not self.is_publish: - raise FailedToGetAssetPublish( - f"The collection {self.id_data.name} is not an asset publish" - ) - - # Will throw error if item is not lib. - lib = lib_util.get_item_lib(self.id_data) - - return AssetPublish(Path(os.path.abspath(bpy.path.abspath(lib.filepath)))) - - -class BSP_task_layer(bpy.types.PropertyGroup): - - """ - Property Group that can represent a minimal TaskLayer. - Note: It misses properties compared to MetadataTaskLayer class, contains only the ones - needed for internal use. Also contains 'use' attribute to avoid creating a new property group - to mimic more the TaskLayer TaskLayerConfig setup. - Is used in BSP_ASSET_scene_properties as collection property. - """ - - task_layer_id: bpy.props.StringProperty( # type: ignore - name="Task Layer ID", - description="Unique Key that is used to query a Task Layer in TaskLayerAssembly.get_task_layer_config()", - ) - task_layer_name: bpy.props.StringProperty( # type: ignore - name="Task Layer Name", - ) - - is_locked: bpy.props.BoolProperty( # type: ignore - name="Is Locked", - ) - - use: bpy.props.BoolProperty( # type: ignore - name="Use", - ) - - def as_dict(self) -> Dict[str, Any]: - return { - "use": self.use, - "is_locked": self.is_locked, - "task_layer_id": self.task_layer_id, - "task_layer_name": self.task_layer_name, - } - - -class BSP_asset_file(bpy.types.PropertyGroup): - - """ - Property Group that can represent a minimal version of an Asset File. - """ - - path_str: bpy.props.StringProperty( # type: ignore - name="Path", - ) - task_layers: bpy.props.CollectionProperty(type=BSP_task_layer) # type: ignore - - status: bpy.props.StringProperty(name="Status") # type: ignore - - returncode_publish: bpy.props.IntProperty( - name="Return Code", - description=( - "This code represents the return code of the subprocess that gets " - "started when publishing. Is used to display a warning in UI if something went wrong" - ), - default=-1, - ) - - @property - def path(self) -> Optional[Path]: - if not self.path_str: - return None - return Path(self.path_str) - - def as_dict(self) -> Dict[str, Any]: - return {"path": self.path} - - def add_task_layer_from_metaclass(self, metadata_task_layer: MetadataTaskLayer): - item = self.task_layers.add() - # TODO: could be made more procedural. - item.task_layer_id = metadata_task_layer.id - item.task_layer_name = metadata_task_layer.name - item.is_locked = metadata_task_layer.is_locked - - def update_props_by_asset_publish(self, asset_publish: AssetPublish) -> None: - self.name = asset_publish.path.name - self.path_str = asset_publish.path.as_posix() - self.status = asset_publish.metadata.meta_asset.status.name - - # Clear task layers. - self.task_layers.clear() - - # Add task layers. - for tl in asset_publish.metadata.meta_task_layers: - self.add_task_layer_from_metaclass(tl) - - -class BSP_ASSET_imported_asset_collection(bpy.types.PropertyGroup): - - # XXX: This is not a pointer due to a bug where disabled/excluded collections - # that have a pointer from the scene cause them to be partially evaluated. - collection_name: bpy.props.StringProperty(name="Collection Name", description="Name of the imported asset collection") # type: ignore - @property - def collection(self): - return bpy.data.collections.get(self.collection_name) - @collection.setter - def collection(self, value): - self.collection_name = value.name - - asset_publishes: bpy.props.CollectionProperty(type=BSP_asset_file) # type: ignore - - def get_asset_publishes_as_bl_enum( - self, context: bpy.types.Context - ) -> List[Tuple[str, str, str]]: - return [ - (p.name, asset_files.get_file_version(p.path), "") - for p in self.asset_publishes - ] - - target_publish: bpy.props.EnumProperty(items=get_asset_publishes_as_bl_enum) # type: ignore - - -class BSP_undo_context(bpy.types.PropertyGroup): - - """ """ - - files_created: bpy.props.CollectionProperty(type=BSP_asset_file) # type: ignore - - def add_step_asset_publish_create(self, asset_publish: AssetPublish) -> None: - item = self.files_created.add() - item.name = asset_publish.path.name - item.path_str = asset_publish.path.as_posix() - - def clear(self): - self.files_created.clear() - - -class BSP_task_layer_lock_plan(bpy.types.PropertyGroup): - - """ - Property Group that can represent a minimal version of a TaskLayerLockPlan. - """ - - path_str: bpy.props.StringProperty( # type: ignore - name="Path", - ) - task_layers: bpy.props.CollectionProperty(type=BSP_task_layer) # type: ignore - - @property - def path(self) -> Optional[Path]: - if not self.path_str: - return None - return Path(self.path_str) - - -class BSP_ASSET_scene_properties(bpy.types.PropertyGroup): - """Scene Properties for Asset Pipeline""" - - def update_asset_collection(self, context): - """There should only be one asset collection per file, so before - initializing another asset collection, wipe any asset collection - data in the entire file. - """ - - for coll in bpy.data.collections: - # Clear Asset Collection attributes. - coll.bsp_asset.clear() - - if not self.asset_collection: - return - - bsp_asset = self.asset_collection.bsp_asset - bsp_asset.entity_name = self.asset_collection.name.split("-")[-1].title() - - # Unitialize Asset Context. - builder.ASSET_CONTEXT = None - - if kitsu_available and is_addon_active("blender_kitsu", context): - # Get active asset. - asset = blender_kitsu.cache.asset_active_get() - asset_type = blender_kitsu.cache.asset_type_active_get() - - if asset: - # Set Asset Collection attributes. - bsp_asset.is_asset = True - bsp_asset.entity_id = asset.id - bsp_asset.entity_name = asset.name - bsp_asset.project_id = asset.project_id - bsp_asset.entity_parent_id = asset_type.id - bsp_asset.entity_parent_name = asset_type.name - - logger.info( - f"Initiated Collection: {self.asset_collection.name} as Kitsu Asset: {asset.name}" - ) - - logger.info(f"Initiated Collection: {self.asset_collection.name}") - - # Init Asset Context. - if bpy.ops.bsp_asset.create_asset_context.poll(): - bpy.ops.bsp_asset.create_asset_context() - - # asset_collection: bpy.props.PointerProperty( - # type=bpy.types.Collection, - # name="Asset Collection", - # update=update_asset_collection, - # ) - @property - def asset_collection(self): - return bpy.data.collections.get(self.asset_collection_name) - - @asset_collection.setter - def asset_collection(self, value): - self.asset_collection_name = value.name - - asset_collection_name: bpy.props.StringProperty(name="Asset Collection", update=update_asset_collection) - - is_publish_in_progress: bpy.props.BoolProperty() # type: ignore - are_task_layers_pushed: bpy.props.BoolProperty() # type: ignore - - task_layers_push: bpy.props.CollectionProperty(type=BSP_task_layer) # type: ignore - task_layers_pull: bpy.props.CollectionProperty(type=BSP_task_layer) # type: ignore - - def task_layers(self, context): - return ( - [(tl.name, tl.name, tl.name) for tl in builder.PROD_CONTEXT.task_layers] - if builder.PROD_CONTEXT - else [] - ) - - asset_publishes: bpy.props.CollectionProperty(type=BSP_asset_file) # type: ignore - - task_layers_push_index: bpy.props.IntProperty(name="Task Layers Owned Index", min=0) # type: ignore - task_layers_pull_index: bpy.props.IntProperty(name="Task Layers Pull Index", min=0) # type: ignore - asset_publishes_index: bpy.props.IntProperty(name="Asset Publishes Index", min=0) # type: ignore - task_layer_lock_plans_index: bpy.props.IntProperty(name="Task Layer Lock Plans Index", min=0) # type: ignore - - undo_context: bpy.props.PointerProperty(type=BSP_undo_context) # type: ignore - - task_layer_lock_plans: bpy.props.CollectionProperty(type=BSP_task_layer_lock_plan) # type: ignore - - imported_asset_collections: bpy.props.CollectionProperty(type=BSP_ASSET_imported_asset_collection) # type: ignore - imported_asset_collections_index: bpy.props.IntProperty(min=0) # type: ignore - - -def get_asset_publish_source_path(context: bpy.types.Context) -> str: - if not builder.ASSET_CONTEXT: - return "" - - if not builder.ASSET_CONTEXT.asset_publishes: - return "" - - return builder.ASSET_CONTEXT.asset_publishes[-1].path.name - - -class BSP_ASSET_tmp_properties(bpy.types.PropertyGroup): - - # Asset publish source - asset_publish_source_path: bpy.props.StringProperty( # type: ignore - name="Source", get=get_asset_publish_source_path - ) - - new_asset_version: bpy.props.BoolProperty( # type: ignore - name="New Version", - description="Controls if new Version should be created when starting the publish", - ) - - -# ----------------REGISTER--------------. - -classes = [ - BSP_task_layer, - BSP_asset_file, - BSP_undo_context, - BSP_ASSET_asset_collection, - BSP_task_layer_lock_plan, - BSP_ASSET_imported_asset_collection, - BSP_ASSET_scene_properties, - BSP_ASSET_tmp_properties, -] - - -def register() -> None: - for cls in classes: - bpy.utils.register_class(cls) - - # Collection Asset Pipeline Properties. - bpy.types.Collection.bsp_asset = bpy.props.PointerProperty( - type=BSP_ASSET_asset_collection - ) - - # Scene Asset Pipeline Properties. - bpy.types.Scene.bsp_asset = bpy.props.PointerProperty( - type=BSP_ASSET_scene_properties - ) - - # Window Manager Properties. - bpy.types.WindowManager.bsp_asset = bpy.props.PointerProperty( - type=BSP_ASSET_tmp_properties - ) - - -def unregister() -> None: - for cls in reversed(classes): - bpy.utils.unregister_class(cls) diff --git a/scripts-blender/addons/asset_pipeline/sys_utils.py b/scripts-blender/addons/asset_pipeline/sys_utils.py deleted file mode 100644 index f9071dfb..00000000 --- a/scripts-blender/addons/asset_pipeline/sys_utils.py +++ /dev/null @@ -1,68 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# This file was made by Jeroen Bakker in the shot-builder repository: -# https://developer.blender.org/diffusion/BSTS/browse/master/shot-builder/shot_builder/sys_utils - - -import sys -import pathlib -import logging -from typing import List, Dict, Union, Any, Optional - -logger = logging.getLogger("BSP") - - -class SystemPathInclude: - """ - Resource class to temporary include system paths to `sys.paths`. - - Usage: - ``` - paths = [pathlib.Path("/home/guest/my_python_scripts")] - with SystemPathInclude(paths) as t: - import my_module - reload(my_module) - ``` - - It is possible to nest multiple SystemPathIncludes. - """ - - def __init__(self, paths_to_add: List[pathlib.Path]): - # TODO: Check if all paths exist and are absolute. - self.__paths = paths_to_add - self.__original_sys_path: List[str] = [] - - def __enter__(self): - self.__original_sys_path = sys.path - new_sys_path = [] - for path_to_add in self.__paths: - # Do not add paths that are already in the sys path. - # Report this to the logger as this might indicate wrong usage. - path_to_add_str = str(path_to_add) - if path_to_add_str in self.__original_sys_path: - logger.warn(f"{path_to_add_str} already added to `sys.path`") - continue - new_sys_path.append(path_to_add_str) - new_sys_path.extend(self.__original_sys_path) - sys.path = new_sys_path - - def __exit__(self, exc_type, exc_value, exc_traceback): - sys.path = self.__original_sys_path diff --git a/scripts-blender/addons/asset_pipeline/tests/__init__.py b/scripts-blender/addons/asset_pipeline/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/scripts-blender/addons/asset_pipeline/tests/test_blender_studio_pipeline.py b/scripts-blender/addons/asset_pipeline/tests/test_blender_studio_pipeline.py deleted file mode 100644 index f8b3ba35..00000000 --- a/scripts-blender/addons/asset_pipeline/tests/test_blender_studio_pipeline.py +++ /dev/null @@ -1,5 +0,0 @@ -from asset_pipeline import __version__ - - -def test_version(): - assert __version__ == "0.1.0" diff --git a/scripts-blender/addons/asset_pipeline/updater/__init__.py b/scripts-blender/addons/asset_pipeline/updater/__init__.py deleted file mode 100644 index 7f0fe0a4..00000000 --- a/scripts-blender/addons/asset_pipeline/updater/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import importlib - -from typing import List, Dict, Union, Any, Set, Optional - -from . import ops, ui -from .asset_updater import AssetUpdater - -# Initialize variables. -ASSET_UPDATER = AssetUpdater() - - -# ----------------REGISTER--------------. - - -def reload() -> None: - global ops - global ui - - importlib.reload(ops) - importlib.reload(ui) - - -def register() -> None: - ops.register() - ui.register() - - -def unregister() -> None: - ui.unregister() - ops.unregister() diff --git a/scripts-blender/addons/asset_pipeline/updater/asset_updater.py b/scripts-blender/addons/asset_pipeline/updater/asset_updater.py deleted file mode 100644 index bbd6f4bb..00000000 --- a/scripts-blender/addons/asset_pipeline/updater/asset_updater.py +++ /dev/null @@ -1,71 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple, Callable -from pathlib import Path - -import bpy - -from .. import lib_util - -logger = logging.getLogger("BSP") - - -class AssetUpdater: - def __init__(self): - self._asset_collections: Set[bpy.types.Collection] = set() - - def collect_asset_collections_in_scene( - self, context: bpy.types.Context - ) -> List[bpy.types.Collection]: - """ - Collects all asset collections that have coll.bsp_asset.is_publish==True in current scene. - Only collects them if they are linked in or library overwritten. - """ - self._asset_collections.clear() - - for coll in context.scene.collection.children_recursive: - - # If item is not coming from a library: Skip. - if lib_util.is_item_local(coll): - continue - - if coll.bsp_asset.is_publish: - self._asset_collections.add(coll) - - @property - def asset_collections(self) -> Set[bpy.types.Collection]: - return self._asset_collections - - def update_asset_collection_libpath( - self, asset_collection: bpy.types.Collection, new_libpath: Path - ) -> bpy.types.Collection: - coll_name = asset_collection.name - lib = lib_util.get_item_lib(asset_collection) - self.update_libpath(lib, new_libpath) - return bpy.data.collections[coll_name] - - def update_libpath(self, lib: bpy.types.Library, new_libpath: Path) -> None: - bpy.ops.wm.lib_relocate( - library=lib.name, - directory=new_libpath.parent.as_posix(), - filename=new_libpath.name, - ) diff --git a/scripts-blender/addons/asset_pipeline/updater/ops.py b/scripts-blender/addons/asset_pipeline/updater/ops.py deleted file mode 100644 index 983a3059..00000000 --- a/scripts-blender/addons/asset_pipeline/updater/ops.py +++ /dev/null @@ -1,137 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import os -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from pathlib import Path - -import bpy -from bpy.app.handlers import persistent - -from . import opsdata - -from .. import util, lib_util, updater - - -class BSP_ASSET_UPDATER_collect_assets(bpy.types.Operator): - bl_idname = "bsp_asset.collect_assets" - bl_label = "Collect Assets" - bl_description = "Scans Scene for imported Assets" - - def execute(self, context: bpy.types.Context) -> Set[str]: - - # Initialize Asset Updater and scan for scene. - updater.ASSET_UPDATER.collect_asset_collections_in_scene(context) - - # Populate context with collected asset collections. - opsdata.populate_context_with_imported_asset_colls( - context, updater.ASSET_UPDATER - ) - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - -class BSP_ASSET_UPDATER_update_asset(bpy.types.Operator): - bl_idname = "bsp_asset.update_asset" - bl_label = "Update Assets" - bl_description = "Updates Asset to target version that is selected in the list view" - - index: bpy.props.IntProperty(name="Index", min=0) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - prop_group = context.scene.bsp_asset.imported_asset_collections[self.index] - - collection: bpy.types.Collection = prop_group.collection - target_publish: str = prop_group.target_publish - asset_file: bpy.types.PropertyGroup = prop_group.asset_publishes[target_publish] - # asset_publish = AssetPublish(asset_file.path) - lib = lib_util.get_item_lib(collection) - libpath = Path(os.path.abspath(bpy.path.abspath(lib.filepath))) - - # Check if same version is loaded. - if Path(bpy.path.abspath(asset_file.path_str)) == libpath: - self.report({"WARNING"}, f"{libpath.name} is already loaded") - # lib.reload() # Crashes blender? TODO: report - return {"CANCELLED"} - - # Collection pointer gets lost after this operation. - updater.ASSET_UPDATER.update_asset_collection_libpath( - collection, asset_file.path - ) - - # TODO: save this to metadata file, so we can inspect this information - # without opening a blend file. - - # Redraw UI. - util.redraw_ui() - - return {"FINISHED"} - - -class BSP_ASSET_UPDATER_update_all(bpy.types.Operator): - bl_idname = "bsp_asset.update_all" - bl_label = "Update All Assets" - bl_description = ( - "Updates all Assets to target version that is selected in the list view" - ) - - def execute(self, context: bpy.types.Context) -> Set[str]: - - for idx, item in enumerate(context.scene.bsp_asset.imported_asset_collections): - bpy.ops.bsp_asset.update_asset(index=idx) - - return {"FINISHED"} - - -@persistent -def collect_assets_in_scene(_): - bpy.ops.bsp_asset.collect_assets() - - -# ----------------REGISTER--------------. - -classes = [ - BSP_ASSET_UPDATER_collect_assets, - BSP_ASSET_UPDATER_update_asset, - BSP_ASSET_UPDATER_update_all, -] - - -def register() -> None: - for cls in classes: - bpy.utils.register_class(cls) - - # Handlers. - bpy.app.handlers.load_post.append(collect_assets_in_scene) - - -def unregister() -> None: - - # Handlers. - bpy.app.handlers.load_post.remove(collect_assets_in_scene) - - for cls in reversed(classes): - bpy.utils.unregister_class(cls) diff --git a/scripts-blender/addons/asset_pipeline/updater/opsdata.py b/scripts-blender/addons/asset_pipeline/updater/opsdata.py deleted file mode 100644 index e7914c38..00000000 --- a/scripts-blender/addons/asset_pipeline/updater/opsdata.py +++ /dev/null @@ -1,99 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -import logging - -from typing import List, Dict, Union, Any, Set, Optional, Tuple -from pathlib import Path - -import bpy - -from .asset_updater import AssetUpdater - -from ..asset_files import AssetPublish -from ..asset_status import AssetStatus - - -logger = logging.getLogger("BSP") - - -def add_imported_asset_coll_to_context( - context: bpy.types.Context, asset_coll: bpy.types.Collection -) -> None: - - asset_publish: AssetPublish = asset_coll.bsp_asset.get_asset_publish() - - # Add item. - item = context.scene.bsp_asset.imported_asset_collections.add() - - # Set collection property. - item.collection = asset_coll - - # Collect all publishes on disk for that asset collection. - asset_dir = asset_publish.asset_dir - for publish in asset_dir.get_asset_publishes(): - - # Dont' offer asset publishes that are still in review. - # But still append the current imported version (if its in review state) - if ( - publish.metadata.meta_asset.status == AssetStatus.REVIEW - and asset_publish != publish - ): - logger.debug( - "Asset-Updater: %s skip %s as status is %s", - asset_publish.metadata.meta_asset.name, - publish.path.name, - AssetStatus.REVIEW.name, - ) - continue - - item_publish = item.asset_publishes.add() - item_publish.update_props_by_asset_publish(publish) - logger.debug( - "Asset-Updater: %s found: %s", - asset_publish.metadata.meta_asset.name, - publish.path.name, - ) - - # Set enum property to latest version. - if item.asset_publishes: - item.target_publish = item.asset_publishes[-1].name - - -def populate_context_with_imported_asset_colls( - context: bpy.types.Context, asset_updater: AssetUpdater -) -> None: - def sorting_keys(coll: bpy.types.Collection) -> Tuple[bool, str]: - """ - This sorting functions moves assets that are deprecated to the top and sorts - the rest of the collections in alphabetical order. - """ - asset_publish: AssetPublish = coll.bsp_asset.get_asset_publish() - return ( - asset_publish.metadata.meta_asset.status != AssetStatus.DEPRECATED, - coll.name, - ) - - context.scene.bsp_asset.imported_asset_collections.clear() - - asset_collections = sorted(asset_updater.asset_collections, key=sorting_keys) - # Add asset publishes. - for asset_coll in asset_collections: - add_imported_asset_coll_to_context(context, asset_coll) diff --git a/scripts-blender/addons/asset_pipeline/updater/ui.py b/scripts-blender/addons/asset_pipeline/updater/ui.py deleted file mode 100644 index 4943b689..00000000 --- a/scripts-blender/addons/asset_pipeline/updater/ui.py +++ /dev/null @@ -1,142 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter -from pathlib import Path -from typing import List, Dict, Union, Any, Set, Optional - -import bpy - -from .ops import ( - BSP_ASSET_UPDATER_collect_assets, - BSP_ASSET_UPDATER_update_asset, - BSP_ASSET_UPDATER_update_all, -) - -from .. import constants, lib_util -from ..asset_status import AssetStatus - - -def draw_imported_asset_collections_in_scene( - self: bpy.types.Panel, - context: bpy.types.Context, - disable: bool = False, - box: Optional[bpy.types.UILayout] = None, -) -> bpy.types.UILayout: - layout: bpy.types.UILayout = self.layout - - if not box: - box = layout.box() - row = box.row(align=True) - row.label(text="Asset Collections") - row.operator( - BSP_ASSET_UPDATER_collect_assets.bl_idname, icon="FILE_REFRESH", text="" - ) - - # Ui-list. - row = box.row() - row.template_list( - "BSP_UL_imported_asset_collections", - "imported_asset_collections_list", - context.scene.bsp_asset, - "imported_asset_collections", - context.scene.bsp_asset, - "imported_asset_collections_index", - rows=constants.DEFAULT_ROWS, - type="DEFAULT", - ) - if disable: - row.enabled = False - - return box - - -class BSP_ASSET_UPDATER_main_panel: - bl_category = "Asset Updater" - bl_label = "Asset Updater" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - - -class BSP_ASSET_UPDATER_PT_vi3d_assets(BSP_ASSET_UPDATER_main_panel, bpy.types.Panel): - def draw(self, context: bpy.types.Context) -> None: - - layout: bpy.types.UILayout = self.layout - box = draw_imported_asset_collections_in_scene(self, context) - - box.operator( - BSP_ASSET_UPDATER_update_all.bl_idname, - text="Update All", - icon="FILE_REFRESH", - ) - return - - -class BSP_UL_imported_asset_collections(bpy.types.UIList): - def draw_item( - self, context, layout, data, item, icon, active_data, active_propname, index - ): - # item: props.SET_imported_asset_collection - - layout: bpy.types.UILayout = layout - coll = item.collection - if self.layout_type in {"DEFAULT", "COMPACT"}: - - base_split = layout.split(factor=0.3, align=True) - - # Asset name. - base_split.label(text=coll.bsp_asset.entity_name) - - icon = "NONE" - - lib = lib_util.get_item_lib(coll) - loaded_asset_publish = item.asset_publishes[Path(lib.filepath).name] - - # If the currently loaded asset publish has deprecated status, display warning icon. - if loaded_asset_publish.status == AssetStatus.DEPRECATED.name: - icon = "ERROR" - - # Asset version. - base_split.label(text=coll.bsp_asset.version, icon=icon) - - # Target version. - base_split.prop(item, "target_publish", text="") - - # Update operator. - base_split.operator( - BSP_ASSET_UPDATER_update_asset.bl_idname, text="", icon="FILE_REFRESH" - ).index = index - - elif self.layout_type in {"GRID"}: - layout.alignment = "CENTER" - layout.label(text=coll.bsp_asset.entity_name) - - -# ----------------REGISTER--------------. - -classes = [BSP_UL_imported_asset_collections, BSP_ASSET_UPDATER_PT_vi3d_assets] - - -def register() -> None: - for cls in classes: - bpy.utils.register_class(cls) - - -def unregister() -> None: - for cls in reversed(classes): - bpy.utils.unregister_class(cls) diff --git a/scripts-blender/addons/asset_pipeline/util.py b/scripts-blender/addons/asset_pipeline/util.py deleted file mode 100644 index dbffa1bc..00000000 --- a/scripts-blender/addons/asset_pipeline/util.py +++ /dev/null @@ -1,188 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -# (c) 2021, Blender Foundation - Paul Golter - -from typing import List, Dict, Union, Any, Set, Optional, Tuple, Generator - -import bpy -from bpy import types -import addon_utils - - -def redraw_ui() -> None: - """ - Forces blender to redraw the UI. - """ - for screen in bpy.data.screens: - for area in screen.areas: - area.tag_redraw() - - -def get_addon_prefs() -> bpy.types.AddonPreferences: - return bpy.context.preferences.addons[__package__].preferences - - -def is_file_saved() -> bool: - return bool(bpy.data.filepath) - - -def traverse_collection_tree( - collection: bpy.types.Collection, -) -> Generator[bpy.types.Collection, None, None]: - yield collection - for child in collection.children: - yield from traverse_collection_tree(child) - - -def del_collection(collection: bpy.types.Collection) -> None: - collection.user_clear() - bpy.data.collections.remove(collection) - -def unlink_collections_recursive( - parent_coll: bpy.types.Collection, - bad_colls: Set[bpy.types.Collection] - ): - for child_coll in parent_coll.children: - if child_coll in bad_colls: - parent_coll.children.unlink(child_coll) - child_coll.use_fake_user = False - else: - unlink_collections_recursive(child_coll, bad_colls) - -def is_addon_active(module_name, context=None): - """Returns whether an addon is loaded and enabled in the current workspace.""" - if not context: - context=bpy.context - is_enabled_in_prefs = addon_utils.check(module_name)[1] - if is_enabled_in_prefs and context.workspace.use_filter_by_owner: - is_enabled_in_workspace = module_name in context.workspace.owner_ids - return is_enabled_in_workspace - - return is_enabled_in_prefs - - -def reset_armature_pose( - rig: bpy.types.Object, - reset_transforms=True, - reset_properties=True, -): - bones = rig.pose.bones - for pb in bones: - if reset_transforms: - pb.location = ((0, 0, 0)) - pb.rotation_euler = ((0, 0, 0)) - pb.rotation_quaternion = ((1, 0, 0, 0)) - pb.scale = ((1, 1, 1)) - - if reset_properties and len(pb.keys()) > 0: - rna_properties = [prop.identifier for prop in pb.bl_rna.properties if prop.is_runtime] - - # Reset custom property values to their default value - for key in pb.keys(): - if key.startswith("$"): continue - if key in rna_properties: continue # Addon defined property. - - ui_data = None - try: - ui_data = pb.id_properties_ui(key) - if not ui_data: continue - ui_data = ui_data.as_dict() - if not 'default' in ui_data: continue - except TypeError: - # Some properties don't support UI data, and so don't have a default value. (like addon PropertyGroups) - pass - - if not ui_data: continue - - if type(pb[key]) not in (float, int, bool): continue - pb[key] = ui_data['default'] - - -ID_INFO = { - (types.WindowManager, 'WINDOWMANAGER', 'window_managers'), - (types.Scene, 'SCENE', 'scenes'), - (types.World, 'WORLD', 'worlds'), - (types.Collection, 'COLLECTION', 'collections'), - - (types.Armature, 'ARMATURE', 'armatures'), - (types.Mesh, 'MESH', 'meshes'), - (types.Camera, 'CAMERA', 'cameras'), - (types.Lattice, 'LATTICE', 'lattices'), - (types.Light, 'LIGHT', 'lights'), - (types.Speaker, 'SPEAKER', 'speakers'), - (types.Volume, 'VOLUME', 'volumes'), - (types.GreasePencil, 'GREASEPENCIL', 'grease_pencils'), - (types.Curve, 'CURVE', 'curves'), - (types.LightProbe, 'LIGHT_PROBE', 'lightprobes'), - - (types.MetaBall, 'METABALL', 'metaballs'), - (types.Object, 'OBJECT', 'objects'), - (types.Action, 'ACTION', 'actions'), - (types.Key, 'KEY', 'shape_keys'), - (types.Sound, 'SOUND', 'sounds'), - - (types.Material, 'MATERIAL', 'materials'), - (types.NodeTree, 'NODETREE', 'node_groups'), - (types.Image, 'IMAGE', 'images'), - - (types.Mask, 'MASK', 'masks'), - (types.FreestyleLineStyle, 'LINESTYLE', 'linestyles'), - (types.Library, 'LIBRARY', 'libraries'), - (types.VectorFont, 'FONT', 'fonts'), - (types.CacheFile, 'CACHE_FILE', 'cache_files'), - (types.PointCloud, 'POINT_CLOUD', 'pointclouds'), - (types.Curves, 'HAIR_CURVES', 'hair_curves'), - (types.Text, 'TEXT', 'texts'), - # (types.Simulation, 'SIMULATION', 'simulations'), - (types.ParticleSettings, 'PARTICLE', 'particles'), - (types.Palette, 'PALETTE', 'palettes'), - (types.PaintCurve, 'PAINT_CURVE', 'paint_curves'), - (types.MovieClip, 'MOVIE_CLIP', 'movieclips'), - - (types.WorkSpace, 'WORKSPACE', 'workspaces'), - (types.Screen, 'SCREEN', 'screens'), - (types.Brush, 'BRUSH', 'brushes'), - (types.Texture, 'TEXTURE', 'textures'), -} - -# Map datablock Python classes to their string representation. -ID_CLASS_TO_IDENTIFIER: Dict[type, Tuple[str, int]] = dict( - [(tup[0], (tup[1])) for tup in ID_INFO] -) - -# Map datablock Python classes to the name of their bpy.data container. -ID_CLASS_TO_STORAGE_NAME: Dict[type, str] = dict( - [(tup[0], (tup[2])) for tup in ID_INFO] -) - -def get_fundamental_id_type(datablock: bpy.types.ID) -> Any: - """Certain datablocks have very specific types. - This function should return their fundamental type, ie. parent class.""" - for id_type in ID_CLASS_TO_IDENTIFIER.keys(): - if isinstance(datablock, id_type): - return id_type - - -def get_storage_of_id(datablock: bpy.types.ID) -> 'bpy_prop_collection': - """Return the storage collection property of the datablock. - Eg. for an object, returns bpy.data.objects. - """ - - fundamental_type = get_fundamental_id_type(datablock) - return getattr(bpy.data, ID_CLASS_TO_STORAGE_NAME[fundamental_type]) -- 2.30.2 From 9f78cbe2bdc652d2dbed89f143a57ed305fc924e Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Thu, 23 Nov 2023 14:21:11 -0500 Subject: [PATCH 421/429] Asset Pipe: Rename Add-on to 'Asset Pipeline' --- .../addons/{asset_pipeline_2 => asset_pipeline}/README.md | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/__init__.py | 2 +- .../addons/{asset_pipeline_2 => asset_pipeline}/config.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/constants.py | 2 +- .../addons/{asset_pipeline_2 => asset_pipeline}/images.py | 0 .../{asset_pipeline_2 => asset_pipeline}/merge/asset_mapping.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/merge/core.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/merge/naming.py | 0 .../{asset_pipeline_2 => asset_pipeline}/merge/publish.py | 0 .../{asset_pipeline_2 => asset_pipeline}/merge/shared_ids.py | 0 .../{asset_pipeline_2 => asset_pipeline}/merge/task_layer.py | 0 .../merge/transfer_data/transfer_core.py | 0 .../merge/transfer_data/transfer_functions/attributes.py | 0 .../merge/transfer_data/transfer_functions/constraints.py | 0 .../merge/transfer_data/transfer_functions/materials.py | 0 .../merge/transfer_data/transfer_functions/modifers.py | 0 .../merge/transfer_data/transfer_functions/parent.py | 0 .../merge/transfer_data/transfer_functions/shape_keys.py | 0 .../transfer_functions/transfer_function_util/drivers.py | 0 .../transfer_functions/transfer_function_util/proximity_core.py | 0 .../transfer_functions/transfer_function_util/visibility.py | 0 .../merge/transfer_data/transfer_functions/vertex_groups.py | 0 .../merge/transfer_data/transfer_ui.py | 0 .../merge/transfer_data/transfer_util.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/merge/util.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/ops.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/prefs.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/props.py | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/sync.py | 0 .../task_layer_configs/Character.json | 0 .../task_layer_configs/Set.json | 0 .../addons/{asset_pipeline_2 => asset_pipeline}/ui.py | 0 32 files changed, 2 insertions(+), 2 deletions(-) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/README.md (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/__init__.py (96%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/config.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/constants.py (95%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/images.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/asset_mapping.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/core.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/naming.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/publish.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/shared_ids.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/task_layer.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_core.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/attributes.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/constraints.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/materials.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/modifers.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/parent.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/shape_keys.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/transfer_function_util/drivers.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/transfer_function_util/proximity_core.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/transfer_function_util/visibility.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_functions/vertex_groups.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_ui.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/transfer_data/transfer_util.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/merge/util.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/ops.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/prefs.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/props.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/sync.py (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/task_layer_configs/Character.json (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/task_layer_configs/Set.json (100%) rename scripts-blender/addons/{asset_pipeline_2 => asset_pipeline}/ui.py (100%) diff --git a/scripts-blender/addons/asset_pipeline_2/README.md b/scripts-blender/addons/asset_pipeline/README.md similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/README.md rename to scripts-blender/addons/asset_pipeline/README.md diff --git a/scripts-blender/addons/asset_pipeline_2/__init__.py b/scripts-blender/addons/asset_pipeline/__init__.py similarity index 96% rename from scripts-blender/addons/asset_pipeline_2/__init__.py rename to scripts-blender/addons/asset_pipeline/__init__.py index 99decd0c..7a5d0bf3 100644 --- a/scripts-blender/addons/asset_pipeline_2/__init__.py +++ b/scripts-blender/addons/asset_pipeline/__init__.py @@ -3,7 +3,7 @@ import importlib from . import ui, ops, props, prefs bl_info = { - "name": "Asset Pipeline 2", + "name": "Asset Pipeline", "author": "Nick Alberelli", "description": "Blender Studio Asset Pipeline Add-on", "blender": (4, 0, 0), diff --git a/scripts-blender/addons/asset_pipeline_2/config.py b/scripts-blender/addons/asset_pipeline/config.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/config.py rename to scripts-blender/addons/asset_pipeline/config.py diff --git a/scripts-blender/addons/asset_pipeline_2/constants.py b/scripts-blender/addons/asset_pipeline/constants.py similarity index 95% rename from scripts-blender/addons/asset_pipeline_2/constants.py rename to scripts-blender/addons/asset_pipeline/constants.py index b215f446..bd94954c 100644 --- a/scripts-blender/addons/asset_pipeline_2/constants.py +++ b/scripts-blender/addons/asset_pipeline/constants.py @@ -1,4 +1,4 @@ -ADDON_NAME = "asset_pipeline_2" +ADDON_NAME = "asset_pipeline" # Delimiter used for naming data within Blender NAME_DELIMITER = "-" diff --git a/scripts-blender/addons/asset_pipeline_2/images.py b/scripts-blender/addons/asset_pipeline/images.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/images.py rename to scripts-blender/addons/asset_pipeline/images.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py b/scripts-blender/addons/asset_pipeline/merge/asset_mapping.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/asset_mapping.py rename to scripts-blender/addons/asset_pipeline/merge/asset_mapping.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/core.py b/scripts-blender/addons/asset_pipeline/merge/core.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/core.py rename to scripts-blender/addons/asset_pipeline/merge/core.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/naming.py b/scripts-blender/addons/asset_pipeline/merge/naming.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/naming.py rename to scripts-blender/addons/asset_pipeline/merge/naming.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/publish.py b/scripts-blender/addons/asset_pipeline/merge/publish.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/publish.py rename to scripts-blender/addons/asset_pipeline/merge/publish.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py b/scripts-blender/addons/asset_pipeline/merge/shared_ids.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/shared_ids.py rename to scripts-blender/addons/asset_pipeline/merge/shared_ids.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/task_layer.py b/scripts-blender/addons/asset_pipeline/merge/task_layer.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/task_layer.py rename to scripts-blender/addons/asset_pipeline/merge/task_layer.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_core.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_core.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_core.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/attributes.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/attributes.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/attributes.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/constraints.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/constraints.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/constraints.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/materials.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/materials.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/materials.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/modifers.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/modifers.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/modifers.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/parent.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/parent.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/parent.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/shape_keys.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/shape_keys.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/shape_keys.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/drivers.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/transfer_function_util/drivers.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/drivers.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/transfer_function_util/drivers.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/proximity_core.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/transfer_function_util/proximity_core.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/proximity_core.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/transfer_function_util/proximity_core.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/visibility.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/transfer_function_util/visibility.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/transfer_function_util/visibility.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/transfer_function_util/visibility.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/vertex_groups.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/vertex_groups.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_functions/vertex_groups.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_functions/vertex_groups.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_ui.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_ui.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_ui.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py b/scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_util.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/transfer_data/transfer_util.py rename to scripts-blender/addons/asset_pipeline/merge/transfer_data/transfer_util.py diff --git a/scripts-blender/addons/asset_pipeline_2/merge/util.py b/scripts-blender/addons/asset_pipeline/merge/util.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/merge/util.py rename to scripts-blender/addons/asset_pipeline/merge/util.py diff --git a/scripts-blender/addons/asset_pipeline_2/ops.py b/scripts-blender/addons/asset_pipeline/ops.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/ops.py rename to scripts-blender/addons/asset_pipeline/ops.py diff --git a/scripts-blender/addons/asset_pipeline_2/prefs.py b/scripts-blender/addons/asset_pipeline/prefs.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/prefs.py rename to scripts-blender/addons/asset_pipeline/prefs.py diff --git a/scripts-blender/addons/asset_pipeline_2/props.py b/scripts-blender/addons/asset_pipeline/props.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/props.py rename to scripts-blender/addons/asset_pipeline/props.py diff --git a/scripts-blender/addons/asset_pipeline_2/sync.py b/scripts-blender/addons/asset_pipeline/sync.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/sync.py rename to scripts-blender/addons/asset_pipeline/sync.py diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json b/scripts-blender/addons/asset_pipeline/task_layer_configs/Character.json similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/task_layer_configs/Character.json rename to scripts-blender/addons/asset_pipeline/task_layer_configs/Character.json diff --git a/scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json b/scripts-blender/addons/asset_pipeline/task_layer_configs/Set.json similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/task_layer_configs/Set.json rename to scripts-blender/addons/asset_pipeline/task_layer_configs/Set.json diff --git a/scripts-blender/addons/asset_pipeline_2/ui.py b/scripts-blender/addons/asset_pipeline/ui.py similarity index 100% rename from scripts-blender/addons/asset_pipeline_2/ui.py rename to scripts-blender/addons/asset_pipeline/ui.py -- 2.30.2 From 8debb722df75359fe1f3e90d39bac5fd19a06930 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 11:05:44 -0500 Subject: [PATCH 422/429] Asset Pipe: Fix collection marked as asset doesn't purge - Published file has asset collection marked as asset - When Published col is loaded as external col, it remains marked as asset and doesn't purge - Non Published col are also not marked as asset so it is safe to always remove asset mark from any external col --- scripts-blender/addons/asset_pipeline/merge/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline/merge/core.py b/scripts-blender/addons/asset_pipeline/merge/core.py index 3046196d..fe1325d6 100644 --- a/scripts-blender/addons/asset_pipeline/merge/core.py +++ b/scripts-blender/addons/asset_pipeline/merge/core.py @@ -179,6 +179,9 @@ def merge_task_layer( local_col = bpy.data.collections[f"{col_base_name}.{local_suffix}"] external_col = bpy.data.collections[f"{col_base_name}.{external_suffix}"] + # External col may come from publish, ensure it is not marked as asset so it purges correctly + external_col.asset_clear() + map = AssetTransferMapping(local_col, external_col, local_tls) error_msg = '' if len(map.conflict_transfer_data) != 0: -- 2.30.2 From 205be688d55e180fd77be76f3027a14d660176fc Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 11:42:59 -0500 Subject: [PATCH 423/429] Asset Pipe: Only Allow Push/Pull in Object Mode --- scripts-blender/addons/asset_pipeline/ops.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts-blender/addons/asset_pipeline/ops.py b/scripts-blender/addons/asset_pipeline/ops.py index 877da191..a60ae442 100644 --- a/scripts-blender/addons/asset_pipeline/ops.py +++ b/scripts-blender/addons/asset_pipeline/ops.py @@ -283,6 +283,13 @@ class ASSETPIPE_OT_sync_pull(bpy.types.Operator): description="Save Current File and Images before Push", ) + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + if context.mode == 'OBJECT': + return True + cls.poll_message_set("Pull is only avaliable in Object Mode") + return False + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): sync_invoke(self, context) return context.window_manager.invoke_props_dialog(self, width=400) @@ -332,6 +339,13 @@ class ASSETPIPE_OT_sync_push(bpy.types.Operator): description="Save Current File and Images before Push", ) + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + if context.mode == 'OBJECT': + return True + cls.poll_message_set("Push is only avaliable in Object Mode") + return False + def invoke(self, context: bpy.types.Context, event: bpy.types.Event): sync_invoke(self, context) return context.window_manager.invoke_props_dialog(self, width=400) -- 2.30.2 From 36af93645fa1e32198f814922f0495bea16ba5da Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 11:47:28 -0500 Subject: [PATCH 424/429] Asset Pipe: Clear old TODO --- scripts-blender/addons/asset_pipeline/ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline/ops.py b/scripts-blender/addons/asset_pipeline/ops.py index a60ae442..75f3450b 100644 --- a/scripts-blender/addons/asset_pipeline/ops.py +++ b/scripts-blender/addons/asset_pipeline/ops.py @@ -51,7 +51,6 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator): # Dynamically Create Task Layer Bools self._asset_pipe = context.scene.asset_pipeline - # TODO Check if this fails config.verify_json_data(Path(self._asset_pipe.task_layer_config_type)) all_task_layers = self._asset_pipe.all_task_layers -- 2.30.2 From ade05e8c1baa4e5926f082fd27a5723a3180256c Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 12:48:20 -0500 Subject: [PATCH 425/429] Asset Pipe: Revert Git Attributes and Git Ignore --- .gitattributes | 3 +-- .gitignore | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.gitattributes b/.gitattributes index 9e90bc2d..719efb30 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,5 +2,4 @@ *.png filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text *.whl filter=lfs diff=lfs merge=lfs -text -*.gif filter=lfs diff=lfs merge=lfs -text -*.blend filter=lfs diff=lfs merge=lfs -text \ No newline at end of file +*.gif filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.gitignore b/.gitignore index 052d3450..a8eecaa3 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,4 @@ ENV/ # Docs node_modules/ -docs/.vitepress/cache - -# Temp -*.blend1 \ No newline at end of file +docs/.vitepress/cache \ No newline at end of file -- 2.30.2 From ea6002dc1aac4e7aa9297d49b7d4d1333aaf40bc Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 13:00:18 -0500 Subject: [PATCH 426/429] Asset Pipe: Hide Ownership Inspector if no Valid Task Layer Config is found --- scripts-blender/addons/asset_pipeline/ui.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline/ui.py b/scripts-blender/addons/asset_pipeline/ui.py index f887fe65..b3616ba5 100644 --- a/scripts-blender/addons/asset_pipeline/ui.py +++ b/scripts-blender/addons/asset_pipeline/ui.py @@ -11,7 +11,7 @@ from . import constants class ASSETPIPE_PT_sync(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_category = 'Asset Pipe 2' + bl_category = 'Asset Pipeline' bl_label = "Asset Management" def draw(self, context: bpy.types.Context) -> None: @@ -74,7 +74,7 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_category = 'Asset Pipe 2' + bl_category = 'Asset Pipeline' bl_label = "Advanced" bl_parent_id = "ASSETPIPE_PT_sync" bl_options = {'DEFAULT_CLOSED'} @@ -108,16 +108,17 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_category = 'Asset Pipe 2' + bl_category = 'Asset Pipeline' bl_label = "Ownership Inspector" + @classmethod + def poll(cls, context: bpy.types.Context) -> bool: + return context.scene.asset_pipeline.is_asset_pipeline_file + def draw(self, context: bpy.types.Context) -> None: layout = self.layout - asset_pipe = context.scene.asset_pipeline scene = context.scene - if not asset_pipe.is_asset_pipeline_file: - layout.label(text="Open valid 'Asset Pipeline' file", icon="ERROR") - return + asset_pipe = scene.asset_pipeline if context.collection in list(asset_pipe.asset_collection.children): col = context.collection -- 2.30.2 From d5a11965851ce38fe73cdd4a5e4c14fd59ca3d15 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 13:02:03 -0500 Subject: [PATCH 427/429] Revert "Asset Pipe: Hide Ownership Inspector if no Valid Task Layer Config is found" This reverts commit ea6002dc1aac4e7aa9297d49b7d4d1333aaf40bc. Reverting this commit because for some reason it appeared to be causing crashes when interacting with Task Layer Preset selector --- scripts-blender/addons/asset_pipeline/ui.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline/ui.py b/scripts-blender/addons/asset_pipeline/ui.py index b3616ba5..f887fe65 100644 --- a/scripts-blender/addons/asset_pipeline/ui.py +++ b/scripts-blender/addons/asset_pipeline/ui.py @@ -11,7 +11,7 @@ from . import constants class ASSETPIPE_PT_sync(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_category = 'Asset Pipeline' + bl_category = 'Asset Pipe 2' bl_label = "Asset Management" def draw(self, context: bpy.types.Context) -> None: @@ -74,7 +74,7 @@ class ASSETPIPE_PT_sync(bpy.types.Panel): class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_category = 'Asset Pipeline' + bl_category = 'Asset Pipe 2' bl_label = "Advanced" bl_parent_id = "ASSETPIPE_PT_sync" bl_options = {'DEFAULT_CLOSED'} @@ -108,17 +108,16 @@ class ASSETPIPE_PT_sync_advanced(bpy.types.Panel): class ASSETPIPE_PT_ownership_inspector(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_category = 'Asset Pipeline' + bl_category = 'Asset Pipe 2' bl_label = "Ownership Inspector" - @classmethod - def poll(cls, context: bpy.types.Context) -> bool: - return context.scene.asset_pipeline.is_asset_pipeline_file - def draw(self, context: bpy.types.Context) -> None: layout = self.layout + asset_pipe = context.scene.asset_pipeline scene = context.scene - asset_pipe = scene.asset_pipeline + if not asset_pipe.is_asset_pipeline_file: + layout.label(text="Open valid 'Asset Pipeline' file", icon="ERROR") + return if context.collection in list(asset_pipe.asset_collection.children): col = context.collection -- 2.30.2 From 796f3e2d0695642f1c282725a47a332b8a624251 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 14:20:32 -0500 Subject: [PATCH 428/429] Asset Pipe: Add Custom Task Layers to README --- .../addons/asset_pipeline/README.md | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/scripts-blender/addons/asset_pipeline/README.md b/scripts-blender/addons/asset_pipeline/README.md index 9f8121ad..73ab0a97 100644 --- a/scripts-blender/addons/asset_pipeline/README.md +++ b/scripts-blender/addons/asset_pipeline/README.md @@ -2,7 +2,7 @@ ## Introduction This Add-On was designed to allow multiple artists to collaborate while contributing to a common Asset. It enables simultaneous work on the same asset by multiple artists. The add-on works by tracking what data each artist contributes to the asset and merges the assets together into a final "Published" asset. This published asset is marked to be discovered by Blender's asset manager. -## Table of Contents + - [Asset Pipeline](#asset-pipeline) @@ -22,6 +22,7 @@ This Add-On was designed to allow multiple artists to collaborate while contribu - [Active](#active) - [Staged](#staged) - [Review](#review) + - [Creating Custom Task Layers](#creating-custom-task-layers) @@ -83,13 +84,13 @@ To setup an asset using "Current File" mode, please open the file you would like ## Push/Pull The Push/Pull process happens in three steps. -### 1. Updating Ownership +### Updating Ownership When you Push/Pull a file, you will be greeted with an operator dialogue. This dialogue will list any new data that it has found in your file. Pressing OK will assign these new pieces of data to your local task layer, if you have multiple local task layers, you will be able to select which one is the owner of each piece of data. Once completed this information will be used to ensure your work is merged properly with the published file. -### 2. Save File +### Save File The add-on will optionally save your current file plus any unsaved/unpacked images will be saved in a directory relative to your asset (configurable in the add-on preferences). It will always create a back-up of your current file, in the case where the merge process fails, you will be prompted to revert your file back to it's pre-merge status. -### 3. Merge with Published File +### Merge with Published File Push and Pull are merging operations done to/from the published file. When you want to share your updated work to the rest of the team select "Push to Publish" to update the published file with your changes. Push will update any Transferable Data you edited, and update any objects/collections you own with the version in your current file. Transferable Data owned by other artists will be re-applied to your objects. If another artist then uses the "Pull to Publish" operator the same process will occur, keeping all objects, collections and Transferable Data that is local to their file, and importing any data that was owned externally by other task layers. @@ -107,4 +108,65 @@ An active publish is a publish that can be referenced by the production into sho A staged asset, is an publish that cannot be referenced by the production, only one staged asset can exist at a time. If a staged publish exists it will replace the active publish as the push/pull target. The staged area exists so artists can collaborate on a new version of an asset that is not ready to be used in production. ### Review -A review publish is simple a way to test out the final published version of your asset. You can create as many review publishes as you want to check your work and ensure the merge process produces results that are expected. Review publish is never used as a push/pull target and is for testing only. \ No newline at end of file +A review publish is simple a way to test out the final published version of your asset. You can create as many review publishes as you want to check your work and ensure the merge process produces results that are expected. Review publish is never used as a push/pull target and is for testing only. + +## Creating Custom Task Layers + +Add your own custom Task Layers to the asset pipeline addon. To create a custom task layer, find one of the templates at `/asset_pipeline/task_layer_configs/` copy one of the task layers to your own custom directory. The layout of the JSON file is as follows... + + +```JSON +{ + // Task Layer Types are formatted as {"Name of Task Layer": "Prefix"} + "TASK_LAYER_TYPES": { + "Modeling": "MOD", + "Rigging": "RIG", + "Shading": "SHD" + }, + + // These are the default or preferred owners for each type of transfer data + "TRANSFER_DATA_DEFAULTS": { + "GROUP_VERTEX": { // Name of Transfer Data Type (not customizable) + "default_owner": "Rigging", // Matching one of the Task Layer types above + "auto_surrender": false // If data type will be surrendered on initialization + }, + "MODIFIER": { + "default_owner": "Rigging", + "auto_surrender": false + }, + "CONSTRAINT": { + "default_owner": "Rigging", + "auto_surrender": false + }, + "MATERIAL": { + "default_owner": "Shading", + "auto_surrender": true + }, + "SHAPE_KEY": { + "default_owner": "Modeling", + "auto_surrender": false + }, + "ATTRIBUTE": { + "default_owner": "Rigging", + "auto_surrender": false + }, + "PARENT": { + "default_owner": "Rigging", + "auto_surrender": false + } + }, + + // These are default attributes created by Blender + "ATTRIBUTE_DEFAULTS": { + "sharp_face": { + "default_owner": "Modeling", + "auto_surrender": true + }, + "UVMap": { + "default_owner": "Shading", + "auto_surrender": true + } + } +} + +``` \ No newline at end of file -- 2.30.2 From eee09b82e99a7014225637dc270f166d016a8005 Mon Sep 17 00:00:00 2001 From: Nick Alberelli Date: Fri, 24 Nov 2023 14:25:59 -0500 Subject: [PATCH 429/429] Asset Pipe: Version Bump --- scripts-blender/addons/asset_pipeline/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts-blender/addons/asset_pipeline/__init__.py b/scripts-blender/addons/asset_pipeline/__init__.py index 7a5d0bf3..4a898c4b 100644 --- a/scripts-blender/addons/asset_pipeline/__init__.py +++ b/scripts-blender/addons/asset_pipeline/__init__.py @@ -7,7 +7,7 @@ bl_info = { "author": "Nick Alberelli", "description": "Blender Studio Asset Pipeline Add-on", "blender": (4, 0, 0), - "version": (0, 1, 2), + "version": (0, 2, 0), "location": "View3D", "warning": "", "doc_url": "", -- 2.30.2