Asset Pipeline v2 #145
@ -3,20 +3,21 @@ import bpy
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .merge.publish import (
|
from .merge.publish import (
|
||||||
find_sync_target,
|
|
||||||
find_all_published,
|
|
||||||
get_next_published_file,
|
get_next_published_file,
|
||||||
)
|
)
|
||||||
from .merge.naming import get_task_layer_col_name
|
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 .merge.transfer_data.transfer_ui import draw_transfer_data
|
||||||
from . import constants
|
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):
|
class ASSETPIPE_OT_create_new_asset(bpy.types.Operator):
|
||||||
@ -107,11 +108,10 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator):
|
class ASSETPIPE_OT_update_ownership(bpy.types.Operator):
|
||||||
bl_idname = "assetpipe.sync_with_publish"
|
bl_idname = "assetpipe.update_ownership"
|
||||||
bl_label = "Sync with Publish"
|
bl_label = "Update Onwership"
|
||||||
bl_description = """'Push'band or 'Pull' data from Published file. Will prompt with a list of new data found before pushing
|
bl_description = """""" # TODO Add description
|
||||||
During 'Push' current blender session will revert to the last saved file and clear the current undo history"""
|
|
||||||
|
|
||||||
_temp_transfer_data = None
|
_temp_transfer_data = None
|
||||||
_invalid_objs = []
|
_invalid_objs = []
|
||||||
@ -123,161 +123,114 @@ class ASSETPIPE_OT_sync_with_publish(bpy.types.Operator):
|
|||||||
description="Show New Transfer Data",
|
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):
|
def invoke(self, context: bpy.types.Context, event: bpy.types.Event):
|
||||||
self._temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data
|
sync_invoke(self, context)
|
||||||
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
|
|
||||||
return context.window_manager.invoke_props_dialog(self, width=400)
|
return context.window_manager.invoke_props_dialog(self, width=400)
|
||||||
|
|
||||||
def draw(self, context: bpy.types.Context):
|
def draw(self, context: bpy.types.Context):
|
||||||
layout = self.layout
|
sync_draw(self, context)
|
||||||
|
|
||||||
row = layout.row()
|
def execute(self, context: bpy.types.Context):
|
||||||
if self.push:
|
sync_execute_update_ownership(self, context)
|
||||||
row.prop(self, "pull")
|
return {'FINISHED'}
|
||||||
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._other_ids) != 0:
|
class ASSETPIPE_OT_sync_pull(bpy.types.Operator):
|
||||||
box = layout.box()
|
bl_idname = "assetpipe.sync_pull"
|
||||||
box.label(text="New 'Other IDs' found")
|
bl_label = "Pull from Publish"
|
||||||
for id in self._other_ids:
|
bl_description = """""" # TODO Add description
|
||||||
box.label(text=id.name)
|
|
||||||
|
|
||||||
if len(self._temp_transfer_data) == 0:
|
_temp_transfer_data = None
|
||||||
layout.label(text="No New Transfer Data found")
|
_invalid_objs = []
|
||||||
else:
|
_other_ids = []
|
||||||
layout.label(text="New Transfer Data will be Pushed to Publish")
|
_temp_dir: Path = None
|
||||||
row = layout.row()
|
_current_file: Path = None
|
||||||
row.prop(self, "expand", text="", icon="COLLAPSEMENU", toggle=False)
|
_task_layer_key: str = ""
|
||||||
row.label(text="Show New Transfer Data")
|
_sync_target: Path = None
|
||||||
objs = [
|
|
||||||
transfer_data_item.obj for transfer_data_item in self._temp_transfer_data
|
|
||||||
]
|
|
||||||
|
|
||||||
if not self.expand:
|
expand: bpy.props.BoolProperty(
|
||||||
return
|
name="Show New Transfer Data",
|
||||||
|
default=False,
|
||||||
|
description="Show New Transfer Data",
|
||||||
|
)
|
||||||
|
|
||||||
for obj in set(objs):
|
@classmethod
|
||||||
obj_ownership = [
|
def poll(cls, context: bpy.types.Context) -> bool:
|
||||||
transfer_data_item
|
if any([img.is_dirty for img in bpy.data.images]):
|
||||||
for transfer_data_item in self._temp_transfer_data
|
cls.poll_message_set("Please save unsaved Images")
|
||||||
if transfer_data_item.obj == obj
|
return False
|
||||||
]
|
if bpy.data.is_dirty:
|
||||||
box = layout.box()
|
cls.poll_message_set("Please save current .blend file")
|
||||||
box.label(text=obj.name, icon="OBJECT_DATA")
|
return False
|
||||||
draw_transfer_data(obj_ownership, box)
|
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):
|
def execute(self, context: bpy.types.Context):
|
||||||
# Find current task Layer
|
# Find current task Layer
|
||||||
temp_transfer_data = context.scene.asset_pipeline.temp_transfer_data
|
sync_execute_update_ownership(self, context)
|
||||||
ownership_set(temp_transfer_data)
|
sync_execute_prepare_sync(self, context)
|
||||||
current_file = Path(bpy.data.filepath)
|
sync_execute_pull(self, context)
|
||||||
temp_dir = Path(bpy.app.tempdir).parent
|
return {'FINISHED'}
|
||||||
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_target = find_sync_target(current_file)
|
|
||||||
if not sync_target.exists():
|
class ASSETPIPE_OT_sync_push(bpy.types.Operator):
|
||||||
self.report({'ERROR'}, "Sync Target could not be determined")
|
bl_idname = "assetpipe.sync_push"
|
||||||
return {'CANCELLED'}
|
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:
|
if self.pull:
|
||||||
temp_file = temp_dir.joinpath(
|
sync_execute_pull(self, context)
|
||||||
current_file.name.replace(".blend", "") + "_Asset_Pipe_Backup.blend"
|
bpy.ops.wm.save_mainfile(filepath=self._current_file.__str__())
|
||||||
)
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
|
|
||||||
if error_msg:
|
sync_execute_push(self, context)
|
||||||
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__())
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
@ -312,7 +265,9 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator):
|
|||||||
|
|
||||||
|
|
||||||
classes = (
|
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_publish_new_version,
|
||||||
ASSETPIPE_OT_create_new_asset,
|
ASSETPIPE_OT_create_new_asset,
|
||||||
)
|
)
|
||||||
|
156
scripts-blender/addons/asset_pipeline_2/sync.py
Normal file
156
scripts-blender/addons/asset_pipeline_2/sync.py
Normal file
@ -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__())
|
@ -29,15 +29,11 @@ class ASSETPIPE_sync(bpy.types.Panel):
|
|||||||
layout.prop(asset_pipe, "asset_collection", text="Asset")
|
layout.prop(asset_pipe, "asset_collection", text="Asset")
|
||||||
layout.label(text="Test UI")
|
layout.label(text="Test UI")
|
||||||
|
|
||||||
# layout.operator(
|
layout.operator("assetpipe.update_ownership", text="Update Ownership")
|
||||||
# "assetpipe.sync_with_publish", text="Update Ownership"
|
layout.operator("assetpipe.sync_push", text="Push to Publish", icon="TRIA_UP")
|
||||||
# ).pull = False
|
|
||||||
layout.operator(
|
layout.operator(
|
||||||
"assetpipe.sync_with_publish", text="Push to Publish", icon="TRIA_UP"
|
"assetpipe.sync_pull", text="Pull from Publish", icon="TRIA_DOWN"
|
||||||
).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")
|
layout.operator("assetpipe.publish_new_version", icon="PLUS")
|
||||||
|
|
||||||
if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE":
|
if asset_pipe.is_asset_pipeline_file and asset_pipe.task_layer_name == "NONE":
|
||||||
|
Loading…
Reference in New Issue
Block a user