Merge branch 'develop' into enchancement/OP-2630_acescg_maya

# Conflicts:
#	openpype/hosts/maya/api/lib_renderproducts.py
This commit is contained in:
Toke Stuart Jepsen 2023-02-23 15:57:06 +00:00
commit f22ece7357
138 changed files with 4763 additions and 1783 deletions

View file

@ -198,12 +198,18 @@ class ARenderProducts:
"""Constructor."""
self.layer = layer
self.render_instance = render_instance
self.multipart = False
self.multipart = self.get_multipart()
# Initialize
self.layer_data = self._get_layer_data()
self.layer_data.products = self.get_render_products()
def get_multipart(self):
raise NotImplementedError(
"The render product implementation does not have a "
"\"get_multipart\" method."
)
def has_camera_token(self):
# type: () -> bool
"""Check if camera token is in image prefix.
@ -532,16 +538,20 @@ class RenderProductsArnold(ARenderProducts):
return prefix
def _get_aov_render_products(self, aov, cameras=None):
"""Return all render products for the AOV"""
products = []
aov_name = self._get_attr(aov, "name")
def get_multipart(self):
multipart = False
multilayer = bool(self._get_attr("defaultArnoldDriver.multipart"))
merge_AOVs = bool(self._get_attr("defaultArnoldDriver.mergeAOVs"))
if multilayer or merge_AOVs:
multipart = True
return multipart
def _get_aov_render_products(self, aov, cameras=None):
"""Return all render products for the AOV"""
products = []
aov_name = self._get_attr(aov, "name")
ai_drivers = cmds.listConnections("{}.outputs".format(aov),
source=True,
destination=False,
@ -599,7 +609,7 @@ class RenderProductsArnold(ARenderProducts):
ext=ext,
aov=aov_name,
driver=ai_driver,
multipart=multipart,
multipart=self.multipart,
camera=camera,
colorspace=colorspace
)
@ -773,6 +783,14 @@ class RenderProductsVray(ARenderProducts):
renderer = "vray"
def get_multipart(self):
multipart = False
image_format = self._get_attr("vraySettings.imageFormatStr")
if image_format == "exr (multichannel)":
multipart = True
return multipart
def get_renderer_prefix(self):
# type: () -> str
"""Get image prefix for V-Ray.
@ -839,11 +857,6 @@ class RenderProductsVray(ARenderProducts):
if default_ext in {"exr (multichannel)", "exr (deep)"}:
default_ext = "exr"
# Define multipart.
multipart = False
if image_format_str == "exr (multichannel)":
multipart = True
products = []
# add beauty as default when not disabled
@ -856,7 +869,7 @@ class RenderProductsVray(ARenderProducts):
ext=default_ext,
camera=camera,
colorspace=lib.get_color_management_output_transform(),
multipart=multipart
multipart=self.multipart
)
)
@ -869,10 +882,10 @@ class RenderProductsVray(ARenderProducts):
productName="Alpha",
ext=default_ext,
camera=camera,
multipart=multipart
multipart=self.multipart
)
)
if multipart:
if self.multipart:
# AOVs are merged in m-channel file, only main layer is rendered
return products
@ -1035,6 +1048,34 @@ class RenderProductsRedshift(ARenderProducts):
renderer = "redshift"
unmerged_aovs = {"Cryptomatte"}
def get_files(self, product):
# When outputting AOVs we need to replace Redshift specific AOV tokens
# with Maya render tokens for generating file sequences. We validate to
# a specific AOV fileprefix so we only need to accout for one
# replacement.
if not product.multipart and product.driver:
file_prefix = self._get_attr(product.driver + ".filePrefix")
self.layer_data.filePrefix = file_prefix.replace(
"<BeautyPath>/<BeautyFile>",
"<Scene>/<RenderLayer>/<RenderLayer>"
)
return super(RenderProductsRedshift, self).get_files(product)
def get_multipart(self):
# For Redshift we don't directly return upon forcing multilayer
# due to some AOVs still being written into separate files,
# like Cryptomatte.
# AOVs are merged in multi-channel file
multipart = False
force_layer = bool(
self._get_attr("redshiftOptions.exrForceMultilayer")
)
if force_layer:
multipart = True
return multipart
def get_renderer_prefix(self):
"""Get image prefix for Redshift.
@ -1074,16 +1115,6 @@ class RenderProductsRedshift(ARenderProducts):
for c in self.get_renderable_cameras()
]
# For Redshift we don't directly return upon forcing multilayer
# due to some AOVs still being written into separate files,
# like Cryptomatte.
# AOVs are merged in multi-channel file
multipart = False
force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa
exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart"))
if exMultipart or force_layer:
multipart = True
# Get Redshift Extension from image format
image_format = self._get_attr("redshiftOptions.imageFormat") # integer
ext = mel.eval("redshiftGetImageExtension(%i)" % image_format)
@ -1105,7 +1136,7 @@ class RenderProductsRedshift(ARenderProducts):
continue
aov_type = self._get_attr(aov, "aovType")
if multipart and aov_type not in self.unmerged_aovs:
if self.multipart and aov_type not in self.unmerged_aovs:
continue
# Any AOVs that still get processed, like Cryptomatte
@ -1140,8 +1171,9 @@ class RenderProductsRedshift(ARenderProducts):
productName=aov_light_group_name,
aov=aov_name,
ext=ext,
multipart=multipart,
camera=camera)
multipart=False,
camera=camera,
driver=aov)
products.append(product)
if light_groups:
@ -1154,8 +1186,9 @@ class RenderProductsRedshift(ARenderProducts):
product = RenderProduct(productName=aov_name,
aov=aov_name,
ext=ext,
multipart=multipart,
camera=camera)
multipart=False,
camera=camera,
driver=aov)
products.append(product)
# When a Beauty AOV is added manually, it will be rendered as
@ -1170,7 +1203,7 @@ class RenderProductsRedshift(ARenderProducts):
products.insert(0,
RenderProduct(productName=beauty_name,
ext=ext,
multipart=multipart,
multipart=self.multipart,
camera=camera))
return products
@ -1190,6 +1223,10 @@ class RenderProductsRenderman(ARenderProducts):
renderer = "renderman"
unmerged_aovs = {"PxrCryptomatte"}
def get_multipart(self):
# Implemented as display specific in "get_render_products".
return False
def get_render_products(self):
"""Get all AOVs.
@ -1329,6 +1366,10 @@ class RenderProductsMayaHardware(ARenderProducts):
{"label": "EXR(exr)", "index": 40, "extension": "exr"}
]
def get_multipart(self):
# MayaHardware does not support multipart EXRs.
return False
def _get_extension(self, value):
result = None
if isinstance(value, int):

View file

@ -42,6 +42,7 @@ Provides:
import re
import os
import platform
import json
from maya import cmds
import maya.app.renderSetup.model.renderSetup as renderSetup
@ -183,7 +184,11 @@ class CollectMayaRender(pyblish.api.ContextPlugin):
self.log.info("multipart: {}".format(
multipart))
assert exp_files, "no file names were generated, this is bug"
self.log.info(exp_files)
self.log.info(
"expected files: {}".format(
json.dumps(exp_files, indent=4, sort_keys=True)
)
)
# if we want to attach render to subset, check if we have AOV's
# in expectedFiles. If so, raise error as we cannot attach AOV

View file

@ -22,6 +22,8 @@ class ExtractGLB(publish.Extractor):
self.log.info("Extracting GLB to: {}".format(path))
cmds.loadPlugin("maya2glTF", quiet=True)
nodes = instance[:]
self.log.info("Instance: {0}".format(nodes))
@ -45,6 +47,7 @@ class ExtractGLB(publish.Extractor):
"glb": True,
"vno": True # visibleNodeOnly
}
with lib.maintained_selection():
cmds.select(nodes, hi=True, noExpand=True)
extract_gltf(staging_dir,

View file

@ -0,0 +1,207 @@
import os
from maya import cmds
import pyblish.api
from openpype.pipeline.publish import (
RepairAction,
ValidateContentsOrder
)
from openpype.pipeline import PublishValidationError
class ValidateGLSLMaterial(pyblish.api.InstancePlugin):
"""
Validate if the asset uses GLSL Shader
"""
order = ValidateContentsOrder + 0.1
families = ['gltf']
hosts = ['maya']
label = 'GLSL Shader for GLTF'
actions = [RepairAction]
optional = True
active = True
def process(self, instance):
shading_grp = self.get_material_from_shapes(instance)
if not shading_grp:
raise PublishValidationError("No shading group found")
invalid = self.get_texture_shader_invalid(instance)
if invalid:
raise PublishValidationError("Non GLSL Shader found: "
"{0}".format(invalid))
def get_material_from_shapes(self, instance):
shapes = cmds.ls(instance, type="mesh", long=True)
for shape in shapes:
shading_grp = cmds.listConnections(shape,
destination=True,
type="shadingEngine")
return shading_grp or []
def get_texture_shader_invalid(self, instance):
invalid = set()
shading_grp = self.get_material_from_shapes(instance)
for shading_group in shading_grp:
material_name = "{}.surfaceShader".format(shading_group)
material = cmds.listConnections(material_name,
source=True,
destination=False,
type="GLSLShader")
if not material:
# add material name
material = cmds.listConnections(material_name)[0]
invalid.add(material)
return list(invalid)
@classmethod
def repair(cls, instance):
"""
Repair instance by assigning GLSL Shader
to the material
"""
cls.assign_glsl_shader(instance)
return
@classmethod
def assign_glsl_shader(cls, instance):
"""
Converting StingrayPBS material to GLSL Shaders
for the glb export through Maya2GLTF plugin
"""
meshes = cmds.ls(instance, type="mesh", long=True)
cls.log.info("meshes: {}".format(meshes))
# load the glsl shader plugin
cmds.loadPlugin("glslShader", quiet=True)
for mesh in meshes:
# create glsl shader
glsl = cmds.createNode('GLSLShader')
glsl_shading_grp = cmds.sets(name=glsl + "SG", empty=True,
renderable=True, noSurfaceShader=True)
cmds.connectAttr(glsl + ".outColor",
glsl_shading_grp + ".surfaceShader")
# load the maya2gltf shader
ogsfx_path = instance.context.data["project_settings"]["maya"]["publish"]["ExtractGLB"]["ogsfx_path"] # noqa
if not os.path.exists(ogsfx_path):
if ogsfx_path:
# if custom ogsfx path is not specified
# the log below is the warning for the user
cls.log.warning("ogsfx shader file "
"not found in {}".format(ogsfx_path))
cls.log.info("Find the ogsfx shader file in "
"default maya directory...")
# re-direct to search the ogsfx path in maya_dir
ogsfx_path = os.getenv("MAYA_APP_DIR") + ogsfx_path
if not os.path.exists(ogsfx_path):
raise PublishValidationError("The ogsfx shader file does not " # noqa
"exist: {}".format(ogsfx_path)) # noqa
cmds.setAttr(glsl + ".shader", ogsfx_path, typ="string")
# list the materials used for the assets
shading_grp = cmds.listConnections(mesh,
destination=True,
type="shadingEngine")
# get the materials related to the selected assets
for material in shading_grp:
pbs_shader = cmds.listConnections(material,
destination=True,
type="StingrayPBS")
if pbs_shader:
cls.pbs_shader_conversion(pbs_shader, glsl)
# setting up to relink the texture if
# the mesh is with aiStandardSurface
arnold_shader = cmds.listConnections(material,
destination=True,
type="aiStandardSurface")
if arnold_shader:
cls.arnold_shader_conversion(arnold_shader, glsl)
cmds.sets(mesh, forceElement=str(glsl_shading_grp))
@classmethod
def pbs_shader_conversion(cls, main_shader, glsl):
cls.log.info("StringrayPBS detected "
"-> Can do texture conversion")
for shader in main_shader:
# get the file textures related to the PBS Shader
albedo = cmds.listConnections(shader +
".TEX_color_map")
if albedo:
dif_output = albedo[0] + ".outColor"
# get the glsl_shader input
# reconnect the file nodes to maya2gltf shader
glsl_dif = glsl + ".u_BaseColorTexture"
cmds.connectAttr(dif_output, glsl_dif)
# connect orm map if there is one
orm_packed = cmds.listConnections(shader +
".TEX_ao_map")
if orm_packed:
orm_output = orm_packed[0] + ".outColor"
mtl = glsl + ".u_MetallicTexture"
ao = glsl + ".u_OcclusionTexture"
rough = glsl + ".u_RoughnessTexture"
cmds.connectAttr(orm_output, mtl)
cmds.connectAttr(orm_output, ao)
cmds.connectAttr(orm_output, rough)
# connect nrm map if there is one
nrm = cmds.listConnections(shader +
".TEX_normal_map")
if nrm:
nrm_output = nrm[0] + ".outColor"
glsl_nrm = glsl + ".u_NormalTexture"
cmds.connectAttr(nrm_output, glsl_nrm)
@classmethod
def arnold_shader_conversion(cls, main_shader, glsl):
cls.log.info("aiStandardSurface detected "
"-> Can do texture conversion")
for shader in main_shader:
# get the file textures related to the PBS Shader
albedo = cmds.listConnections(shader + ".baseColor")
if albedo:
dif_output = albedo[0] + ".outColor"
# get the glsl_shader input
# reconnect the file nodes to maya2gltf shader
glsl_dif = glsl + ".u_BaseColorTexture"
cmds.connectAttr(dif_output, glsl_dif)
orm_packed = cmds.listConnections(shader +
".specularRoughness")
if orm_packed:
orm_output = orm_packed[0] + ".outColor"
mtl = glsl + ".u_MetallicTexture"
ao = glsl + ".u_OcclusionTexture"
rough = glsl + ".u_RoughnessTexture"
cmds.connectAttr(orm_output, mtl)
cmds.connectAttr(orm_output, ao)
cmds.connectAttr(orm_output, rough)
# connect nrm map if there is one
bump_node = cmds.listConnections(shader +
".normalCamera")
if bump_node:
for bump in bump_node:
nrm = cmds.listConnections(bump +
".bumpValue")
if nrm:
nrm_output = nrm[0] + ".outColor"
glsl_nrm = glsl + ".u_NormalTexture"
cmds.connectAttr(nrm_output, glsl_nrm)

View file

@ -0,0 +1,31 @@
from maya import cmds
import pyblish.api
from openpype.pipeline.publish import (
RepairAction,
ValidateContentsOrder
)
class ValidateGLSLPlugin(pyblish.api.InstancePlugin):
"""
Validate if the asset uses GLSL Shader
"""
order = ValidateContentsOrder + 0.15
families = ['gltf']
hosts = ['maya']
label = 'maya2glTF plugin'
actions = [RepairAction]
def process(self, instance):
if not cmds.pluginInfo("maya2glTF", query=True, loaded=True):
raise RuntimeError("maya2glTF is not loaded")
@classmethod
def repair(cls, instance):
"""
Repair instance by enabling the plugin
"""
return cmds.loadPlugin("maya2glTF", quiet=True)