Validate the generated output maps for missing channels

This commit is contained in:
Roy Nieterau 2023-04-03 16:03:57 +02:00
parent 4300939199
commit 9d68db0e16
4 changed files with 126 additions and 12 deletions

View file

@ -5,7 +5,8 @@ from openpype.pipeline import CreatedInstance, Creator, CreatorError
from openpype.lib import (
EnumDef,
UILabelDef,
NumberDef
NumberDef,
BoolDef
)
from openpype.hosts.substancepainter.api.pipeline import (
@ -80,6 +81,13 @@ class CreateTextures(Creator):
EnumDef("exportPresetUrl",
items=get_export_presets(),
label="Output Template"),
BoolDef("allowSkippedMaps",
label="Allow Skipped Output Maps",
tooltip="When enabled this allows the publish to ignore "
"output maps in the used output template if one "
"or more maps are skipped due to the required "
"channels not being present in the current file.",
default=True),
EnumDef("exportFileFormat",
items={
None: "Based on output template",

View file

@ -97,7 +97,7 @@ class CollectTextureSet(pyblish.api.InstancePlugin):
representation["stagingDir"] = staging_dir
# Clone the instance
image_instance = context.create_instance(instance.name)
image_instance = context.create_instance(image_subset)
image_instance[:] = instance[:]
image_instance.data.update(copy.deepcopy(instance.data))
image_instance.data["name"] = image_subset

View file

@ -1,6 +1,7 @@
from openpype.pipeline import KnownPublishError, publish
import substance_painter.export
from openpype.pipeline import KnownPublishError, publish
class ExtractTextures(publish.Extractor,
publish.ColormanagedPyblishPluginMixin):
@ -31,21 +32,19 @@ class ExtractTextures(publish.Extractor,
"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"Processing stack: {texture_set_name} {stack_name}")
self.log.info(f"Exported stack: {texture_set_name} {stack_name}")
for texture_map in maps:
self.log.info(f"Exported texture: {texture_map}")
# TODO: Confirm outputs match what we collected
# TODO: Confirm the files indeed exist
# TODO: make sure representations are registered
# 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
# some anatomy and other instance data needs to be collected prior
context = instance.context
for image_instance in instance:
representation = next(iter(image_instance.data["representations"]))
colorspace = image_instance.data.get("colorspace")
if not colorspace:
@ -53,10 +52,9 @@ class ExtractTextures(publish.Extractor,
f"{image_instance}")
continue
for representation in image_instance.data["representations"]:
self.set_representation_colorspace(representation,
context=context,
colorspace=colorspace)
self.set_representation_colorspace(representation,
context=context,
colorspace=colorspace)
# The TextureSet instance should not be integrated. It generates no
# output data. Instead the separated texture instances are generated

View file

@ -0,0 +1,108 @@
import copy
import os
import pyblish.api
import substance_painter.export
from openpype.pipeline import PublishValidationError
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)
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)
config["exportParameters"][0]["parameters"]["sizeLog2"] = [1, 1]
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 allow_skipped_maps:
# TODO: This is changing state on the instance's which
# usually should not be done during validation.
self.log.warning(f"Disabling texture instance: "
f"{image_instance}")
image_instance.data["active"] = False
image_instance.data["integrate"] = False
representation.setdefault("tags", []).append("delete")
continue
if missing:
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"
)