Code Snippet Corner


Houdini Blender Import Export

make sure you set a correct path to the package .json file


Camera Focus Handler

## Niclas Schlapmann - Freelance 3D Technical Artist
## www.enoni.de
## hello@enoni.de
## ns_Version::ns_add_cam_slate_info
## 27.11.2021


import hou

sel = hou.selectedNodes()

if sel:
	## focus null ##
	focus_null = sel[0].parent().createNode("null", sel[0].name() + "_focus")
	focus_null.setPosition(sel[0].position() + hou.Vector2(-1, -2))
	focus_null.setInput(0, sel[0])
	focus_null.setParms({"controltype" : 3,
						 "orientation" : 3,
						 "constraints_on" : 1,
						 "constraints_path": "constraints",
						 "tz" : -1})
	focus_null.setParms({"geosizey" : sel[0].parm("resy").eval() / sel[0].parm("resx").eval()})
	
	## constraint chop ##
	chop_constr_net = focus_null.createNode("chopnet", "constraints")
	
	chop_worldspace = chop_constr_net.createNode("constraintgetworldspace", "getworldspace")
	chop_worldspace.setParms({
		"obj_path" : "../..",
		"vex_range": 1
		})
	chop_worldspace_sample_rate_parm = chop_worldspace.parm("vex_rate")
	chop_worldspace_sample_rate_parm.setExpression('''$CHOPMOTIONSAMPLES*$FPS''')

	chop_object = chop_constr_net.createNode("constraintobject", "cam")
	chop_object.setParms({
		"obj_path" : "../../../" + sel[0].name(),
		"vex_range": 1
		})
	chop_object_sample_rate_parm = chop_object.parm("vex_rate")
	chop_object_sample_rate_parm.setExpression('''$CHOPMOTIONSAMPLES*$FPS''')

	chop_look_at = chop_constr_net.createNode("constraintlookat", "lookat")
	chop_look_at.setParms({"mode" : 1})
	chop_look_at_sample_rate_parm = chop_look_at.parm("vex_rate")
	chop_look_at_sample_rate_parm.setExpression('''$CHOPMOTIONSAMPLES*$FPS''')
	chop_look_at.setInput(0, chop_worldspace)
	chop_look_at.setInput(1, chop_object)
	chop_look_at.setAudioFlag(True) 

	chop_worldspace.moveToGoodPosition()
	chop_object.moveToGoodPosition()
	chop_look_at.moveToGoodPosition()


	## line obj ##
	line_geo = sel[0].parent().createNode("geo", sel[0].name() + "_line")
	line_geo.setPosition(sel[0].position() + hou.Vector2(1, -1.5))
	line_geo.setInput(0, sel[0])

	add_node = line_geo.createNode("add")
	measure_node = line_geo.createNode("measure")
	measure_node.setPosition(add_node.position() + hou.Vector2(0, -1))
	measure_node.setInput(0, add_node)
	null_out = line_geo.createNode("null", "OUT")
	null_out.setPosition(measure_node.position() + hou.Vector2(0, -1))
	null_out.setInput(0, measure_node)
	null_out.setDisplayFlag(True)

	add_node.setParms({"points" : 2})
	add_node.setParms({"usept0" : 1})
	add_node.setParms({"usept1" : 1})
	add_node.parm("stdswitcher1").set(1)
	add_node.parm("switcher1").set(1)

	## world space pts expressions ##
	pt0x_parm = add_node.parm("pt0x")
	pt0x_parm.setExpression('''{

matrix src_xform = optransform("''' + sel[0].path() + '''");
matrix target_xform_inverted = invert(optransform(opcreator(".")));
matrix final_xform = src_xform * target_xform_inverted;


float rest_value = 0.000000;
float xform_comp = final_xform[3][0];
float result = rest_value + xform_comp;
return result;

}''')
	pt0y_parm = add_node.parm("pt0y")
	pt0y_parm.setExpression('''{

matrix src_xform = optransform("''' + sel[0].path() + '''");
matrix target_xform_inverted = invert(optransform(opcreator(".")));
matrix final_xform = src_xform * target_xform_inverted;


float rest_value = 0.000000;
float xform_comp = final_xform[3][1];
float result = rest_value + xform_comp;
return result;

}''')
	pt0z_parm = add_node.parm("pt0z")
	pt0z_parm.setExpression('''{

matrix src_xform = optransform("''' + sel[0].path() + '''");
matrix target_xform_inverted = invert(optransform(opcreator(".")));
matrix final_xform = src_xform * target_xform_inverted;


float rest_value = 0.000000;
float xform_comp = final_xform[3][2];
float result = rest_value + xform_comp;
return result;

}''')
	pt1x_parm = add_node.parm("pt1x")
	pt1x_parm.setExpression('''{

matrix src_xform = optransform("''' + sel[0].path() + "_focus" + '''");
matrix target_xform_inverted = invert(optransform(opcreator(".")));
matrix final_xform = src_xform * target_xform_inverted;


float rest_value = 0.000000;
float xform_comp = final_xform[3][0];
float result = rest_value + xform_comp;
return result;

}''')
	pt1y_parm = add_node.parm("pt1y")
	pt1y_parm.setExpression('''{

matrix src_xform = optransform("''' + sel[0].path() + "_focus" + '''");
matrix target_xform_inverted = invert(optransform(opcreator(".")));
matrix final_xform = src_xform * target_xform_inverted;


float rest_value = 0.000000;
float xform_comp = final_xform[3][1];
float result = rest_value + xform_comp;
return result;

}''')
	pt1z_parm = add_node.parm("pt1z")
	pt1z_parm.setExpression('''{

matrix src_xform = optransform("''' + sel[0].path() + "_focus" + '''");
matrix target_xform_inverted = invert(optransform(opcreator(".")));
matrix final_xform = src_xform * target_xform_inverted;


float rest_value = 0.000000;
float xform_comp = final_xform[3][2];
float result = rest_value + xform_comp;
return result;

}''')

measure_node.setParms({"measure" : 0,
					   "attribname" : "focus_distance"})


## cam focus distance ##
focus_parm = sel[0].parm("focus")
focus_parm.setExpression('''prim("''' + null_out.path() + '''", 0, "focus_distance", 0)''')

Add Camera Slate

## Niclas Schlapmann - Freelance 3D Technical Artist
## www.enoni.de
## hello@enoni.de
## ns_Version::ns_add_cam_slate_info
## 27.11.2021


import hou

sel = hou.selectedNodes()

if sel:
	if not sel[0].parm(sel[0].path() + "/vcomment"):
		slateParm = hou.StringParmTemplate("vcomment", "Camera Slate", 1)
		slateParm.setTags({"editor": "1"}) ## multi line string ##
		sel[0].addSpareParmTuple(slateParm, ('ns_Version',),create_missing_folders=True)
	sel[0].setParms({
		"vcomment": 
'''CAM: $OS
`floor(chs("focal"))`.`floor(frac(chs("focal"))*100)`mm @ `chs("resx")`x`chs("resy")`
DISTANCE: `floor(chs("focus"))`.`floor(frac(chs("focus"))*100)`
-----------------------------------------------
FRAME: $F
-----------------------------------------------
OPERATOR: $NS_OPERATOR
-----------------------------------------------
TASK: $NS_TASK || SHOT: $NS_SHOT
-----------------------------------------------
$HIPFILE
'''})

Quick Deadline Submission for Redshift ROPs

Redshift Standalone

## Niclas Schlapmann - Freelance 3D Technical Artist
## www.enoni.de
## hello@enoni.de
## ns_deadline_submitter_standalone
## 29.10.2021
##################################### Imports ####################################
import hou
##################################################################################
import os
import sys
import getpass
import time
from time import *
import traceback
import json
###################################### Date ######################################
lt = localtime()
year, month, day, hour ,minute, sec = lt[0:6]
date = str(year)[2:4] + "-" + str(month).zfill(2) + "-" + str(day).zfill(2) + " - " + str(hour).zfill(2) + ":" + str(minute).zfill(2) + ":" + str(sec).zfill(2)
user = getpass.getuser()
################################### Init Data ####################################


renderNode = kwargs.get("node", None)

jobname = hou.getenv("HIPNAME")
pool = "houdini"
secondarypool = "houdini"
pool_rs = "redshift"
secondarypool_rs = "redshift"
comment = "submitted by <" + user + "> " + date
department = "enoni.de"
framelist = str(int(renderNode.evalParm(renderNode.path() + "/f1"))) + "-" + str(int(renderNode.evalParm(renderNode.path() + "/f2")))

## create prop dictionary ##
jobProperties = {
'batch': False,
'jobname': jobname,
'comment': comment,
'department': department,
'pool': pool,
'secondarypool': secondarypool,
'group': 'none',
'priority': 99,
'tasktimeout': 0,
'autotimeout': 0,
'concurrent': 1,
'machinelimit': 0,
'slavelimit': 1,
'limits': '',
'onjobcomplete': 'Nothing',
'jobsuspended': 0,
'shouldprecache': 1,
'isblacklist': 0,
'machinelist': '',
'overrideframes': 1,
'framelist': framelist,
'framespertask': 1,
'bits': '64bit',
'submitscene': 0,
'isframedependent': 0,
'gpuopenclenable': 0,
'gpuspertask': 0,
'gpudevices': '',
'ignoreinputs': 0,
'separateWedgeJobs': 0,
'mantrajob': 0,
'mantrapool': 'none',
'mantrasecondarypool': '',
'mantragroup': 'none',
'mantrapriority': 50,
'mantratasktimeout': 0,
'mantraautotimeout': 0,
'mantraconcurrent': 1,
'mantramachinelimit': 0,
'mantraslavelimit': 1,
'mantralimits': '',
'mantraonjobcomplete': 'Nothing',
'mantraisblacklist': 0,
'mantramachinelist': '',
'mantrathreads': 0,
'mantralocalexport': 0,
'arnoldjob': 1,
'arnoldpool': 'arnold',
'arnoldsecondarypool': 'arnold',
'arnoldgroup': 'none',
'arnoldpriority': 50,
'arnoldtasktimeout': 0,
'arnoldautotimeout': 0,
'arnoldconcurrent': 1,
'arnoldmachinelimit': 0,
'arnoldslavelimit': 1,
'arnoldonjobcomplete': 'Nothing',
'arnoldlimits': '',
'arnoldisblacklist': 0,
'arnoldmachinelist': '',
'arnoldthreads': 0,
'arnoldlocalexport': 1,
'rendermanjob': 0,
'rendermanpool': 'none',
'rendermansecondarypool': '',
'rendermangroup': 'none',
'rendermanpriority': 50,
'rendermantasktimeout': 0,
'rendermanconcurrent': 1,
'rendermanmachinelimit': 0,
'rendermanlimits': '',
'rendermanonjobcomplete': 'Nothing',
'rendermanisblacklist': 0,
'rendermanmachinelist': '',
'rendermanthreads': 0,
'rendermanarguments': '',
'rendermanlocalexport': 0,
'redshiftjob': 1,
'redshiftpool': pool_rs,
'redshiftsecondarypool': secondarypool_rs,
'redshiftgroup': 'none',
'redshiftpriority': 50,
'redshifttasktimeout': 0,
'redshiftautotimeout': 0,
'redshiftconcurrent': 1,
'redshiftmachinelimit': 0,
'redshiftslavelimit': 1,
'redshiftlimits': '',
'redshiftonjobcomplete': 'Nothing',
'redshiftisblacklist': 0,
'redshiftmachinelist': '', 
'redshiftarguments': '', 
'redshiftlocalexport': 1,
'vrayjob': 0, 
'vraypool': 'none',
'vraysecondarypool': '', 
'vraygroup': 'none',
'vraypriority': 50, 
'vraytasktimeout': 0, 
'vrayautotimeout': 0, 
'vrayconcurrent': 1, 
'vraymachinelimit': 0,
'vrayslavelimit': 1,
'vraylimits': '', 
'vrayonjobcomplete': 'Nothing', 
'vrayisblacklist': 0,
'vraymachinelist': '', 
'vraythreads': 0, 
'vrayarguments': '', 
'vraylocalexport': 0, 
'tilesenabled': 0,
'tilesinx': 3, 
'tilesiny': 3,
'tilessingleframeenabled': 1, 
'tilessingleframe': 1,
'jigsawenabled': 1,
'jigsawregioncount': 0,
'jigsawregions': [],
'submitdependentassembly': 1,
'backgroundoption': 'Blank Image',
'backgroundimage': '',
'erroronmissingtiles': '1', 
'erroronmissingbackground': '0',
'cleanuptiles': '1'
}

## set to archive rendering ##
renderNode.setParms({"RS_archive_enable" : 1})  

## submit to Deadline ##
flag = 0

## imports and sys pathes for deadline ##
try:
    from CallDeadlineCommand import CallDeadlineCommand
except ImportError:
    path = ""
    print( "The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.\n" )
    hou.ui.displayMessage( "The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.", title="Submit Houdini To Deadline" )
else:
    path = CallDeadlineCommand([ "-GetRepositoryPath", "submission/Houdini/Main" ]).strip()

if path:
    path = path.replace( "\\", "/" )
    
    # Add the path to the system path
    if path not in sys.path:
        print("Appending \"" + path + "\" to system path to import SubmitHoudiniToDeadline module")
        sys.path.append( path )
    else:
        pass

    # Import the script and call the main() function
    try:
        import SubmitHoudiniToDeadline
    except:
        print( traceback.format_exc() )
        print( "The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository." )
else:
    print( "The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository." )

## get deadline info ##
print( "Grabbing submitter info..." )
try:
    output = json.loads( CallDeadlineCommand( [ "-prettyJSON", "-GetSubmissionInfo", "Pools", "Groups", "MaxPriority", "TaskLimit", "UserHomeDir", "RepoDir:submission/Houdini/Main", "RepoDir:submission/Integration/Main", "RepoDirNoCustom:draft", "RepoDirNoCustom:submission/Jigsaw", ] ) )
except:
    print( "Unable to get submitter info from Deadline:\n\n" + traceback.format_exc() )
    raise

if output[ "ok" ]:
    submissionInfo = output[ "result" ]
    hou.putenv("Deadline_Submission_Info", json.dumps( submissionInfo ) )
else:
    print( "DeadlineCommand returned a bad result and was unable to grab the submitter info.\n\n" + output[ "result" ] )
    raise Exception( output[ "result" ] )

## submit render job ##
try:
    import SubmitHoudiniToDeadlineFunctions as SHTDFunctions
    flag = 1
except Exception as e:
    print(e)
    hou.ui.displayMessage("Library import failure. Make sure you have a proper Deadline installation.")

if flag:
    try:
        jobIds = SHTDFunctions.SubmitRenderJob( renderNode, jobProperties, "")
    except Exception as e:
        print(e)
        hou.ui.displayMessage("Can`t submitting to Deadline Repository.")

# un-set archive rendering ##
renderNode.setParms({"RS_archive_enable" : 0})  

Hython

## Niclas Schlapmann - Freelance 3D Technical Artist
## www.enoni.de
## hello@enoni.de
## ns_deadline_submitter_hython
## 29.10.2021
##################################### Imports ####################################
import hou
##################################################################################
import os
import sys
import getpass
import time
from time import *
import traceback
import json
###################################### Date ######################################
lt = localtime()
year, month, day, hour ,minute, sec = lt[0:6]
date = str(year)[2:4] + "-" + str(month).zfill(2) + "-" + str(day).zfill(2) + " - " + str(hour).zfill(2) + ":" + str(minute).zfill(2) + ":" + str(sec).zfill(2)
user = getpass.getuser()
################################### Init Data ####################################


renderNode = kwargs.get("node", None)

jobname = hou.getenv("HIPNAME")
pool = "houdini"
secondarypool = "houdini"
pool_rs = "redshift"
secondarypool_rs = "redshift"
comment = "submitted by <" + user + "> " + date
department = "enoni.de"
framelist = str(int(renderNode.evalParm(renderNode.path() + "/f1"))) + "-" + str(int(renderNode.evalParm(renderNode.path() + "/f2")))


## create prop dictionary ##
jobProperties = {
'batch': False,
'jobname': jobname,
'comment': comment,
'department': department,
'pool': pool,
'secondarypool': secondarypool,
'group': 'none',
'priority': 99,
'tasktimeout': 0,
'autotimeout': 0,
'concurrent': 1,
'machinelimit': 0,
'slavelimit': 1,
'limits': '',
'onjobcomplete': 'Nothing',
'jobsuspended': 0,
'shouldprecache': 1,
'isblacklist': 0,
'machinelist': '',
'overrideframes': 1,
'framelist': framelist,
'framespertask': 1,
'bits': '64bit',
'submitscene': 0,
'isframedependent': 0,
'gpuopenclenable': 0,
'gpuspertask': 0,
'gpudevices': '',
'ignoreinputs': 0,
'separateWedgeJobs': 0,
'mantrajob': 0,
'mantrapool': 'none',
'mantrasecondarypool': '',
'mantragroup': 'none',
'mantrapriority': 50,
'mantratasktimeout': 0,
'mantraautotimeout': 0,
'mantraconcurrent': 1,
'mantramachinelimit': 0,
'mantraslavelimit': 1,
'mantralimits': '',
'mantraonjobcomplete': 'Nothing',
'mantraisblacklist': 0,
'mantramachinelist': '',
'mantrathreads': 0,
'mantralocalexport': 0,
'arnoldjob': 1,
'arnoldpool': 'arnold',
'arnoldsecondarypool': 'arnold',
'arnoldgroup': 'none',
'arnoldpriority': 50,
'arnoldtasktimeout': 0,
'arnoldautotimeout': 0,
'arnoldconcurrent': 1,
'arnoldmachinelimit': 0,
'arnoldslavelimit': 1,
'arnoldonjobcomplete': 'Nothing',
'arnoldlimits': '',
'arnoldisblacklist': 0,
'arnoldmachinelist': '',
'arnoldthreads': 0,
'arnoldlocalexport': 1,
'rendermanjob': 0,
'rendermanpool': 'none',
'rendermansecondarypool': '',
'rendermangroup': 'none',
'rendermanpriority': 50,
'rendermantasktimeout': 0,
'rendermanconcurrent': 1,
'rendermanmachinelimit': 0,
'rendermanlimits': '',
'rendermanonjobcomplete': 'Nothing',
'rendermanisblacklist': 0,
'rendermanmachinelist': '',
'rendermanthreads': 0,
'rendermanarguments': '',
'rendermanlocalexport': 0,
'redshiftjob': 0,
'redshiftpool': pool_rs,
'redshiftsecondarypool': secondarypool_rs,
'redshiftgroup': 'none',
'redshiftpriority': 50,
'redshifttasktimeout': 0,
'redshiftautotimeout': 0,
'redshiftconcurrent': 1,
'redshiftmachinelimit': 0,
'redshiftslavelimit': 1,
'redshiftlimits': '',
'redshiftonjobcomplete': 'Nothing',
'redshiftisblacklist': 0,
'redshiftmachinelist': '', 
'redshiftarguments': '', 
'redshiftlocalexport': 1,
'vrayjob': 0, 
'vraypool': 'none',
'vraysecondarypool': '', 
'vraygroup': 'none',
'vraypriority': 50, 
'vraytasktimeout': 0, 
'vrayautotimeout': 0, 
'vrayconcurrent': 1, 
'vraymachinelimit': 0,
'vrayslavelimit': 1,
'vraylimits': '', 
'vrayonjobcomplete': 'Nothing', 
'vrayisblacklist': 0,
'vraymachinelist': '', 
'vraythreads': 0, 
'vrayarguments': '', 
'vraylocalexport': 0, 
'tilesenabled': 0,
'tilesinx': 3, 
'tilesiny': 3,
'tilessingleframeenabled': 1, 
'tilessingleframe': 1,
'jigsawenabled': 1,
'jigsawregioncount': 0,
'jigsawregions': [],
'submitdependentassembly': 1,
'backgroundoption': 'Blank Image',
'backgroundimage': '',
'erroronmissingtiles': '1', 
'erroronmissingbackground': '0',
'cleanuptiles': '1'
}

## un-set archive rendering ##
renderNode.setParms({"RS_archive_enable" : 0}) 


## submit to Deadline ##
flag = 0

## imports and sys pathes for deadline ##
try:
    from CallDeadlineCommand import CallDeadlineCommand
except ImportError:
    path = ""
    print( "The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.\n" )
    hou.ui.displayMessage( "The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.", title="Submit Houdini To Deadline" )
else:
    path = CallDeadlineCommand([ "-GetRepositoryPath", "submission/Houdini/Main" ]).strip()

if path:
    path = path.replace( "\\", "/" )
    
    # Add the path to the system path
    if path not in sys.path:
        print("Appending \"" + path + "\" to system path to import SubmitHoudiniToDeadline module")
        sys.path.append( path )
    else:
        pass

    # Import the script and call the main() function
    try:
        import SubmitHoudiniToDeadline
    except:
        print( traceback.format_exc() )
        print( "The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository." )
else:
    print( "The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository." )

## get deadline info ##
print( "Grabbing submitter info..." )
try:
    output = json.loads( CallDeadlineCommand( [ "-prettyJSON", "-GetSubmissionInfo", "Pools", "Groups", "MaxPriority", "TaskLimit", "UserHomeDir", "RepoDir:submission/Houdini/Main", "RepoDir:submission/Integration/Main", "RepoDirNoCustom:draft", "RepoDirNoCustom:submission/Jigsaw", ] ) )
except:
    print( "Unable to get submitter info from Deadline:\n\n" + traceback.format_exc() )
    raise

if output[ "ok" ]:
    submissionInfo = output[ "result" ]
    hou.putenv("Deadline_Submission_Info", json.dumps( submissionInfo ) )
else:
    print( "DeadlineCommand returned a bad result and was unable to grab the submitter info.\n\n" + output[ "result" ] )
    raise Exception( output[ "result" ] )

## submit render job ##
try:
    import SubmitHoudiniToDeadlineFunctions as SHTDFunctions
    flag = 1
except Exception as e:
    print(e)
    hou.ui.displayMessage("Library import failure. Make sure you have a proper Deadline installation.")

if flag:
    try:
        SHTDFunctions.SaveScene()
        jobIds = SHTDFunctions.SubmitRenderJob( renderNode, jobProperties, "")
    except Exception as e:
        print(e)
        hou.ui.displayMessage("Can`t submitting to Deadline Repository.")

Houdini menu for Redshift ROPs

<?xml version="1.0" encoding="UTF-8"?>

<menuDocument>
    <menu>
      <separatorItem/>
      <subMenu id="Deadline Scripts">
          <label>[ ns_Version ]</label>
            <context>
            </context>
          
            <scriptItem id="ns_deadline_submitter_standalone">
            <expression>
  node = kwargs.get("node", None)
  if node is None:
      return False
  return node.type().name() == "Redshift_ROP"
              </expression>
              <label>Deadline Submitter RS-Standalone</label>
              <scriptPath>$HOUDINI_USER_PREF_DIR/scripts/ns_deadline_submitter_standalone.py</scriptPath>
            </scriptItem>

            <scriptItem id="ns_deadline_submitter_hython">
            <expression>
  node = kwargs.get("node", None)
  if node is None:
      return False
  return node.type().name() == "Redshift_ROP"
              </expression>
              <label>Deadline Submitter Hython</label>
              <scriptPath>$HOUDINI_USER_PREF_DIR/scripts/ns_deadline_submitter_hython.py</scriptPath>
            </scriptItem>

      </subMenu>    
    </menu>
</menuDocument>

Set your preferred path to the scripts, for triggering the Houdini menu. Here it linked to C:\Users\<USER>\Documents\houdiniXX.X\scripts\

Put the OPmenu.xml to C:\Users\\Documents\houdiniXX.X\ or a other place Houdini can recognize it.


Redshift Texture Preconverting

– Converts referenced texture files to .rstexbins, inside SideFX Houdini.
(It also does a check if referenced texture files physically exist.)
– Supports file sequences.
– Supports UDIM files.

https://gitlab.com/e_noni/rs_texture_converter


Nodetype specific menu items

Custom OPmenu.xml:
The <expression></expression> block let you separate the right menu items/scripts per nodetype. In this case the menu items appears just for Redshift_ROP`s.

<?xml version="1.0" encoding="UTF-8"?>

<menuDocument>
    <menu>
      <separatorItem/>
      <subMenu id="ns_version_menu">
          <label>[ ns_Version ]</label>
            <context>
            </context>
            <scriptItem id="ns_copy_node_path">
              <label>Copy Node Path</label>
              <context>
              </context>
                <scriptCode>
<![CDATA[
from PySide2 import QtWidgets
nodePath = kwargs['node'].path()
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(nodePath)
]]>
              </scriptCode>
            </scriptItem>
          <separatorItem/>
          <scriptItem id="ns_copy_rs_render_location">
              <expression>
node = kwargs.get("node", None)
if node is None:
    return False
return node.type().name() == "Redshift_ROP"
                </expression>
              <label>Open RS Render Location</label>
              <scriptCode>
<![CDATA[
import os
import sys
import subprocess
try:
    node = kwargs['node']
    renderOutput = node.parm(node.path() + "/" + "RS_outputFileNamePrefix").eval()
    renderOutput = renderOutput.replace("%AOV%", "beauty")                               ## Redshift specific
    locationParts = renderOutput.split("/")
    locationParts.pop()
    ns_path = ""
    for part in locationParts:
        ns_path = ns_path + part + "/"

    if ns_path[-1] == "/":
        ns_path = ns_path.rstrip("/")

    if os.path.exists(ns_path.replace("/", os.sep)):
        if sys.platform == "darwin":  ## macOS
            subprocess.Popen(["open", "--", ns_path.replace("/", os.sep)])
        if sys.platform == "linux2":  ## Linux
            subprocess.Popen(["xdg-open", "--", ns_path.replace("/", os.sep)])
        if sys.platform == "win32":   ## Windows
            subprocess.Popen(["explorer", ns_path.replace("/", os.sep)])
    else:
        hou.ui.displayMessage("No valid render path found.")
except Exception as e:
    #print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
    hou.ui.displayMessage("No valid render/structure found.")
]]>
              </scriptCode>
            </scriptItem>
          <scriptItem id="ns_open_rs_render_in_djview">
              <expression>
node = kwargs.get("node", None)
if node is None:
    return False
return node.type().name() == "Redshift_ROP"
                </expression>
              <label>Open RS beauty in djv Player</label>
              <scriptCode>
<![CDATA[
import os
import sys
import subprocess

djv_path = "C:\\Program Files\\djv-1.1.0-Windows-64\\bin\\djv_view.exe"

try:
    node = kwargs['node']
    renderOutput = node.parm(node.path() + "/" + "RS_outputFileNamePrefix").eval()
    renderOutput = renderOutput.replace("%AOV%", "beauty")                               ## Redshift specific
    ns_path = renderOutput

    if ns_path[-1] == "/":
        ns_path = ns_path.rstrip("/")


    if os.path.exists(ns_path.replace("/", os.sep)):
        if sys.platform == "darwin":  ## macOS
            pass
        if sys.platform == "linux2":  ## Linux
            pass
        if sys.platform == "win32":   ## Windows
            subprocess.Popen([djv_path, ns_path.replace("\\", os.sep)])
    else:
        hou.ui.displayMessage("No valid render path found.")
except Exception as e:
    #print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
    hou.ui.displayMessage("No valid render/structure found.")
]]>
              </scriptCode>
            </scriptItem>
      </subMenu>    
    </menu>
</menuDocument>

Automatic Custom RS AOV’s

def ns_customAOVs(self):
    rop = hou.node(ropItem)

    # Gather all redshift::StoreColorToAOV ####################################################
    storeColorToAOV_Intstances = []

    for node_type in hou.vopNodeTypeCategory().nodeTypes().values():

        components = node_type.nameComponents()

        if components[2] == ("StoreColorToAOV"):
            storeColorToAOV_Intstances.extend(node_type.instances())

    if storeColorToAOV_Intstances.__len__() != 0:
        for storeColorToAOV in storeColorToAOV_Intstances:
        # Check Inputs ########################################################################
            for i in range(0, 7):
                parm_value_aov_name = hou.parm(storeColorToAOV.path() + "/aov_name" + str(i)).eval()
                if parm_value_aov_name != "":
                        # Add Custom AOVs to ROP ##################################################
                        aovCount = hou.parm(ropItem + "/RS_aov").eval()
                        aovsSuffixes = []
                        for i in xrange(aovCount):
                            aovsSuffixes.append(hou.parm(ropItem + "/RS_aovSuffix_" + str(i+1)).eval())
                        if parm_value_aov_name not in aovsSuffixes:
                            rop.setParms({"RS_aov": aovCount + 1})
                            rop.setParms({"RS_aovID_" + str(aovCount + 1): 38})       # Custom
                            rop.setParms({"RS_aovCustomDT_" + str(aovCount + 1): 0})  # RGB
                            rop.setParms({"RS_aovSuffix_" + str(aovCount + 1): parm_value_aov_name.lower().replace(" ", "_")}) #Name
    ###########################################################################################


    # Gather all redshift::StoreIntegerToAOV ####################################################
    storeIntegerToAOV_Intstances = []

    for node_type in hou.vopNodeTypeCategory().nodeTypes().values():

        components = node_type.nameComponents()

        if components[2] == ("StoreIntegerToAOV"):
            storeIntegerToAOV_Intstances.extend(node_type.instances())

    if storeIntegerToAOV_Intstances.__len__() != 0:
        for storeIntegerToAOV in storeIntegerToAOV_Intstances:
            # Check Inputs ########################################################################
            for i in range(0, 7):
                parm_value_aov_name = hou.parm(storeIntegerToAOV.path() + "/aov_name" + str(i)).eval()
                if parm_value_aov_name != "":
                    # Add Custom AOVs to ROP ##################################################
                    aovCount = hou.parm(ropItem + "/RS_aov").eval()
                    aovsSuffixes = []
                    for i in xrange(aovCount):
                        aovsSuffixes.append(hou.parm(ropItem + "/RS_aovSuffix_" + str(i+1)).eval())
                    if parm_value_aov_name not in aovsSuffixes:
                        rop.setParms({"RS_aov": aovCount + 1})
                        rop.setParms({"RS_aovID_" + str(aovCount + 1): 38})       # Custom
                        rop.setParms({"RS_aovCustomDT_" + str(aovCount + 1): 2})  # POINT?
                        rop.setParms({"RS_aovSuffix_" + str(aovCount + 1): parm_value_aov_name.lower().replace(" ", "_")})  # Name
    ###########################################################################################


    # Gather all redshift::StoreScalarToAOV ####################################################
    storeScalarToAOV_Intstances = []

    for node_type in hou.vopNodeTypeCategory().nodeTypes().values():

        components = node_type.nameComponents()

        if components[2] == ("StoreScalarToAOV"):
            storeScalarToAOV_Intstances.extend(node_type.instances())

    if storeScalarToAOV_Intstances.__len__() != 0:
        for storeScalarToAOV in storeScalarToAOV_Intstances:
            # Check Inputs ########################################################################
            for i in range(0, 7):
                parm_value_aov_name = hou.parm(storeScalarToAOV.path() + "/aov_name" + str(i)).eval()
                if parm_value_aov_name != "":
                    # Add Custom AOVs to ROP ##################################################
                    aovCount = hou.parm(ropItem + "/RS_aov").eval()
                    aovsSuffixes = []
                    for i in xrange(aovCount):
                        aovsSuffixes.append(hou.parm(ropItem + "/RS_aovSuffix_" + str(i+1)).eval())
                    if parm_value_aov_name not in aovsSuffixes:
                        rop.setParms({"RS_aov": aovCount + 1})
                        rop.setParms({"RS_aovID_" + str(aovCount + 1): 38})       # Custom
                        rop.setParms({"RS_aovCustomDT_" + str(aovCount + 1): 3})  # SCALAR
                        rop.setParms({"RS_aovSuffix_" + str(aovCount + 1): parm_value_aov_name.lower().replace(" ", "_")})  # Name
    ###########################################################################################

Match Translations Scripts

These scripts can nothing you cant do with the Houdini “Scene Data…” -Editor (came with H17), but the access its quicker with a script – like in the good old XSI.

Match Rotation shelf script:

import hou

sel = hou.selectedNodes()

if len(sel) == 2:
    obj_source = sel[1]
    obj_target = sel[0]
    
    string_RX_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "RX");
return result;
}'''

    string_RY_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "RY");
return result;
}'''

    string_RZ_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "RZ");
return result;
}'''
    obj_target.parm("rx").setExpression(string_RX_EULER)
    obj_target.parm("ry").setExpression(string_RY_EULER)
    obj_target.parm("rz").setExpression(string_RZ_EULER)
    if hou.ui.displayMessage("Keep it as a Constraint?" , buttons=("Yes", "No")) == 0:
        pass
    else:
        obj_target.parm("rx").deleteAllKeyframes()
        obj_target.parm("ry").deleteAllKeyframes()
        obj_target.parm("rz").deleteAllKeyframes()
       
else:
    hou.ui.displayMessage("Select first your goal than a source OBJ!")

Match Translation shelf script:

import hou

sel = hou.selectedNodes()

if len(sel) == 2:
    obj_source = sel[1]
    obj_target = sel[0]
    
    string_TX_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "TX");
return result;
}'''

    string_TY_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "TY");
return result;
}'''

    string_TZ_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "TZ");
return result;
}'''
    obj_target.parm("tx").setExpression(string_TX_EULER)
    obj_target.parm("ty").setExpression(string_TY_EULER)
    obj_target.parm("tz").setExpression(string_TZ_EULER)
    if hou.ui.displayMessage("Keep it as a Constraint?" , buttons=("Yes", "No")) == 0:
        pass
    else:
        obj_target.parm("tx").deleteAllKeyframes()
        obj_target.parm("ty").deleteAllKeyframes()
        obj_target.parm("tz").deleteAllKeyframes()
       
else:
    hou.ui.displayMessage("Select first your goal than a source OBJ!")

Match Scaling shelf script:

import hou

sel = hou.selectedNodes()

if len(sel) == 2:
    obj_source = sel[1]
    obj_target = sel[0]
    
    string_SX_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "SX");
return result;
}'''

    string_SY_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "SY");
return result;
}'''

    string_SZ_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "SZ");
return result;
}'''
    obj_target.parm("sx").setExpression(string_SX_EULER)
    obj_target.parm("sy").setExpression(string_SY_EULER)
    obj_target.parm("sz").setExpression(string_SZ_EULER)
    if hou.ui.displayMessage("Keep it as a Constraint?" , buttons=("Yes", "No")) == 0:
        pass
    else:
        obj_target.parm("sx").deleteAllKeyframes()
        obj_target.parm("sy").deleteAllKeyframes()
        obj_target.parm("sz").deleteAllKeyframes()
       
else:
    hou.ui.displayMessage("Select first your goal than a source OBJ!")

Match all Transforms:

import hou

sel = hou.selectedNodes()

if len(sel) == 2:
    obj_source = sel[1]
    obj_target = sel[0]
    
    string_TX_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "TX");
return result;
}'''

    string_TY_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "TY");
return result;
}'''

    string_TZ_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "TZ");
return result;
}'''

    string_RX_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "RX");
return result;
}'''

    string_RY_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "RY");
return result;
}'''

    string_RZ_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "RZ");
return result;
}'''

    string_SX_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "SX");
return result;
}'''

    string_SY_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "SY");
return result;
}'''

    string_SZ_EULER = '''{

matrix src_xform = optransform("''' +  obj_source.path() + '''");
matrix target_xform_inverted = invert(optransform(opinputpath(".", 0)));
matrix final_xform = src_xform * target_xform_inverted;


matrix rest_xform = identity(4);
matrix self_xform = rest_xform * final_xform;

float result = explodematrixpr(
        self_xform, 
        vector3(ch("./px"), ch("./py"), ch("./pz")),
        vector3(ch("./prx"), ch("./pry"), ch("./prz")),
        chs("./xOrd"), chs("./rOrd"), "SZ");
return result;
}'''
    
    obj_target.parm("tx").setExpression(string_TX_EULER)
    obj_target.parm("ty").setExpression(string_TY_EULER)
    obj_target.parm("tz").setExpression(string_TZ_EULER)
    obj_target.parm("rx").setExpression(string_RX_EULER)
    obj_target.parm("ry").setExpression(string_RY_EULER)
    obj_target.parm("rz").setExpression(string_RZ_EULER)
    obj_target.parm("sx").setExpression(string_SX_EULER)
    obj_target.parm("sy").setExpression(string_SY_EULER)
    obj_target.parm("sz").setExpression(string_SZ_EULER)
   
    if hou.ui.displayMessage("Keep it as a Constraint?" , buttons=("Yes", "No")) == 0:
        pass
    else:
        obj_target.parm("tx").deleteAllKeyframes()
        obj_target.parm("ty").deleteAllKeyframes()
        obj_target.parm("tz").deleteAllKeyframes()
        obj_target.parm("rx").deleteAllKeyframes()
        obj_target.parm("ry").deleteAllKeyframes()
        obj_target.parm("rz").deleteAllKeyframes()
        obj_target.parm("sx").deleteAllKeyframes()
        obj_target.parm("sy").deleteAllKeyframes()
        obj_target.parm("sz").deleteAllKeyframes()
       
else:
    hou.ui.displayMessage("Select first your goal than a source OBJ!")