mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Merge branch 'develop' into enchancement/OP-2630_acescg_maya
# Conflicts: # openpype/hosts/maya/api/lib_renderproducts.py
This commit is contained in:
commit
f22ece7357
138 changed files with 4763 additions and 1783 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
207
openpype/hosts/maya/plugins/publish/validate_glsl_material.py
Normal file
207
openpype/hosts/maya/plugins/publish/validate_glsl_material.py
Normal 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)
|
||||
31
openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py
Normal file
31
openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue