Jump to content

Maya Python to Houdini Python


Recommended Posts


I'm trying to port some maya python rigging scripts to houdini python scripts to post it on my rigging blog as examples, shameless plug follows: Please visit my blog at


If all goes well maybe start some sort of transition guide or repository about python in maya and houdini.

I know that sort of thing will help the houdini community.

Anyway I'm kind of lost at the first attempt to get a selection out of houdini.

Here is maya code


which will return a list of all the objects selected, transforms, shaders, custom nodes you name it.

I was advised that I could do something like this in houdini

loop trough all children of /obj and test for selection with hou.Node.isSelected().

This works but I think this method has some big issues. First is the loop, if I have thousands of objects it might take a while . The bigger issue to me id the fact that this seems to go backwards, instead of saying give me the selection it says give me all the objects then take the selected out.

Could someone (Luke, maybe) please point me to a better, simpler way of getting my selection from houdini, wink wink, see the simplicity of mayas



ps. I'm sure tons of question will follow because here is the script I'm trying to port:

import maya.cmds as mc

# Build the UI
def startUI():
	print 'starting the UI'

# end of UI

def createJoints(nrJoints, boneLength):
	# create a number of joints, 'nrJoints' along the Y axis
	# the joints will be spread apart by the 'boneLength' value
	print 'Create Joints'
	jointsList = []
	# create the root joint then orient it to point in +Y
	jointName = mc.joint(position=(0,0,0), name='spineJoint1', orientation=(-90,0,90))
	for i in range(2, nrJoints):
		jointName = mc.joint(position=(boneLength,0,0), relative=True, name='spineJoint' + str(i))
	return jointsList

def addBoneLengthToJoints(jointsList):
	# add an 'originalLength' attribute to every joint that follows after root
	# that stores the original length. Since the translate X is taken as original
	# length the joints must be oriented along X for custon skeletons
	print 'Add original bone length attribute to joints'
	selection = mc.ls(selection=True)
	# take the root joint out of the joint list because there is no bone length
	# on the root joint
	stretchJointsList = jointsList[1:]
	for i in stretchJointsList:
		mc.select(i, replace=True)
		tempXvalue = mc.getAttr(i + '.translateX')
		mc.addAttr(longName='originalLength', attributeType='double', defaultValue=tempXvalue)
		mc.setAttr(i + '.originalLength', lock=True)
	return stretchJointsList

def createSpineHierarchy():
	# create a spine hierarchy where the joints are unde the skeleton group,
	# the controls under controls group and everything else is under the helpers group
	print 'Create spine hierarchy'
	spineRig = mc.createNode('transform', name='spineRig')
	spineSkeleton = mc.createNode('transform', name='spineSkeleton', parent=spineRig)
	spineControls = mc.createNode('transform', name='spineControls', parent=spineRig)
	spineHelpers = mc.createNode('transform', name='spineHelpers', parent=spineRig)
	mc.setAttr('spineSkeleton.template', 1)
	spineHierarchyList = [spineRig, spineSkeleton, spineControls, spineHelpers]
	return spineHierarchyList

def buildStretchySpine(spineJointsList, stretchJointsList):
	# create the stretchy spine setup based on the provided diagram
	print 'Build stretchy spine'
	# adding spline IK solver to the bones
	print 'Add IK solver'
	mc.ikHandle(name='spineIkHandle', startJoint=spineJointsList[0], endEffector=spineJointsList[-1], solver='ikSplineSolver', numSpans=2)
	spineSolverCurveShape = mc.ikHandle('spineIkHandle', query=True, curve=True)
	spineSolverCurve = mc.listRelatives(spineSolverCurveShape, parent=True)
	mc.setAttr(spineSolverCurve[0] + '.template', 1)
	spineCurve = 'spineCurve'
	mc.rename(spineSolverCurve[0], spineCurve)
	mc.parent(spineCurve, 'spineHelpers')
	# create the curveInfo node that will compute the arclen of the spine
	print "Add cruveInfo node that computes the spine's length"
	spineCurveInfoNode = mc.arclen(spineCurve, constructionHistory=True)
	spineLengthNode = 'spineComputeCurveLength'
	mc.rename(spineCurveInfoNode, spineLengthNode)
	# store the original curve length in the 'originalLength' attribute
	print "Add spine curve original length attribute"
	spineCurveOrigLength = mc.getAttr(spineLengthNode + '.arcLength')
	mc.select(spineCurve, replace=True)
	mc.addAttr(longName='originalLength', attributeType='double', defaultValue=spineCurveOrigLength)
	mc.setAttr(spineCurve + '.originalLength', lock=True)
	mc.parent('spineIkHandle', 'spineHelpers')
	mc.setAttr('spineIkHandle.visibility', 0)
	# create a locator which will be a control for spine options, stretchiness
	# we default this value to .2 which means that the spine will stretch and
	# compress with .2 of the difference between the original length and the
	# current length
	print 'Create a control for the spine options'
	spineOptions = mc.createNode('transform', name='spineOptionsCtrl', parent='spineControls')
	mc.createNode('locator', name='spineOptionsCtrlShape', parent=spineOptions)
	mc.setAttr(spineOptions + '.translateX', 4)
	mc.parentConstraint('spineJoint1', spineOptions, maintainOffset=True)
	print 'Add spine options attributes'
	mc.select(spineOptions, replace=True)
	mc.addAttr(longName='spineStretchiness', attributeType='double', minValue=0, maxValue=1, defaultValue=0.2, keyable=True)
	# create the stretchiness network which mainly consists of adding shading nodes
	# and connect attributes
	print 'Add stretchiness network'
	# create a plusMinusAverage node. this node will take the current length
	# and subtract the original length to find out with how much the curve changed 
	spineSubtractOriginalCurveLengthNode = mc.shadingNode('plusMinusAverage', asUtility=True, name='spineSubtractOriginalCurveLength')
	mc.connectAttr(spineLengthNode + '.arcLength', spineSubtractOriginalCurveLengthNode + '.input1D[0]', force=True)
	mc.connectAttr(spineCurve + '.originalLength', spineSubtractOriginalCurveLengthNode + '.input1D[1]', force=True)
	mc.setAttr(spineSubtractOriginalCurveLengthNode + '.operation', 2)
	# create a multiplyDivide node that multiplies the result from the subtract node with
	# the spine stretchiness to find out what's the length we care about
	spineMultiplyStretchinessNode = mc.shadingNode('multiplyDivide', asUtility=True, name='spineMultiplyStretchiness')
	mc.connectAttr(spineSubtractOriginalCurveLengthNode + '.output1D', spineMultiplyStretchinessNode + '.input1X', force=True)
	mc.connectAttr(spineOptions + '.spineStretchiness', spineMultiplyStretchinessNode + '.input2X', force=True)
	# create a plusMinusAverage node and we add back the original curve to find out the
	# total length of the curve we have to stretch the bones
	spineAddOriginalCurveLengthNode = mc.shadingNode('plusMinusAverage', asUtility=True, name='spineAddOriginalCurveLength')
	mc.connectAttr(spineMultiplyStretchinessNode + '.outputX', spineAddOriginalCurveLengthNode + '.input1D[0]', force=True)
	mc.connectAttr(spineCurve + '.originalLength', spineAddOriginalCurveLengthNode + '.input1D[1]', force=True)
	# create a multiplyDivide node and divide the total length we care about with the original
	# length to find out the ratio.
	spineDivideByOriginalCurveLengthNode = mc.shadingNode('multiplyDivide', asUtility=True, name='spineDivideByOriginalCurveLength')
	mc.connectAttr(spineAddOriginalCurveLengthNode + '.output1D', spineDivideByOriginalCurveLengthNode + '.input1X', force=True)
	mc.connectAttr(spineCurve + '.originalLength', spineDivideByOriginalCurveLengthNode + '.input2X', force=True)
	mc.setAttr(spineDivideByOriginalCurveLengthNode + '.operation', 2)
	# once we know the ration we have to create a multiplyDivide node for each joint
	# and multyply the original bone length with the computed ratio
	print "Change the bone Tx value to accomodate stretchiness"
	for i in stretchJointsList:
		spineMultiplyByOriginalBoneLengthNode = mc.shadingNode('multiplyDivide', asUtility=True, name='spine' + i + 'MultiplyOriginalLength')
		mc.connectAttr(spineDivideByOriginalCurveLengthNode + '.outputX', spineMultiplyByOriginalBoneLengthNode + '.input1X', force=True)
		mc.connectAttr(i + '.originalLength', spineMultiplyByOriginalBoneLengthNode + '.input2X', force=True)
		mc.connectAttr(spineMultiplyByOriginalBoneLengthNode + '.outputX', i + '.translateX', force=True)

def createClusters():
	print 'Create clusters'
	for i in range(0,5):
		cv = 'spineCurve.cv[%d]' %i
		cluster = mc.cluster(cv, name='spineCurveCluster' + str(i))
		mc.parent(cluster, 'spineHelpers')

def main(createJointsOption, nrJoints, boneLength):
	if createJointsOption==1:
		spineJointsList = createJoints(nrJoints, 2)
	spineHierarchyList = createSpineHierarchy()
	mc.parent(spineJointsList[0], spineHierarchyList[1])
	stretchJointsList = addBoneLengthToJoints(spineJointsList)
	buildStretchySpine(spineJointsList, stretchJointsList)

main(1, 20, 2)

Edited by calin_casian
Link to comment
Share on other sites

Thank you Calin, porting Maya rigs can be extremely helpful for us and for a number of reasons actually (not only its usage).

As to getting selection, surly someone from SESI can give better advice, but maybe you can use Shelf Tools. In its modules there are many useful stuff (not sure if all can be used in custom scripts outside shelf context).

Here for example (from Combine tool):

# Ask for the objects to combine
selected_objects = list(scene_viewer.selectObjects(
	prompt = toolutils.selectionPrompt(hou.objNodeTypeCategory()),
	allow_multisel = True,
	allowed_types = ("geo",)))

Your work is really helpful here, thanks again!



PS Could you (or admins) add code tags to first post? thanks.

Edited by SYmek
Link to comment
Share on other sites

Thanks Simon, I'll look into the list() thing, seems the right way to do it although shading nodes don't have representation in the viewer but maybe there's something else.

If you're interested to see some rig bits in both packages please check my blog regularly or subscribe to the RSS.

I'll try and post here a link every time I'll post some scenes and scripts.



Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...