Last active
April 26, 2023 06:32
-
-
Save BigRoy/57a80e9bd9cccdd2c40de445f06437c0 to your computer and use it in GitHub Desktop.
Get the export settings from Substance Painter project to find the export color spaces configured for the project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Substance Painter OCIO management | |
Adobe Substance 3D Painter supports OCIO color management using a per project | |
configuration. Output color spaces are defined at the project level | |
More information see: | |
- https://substance3d.adobe.com/documentation/spdoc/color-management-223053233.html # noqa | |
- https://substance3d.adobe.com/documentation/spdoc/color-management-with-opencolorio-225969419.html # noqa | |
""" | |
import substance_painter.export | |
import substance_painter.js | |
import json | |
def _convert_stack_path_to_cmd_str(stack_path): | |
return json.dumps(stack_path) | |
def get_channels(stack_path): | |
stack_path = _convert_stack_path_to_cmd_str(stack_path) | |
cmd = f"alg.mapexport.channelIdentifiers({stack_path})" | |
return substance_painter.js.evaluate(cmd) | |
def get_channel_format(stack_path, channel): | |
stack_path = _convert_stack_path_to_cmd_str(stack_path) | |
cmd = f"alg.mapexport.channelFormat({stack_path}, '{channel}')" | |
return substance_painter.js.evaluate(cmd) | |
def get_document_structure(): | |
cmd = f"alg.mapexport.documentStructure()" | |
return substance_painter.js.evaluate(cmd) | |
def _iter_document_stack_channels(): | |
"""Yield all stack paths and channels project""" | |
for material in get_document_structure()["materials"]: | |
material_name = material["name"] | |
for stack in material["stacks"]: | |
stack_name = stack["name"] | |
for channel in stack["channels"]: | |
if stack_name: | |
stack_path = [material_name, stack_name] | |
else: | |
stack_path = material_name | |
yield stack_path, channel | |
def _get_first_color_and_data_stack_and_channel(): | |
color_channel = None | |
data_channel = None | |
for stack_path, channel in _iter_document_stack_channels(): | |
channel_format = get_channel_format(stack_path, channel) | |
if channel_format["color"]: | |
color_channel = (stack_path, channel) | |
else: | |
data_channel = (stack_path, channel) | |
if color_channel and data_channel: | |
return color_channel, data_channel | |
return color_channel, data_channel | |
def get_project_channel_data(): | |
keys = [ | |
"project", | |
"mesh", | |
"textureSet", | |
"sceneMaterial", | |
"udim", | |
"colorSpace" | |
] | |
query = {key: f"${key}" for key in keys} | |
config = { | |
"exportPath": "/", | |
"exportShaderParams": False, | |
"defaultExportPreset": "query_preset", | |
"exportPresets": [{ | |
"name": "query_preset", | |
# List of maps making up this export preset. | |
"maps": [{ | |
"fileName": json.dumps(query), | |
# List of source/destination defining which channels will | |
# make up the texture file. | |
"channels": [], | |
"parameters": { | |
"fileFormat": "exr", | |
"bitDepth": "32f", | |
"dithering": False, | |
"sizeLog2": 4, | |
"paddingAlgorithm": "passthrough", | |
"dilationDistance": 16 | |
} | |
}] | |
}], | |
#"exportParamet | |
} | |
def _get_query_output(config): | |
# Return the basename of the first output path | |
result = substance_painter.export.list_project_textures(config) | |
path = next(iter(result.values()))[0] | |
# strip extension and slash since we know relevant json data starts | |
# and ends with { and } characters | |
path = path.strip("/\\.exr") | |
return json.loads(path) | |
# Query for each type of channel (color and data) | |
color_channel, data_channel = _get_first_color_and_data_stack_and_channel() | |
colorspaces = {} | |
for key, channel_data in { | |
"data": data_channel, | |
"color": color_channel | |
}.items(): | |
if channel_data is None: | |
# No channel of that datatype anywhere in the Stack so we're | |
# unable to identify the output color space of the project | |
colorspaces[key] = None | |
continue | |
stack, channel = channel_data | |
# Stack must be a string | |
if not isinstance(stack, str): | |
# Assume iterable | |
stack = "/".join(stack) | |
# Define the temp output config | |
config["exportList"] = [{"rootPath": stack}] | |
config_map = config["exportPresets"][0]["maps"][0] | |
config_map["channels"] = [ | |
{ | |
"destChannel": x, | |
"srcChannel": x, | |
"srcMapType": "documentMap", | |
"srcMapName": channel | |
} for x in "RGB" | |
] | |
if key == "color": | |
# Query for each bit depth | |
# Color space definition can have a different OCIO config set | |
# for 8-bit, 16-bit and 32-bit outputs so we need to check each | |
# bit depth | |
for depth in ["8", "16", "16f", "32f"]: | |
config_map["parameters"]["bitDepth"] = depth # noqa | |
colorspaces[key + depth] = _get_query_output(config) | |
else: | |
# Data channel (not color managed) | |
colorspaces[key] = _get_query_output(config) | |
return colorspaces | |
result = get_project_channel_data() | |
print(json.dumps(result, indent=4)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example JSON output data: