Skip to content

Commit

Permalink
Merge pull request #1 from nfa-vfxim/dev
Browse files Browse the repository at this point in the history
First pre-release
  • Loading branch information
bkamphues authored Nov 27, 2020
2 parents 872442d + 8b3f072 commit 6798d62
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 5 deletions.
19 changes: 18 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
# SOFTWARE.

import sgtk
import hou


class TkHoudiniArnold(sgtk.platform.Application):
# shotgun arnold output node
Expand All @@ -29,4 +31,19 @@ def init_app(self):
# initialize the app

tk_houdini_arnold = self.import_module("tk_houdini_arnold")
self.handler = tk_houdini_arnold.TkHoudiniArnoldHandler(self)
self.handler = tk_houdini_arnold.TkHoudiniArnoldHandler(self)

# register callback
hou.hipFile.addEventCallback(self.handler.sceneWasSaved)

def destroy_app(self):
# breakdown the app

hou.hipFile.removeEventCallback(self.handler.sceneWasSaved)

def getWorkFileTemplate(self):
# return the work file template object

template = self.get_template("work_file_template")

return template
Binary file removed otls/sgtk_arnold.hdanc
Binary file not shown.
Binary file added otls/sgtk_arnold.otl
Binary file not shown.
215 changes: 211 additions & 4 deletions python/tk_houdini_arnold/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,223 @@

import sgtk
import hou
import os


class TkHoudiniArnoldHandler(object):
# handler class for the actual business logic

def __init__(self, app):
# initialize class

self.app = app

# class wide methods

def getDifferentFileAOVs(self, node):
# return all enabled aov file parameters

parms = node.globParms("ar_aov_separate* ^*_file*")

return parms

def getOutputPath(self, node):
# return the beauty output path currently set

outputPath = node.parm("outputPath").eval()

return outputPath

def getNodes(self):
# return all sgtk_arnold node instances

try:
nodes = hou.nodeType(hou.ropNodeTypeCategory(), "sgtk_arnold").instances()
return nodes
except AttributeError:
return

def sceneWasSaved(self, event_type):
# event callback for saving the scene

if event_type == hou.hipFileEventType.AfterSave:

nodes = self.getNodes()

if nodes:
for node in nodes:
self.updateNode(node)
else:
self.app.logger.info(
"Skipping SGTK Arnold collection since no instances were found."
)

# methods executed by the hda

def executeToDeadline():
# execute the render to deadline logic

def updateNode(self, node):
# update all node parameters
beautyOutputLabel = node.parm("outputLabel")
beautyOutputString = node.parm("outputPath")

# update beauty label and string
try:
beautyPath = self.__getBeautyPath(node)
beautyOutputLabel.set(os.path.split(beautyPath)[1])
beautyOutputString.set(beautyPath)

# get all aov parms that are enabled
aovs = self.getDifferentFileAOVs(node)

# for each found parm, update the filepath
for aov in aovs:
self.__updateAOVParm(node, aov)

except Exception as e:
self.app.logger.error(
"One or more parameters on %s could not be set:" % (node)
)
self.app.logger.error(e)

def useDifferentFileAOV(self, **kwargs):
# callback for use different file on aov's

node = kwargs["node"]
parm = kwargs["parm"]

self.__updateAOVParm(node, parm)

# private functions

def __getBeautyPath(self, node):
# get the main render template path
self.app.logger.debug(node)

# get template objects
renderTemplate = self.app.get_template("output_render_template")
workFileTemplate = self.app.get_template("work_file_template")
workFilePath = hou.hipFile.path()

# get fields from work file
fields = workFileTemplate.get_fields(workFilePath)

# format sequence key to houdini formatting
fields["SEQ"] = "FORMAT: $F"

# resolve camera
cam = self.__getCameraNode(node)

if cam:
# add resolution to fields
fields["width"] = cam.parm("resx").eval()
fields["height"] = cam.parm("resy").eval()
else:
raise Exception("No camera was selected!")

self.app.logger.debug(
"Using the following fields for path creation: %s" % fields
)

# apply fields and create path
path = renderTemplate.apply_fields(fields)
path = path.replace("\\", "/")

self.app.logger.debug("Built the following path from template: %s" % (path))

return path

def __getAOVPath(self, aov, node):
# get the aov render template path

# get template objects
aovTemplate = self.app.get_template("output_aov_template")
workFileTemplate = self.app.get_template("work_file_template")
workFilePath = hou.hipFile.path()

# get fields from work file
fields = workFileTemplate.get_fields(workFilePath)

# format sequence key to houdini formatting
fields["SEQ"] = "FORMAT: $F"

# resolve camera
cam = self.__getCameraNode(node)

if cam:
# add resolution to fields
fields["width"] = cam.parm("resx").eval()
fields["height"] = cam.parm("resy").eval()
else:
raise Exception("No camera was selected!")

# add aov name to fields
fields["aov_name"] = aov

self.app.logger.debug(
"Using the following fields for path creation: %s" % fields
)

# apply fields and create path
path = aovTemplate.apply_fields(fields)
path = path.replace("\\", "/")

self.app.logger.debug("Built the following path from template: %s" % path)

return path

def __getCameraNode(self, arnoldNode):
# return the camera node

camPath = arnoldNode.evalParm("camera")
camNode = hou.node(camPath)

if hou.node(camPath):
return camNode
else:
# get a new camera
newCamPath = hou.ui.selectNode(
node_type_filter=hou.nodeTypeFilter.ObjCamera, title="Choose camera"
)

# if a new camera wasn't selected, display a message to the user and exit
if newCamPath is None:
hou.ui.displayMessage(
"No camera was selected. Create a camera, select it in '%s' and save the file again."
% (arnoldNode),
buttons=("OK",),
severity=hou.severityType.Warning,
)
return
else:
camNode = hou.node(newCamPath)
arnoldNode.parm("camera").set(newCamPath)
return camNode

def __updateAOVParm(self, node, parm):
# update the parameter

# replace the parameter basename with nothing, leaving the aov number
aov_number = parm.name().replace("ar_aov_separate", "")
self.app.logger.debug("running on aov_number %s" % (aov_number))

# when checkbox is ticked
if parm.eval():
node.parm("ar_aov_separate_file%s" % aov_number).lock(False)

# get the custom layer name
value = node.parm("ar_aov_exr_layer_name%s" % (aov_number)).eval()

# if the custom layer name was not used, use aov label
if not value:
value = node.parm("ar_aov_label%s" % (aov_number)).eval()

node.parm("ar_aov_separate_file%s" % (aov_number)).set(
self.__getAOVPath(value, node)
)
node.parm("ar_aov_separate_file%s" % aov_number).lock(True)

# when checkbox is not ticked
else:
parm_path = node.parm("ar_aov_separate_file%s" % aov_number)
parm_path.lock(False)
parm_path.set("Disabled")
parm_path.lock(True)
9 changes: 9 additions & 0 deletions todo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## To Do List

- ~~Fix scenario of no node instances in event callback when opening a completely new file.~~
- Fix locked node in renderfarm.
- ~~Fix collector not matching ID's.~~
- ~~Fix no selected camera bug~~.
- Add Deadline subprocess functions.
- Add slate/preview functions.
- ~~Move repo to /nfa-vfxim instead of /bkamphues.~~

0 comments on commit 6798d62

Please sign in to comment.