mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Validate the generated output maps for missing channels
This commit is contained in:
parent
4300939199
commit
9d68db0e16
4 changed files with 126 additions and 12 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue