Wheel Rig
Script creates a simple rig for a variety of wheels
User Guide
Select wheel geometry then run the script. This script works on a variety of wheel sizes.
Outliner contains one main group (Wheel_01_rig) with two groups underneath.
The rotate_GRP contains the constraints and the offset CTRL with the Geometry. The CTRL_GRP contains the root control and the locator.
Outliner
Root Control
Auto OFF: Wheel will not move with the Translate Z of the root control.
​
Auto ON: Wheel will move with the Translate Z of the root control. Change the amount of motion with the Auto Rot attribute.
Offset Control
Rotate X: Manually control the rotation of the wheel.
Script
####################### Wheel Rig ###########################
##
## Create a simple rig for a wheel
##
## Created by Abby O'Malley
## aabrbty.com
## Follow me on instagram! @aabrbty
## Reach out for questions/comments!
##
##############################################################################
import maya.cmds as cmds
### Assign selected wheel to variable and get its shape node ###
curr_whl = cmds.ls(sl = True)[0]
print("Current Wheel: ", curr_whl)#X
whlShape = cmds.listRelatives(curr_whl)[0]
print("Current Wheel Shape: ", whlShape)#X
### Prefix ###
PFX = curr_whl + "_"
print("Prefix: ", PFX)#X
"""
### RESET (for Testing) ###
if cmds.objExists (PFX+"*"): cmds.delete (PFX+"*")
"""
### Get Size of Wheel ###
sizeMaxX = (cmds.getAttr(curr_whl + '.boundingBoxMaxX'))
sizeMinX = cmds.getAttr(curr_whl + '.boundingBoxMinX')
sizeMaxY = cmds.getAttr(curr_whl + '.boundingBoxMaxY')
sizeMinY = cmds.getAttr(curr_whl + '.boundingBoxMinY')
sizeMaxZ = (cmds.getAttr(curr_whl + '.boundingBoxMaxZ'))
sizeMinZ = cmds.getAttr(curr_whl + '.boundingBoxMinZ')
sizeX = sizeMaxX-sizeMinX
sizeY = sizeMaxY-sizeMinY
sizeZ = sizeMaxZ-sizeMinZ
print("sizeX: ", sizeX)#X
print("sizeY: ", sizeY)#X
print("sizeZ: ", sizeZ)#X
"""
## Test Bounding Box ##
cmds.polyCube(n = "bboxcube")
cmds.scale(sizeX,sizeY,sizeZ)
cmds.matchTransform("bboxcube",f"{curr_whl}",px=True,py=True,pz=True)
"""
#### Create Root CTRL ####
cmds.circle(n=f"{PFX}root_CTRL", nr=(0,1,0), c = (0,0,0))
cmds.group(f"{PFX}root_CTRL", n= f"{PFX}CTRL_GRP")
cmds.matchTransform(f"{PFX}CTRL_GRP", f"{curr_whl}", px=True,pz= True, py=True)
cmds.move((sizeY*.5)*-1,y=True, r=True)
#cmds.move((sizeX),x=True, r=True)
### Size the Root CTRL ###
cmds.select(f"{PFX}root_CTRL")
if sizeZ >= sizeX:
rootScale = (sizeZ - sizeX)/2.5
cmds.scale(rootScale,rootScale,rootScale)
print ("IF one")
elif sizeX >= sizeZ:
rootScale = (sizeX - sizeZ)*3
cmds.scale(rootScale,rootScale,rootScale)
print("IF two")
## Shape Ctrl ##
cmds.select(f"{PFX}root_CTRL.cv[5]")
cmds.move(0,0,rootScale*.63, r= 1)
## Delete History / Freeze Transforms ##
cmds.select(f"{PFX}root_CTRL")
cmds.FreezeTransformations(f"{PFX}root_CTRL")
cmds.DeleteHistory(f"{PFX}root_CTRL")
### Add auto attr to root CTRL ###
cmds.select(f"{PFX}root_CTRL")
cmds.addAttr(ln="auto", at="bool", dv=0)
cmds.setAttr(f"{PFX}root_CTRL.auto",1, k=1)
### Add auto rotation attr to root CTRL ###
cmds.addAttr(ln="autoRot", at="float", dv=0)
cmds.setAttr(f"{PFX}root_CTRL.autoRot",1, k=1)
#### Create Locator ####
cmds.spaceLocator(n=f"{PFX}LOC")
cmds.matchTransform(f"{PFX}LOC", f"{curr_whl}")
cmds.parent(f"{PFX}LOC",f"{PFX}root_CTRL")# put LOC under root_CTRL
cmds.FreezeTransformations(f"{PFX}LOC")
cmds.DeleteHistory(f"{PFX}LOC")
#### Create Rotate GRP ####
cmds.group(f"{curr_whl}", n=f"{PFX}rotate_GRP")
### Apply Point and Scale Constraint to LOC and rotate_GRP ###
cmds.pointConstraint(f"{PFX}LOC", f"{PFX}rotate_GRP", mo=1)
cmds.scaleConstraint(f"{PFX}LOC", f"{PFX}rotate_GRP", mo=1)
cmds.orientConstraint(f"{PFX}LOC", f"{PFX}rotate_GRP", mo=1)
### Add offset attr to rotate GRP ###
cmds.select(f"{PFX}rotate_GRP")
cmds.addAttr(ln="offset", at="float", dv=0)
cmds.setAttr(f"{PFX}rotate_GRP.offset",1, k=1)
### Offset CTRL ###
cmds.circle(n=f"{PFX}offset_CTRL", nr=(1,0,0), c = (0,0,0))
cmds.parent(f"{PFX}offset_CTRL",f"{PFX}root_CTRL")
cmds.matchTransform(f"{PFX}offset_CTRL", curr_whl, px= True, py=True, pz= True)
cmds.move(sizeX*.7,0,0, r=1)
offsetScale = sizeY*.15
cmds.scale(offsetScale,offsetScale,offsetScale)
## Shape Ctrl ##
cmds.select(f"{PFX}offset_CTRL.cv[0]",f"{PFX}offset_CTRL.cv[2]",f"{PFX}offset_CTRL.cv[4]",f"{PFX}offset_CTRL.cv[6]", add=False)
offsetScale2 = sizeY*.015
cmds.scale(offsetScale2, offsetScale2, offsetScale2, r=True)
## Delete History / Freeze Transforms ##
cmds.select(f"{PFX}offset_CTRL")
cmds.FreezeTransformations(f"{PFX}offset_CTRL")
cmds.DeleteHistory(f"{PFX}offset_CTRL")
## Connect offset CTRL to GEO ##
cmds.parent(curr_whl, f"{PFX}offset_CTRL")
cmds.parent(f"{PFX}offset_CTRL", f"{PFX}rotate_GRP")
#************************* Auto ANIMTAION *************************#
## Set Driven Key ## : DR= Auto DN = AutoRot
cmds.setAttr(f"{PFX}root_CTRL.auto", 1)
cmds.setDrivenKeyframe(f"{PFX}root_CTRL.autoRot", v= 2, cd = f"{PFX}root_CTRL.auto")
cmds.setAttr(f"{PFX}root_CTRL.auto", 0)
cmds.setDrivenKeyframe(f"{PFX}root_CTRL.autoRot", v= 0, cd = f"{PFX}root_CTRL.auto")
### EXPRESSION ###
cmds.expression(s= f"{curr_whl}.rotateX = {PFX}root_CTRL.translateZ*{PFX}root_CTRL.autoRot")
#************************** CLEAN UP **************************#
#### Lock and Hide Attributes for CTRLs ####
## Root CTRL ##
cmds.setAttr(f"{PFX}root_CTRL.visibility", lock=1, k=0,cb=0)
## Offrot CTRL ##
cmds.select(f"{PFX}offset_CTRL")
cmds.setAttr(f"{PFX}offset_CTRL.translateX", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.translateY", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.translateZ", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.rotateY", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.rotateZ", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.scaleX", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.scaleY", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.scaleZ", lock=1, k=0,cb=0)
cmds.setAttr(f"{PFX}offset_CTRL.visibility", lock=1, k=0,cb=0)
### Group groups ###
cmds.group(f"{PFX}rotate_GRP", f"{PFX}CTRL_GRP", n=f"{PFX}rig")
### Color CTRLS ###
## Root ##
cmds.setAttr(f"{PFX}root_CTRLShape.overrideEnabled", 1)
cmds.setAttr(f"{PFX}root_CTRLShape.overrideColor", 17)
## Offset ##
cmds.setAttr(f"{PFX}offset_CTRLShape.overrideEnabled", 1)
cmds.setAttr(f"{PFX}offset_CTRLShape.overrideColor", 18)
##
## Create a Backdrop, Lights, and Camera for Prop rendering
## Manually change common render settings
## Manually adj lights and cam as needed
##
## Created by Abby O'Malley
## Follow me on instagram! @aabrbty
## Reach out for questions/comments!
##
##############################################################################
import maya.cmds as cmds
import mtoa.utils as mutils
### Globals ###
PFX = "PS_"
if cmds.objExists(f"{PFX}*"):
cmds.delete(f"{PFX}*")
if cmds.objExists("PropSet"):
cmds.delete("PropSet")
### Clean up Obj ###
def cleanup(obj):
cmds.select(obj)
cmds.DeleteHistory()
cmds.FreezeTransformations()
### Create Backdrop ###
def Backdrop():
BackdropName = f"{PFX}Backdrop"
cmds.polyCube(n= f"{BackdropName}")
cmds.move(0,-.5,0, f"{BackdropName}.scalePivot", f"{BackdropName}.rotatePivot")
cmds.move(0,.5,0)
cmds.scale(117,45,60)
cmds.delete(f"{BackdropName}.f[0:1]",f"{BackdropName}.f[4:5]")
cleanup(f"{BackdropName}")
cmds.polyBevel3(f"{BackdropName}.e[2]", offset=18, segments = 16)
cmds.scale(1.638, z=True)
cmds.scale(1.455, y=True)
cmds.move(4.522, z=True)
cmds.polySoftEdge(f"{BackdropName}", a=180)
## Assign AiSS to Backdrop ##
BackdropTXT = cmds.shadingNode('aiStandardSurface',n = f"{BackdropName}_TXT", asShader=True)
AiSSSet = cmds.sets( renderable=True, noSurfaceShader=True, empty=True, n= f"{BackdropName}set");
cmds.connectAttr( BackdropTXT+".outColor", AiSSSet+".surfaceShader", force=True)
cmds.select(f"{BackdropName}")
cmds.hyperShade(assign = BackdropTXT)
### Create Lights ###
def Lights():
## Key Light ##
mutils.createLocator('aiAreaLight', asLight=True)
keylight = cmds.rename('aiAreaLight1', f"{PFX}Key")
cmds.select(f"{PFX}Key")
cmds.move(21.517,6.564,25.303)
cmds.rotate(-2.726,45,0)
cmds.scale(8.902,8.902,8.902)
cmds.setAttr(f"{PFX}KeyShape.color", 1, .888, .829)
cmds.setAttr(f"{PFX}KeyShape.intensity", 1135.516)
cmds.setAttr(f"{PFX}KeyShape.exposure", 1.250)
cmds.setAttr(f"{PFX}KeyShape.aiSamples", 3)
cmds.setAttr(f"{PFX}KeyShape.aiTranslator", "disk", type="string")
"""
## Fill Light - Directional ##
cmds.directionalLight(rot=(-30,-45,0), intensity=2.111, rgb=(.692,.728,1))
fill = cmds.rename("directionalLight1", f"{PFX}Fill")
cmds.move(-17.402,22.710,20.709)
cmds.scale(10.733,10.733,10.733)
cmds.setAttr(f"{PFX}FillShape.aiExposure",-0.750)
cmds.setAttr(f"{PFX}FillShape.aiSamples", 3)
cmds.setAttr(f"{PFX}FillShape.aiCastShadows",0)
cmds.setAttr(f"{PFX}FillShape.aiCastVolumetricShadows",0)
"""
## Fill Light - Area ##
mutils.createLocator('aiAreaLight', asLight=True)
area01 = cmds.rename('aiAreaLight1', f"{PFX}Fill")
cmds.select(f"{PFX}Fill")
cmds.move(-24.333, 15.946, 25.303)
cmds.rotate(-12.935, -35.536, -2.759)
cmds.scale(8.902, 8.902, 8.902)
cmds.setAttr(f"{PFX}FillShape.color", .860, .876, 1)
cmds.setAttr(f"{PFX}FillShape.intensity", 1135.516)
cmds.setAttr(f"{PFX}FillShape.exposure", 1)
cmds.setAttr(f"{PFX}FillShape.aiSamples", 3)
cmds.setAttr(f"{PFX}FillShape.aiTranslator", "disk", type="string")
## Rim Light ##
mutils.createLocator('aiAreaLight', asLight=True)
rimlight = cmds.rename('aiAreaLight1', f"{PFX}Rim")
cmds.select(f"{PFX}Rim")
cmds.move(0, 29.103,-22.901)
cmds.rotate(-24.143,180, 0)
cmds.scale(8.902, 8.902, 8.902)
cmds.setAttr(f"{PFX}RimShape.color", 1, 1, 1)
cmds.setAttr(f"{PFX}RimShape.intensity", 1431.516)
cmds.setAttr(f"{PFX}RimShape.exposure", .894)
cmds.setAttr(f"{PFX}RimShape.aiSamples", 2)
cmds.setAttr(f"{PFX}RimShape.aiDiffuse", .992)
cmds.setAttr(f"{PFX}RimShape.aiIndirect", 2.129)
cmds.lightlink(b=True, light= f"{PFX}Rim", object= f"{PFX}Backdrop")
## Skydome Light ##
mutils.createLocator('aiSkyDomeLight', asLight=True)
area01 = cmds.rename('aiSkyDomeLight1', f"{PFX}Skydome")
cmds.select(f"{PFX}Skydome")
cmds.move(0, 0, 0)
cmds.rotate(0, 0, 0)
cmds.scale(1, 1, 1)
cmds.setAttr(f"{PFX}SkydomeShape.color", .664, .687, .750)
cmds.setAttr(f"{PFX}SkydomeShape.intensity", .2)
cmds.setAttr(f"{PFX}SkydomeShape.exposure", 0)
cmds.setAttr(f"{PFX}SkydomeShape.aiSamples", 2)
cmds.select(f"{PFX}Skydome")
cmds.createDisplayLayer(n=f"{PFX}Skdome_LAY")
### Create Camera ###
def Camera():
cmds.camera(n=f"{PFX}rendCAM", fl=50, hfa=1.417, vfa=.945, fs=5.6, coi=21.401)
cmds.move(0.607, 15.036, 51.905)
cmds.rotate(-10.796, 0, 0)
cmds.scale(2, 2, 2)
cmds.setAttr ("defaultArnoldRenderOptions.AASamples",4)
cmds.setAttr ("defaultArnoldRenderOptions.GIDiffuseSamples", 2)
def GroupSet():
cmds.select(f"{PFX}*")
cmds.group(n= "PropSet")
cmds.select(cl=True)
### MAIN ###
def Main():
Backdrop()
Lights()
Camera()
GroupSet()
Main()