This commit is contained in:
wikoreman 2018-08-27 10:33:30 +02:00
commit 0ebd3fa873
6 changed files with 453 additions and 8 deletions

View file

@ -123,7 +123,9 @@ class SubmitDependentImageSequenceJobDeadline(pyblish.api.InstancePlugin):
label = "Submit image sequence jobs to Deadline"
order = pyblish.api.IntegratorOrder + 0.1
hosts = ["fusion", "maya"]
families = ["colorbleed.saver.deadline", "colorbleed.renderlayer"]
families = ["colorbleed.saver.deadline",
"colorbleed.renderlayer",
"colorbleed.vrayscene"]
def process(self, instance):
@ -134,8 +136,10 @@ class SubmitDependentImageSequenceJobDeadline(pyblish.api.InstancePlugin):
# Get a submission job
job = instance.data.get("deadlineSubmissionJob")
if not job:
raise RuntimeError("Can't continue without valid deadline "
"submission prior to this plug-in.")
self.log.warning("Can't continue without valid deadline "
"submission prior to this plug-in.")
self.log.info("Skipping Publish Job")
return
data = instance.data.copy()
subset = data["subset"]
@ -155,15 +159,17 @@ class SubmitDependentImageSequenceJobDeadline(pyblish.api.InstancePlugin):
# This assumes the output files start with subset name and ends with
# a file extension.
if "ext" in instance.data:
ext = re.escape(instance.data["ext"])
ext = instance.data["ext"].strip(".")
else:
ext = "\.\D+"
regex = "^{subset}.*\d+{ext}$".format(subset=re.escape(subset),
ext=ext)
regex = "^{subset}.*\d+\.{ext}$".format(subset=re.escape(subset),
ext=re.escape(ext))
# Remove deadline submission job, not needed in metadata
data.pop("deadlineSubmissionJob")
# Write metadata for publish job
render_job = data.pop("deadlineSubmissionJob")
metadata = {
"regex": regex,
"startFrame": start,
@ -189,7 +195,7 @@ class SubmitDependentImageSequenceJobDeadline(pyblish.api.InstancePlugin):
override = data["overrideExistingFrame"]
# override = data.get("overrideExistingFrame", False)
out_file = render_job.get("OutFile")
out_file = job.get("OutFile")
if not out_file:
raise RuntimeError("OutFile not found in render job!")

View file

@ -0,0 +1,34 @@
from collections import OrderedDict
import avalon.maya
class CreateVRayScene(avalon.maya.Creator):
label = "VRay Scene"
family = "colorbleed.vrayscene"
# icon = "blocks"
def __init__(self, *args, **kwargs):
super(CreateVRayScene, self).__init__(*args, **kwargs)
# We won't be publishing this one
self.data["id"] = "avalon.vrayscene"
# We don't need subset or asset attributes
self.data.pop("subset", None)
self.data.pop("asset", None)
self.data.pop("active", None)
data = OrderedDict(**self.data)
data["camera"] = "persp"
data["suspendRenderJob"] = False
data["suspendPublishJob"] = False
data["includeDefaultRenderLayer"] = False
data["extendFrames"] = False
data["pools"] = ""
self.data = data
self.options = {"useSelection": False} # Force no content

View file

@ -0,0 +1,111 @@
import os
import pyblish.api
from avalon import api, maya
from maya import cmds
class CollectVRayScene(pyblish.api.ContextPlugin):
"""Collect all information prior for exporting vrscenes
"""
order = pyblish.api.CollectorOrder
label = "Collect VRay Scene"
hosts = ["maya"]
def process(self, context):
# Sort by displayOrder
def sort_by_display_order(layer):
return cmds.getAttr("%s.displayOrder" % layer)
asset = api.Session["AVALON_ASSET"]
work_dir = context.data["workspaceDir"]
# Get VRay Scene instance
vray_scenes = maya.lsattr("family", "colorbleed.vrayscene")
if not vray_scenes:
self.log.info("No instance found of family: `colorbleed.vrayscene`")
return
assert len(vray_scenes) == 1, "Multiple vrayscene instances found!"
vray_scene = vray_scenes[0]
vrscene_data = {k: cmds.getAttr("%s.%s" % (vray_scene, k)) for
k in cmds.listAttr(vray_scene, userDefined=True)}
# Output data
start_frame = int(cmds.getAttr("defaultRenderGlobals.startFrame"))
end_frame = int(cmds.getAttr("defaultRenderGlobals.endFrame"))
# Create output file path with template
file_name = context.data["currentFile"].replace("\\", "/")
vrscene = ("vrayscene", "<Scene>", "<Scene>_<Layer>", "<Layer>")
vrscene_output = os.path.join(work_dir, *vrscene)
vrscene_data["startFrame"] = start_frame
vrscene_data["endFrame"] = end_frame
vrscene_data["vrsceneOutput"] = vrscene_output
context.data["startFrame"] = start_frame
context.data["endFrame"] = end_frame
# Check and create render output template for render job
# outputDir is required for submit_publish_job
if not vrscene_data.get("suspendRenderJob", False):
renders = ("renders", "<Scene>", "<Scene>_<Layer>", "<Layer>")
output_renderpath = os.path.join(work_dir, *renders)
vrscene_data["outputDir"] = output_renderpath
# Get resolution
resolution = (cmds.getAttr("defaultResolution.width"),
cmds.getAttr("defaultResolution.height"))
# Get format extension
extension = cmds.getAttr("vraySettings.imageFormatStr")
# Get render layers
render_layers = [i for i in cmds.ls(type="renderLayer") if
cmds.getAttr("{}.renderable".format(i)) and not
cmds.referenceQuery(i, isNodeReferenced=True)]
# Check if we need to filter out the default render layer
if vrscene_data.get("includeDefaultRenderLayer", True):
render_layers = [r for r in render_layers
if r != "defaultRenderLayer"]
render_layers = sorted(render_layers, key=sort_by_display_order)
for layer in render_layers:
if layer.endswith("defaultRenderLayer"):
layer = "masterLayer"
data = {
"subset": layer,
"setMembers": layer,
"startFrame": start_frame,
"endFrame": end_frame,
"renderer": "vray",
"resolution": resolution,
"ext": extension,
# instance subset
"family": "VRay Scene",
"families": ["colorbleed.vrayscene"],
"asset": asset,
"time": api.time(),
"author": context.data["user"],
# Add source to allow tracing back to the scene from
# which was submitted originally
"source": file_name
}
data.update(vrscene_data)
instance = context.create_instance(layer)
self.log.info("Created: %s" % instance.name)
instance.data.update(data)

View file

@ -0,0 +1,249 @@
import getpass
import json
import os
from copy import deepcopy
import pyblish.api
from avalon import api
from avalon.vendor import requests
from maya import cmds
class VraySubmitDeadline(pyblish.api.InstancePlugin):
"""Export the scene to `.vrscene` files per frame per render layer
vrscene files will be written out based on the following template:
<project>/vrayscene/<Scene>/<Scene>_<Layer>/<Layer>
A dependency job will be added for each layer to render the framer
through VRay Standalone
"""
label = "Submit to Deadline ( vrscene )"
order = pyblish.api.IntegratorOrder
hosts = ["maya"]
families = ["colorbleed.vrayscene"]
def process(self, instance):
AVALON_DEADLINE = api.Session.get("AVALON_DEADLINE",
"http://localhost:8082")
assert AVALON_DEADLINE, "Requires AVALON_DEADLINE"
context = instance.context
deadline_url = "{}/api/jobs".format(AVALON_DEADLINE)
deadline_user = context.data.get("deadlineUser", getpass.getuser())
filepath = context.data["currentFile"]
filename = os.path.basename(filepath)
task_name = "{} - {}".format(filename, instance.name)
batch_name = "VRay Scene Export - {}".format(filename)
# Get the output template for vrscenes
vrscene_output = instance.data["vrsceneOutput"]
# This is also the input file for the render job
first_file = self.format_output_filename(instance,
filename,
vrscene_output)
# Primary job
self.log.info("Submitting export job ..")
payload = {
"JobInfo": {
# Top-level group name
"BatchName": batch_name,
# Job name, as seen in Monitor
"Name": task_name,
# Arbitrary username, for visualisation in Monitor
"UserName": deadline_user,
"Plugin": "MayaCmd",
"Frames": "1",
"Comment": context.data.get("comment", ""),
},
"PluginInfo": {
# Mandatory for Deadline
"Version": cmds.about(version=True),
# Input
"SceneFile": filepath,
# Output directory and filename
"OutputFilePath": vrscene_output.replace("\\", "/"),
"CommandLineOptions": self.build_command(instance),
"UseOnlyCommandLineOptions": True,
"SkipExistingFrames": True,
},
# Mandatory for Deadline, may be empty
"AuxFiles": []
}
environment = dict(AVALON_TOOLS="global;python36;maya2018")
environment.update(api.Session.copy())
jobinfo_environment = self.build_jobinfo_environment(environment)
payload["JobInfo"].update(jobinfo_environment)
self.log.info("Job Data:\n{}".format(json.dumps(payload)))
response = requests.post(url=deadline_url, json=payload)
if not response.ok:
raise RuntimeError(response.text)
# Secondary job
# Store job to create dependency chain
dependency = response.json()
if instance.data["suspendRenderJob"]:
self.log.info("Skipping render job and publish job")
return
self.log.info("Submitting render job ..")
start_frame = int(instance.data["startFrame"])
end_frame = int(instance.data["endFrame"])
ext = instance.data.get("ext", "exr")
# Create output directory for renders
render_ouput = self.format_output_filename(instance,
filename,
instance.data["outputDir"],
dir=True)
self.log.info("Render output: %s" % render_ouput)
# Update output dir
instance.data["outputDir"] = render_ouput
# Format output file name
sequence_filename = ".".join([instance.name, "%04d", ext])
output_filename = os.path.join(render_ouput, sequence_filename)
payload_b = {
"JobInfo": {
"JobDependency0": dependency["_id"],
"BatchName": batch_name,
"Name": "Render {}".format(task_name),
"UserName": deadline_user,
"Frames": "{}-{}".format(start_frame, end_frame),
"Plugin": "Vray",
"OverrideTaskExtraInfoNames": False,
"Whitelist": "cb7"
},
"PluginInfo": {
"InputFilename": first_file,
"OutputFilename": output_filename,
"SeparateFilesPerFrame": True,
"VRayEngine": "V-Ray",
"Width": instance.data["resolution"][0],
"Height": instance.data["resolution"][1],
},
"AuxFiles": [],
}
# Add vray renderslave to environment
tools = environment["AVALON_TOOLS"] + ";vrayrenderslave"
environment_b = deepcopy(environment)
environment_b["AVALON_TOOLS"] = tools
jobinfo_environment_b = self.build_jobinfo_environment(environment_b)
payload_b["JobInfo"].update(jobinfo_environment_b)
self.log.info(json.dumps(payload_b))
# Post job to deadline
response_b = requests.post(url=deadline_url, json=payload_b)
if not response_b.ok:
raise RuntimeError(response_b.text)
# Add job for publish job
if not instance.data.get("suspendPublishJob", False):
instance.data["deadlineSubmissionJob"] = response_b.json()
def build_command(self, instance):
"""Create command for Render.exe to export vray scene
Returns:
str
"""
cmd = ('-r vray -proj {project} -cam {cam} -noRender -s {startFrame} '
'-e {endFrame} -rl {layer} -exportFramesSeparate')
return cmd.format(project=instance.context.data["workspaceDir"],
cam=instance.data.get("cam", "persp"),
startFrame=instance.data["startFrame"],
endFrame=instance.data["endFrame"],
layer=instance.name)
def build_jobinfo_environment(self, env):
"""Format environment keys and values to match Deadline rquirements
Returns:
dict
"""
return {"EnvironmentKeyValue%d" % index: "%s=%s" % (k, env[k])
for index, k in enumerate(env)}
def format_output_filename(self, instance, filename, template, dir=False):
"""Format the expected output file of the Export job
Example:
<Scene>/<Scene>_<Layer>/<Layer>
"shot010_v006/shot010_v006_CHARS/CHARS"
Args:
instance:
filename(str):
dir(bool):
Returns:
str
"""
def smart_replace(string, key_values):
new_string = string
for key, value in key_values.items():
new_string = new_string.replace(key, value)
return new_string
# Ensure filename has no extension
file_name, _ = os.path.splitext(filename)
# Reformat without tokens
output_path = smart_replace(template,
{"<Scene>": file_name,
"<Layer>": instance.name})
if dir:
return output_path.replace("\\", "/")
start_frame = int(instance.data["startFrame"])
filename_zero = "{}_{:04d}.vrscene".format(output_path, start_frame)
result = filename_zero.replace("\\", "/")
return result

View file

@ -0,0 +1,45 @@
import pyblish.api
import colorbleed.api
from maya import cmds
class ValidateTranslatorEnabled(pyblish.api.ContextPlugin):
order = colorbleed.api.ValidateContentsOrder
label = "VRay Translator Settings"
families = ["colorbleed.vrayscene"]
actions = [colorbleed.api.RepairContextAction]
def process(self, context):
# Get vraySettings node
vray_settings = cmds.ls(type="VRaySettingsNode")
assert vray_settings, "Please ensure a VRay Settings Node is present"
node = vray_settings[0]
if not cmds.getAttr("{}.vrscene_on".format(node)):
self.info.error("Export vrscene not enabled")
if not cmds.getAttr("{}.misc_eachFrameInFile".format(node)):
self.info.error("Each Frame in File not enabled")
vrscene_filename = cmds.getAttr("{}.vrscene_filename".format(node))
if vrscene_filename != "vrayscene/<Scene>/<Scene>_<Layer>/<Layer>":
self.info.error("Template for file name is wrong")
@classmethod
def repair(cls, context):
vray_settings = cmds.ls(type="VRaySettingsNode")
if not vray_settings:
node = cmds.createNode("VRaySettingsNode")
else:
node = vray_settings[0]
cmds.setAttr("{}.vrscene_on".format(node), True)
cmds.setAttr("{}.misc_eachFrameInFile".format(node), True)
cmds.setAttr("{}.vrscene_filename".format(node),
"vrayscene/<Scene>/<Scene>_<Layer>/<Layer>",
type="string")