Bases: InstancePlugin
Validate all output maps for Output Template are generated.
Output maps will be skipped by Substance Painter if it is an output map in the Substance Output Template which uses channels that the current substance painter project has not painted or generated.
Source code in client/ayon_substancepainter/plugins/publish/validate_ouput_maps.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161 | class ValidateOutputMaps(pyblish.api.InstancePlugin):
"""Validate all output maps for Output Template are generated.
Output maps will be skipped by Substance Painter if it is an output
map in the Substance Output Template which uses channels that the current
substance painter project has not painted or generated.
"""
order = pyblish.api.ValidatorOrder
label = "Validate output maps"
hosts = ["substancepainter"]
families = ["textureSet"]
def process(self, instance):
config = instance.data["exportConfig"]
# Substance Painter API does not allow to query the actual output maps
# it will generate without actually exporting the files. So we try to
# generate the smallest size / fastest export as possible
config = copy.deepcopy(config)
invalid_channels = self.get_invalid_channels(instance, config)
if invalid_channels:
raise PublishValidationError(
"Invalid Channel(s): {} found in texture set {}".format(
invalid_channels, instance.name
))
parameters = config["exportParameters"][0]["parameters"]
parameters["sizeLog2"] = [1, 1] # output 2x2 images (smallest)
parameters["paddingAlgorithm"] = "passthrough" # no dilation (faster)
parameters["dithering"] = False # no dithering (faster)
result = substance_painter.export.export_project_textures(config)
if result.status != substance_painter.export.ExportStatus.Success:
raise PublishValidationError(
"Failed to export texture set: {}".format(result.message)
)
generated_files = set()
for texture_maps in result.textures.values():
for texture_map in texture_maps:
generated_files.add(os.path.normpath(texture_map))
# Directly clean up our temporary export
os.remove(texture_map)
creator_attributes = instance.data.get("creator_attributes", {})
allow_skipped_maps = creator_attributes.get("allowSkippedMaps", True)
error_report_missing = []
for image_instance in instance:
# Confirm whether the instance has its expected files generated.
# We assume there's just one representation and that it is
# the actual texture representation from the collector.
representation = next(iter(image_instance.data["representations"]))
staging_dir = representation["stagingDir"]
filenames = representation["files"]
if not isinstance(filenames, (list, tuple)):
# Convert single file to list
filenames = [filenames]
missing = []
for filename in filenames:
filepath = os.path.join(staging_dir, filename)
filepath = os.path.normpath(filepath)
if filepath not in generated_files:
self.log.warning(f"Missing texture: {filepath}")
missing.append(filepath)
if not missing:
continue
if allow_skipped_maps:
# TODO: This is changing state on the instance's which
# should not be done during validation.
self.log.warning(f"Disabling texture instance: "
f"{image_instance}")
image_instance.data["active"] = False
image_instance.data["publish"] = False
image_instance.data["integrate"] = False
representation.setdefault("tags", []).append("delete")
continue
else:
error_report_missing.append((image_instance, missing))
if error_report_missing:
message = (
"The Texture Set skipped exporting some output maps which are "
"defined in the Output Template. This happens if the Output "
"Templates exports maps from channels which you do not "
"have in your current Substance Painter project.\n\n"
"To allow this enable the *Allow Skipped Output Maps* setting "
"on the instance.\n\n"
f"Instance {instance} skipped exporting output maps:\n"
""
)
for image_instance, missing in error_report_missing:
missing_str = ", ".join(missing)
message += f"- **{image_instance}** skipped: {missing_str}\n"
raise PublishValidationError(
message=message,
title="Missing output maps"
)
def get_invalid_channels(self, instance, config):
"""Function to get invalid channel(s) from export channel
filtering
Args:
instance (pyblish.api.Instance): Instance
config (dict): export config
Raises:
PublishValidationError: raise Publish Validation
Error if any invalid channel(s) found
Returns:
list: invalid channel(s)
"""
creator_attrs = instance.data["creator_attributes"]
export_channel = creator_attrs.get("exportChannel", [])
if not export_channel:
return []
export_presets = config.get("exportPresets", [])
if not export_presets:
return []
invalid_channels = []
for channel in export_channel:
for export_preset in export_presets:
maps = export_preset.get("maps", [])
if not maps:
raise PublishValidationError(
"No Texture Map Exported with texture set: {}.".format(
instance.name)
)
included = any(map_includes_channel(
channel_map, channel) for channel_map in maps
)
if included:
break
else:
# not found in any export preset
invalid_channels.append(channel)
return invalid_channels
|
get_invalid_channels(instance, config)
Function to get invalid channel(s) from export channel filtering
Parameters:
| Name | Type | Description | Default |
instance | Instance | | required |
config | dict | | required |
Raises:
| Type | Description |
PublishValidationError | raise Publish Validation Error if any invalid channel(s) found |
Returns:
| Name | Type | Description |
list | | |
Source code in client/ayon_substancepainter/plugins/publish/validate_ouput_maps.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161 | def get_invalid_channels(self, instance, config):
"""Function to get invalid channel(s) from export channel
filtering
Args:
instance (pyblish.api.Instance): Instance
config (dict): export config
Raises:
PublishValidationError: raise Publish Validation
Error if any invalid channel(s) found
Returns:
list: invalid channel(s)
"""
creator_attrs = instance.data["creator_attributes"]
export_channel = creator_attrs.get("exportChannel", [])
if not export_channel:
return []
export_presets = config.get("exportPresets", [])
if not export_presets:
return []
invalid_channels = []
for channel in export_channel:
for export_preset in export_presets:
maps = export_preset.get("maps", [])
if not maps:
raise PublishValidationError(
"No Texture Map Exported with texture set: {}.".format(
instance.name)
)
included = any(map_includes_channel(
channel_map, channel) for channel_map in maps
)
if included:
break
else:
# not found in any export preset
invalid_channels.append(channel)
return invalid_channels
|