Archive for the ‘Maya’ Category

Local timewarps for animated objects

Sure, scene timewarp is cool, but what if you want to just timewarp a few objects (say, your tracked camera)??

Select the objects that you want to timewarp, and run this script. Any animation curves on your selection will have a new animCurve node plugged into their “time” input, that you can tweak to timewarp your animation.


def makeWarpCurve():
    timeWarpNode = cmds.createNode('animCurveTT', name='timeWarper1')
    startTime= cmds.playbackOptions( q=True, min=True)
    endTime= cmds.playbackOptions( q=True, max=True)
    
    cmds.setAttr(timeWarpNode + '.preInfinity', 1)
    cmds.setAttr(timeWarpNode + '.postInfinity', 1)

    cmds.addAttr(timeWarpNode, ln='isTimeWarp',  at='bool')

    cmds.setKeyframe(timeWarpNode, time=(startTime,), insert=True)
    cmds.setKeyframe(timeWarpNode, time=(endTime,), insert=True)
    cmds.keyframe(timeWarpNode, index=(0,), absolute=True, valueChange=startTime)
    cmds.keyframe(timeWarpNode, index=(1,), absolute=True, valueChange=endTime)
    cmds.keyTangent(timeWarpNode, index=(0,), itt='linear', ott='linear')
    cmds.keyTangent(timeWarpNode, index=(1,), itt='linear', ott='linear')
    
    return timeWarpNode


def applyTimeWarp(objects, warpCurve=None):
    if not warpCurve:
        warpCurve = makeWarpCurve()
    else:
        if not cmds.objExists(warpCurve):
            print 'Cannot find warpCurve: "%s"' % warpCurve
            return
        
    for obj in objects:
        print obj
        for conn in cmds.listConnections( obj, d=False, s=True, type='animCurve'):
            print '\t', conn
            cmds.connectAttr(warpCurve + '.output', conn + '.input',  force=True)
    
    return warpCurve

def selectAllWarpNodes():
    wn = getAllWarpNodes()
    if wn:
        cmds.select(wn)
    else:
        print 'No warp nodes found'
    

def getAllWarpNodes():
    warpNodes = cmds.ls('*.isTimeWarp', recursive=True, objectsOnly=True)
    return warpNodes
    
    
def warpSelected():
    sel  = cmds.ls(sl=True)
    if sel:
        warpCurve = applyTimeWarp(sel)
        cmds.select(warpCurve)

warpSelected()

You can have multiple timewarps in a scene. Just select each object (or group of objects) separately and make new timewarpers.

I have no idea how it interacts with scene timewarp. Probably the universe will implode on itself.

the “getAllWarpNodes()” function will return all timewarper nodes in the scene. Or do a “selectAllWarpNodes()” to select them all.

Nodes missing from the hypershade

If you create a shader node via scripting without specifying “asShader=True” (there may be gui ways of getting the same effect), you may find that said nodes don’t appear in the top hypershade tabs. Maddening.

There’s no need to nuke the nodes and start again – they can be fixed in place. There’s a number of hidden sets that the hypershade tabs rely upon, so it’s trivial to reconnect them. Code below – select the offending nodes, and run this.

import maya.cmds as cmds

def fixShadingNodes(inNode):
    target = None
    
    nodeType = cmds.nodeType(inNode)
    if cmds.getClassification(nodeType, satisfies="shader"):
        target = 'defaultShaderList1.shaders'
    elif cmds.getClassification(nodeType, satisfies="texture"):
        target = 'defaultTextureList1.textures'
    elif cmds.getClassification(nodeType, satisfies="utility"):
        target = 'defaultRenderUtilityList1.utilities'
    
    if target:
        cmds.connectAttr(sel + '.message', target, na=True, force=True)

for sel in cmds.ls(sl=True):
    fixShadingNodes(sel)
    

Or apply it to all instances of a node type

for x in cmds.ls(type='aiImage'):
    fixShadingNodes(x)

From keyframe animation to deform animation in Maya

Usually point caching systems will only deal with objects that are driven by deformation (eg, nCache) – hence the name, point caching. Other bigger, badder systems (eg, alembic) can cope with both transforms and deformations. However, what if you have a transform driven object that you desperately need to export to a point cache?
Read more

ICE instances into Maya

There was a follow up to an old discussion on the Softimage mailing list concerning transferring particle instance orders from Soft into Maya. I thought I’d document what I said earlier.
Read more

Duplicating Shave setups

Shave and a Haircut… interesting bit of kit.

I’ve been needing to duplicate hair setups onto meshes that aren’t world aligned (they have different animation caches on them, otherwise the same mesh), which means that Shave’s built in duplicating tools won’t necessarily work. For some reason, trying to script the duplication and plugging in a new mesh wasn’t working, until i decided to call a scene refresh. That fixed it! The old refresh trick…

Read more

Pose driver

In rigging land, and I’m making a rotation driver from built-in maya nodes. This usually drives some front of chain blendshapes (use Chad Vernon’s awesome cvShapeInverter tool) to do corrective blends. This is similar in concept to the Comet poseDeformer.
Read more

Maya QT windows – inaccessible titlebars

The very first time you call a QT interface window (in a given user preferences set), it will draw the window so that the titlebar is offscreen. Problematic, because you can’t move the window, and it tends to freak your users out.

This is because Maya doesn’t have a window pref for it yet, and it’s default is brain meltingly stupid. Mac maya doesn’t suffer from this problem. Not sure about Linux.

So, use this function after you showWindow(). For example:

uiFile = "/path/to/uiFile.ui"
rlDialog = cmds.loadUI(f=uiFile)
cmds.showWindow(rlDialog)
windowPosFix(rlDialog)

def windowPosFix(windowName):
    """
    Fix for QT windows with no position information. If they are positioned at the very
    top left of screen, they are moved somewhere more sensible.
    
    @param windowName: Window Name
    @type windowName: String
    """
    
    if cmds.window(windowName, query=True, exists=True):
        winPos = cmds.window(windowName, query=True, topLeftCorner=True)
        if winPos[0] <=0 or winPos[1] <=0:
            cmds.window(windowName, edit=True, topLeftCorner=[150,150])

Maya QT interfaces in a class

Most tutorials online have suggested the way to fire commands inside QT interfaces launched in Maya (via cmds.loadUi – not involving pyQT) is to add a string property like:

+command="myPythonInstance.pythonCommand()"

Pretty craptastical – it can only fire commands inside a specific python module, the name of which has to be known when you’re laying out your interface.

Ideally, we’d like to have our interface instanced from a python class. This would allow us, amongst other things, so have multiple, independant instances. However it seems that a “self” reference can’t get passed to an interface, which is what you’d reallly like to do.

+command=self.pythonCommand

A (reasonably hacky) solution is to create the widget callbacks from inside our python class, after we’ve loaded the interface via cmds.loadUi().

import maya.cmds as cmds

class myUi(object):
	def __init__(self, *args):
		uiFile = '/path/to/uiFile.ui'
		rlDialog = cmds.loadUI(f=uiFile)
		cmds.showWindow(rlDialog)
		cmds.button( "ovrAdd_btn", edit=True, command=self.overrideAdd )

	def overrideAdd(self, *args):
		print 'do something!'

Which is slightly better, but we’re still referring to our widget by a string name. This is problematic if you have multiple instances of this class – targeting “ovrAdd_btn” is going to get confusing. We need to find out exactly which control, in amongst the whole widget mess, is the one that we’ve spawned when we’re called cmds.loadUI().

Sadly, loadUI() doesn’t give us any help in this department. So, and here’s the hacky bit, we can trawl through the widgets to find those belonging to our class instance, store them in a dict and then we can refer back to them by a simplified name. Easy!

import maya.cmds as cmds

def widgetPath(windowName, widgetNames):
    """
    
    @param windowName: Window instance name to search
    @param widgetNames: list of names to search for
    """
    
    returnDict = {}
    mayaWidgetList = cmds.lsUI(dumpWidgets=True)
    
    for widget in widgetNames:
        for mayaWidge in mayaWidgetList:
            if windowName in mayaWidge:
                if mayaWidge.endswith(widget):
                    returnDict[widget] = mayaWidge
                    
    return returnDict

class myUi(object):
	def __init__(self, *args):
		uiWidgetList = ['ovrAdd_btn']

		uiFile = '/path/to/uiFile.ui'
		rlDialog = cmds.loadUI(f=uiFile)
		self.uiObjects = widgetPath(rlDialog, uiWidgetList)
		
		cmds.showWindow(rlDialog)
		
		cmds.button( self.uiObjects["ovrAdd_btn"], edit=True, command=self.overrideAdd )

	def overrideAdd(self, *args):
		print 'do something!'

Trawling through Maya’s widget list isn’t particularly elegant, but you only have to do it once per class initialisation, so there isn’t too much of a cost. The advantage is that you can now have per-instance widget names.

Why not just use pyQT proper, you ask? Installing external dependencies isn’t always an option for our less techy brethren, or easily done in a studio-wide fashion. Using pyQT would be far better (and give all sorts of other benefits), but if you’re constrained to using vanilla maya / python, this seems to do the trick.

Name changed callbacks in maya/python/api

Could not figure this out for quite a while – maya API is a little obtuse if you’re not used to it, and with all the examples in c++, you’re on your own when it comes to translating to python. Ergh.

 

from maya import OpenMaya

def nameChangedCallback( *args):
    node = args[0]
    # convert the MObject to a dep node
    depNode = OpenMaya.MFnDependencyNode(node)
    oldName = args[1]

    print '----\nNameChangedCallback'
    print 'newName: ', depNode.name()
    print 'oldName', oldName
    print 'type', depNode.typeName()

    

# passing in a null MObject (ie, without a name as an argument)
# registers the callback to get all name changes in the scene
# if you wanted to monitor a specific object's name changes
# you could pass a name to the MObject

nullMObject = OpenMaya.MObject()
OpenMaya.MNodeMessage.addNameChangedCallback( nullMObject, nameChangedCallback )

 

EXR compression in maya

If you want to script it, it’s here:
setAttr “mentalrayGlobals.imageCompression” 4

Return top