From f08443f844f1857a109e2342c37492f7f5018057 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 21 May 2024 17:45:57 +0800 Subject: [PATCH] supports exporting layer stack with specific channel & output with specific channel --- .../hosts/substancepainter/api/lib.py | 57 +++++++++++++++++++ .../plugins/create/create_textures.py | 39 +++++++++++-- .../publish/collect_textureset_images.py | 6 +- .../plugins/publish/extract_textures.py | 29 ++++++---- 4 files changed, 113 insertions(+), 18 deletions(-) diff --git a/client/ayon_core/hosts/substancepainter/api/lib.py b/client/ayon_core/hosts/substancepainter/api/lib.py index 64c39943ce..df70a72b13 100644 --- a/client/ayon_core/hosts/substancepainter/api/lib.py +++ b/client/ayon_core/hosts/substancepainter/api/lib.py @@ -3,6 +3,8 @@ import re import json from collections import defaultdict +import contextlib +import substance_painter as sp import substance_painter.project import substance_painter.resource import substance_painter.js @@ -640,3 +642,58 @@ def prompt_new_file_with_mesh(mesh_filepath): return return project_mesh + + +def get_export_presets_by_filtering(export_preset_name, channel_type_list): + new_maps = [] + + export_presets = get_export_presets() + resource_presets = substance_painter.export.list_resource_export_presets() + preset = next((preset for preset in resource_presets + if preset.resource_id.name == ( + export_presets[export_preset_name])), None) + + if preset is not None: + maps = preset.list_output_maps() + for channel_map in maps: + for n in channel_type_list: + if n in channel_map["fileName"]: + new_maps.append(channel_map) + # Create a new preset + return { + "exportPresets": [ + { + "name": export_preset_name, + "maps": new_maps + } + ], + } + return {} + + +@contextlib.contextmanager +def supsend_publish_layer_stack(node_ids, channel_type): + all_selected_nodes = [] + opacity_set_list = [] + stack = sp.textureset.get_active_stack() + stack_root_layers = sp.layerstack.get_root_layer_nodes(stack) + if node_ids and channel_type: + for node_id in node_ids: + node = sp.layerstack.get_node_by_uid(int(node_id)) + all_selected_nodes.append(node) + filtered_nodes = [node for node in stack_root_layers + if node not in all_selected_nodes] + for node in filtered_nodes: + for channel in channel_type: + chan = getattr(sp.textureset.ChannelType, channel) + opacity_set_list.append((chan, node.get_opacity(chan))) + try: + for node in filtered_nodes: + for channel, _ in opacity_set_list: + node.set_opacity(0.0, channel) + yield + finally: + for node in filtered_nodes: + for channel, opacity in opacity_set_list: + node.set_opacity(opacity, channel) + diff --git a/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py b/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py index f46afadb5a..d36bf76568 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py +++ b/client/ayon_core/hosts/substancepainter/plugins/create/create_textures.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- """Creator plugin for creating textures.""" - from ayon_core.pipeline import CreatedInstance, Creator, CreatorError from ayon_core.lib import ( EnumDef, @@ -18,6 +17,7 @@ from ayon_core.hosts.substancepainter.api.pipeline import ( from ayon_core.hosts.substancepainter.api.lib import get_export_presets import substance_painter.project +import substance_painter as sp class CreateTextures(Creator): @@ -42,10 +42,20 @@ class CreateTextures(Creator): "exportFileFormat", "exportSize", "exportPadding", - "exportDilationDistance" + "exportDilationDistance", + "useCustomExportPreset", + "exportChannel" ]: if key in pre_create_data: creator_attributes[key] = pre_create_data[key] + #TODO: add the layer stack option + if sp.application.version_info()[0] >= 10 or ( + pre_create_data.get("use_selection")): + stack = sp.textureset.get_active_stack() + + instance_data["selected_node_id"] = [ + node_number.uid() for node_number in + sp.layerstack.get_selected_nodes(stack)] instance = self.create_instance_in_context(product_name, instance_data) @@ -88,8 +98,25 @@ class CreateTextures(Creator): return instance def get_instance_attr_defs(self): - + layer_stack_channel_enum = ["BaseColor", "Metallic", "Roughness", + "Normal", "Height", "Specular", + "SpecularEdgeColor", "Emissive", "Opacity", + "Displacement", "Glossiness", "Anisotropylevel", + "AO", "Anisotropyangle", "Transmissive", + "Reflection", "Diffuse", "Ior", + "Specularlevel", "BlendingMask", "Translucency", + "Scattering", "ScatterColor", "SheenOpacity", + "SheenRoughness", "SheenColor", "CoatOpacity", + "CoatColor", "CoatRoughness", "CoatSpecularLevel", + "CoatNormal"] return [ + EnumDef("exportChannel", + items=layer_stack_channel_enum, + multiselection=True, + default=None, + label="Export Channel(s)", + tooltip="Choose the channel which you " + "want to solely export"), EnumDef("exportPresetUrl", items=get_export_presets(), label="Output Template"), @@ -149,7 +176,6 @@ class CreateTextures(Creator): }, default=None, label="Size"), - EnumDef("exportPadding", items={ "passthrough": "No padding (passthrough)", @@ -172,4 +198,7 @@ class CreateTextures(Creator): def get_pre_create_attr_defs(self): # Use same attributes as for instance attributes - return self.get_instance_attr_defs() + return [ + BoolDef("use_selection", label="Use selection", + tooltip="Select Layer Stack(s) for exporting") + ] + self.get_instance_attr_defs() diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py index 20aaa56993..b90e77db80 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/collect_textureset_images.py @@ -8,6 +8,7 @@ import substance_painter.textureset from ayon_core.pipeline import publish from ayon_core.hosts.substancepainter.api.lib import ( get_parsed_export_maps, + get_export_presets_by_filtering, strip_template ) from ayon_core.pipeline.create import get_product_name @@ -207,5 +208,8 @@ class CollectTextureSet(pyblish.api.InstancePlugin): for key, value in dict(parameters).items(): if value is None: parameters.pop(key) - + channel_layer = creator_attrs.get("exportChannel", []) + if channel_layer: + maps = get_export_presets_by_filtering(preset_url, channel_layer) + config.update(maps) return config diff --git a/client/ayon_core/hosts/substancepainter/plugins/publish/extract_textures.py b/client/ayon_core/hosts/substancepainter/plugins/publish/extract_textures.py index 0fa7b52f45..973a62de7a 100644 --- a/client/ayon_core/hosts/substancepainter/plugins/publish/extract_textures.py +++ b/client/ayon_core/hosts/substancepainter/plugins/publish/extract_textures.py @@ -1,6 +1,6 @@ import substance_painter.export - from ayon_core.pipeline import KnownPublishError, publish +from ayon_core.hosts.substancepainter.api.lib import supsend_publish_layer_stack class ExtractTextures(publish.Extractor, @@ -25,19 +25,24 @@ class ExtractTextures(publish.Extractor, def process(self, instance): config = instance.data["exportConfig"] - result = substance_painter.export.export_project_textures(config) + creator_attrs = instance.data["creator_attributes"] + export_channel = creator_attrs.get("exportChannel", []) + node_ids = instance.data.get("selected_node_id", []) - if result.status != substance_painter.export.ExportStatus.Success: - raise KnownPublishError( - "Failed to export texture set: {}".format(result.message) - ) + with supsend_publish_layer_stack(node_ids, export_channel): + result = substance_painter.export.export_project_textures(config) - # Log what files we generated - for (texture_set_name, stack_name), maps in result.textures.items(): - # Log our texture outputs - self.log.info(f"Exported stack: {texture_set_name} {stack_name}") - for texture_map in maps: - self.log.info(f"Exported texture: {texture_map}") + if result.status != substance_painter.export.ExportStatus.Success: + raise KnownPublishError( + "Failed to export texture set: {}".format(result.message) + ) + + # Log what files we generated + for (texture_set_name, stack_name), maps in result.textures.items(): + # Log our texture outputs + self.log.info(f"Exported stack: {texture_set_name} {stack_name}") + for texture_map in maps: + self.log.info(f"Exported texture: {texture_map}") # We'll insert the color space data for each image instance that we # added into this texture set. The collector couldn't do so because