Category Archives: animation

Joint Based face Rig Demo

I Just dug this out of the archives.  It’s a demo of a rig I built while doing a CG society course early this year, it uses a LOT of joints, all driven by set driven keys.  It’s the first time I’ve digressed from using Blendshapes in a rig setup and it does work very nicely.

A lot of credit must go to Judd Simantov who ran the course, and produced a wonderfully appealing model for the students to work with.

The video is 3 minutes long and is mostly just me doodling and making faces for fun, so I give you a Ace Frehley as it’s soundtrack!

another rig tool….here’s a stretchy spine creator.

Okay Stretchy spine tool?

….lots of them out there right?

Well there might be, but, this one does volume preservation as well, and you can control it on a joint by joint basis, all node based its pretty quick to setup too. here’s a short vid to demonstrate …

Okay, so the actually spine rig setup is a bit barebones at the mo, it would be nice to include some variations on the rig control setup, but the structure is there, and I hope to make the time to really push this once I get down to building my rig tool

…Don’t expect too much, I’m just a noob!

here’s a link to it on Creative Crash.

http://www.creativecrash.com/maya/downloads/character-rigs/c/nk_spine_creator

make stretchy IK

nk_makeStretchyIK_RPSC_GUI

As promised…

Here’s a link to the stretchy IK tool I’ve just created, I hope people will find some good uses for it.  It creates a stretchy ik chain for both RP and SC solvers and is not limited by joint numbers.  It is also node based, so no expressions, so faster for animators…one hopes!

I’ve kept it pretty basic, so if anyone wanted to go in and meddle with finished result they can.  I tend to try and use my tools to get my as far to the end goal as possible, then I just hack at the rig until it does what I want….

I could add a  feature to switch the stretching and on and off….

….but I wont!

http://www.creativecrash.com/maya/script/nk_makestretchyik_rpsc

….Off to watch Monstors University tonight…. Here’s nk_makeStretchyIK_RPSC

Happy first Wednesday of the week folks!

Here’s a Python script that creates a stretchy joint chain, I’ll aim to incorporate this into a more user friendly GUI soon…

It works on RotatePlane and Single Chain solvers. Enjoy.


import maya.cmds as mc

def nk_makeStretchyIK_RPSC(ikH = "ikHandle1", prefix="prefix", StretchCondition=True):
    '''
    ABOUT
    --------
    Makes an ik handle stretchy.  works on RP or SC solvers.

    REQUIRES
    --------
    nk_measure2Points

    USAGE
    --------
    * Declare ikHandle, give it a prefix run script

    NOTES
    --------
    * This creates a grp with all the distance locators, parent these to the parent of the first joint in the chain.
    ... So if the first joint is an upper arm, parent the Distance grp to the clav joint(assuming that is the parent)

    VERSIONS
    --------
    v_01:
    *   Stretchy all the time.
    v_02:
    *   Introduced an an option to only stretch when extended beyond the full length of the chain.

    TODO
    --------
    * Add an option for inverse scale

    '''

    ## --- Get the joints ---
    joints = mc.ikHandle(ikH, q=True, jl=True)
    # Since the joingList query does not return the end joint
    # ...We also need to get the end joint and append it to the list joints[]
    # ...We can do this by getting the last joint in the list, and querying its child
    lastJointinList = joints[(len(joints)-1)]
    endJoint = mc.listRelatives(lastJointinList, c=True, type="joint")
    joints.append(endJoint[0])

    ## ---Get the Distance---
    measureGrp =mc.group(em=True, n=prefix+'_distanceLocators_grp')
    distLocators = []
    #Get the position of each joint
    for jnt in joints:
        jointPostion = mc.xform(jnt, q=True, ws=True, t=True)
        #Create distance Locators
        distLocator = mc.spaceLocator(n=(prefix+'_'+jnt+'_'+'distLoc'))
        #Position Locators
        mc.xform(distLocator, t = (jointPostion[0], jointPostion[1], jointPostion[2]))
        #parent to grp
        mc.parent(distLocator,measureGrp)
        #add locators to list
        distLocators.append(distLocator[0])

    #Empty List to store Distance Between Noded
    distanceBetweenNodes = []
    #Measure the distance and the dbNodes to the list
    for i in range(len(distLocators)-1):
        distanceNode = nk_measure2Points(distLocators[i], distLocators[i+1],(distLocators[i]+'_to_'+distLocators[i+1]+'_dbNode'))
        distanceBetweenNodes.append(distanceNode)

    ## --- Setup Scale ---
    ## NewDistance/Original scale = StretchScale
    ## OriginalScale/NewDistance = InvScale

    #Add the distance -- create plus minus average --
    pmaNode = mc.shadingNode("plusMinusAverage", asUtility=True, n=(prefix+"_stretch_pma"))

    for i in range(len(distanceBetweenNodes)):

        print distanceBetweenNodes[i]
        print (pmaNode + '.input1[{i}]'.format(i=i))
        mc.connectAttr(('{db}.distance'.format(db=distanceBetweenNodes[i])), (pmaNode + '.input1D[{i}]'.format(i=i)))

    # -- get the Ik distance --
    ##Create a locator point constrain to ikHandle and add to grp
    ikDistanceLoc = mc.spaceLocator(n=(prefix +'_' + ikH + '_distLoc'))

    mc.parent(ikDistanceLoc[0], measureGrp)
    mc.pointConstraint(ikH, ikDistanceLoc[0])
    ikDistanceNode = nk_measure2Points(distLocators[0],ikDistanceLoc[0], (ikDistanceLoc[0]+'_dbNode'))

    # -- Stretch Condition --
    if StretchCondition == True:
        # If the Ik Handle stretches beyond the length of the total length, then the condtion will output the ikDistance to the mdNode
        # ... The scale factor will == ikDistance/OrginalDistance(orThe full Length of the joint chain)
        # If the ik Distance is less than the OrigDistance, the condition will output the OrginalDistance
        # ... The scale factor will == OrigDistance/OrigDistance. So the value of scale will be 1.

        condtionNode = mc.shadingNode("condition", asUtility=True, n=prefix+"_stretchScale_Cond")
        mc.setAttr (('{condtionNode}.operation'.format(condtionNode=condtionNode)), 3)
        mc.connectAttr (('{ikDistanceNode}.distance'.format(ikDistanceNode=ikDistanceNode)) ,('{condtionNode}.firstTerm'.format(condtionNode=condtionNode)))
        mc.connectAttr (('{ikDistanceNode}.distance'.format(ikDistanceNode=ikDistanceNode)) ,('{condtionNode}.colorIfTrueR'.format(condtionNode=condtionNode)))

        mc.connectAttr (('{pmaNode}.output1D'.format(pmaNode=pmaNode)),('{condtionNode}.secondTerm'.format(condtionNode=condtionNode)))
        mc.connectAttr (('{pmaNode}.output1D'.format(pmaNode=pmaNode)), ('{condtionNode}.colorIfFalseR'.format(condtionNode=condtionNode)))

        #create multiply divide node
        stretch_mdNode = mc.shadingNode("multiplyDivide", asUtility=True, n=prefix+"_stretchScale_md")
        mc.setAttr((stretch_mdNode+'.operation'),2)
        # Divide ik distance by OrigDistance
        #mc.connectAttr ikDistanceNode > to mdi1x
        mc.connectAttr ('{condtionNode}.outColorR'.format(condtionNode=condtionNode), ('{stretch_mdNode}.input1X').format(stretch_mdNode=stretch_mdNode))
        mc.connectAttr (('{pmaNode}.output1D'.format(pmaNode=pmaNode)), ('{stretch_mdNode}.input2X'.format(stretch_mdNode=stretch_mdNode)))

    if StretchCondition == False:
        # If the user does not require a condtion, the ikHandle will stretch when extended and when shortened

        #create multiply divide node
        stretch_mdNode = mc.shadingNode("multiplyDivide", asUtility=True, n=prefix+"_stretchScale_md")
        mc.setAttr((stretch_mdNode+'.operation'),2)
        # Divide ik distance by OrigDistance
        #mc.connectAttr ikDistanceNode > to mdi1x
        mc.connectAttr ('{ikDistanceNode}.distance'.format(ikDistanceNode=ikDistanceNode), ('{stretch_mdNode}.input1X').format(stretch_mdNode=stretch_mdNode))
        mc.connectAttr (('{pmaNode}.output1D'.format(pmaNode=pmaNode)), ('{stretch_mdNode}.input2X'.format(stretch_mdNode=stretch_mdNode)))

    # we dont wat to attach to the last joint, so loop through the joints and connect except the last one
    jntCnt = len(joints)-1
    for i in range(jntCnt):

        mc.connectAttr((stretch_mdNode + '.outputX'), (joints[i]+ '.scaleX'), force = True )

    ##For some reason the ikHandle seems to not hold in Place if it is in worldSpace
    #...or not parented to a transform
    #...As such, we just need to pop it back to it's oirginal position
    #...here it just snaps back to the last locator
    mc.delete(mc.pointConstraint(distLocators[0-1],ikH))

#nk_makeStretchyIK_RPSC(ikH = "ikHandle2", prefix = "prefix")

##+++++++++++++++++++++++++++++++++##
##++++++++nk_measure2Points++++++++##
##+++++++++++++++++++++++++++++++++##

def nk_measure2Points(itemA='itemA', itemB='itemA', distanceBetweenNodeName='dbNode'):

    dbNode = mc.createNode ('distanceBetween', name=distanceBetweenNodeName)

    mc.connectAttr('{itemA}.worldMatrix'.format(itemA=itemA),'{dbNode}.inMatrix1'.format(dbNode=dbNode))
    mc.connectAttr('{itemB}.worldMatrix'.format(itemB=itemB),'{dbNode}.inMatrix2'.format(dbNode=dbNode))

    mc.connectAttr('{itemA}.rotatePivotTranslate'.format(itemA=itemA),'{dbNode}.point1'.format(dbNode=dbNode))
    mc.connectAttr('{itemB}.rotatePivotTranslate'.format(itemB=itemB),'{dbNode}.point2'.format(dbNode=dbNode))

    return dbNode


I’m back, and here’s nk_ribbonSpine.py

So After a few months of trying to get my site back online due to some problematic bugs, and a busy work schedule, I’m finally able to post again…. and Here in all it’s glory is my ribbon creator script.

I’ve never liked doing anything more than once, so as a 3D artist, I decided it to take the time to script my own tools while rigging. I’ve used Mel for a few years, but for the past 6 months I’ve been learning python and it’s been highly refreshing. Here’s a tool I’ve just completed that creates 2 types of ribbon spines, a Basic ribbon and an Offset ribbon as seen in Aaron Holly’s DVD’s on creature rigging.

Many thanks also to Suchan Bajracharya of Puppeteer Lounge, who created the Sinka Rig. It makes use of both these types, which I myself am implementing into my own rig setups now.  I’ve also just started a workshop with PL, so I hope to really push my rigging skillset to a new level over the coming months.

Just a note, this version currently only works fully in Maya 2014, since, if I’m not mistaken the skinCluster functionality has evolved from previous versions of Maya.

I know, the GUI is a little ugly and raw, but this is part of my learning curve; I’m currently just getting used to building UI’s in Maya. With all knowledge I’ve saturated in recent years, I’m hoping I can start to share that knowledge with the world of animation TD’s and those learning like me.

I’ll try and do an update soon on how this might be used in A rig, so please keep you’re eyes peeled.


import maya.cmds as mc 

def nk_ribbonSpine():
    
	'''
	Version
	-----------------------
	v02
	* Switched the prefix so that the prefix specified of this is an offset ribbon, or a basic
	* Fixed double transforms on the nurbsPlane and the the follicles
	
	About
	-----------------------
	Creates a ribbon spine based on the Aaron Holly's method. 
	
	Returns
	-----------------------
	None
	
	Requires
	-----------------------
	None
	
	Example
	-----------------------
	Source the code, then run the following line to bring up the nk_ribbonSpine UI:
	nk_ribbonSpine()
	
	None
	
	TODO
	-----------------------
	* Add an option to switch the direction of the spine so it can be created facing in x or y
	'''
	

	# GUI FOR RIBBON SPLINE

	# delete the ui if it exists
	if (mc.window('nk_ribbonSpineGui', exists=True)):
	    mc.deleteUI('nk_ribbonSpineGui')

 	# Create the Gui 
 	mc.window('nk_ribbonSpineGui', title='nk_ribbonSpine', h=125, w=150)
 	
 	#Create main Col
 	mc.columnLayout('mainCol')
 	
 	# FrameLayot - Prefix
 	mc.frameLayout('prefixFL', label='prefix')
	mc.textFieldGrp('prefix', label='PREFIX', text='CHAR_SIDE_LIMB')
	mc.setParent('mainCol')
	
	# FrameLayout - Ribbon Type
	mc.frameLayout('ribbonTypeFL', label='Ribbon Type')
	mc.button('basicRbnBTN', l='BASIC', c='basicRibbon()')
	mc.button('offsetRbnBTN', l='OFFSET', c='offsetRibbon()' )
	mc.setParent('mainCol')
	
	
	mc.window('nk_ribbonSpineGui', e=True, h=125, w=150)
    
 	mc.showWindow('nk_ribbonSpineGui')

 
####RIBBON SPINE FUNCTION BELOW HERE###################

def nk_createRibbonSpine(prefix='CHAR_SIDE_LIMB', type='offset'):

	##+++++++++++++++++++++++++++++++++##
	##+++++++nk_createRibbonSpine++++++##
	##+++++++++++++++++++++++++++++++++##

	
	#Check the type of ribbon
	if type == 'basic':
		suffix = 'rbnBasic'

	if type =='offset':
		suffix = 'rbnOffset'
	
	
	#create group for ribbon rig
	ribbonRigGrp = mc.group(em=True, n=(prefix + '_' + suffix + '_rig_grp'))
	# ribbonRigGrp = mc.group(em=True, n=(prefix))

	# Create nurbs plane
	ribbonPlane = mc.nurbsPlane (n=(prefix + '_' + suffix + 'Plane'), p=[0, 0, 0], ax= [0, 0 ,1], w=1 ,lr=5 ,d=3, u=1, v=5, ch=0)
	ribbonPlaneShape = mc.listRelatives(ribbonPlane, c=True, s=True) #get shape node
	
    # CLEANUP: Parent plane to rigGrp and turn off inheritsTransforms
	mc.parent(ribbonPlane[0], ribbonRigGrp)
	mc.setAttr('{ribbonPlane}.inheritsTransform'.format(ribbonPlane=ribbonPlane[0]),0, lock=True)
	#mc.setAttr('{ribbonPlane}.inheritsTransform'.format(ribbonPlane=ribbonPlane[0]), lock=True)
	

	## rebuildSurface
	mc.rebuildSurface(ribbonPlane[0], rpo=1, rt=0, end=1, kr=0, kcp=0, kc=0, su=1, du=1, sv=0, dv=3, tol=0.01, dir=0)
	
	# Create follicles * 5
	
	##Create an empty group for the follicles
	folGroup = mc.group(em=True, n=(prefix + '_' + suffix + '_follicleGrp'))
		
    # CLEANUP: Parent follicleGrp to rigGrp
	mc.parent(folGroup, ribbonRigGrp)
	
	##Create a list for the follicles
	folList = []

    ##Create the follicles and ribbon joints
	for f in range(5):
		follicle = mc.createNode('follicle', n=('{prefix}_{suffix}_FollicleShape_{number}'.format(prefix=prefix, suffix=suffix ,number=(f+1))))
		print follicle
		follicleTransform = mc.listRelatives(follicle,  p=True) #get transform node

		ribbonJnt = mc.joint(n='{prefix}_{suffix}_jnt_{f}'.format(prefix=prefix,suffix=suffix,f=f+1))#create joint
		grp=mc.group(n=(ribbonJnt+'_offsetGrp'))#grp joint

		## connect folliclesShapes to the plane
		mc.connectAttr(('{ribbonPlaneShape}.local'.format(ribbonPlaneShape=ribbonPlaneShape[0])) ,('{follicle}.inputSurface'.format(follicle=follicle)))
		mc.connectAttr(('{ribbonPlaneShape}.worldMatrix[0]'.format(ribbonPlaneShape=ribbonPlaneShape[0])) ,('{follicle}.inputWorldMatrix'.format(follicle=follicle)))
		## connect follicleShapes to follicleTransform
		mc.connectAttr((follicle+'.outTranslate'), (follicleTransform[0]+'.translate') )
		mc.connectAttr((follicle+'.outRotate'), (follicleTransform[0]+'.rotate') )

		##position the follicles along the plane
		mc.setAttr((follicle+'.parameterU'), 0.5)
		vSpanHeight = ((f+1.0)/5.0) - .1
		mc.setAttr((follicle+'.parameterV'), vSpanHeight)
		
		#Turn off inherit transforms on the follicles
		mc.setAttr('{follicleTransform}.inheritsTransform'.format(follicleTransform=follicleTransform[0]),0, lock=True)
		
		##parent the follicle to the group and add to lists
		mc.parent(follicleTransform[0], folGroup)
		folList.append(follicle)
		
	# Create ribbon bind joints and setup aim constraints
	mc.select(cl=True)

	bindBaseJnt = mc.joint(p=(0,-2.5,0), o=(0,0,90), rad=2, n=(prefix+'_'+suffix+'_base_bind_01'))
	mc.joint(p=(0,-2,0), n=(prefix+'_ribbon_base_bind_02'))

	mc.select(cl=True)

	bindTipJnt = mc.joint(p=(0,2.5,0), o=(0,0,-90), rad=2, n=(prefix+'_'+suffix+'_tip_bind_01'))
	mc.joint(p=(0,2,0), n=(prefix+'_ribbon_tip_bind_02'))

	mc.select(cl=True)


	bindMidJnt = mc.joint(p=(0,0,0), o=(0,0,0), rad=2, n=(prefix+'_'+suffix+'_mid_bind_01'))
	mc.select(cl=True)

    # create control Locators and parent bindJoint to aimLocator
	##baseLocators
	basePosLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_base_pos_loc'))
	baseUpLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_base_up_loc'))
	baseAimLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_base_aim_loc'))

	mc.parent(baseUpLoc,baseAimLoc,basePosLoc )#parent locators
	mc.xform(basePosLoc, t=(0,-2.5,0))#move locator to base position
	mc.xform(baseUpLoc, ws=True, t=(0,-2.5,2))# offset Up locator in Z
	mc.parent(bindBaseJnt,baseAimLoc)#parent jnt to aim locator
	
	
	#CLEANUP: startPosLoc to rigGrp
	mc.parent(basePosLoc, ribbonRigGrp)
	
	##midLocators
	midPosLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_mid_pos_loc'))
	midUpLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_mid_up_loc'))
	midAimLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_mid_aim_loc'))

	mc.parent(midUpLoc,midAimLoc,midPosLoc )#parent locators
	mc.xform(midUpLoc, ws=True, t=(0,0,2))# offset Up locator in Z
	mc.parent(bindMidJnt,midAimLoc)#parent jnt to aim locator
	
	mc.parent(midPosLoc, ribbonRigGrp)
	
	##tipLocators
	tipPosLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_tip_pos_loc'))
	tipUpLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_tip_up_loc'))
	tipAimLoc = mc.spaceLocator(n=(prefix+'_'+suffix+'_tip_aim_loc'))

	mc.parent(tipUpLoc,tipAimLoc,tipPosLoc)#parent locators
	mc.xform(tipPosLoc, t=(0,2.5,0))#move locator to tip position
	mc.xform(tipUpLoc, ws=True, t=(0,2.5,2))# offset Up locator in Z
	mc.parent(bindTipJnt,tipAimLoc)#parent jnt to aim locator
	
	#CLEANUP: tipPosLoc to rigGrp 
	mc.parent(tipPosLoc, ribbonRigGrp)
	
	#setup aim relationsips
	##basic
	if type=='basic':
		##baseAimLoc > tipPos##
		mc.aimConstraint(tipPosLoc[0],baseAimLoc[0], aim=(0,1,0), u=(0,0,1), wut='object', wuo=baseUpLoc[0])
		##tipAimLoc > basePos##
		mc.aimConstraint(basePosLoc[0],tipAimLoc[0], aim=(0,-1,0), u=(0,0,1), wut='object', wuo=tipUpLoc[0])
		##midAimLoc > tipPos##
		mc.aimConstraint(tipPosLoc[0],midAimLoc[0], aim=(0,1,0), u=(0,0,1), wut='object', wuo=midUpLoc[0])
		
		# midPosLoc to follow both base and tip
		mc.pointConstraint(basePosLoc[0],tipPosLoc[0],midPosLoc[0], mo=False)
		# midPosAim to follow both base and tip Aim
		mc.pointConstraint(baseUpLoc[0],tipUpLoc[0],midUpLoc[0], mo=False)
		
	if type=='offset':
		
		##baseAimLoc > midBindJoint##
		mc.aimConstraint(bindMidJnt,baseAimLoc[0], aim=(0,1,0), u=(0,0,1), wut='object', wuo=baseUpLoc[0])
		#tipAimLoc > midBindJoint##
		mc.aimConstraint(bindMidJnt,tipAimLoc[0], aim=(0,-1,0), u=(0,0,1), wut='object', wuo=tipUpLoc[0])
		##midAimLoc > tipPos##
		mc.aimConstraint(tipPosLoc[0],midAimLoc[0], aim=(0,1,0), u=(0,0,1), wut='object', wuo=midUpLoc[0])
		
		# midPosLoc to follow both base and tip
		mc.pointConstraint(basePosLoc[0],tipPosLoc[0],midPosLoc[0], mo=False)
		# midPosAim to follow both base and tip Aim
		mc.pointConstraint(baseUpLoc[0],tipUpLoc[0],midUpLoc[0], mo=False)

	# Bind skin	
	mc.select(cl=True)
	print bindBaseJnt 
	print bindMidJnt 
	print bindTipJnt
	mc.skinCluster(bindBaseJnt,bindMidJnt,bindTipJnt,ribbonPlane, n=(prefix+'_'+suffix+'_skinCluster'), tsb=True, ih=True, bm=0, sm=0, nw=1, wd=0, mi=3, omi=False, dr=4)

def basicRibbon():
	prefix = mc.textFieldGrp('prefix', q=True, text=True)
	nk_createRibbonSpine(prefix, type='basic')

def offsetRibbon():
	prefix = mc.textFieldGrp('prefix', q=True, text=True)
	nk_createRibbonSpine(prefix, type='offset')




The Seven Wonders – my part in its downfall!!!

So here are some highlights from my work on the Seven Wonders project.  My first time working in a game engine, and I must say despite a few small hurdles towards the beginning, I soon felt privileged to be using Cryengine 3, an amzing realtime rendering engine, with some fantastic cinematic features.

Here’s a quick breakdown of the shots I was responsible for.

  1. Nano suit macro close up.  Elements created in Maya and Z-Brush, animated in Maya, lit rendered using Cryengine.   A lot of mel scripting went in to these few seconds of animation, I’m aiming to do a little breakdown of the process soon, and will link it here as soon it’s up.
  2. Helicopters, New York intro.  All assets and FX created, animated and lit in engine
  3. Typhoon Gun chamber and bullet time(err…bullets!).  Again created in Engine.  Additional bullet effects created using engine based particles systems.
  4. Launch Trailer Text sequences.  Created Text models and textures using Maya and Photoshop.  All set dressing and lighting and FX again created using the engine’s magic.
  5. Brain fly-through.  Neuron assets created in Maya. Camera and FX animation all done using….Maya!  Just kidding, it was the Cryengine again.