Пример #1
0
 def compute(self,plug,dataBlock):
     #print self.thisMObject().apiType()
     #print self.thisMObject().apiTypeStr()
     depNodeFn = OpenMaya.MFnDependencyNode( self.thisMObject() )
     attrObject = depNodeFn.attribute( 'sTx' )
     plug = OpenMaya.MPlug( self.thisMObject(), attrObject )
     
     #print plug.name()
     #print plug.asDouble()
     
     time = OpenMayaAnim.MAnimControl.currentTime()              
     
     value_curr = plug.asDouble(OpenMaya.MDGContext(time))
     print 'Values in time %d ---> %d' % (time.value() ,value_curr)
     
     value_10 = plug.asDouble(OpenMaya.MDGContext(time+10))
     print 'Values in time %d ---> %d' % ((time.value()+10) ,value_10)
     
     value_20 = plug.asDouble(OpenMaya.MDGContext(time+20))
     print 'Values in time %d ---> %d' % ((time.value()+20) ,value_20)
     
     if plug == speedNode.realTime:
         dataHandletimeSVal =  dataBlock.inputValue(speedNode.startFrame)
         timeSVal = dataHandletimeSVal.asFloat()
         dataHandletimeEVal =  dataBlock.inputValue(speedNode.endFrame)
         timeEVal = dataHandletimeEVal.asFloat()
         
         timeFrameS =  ((timeEVal-timeSVal)/30.00)
         
         dataHandtimeVal = dataBlock.outputValue(speedNode.realTime)
         dataHandtimeVal.setFloat(timeFrameS)
         dataBlock.setClean(plug)
         
     if plug == speedNode.vecAttrOut:
         print 'Hello'
         dataBlock.setClean(plug)
         
     if plug ==  speedNode.outTx or speedNode.outTy or speedNode.outTz:
         dataHandleInTxVal =  dataBlock.inputValue(speedNode.sTx)
         txVal =  dataHandleInTxVal.asFloat()
         dataHandleInTyVal =  dataBlock.inputValue(speedNode.sTy)
         tyVal =  dataHandleInTyVal.asFloat()
         dataHandleInTzVal =  dataBlock.inputValue(speedNode.sTz)
         tzVal =  dataHandleInTzVal.asFloat()
         
         newTxVal =  (txVal*2)
         newTyVal =  (tyVal*2)
         newTzVal =  (tzVal*2)
         
         dataHandleOutTxVal = dataBlock.outputValue(speedNode.outTx)
         dataHandleOutTxVal.setFloat(newTxVal)
         dataHandleOutTyVal = dataBlock.outputValue(speedNode.outTy)
         dataHandleOutTyVal.setFloat(newTyVal)
         dataHandleOutTzVal = dataBlock.outputValue(speedNode.outTz)
         dataHandleOutTzVal.setFloat(newTzVal)
         dataBlock.setClean(plug)     
Пример #2
0
def getWorldValueAtFrame(attr, frame):
    mSelectionList = OpenMaya.MSelectionList()
    mSelectionList.add(attr)
    plug = OpenMaya.MPlug()
    mSelectionList.getPlug(0, plug)
    context = OpenMaya.MDGContext(OpenMaya.MTime(frame))
    return plug.asDouble(context)
Пример #3
0
def scene_frame(frame):
    """
    Temporarily evaluate the scene at the given time.
    """
    mtime = om.MTime()
    mtime.setValue(frame)
    with MDGContextGuard(om.MDGContext(mtime)) as guard:
        yield guard
Пример #4
0
    def compute(self, plug, dataBlock):

        if plug == velocityNode.velocity:
            plugX = OpenMaya.MPlug(
                self.thisMObject(),
                OpenMaya.MFnDependencyNode(
                    self.thisMObject()).attribute('inPutX'))
            plugY = OpenMaya.MPlug(
                self.thisMObject(),
                OpenMaya.MFnDependencyNode(
                    self.thisMObject()).attribute('inPutY'))
            plugZ = OpenMaya.MPlug(
                self.thisMObject(),
                OpenMaya.MFnDependencyNode(
                    self.thisMObject()).attribute('inPutZ'))
            time = OpenMayaAnim.MAnimControl.currentTime()
            valXPast = plugX.asDouble(OpenMaya.MDGContext(time - 1))
            valYPast = plugY.asDouble(OpenMaya.MDGContext(time - 1))
            valZPast = plugZ.asDouble(OpenMaya.MDGContext(time - 1))
            valXcurr = plugX.asDouble(OpenMaya.MDGContext(time))
            valYcurr = plugY.asDouble(OpenMaya.MDGContext(time))
            valZcurr = plugZ.asDouble(OpenMaya.MDGContext(time))
            valXNext = plugX.asDouble(OpenMaya.MDGContext(time + 1))
            valYNext = plugY.asDouble(OpenMaya.MDGContext(time + 1))
            valZNext = plugZ.asDouble(OpenMaya.MDGContext(time + 1))

            velocityVal = (((math.sqrt(((valXNext - valXcurr) *
                                        (valXNext - valXcurr)) +
                                       ((valYNext - valYcurr) *
                                        (valYNext - valYcurr)) +
                                       ((valZNext - valZcurr) *
                                        (valZNext - valZcurr))) + math.sqrt(
                                            ((valXcurr - valXPast) *
                                             (valXcurr - valXPast)) +
                                            ((valYcurr - valYPast) *
                                             (valYcurr - valYPast)) +
                                            ((valZcurr - valZPast) *
                                             (valZcurr - valZPast)))) * 0.5) *
                           30)

            dataHandlevelocityVal = dataBlock.outputValue(
                velocityNode.velocity)
            dataHandlevelocityVal.setFloat(velocityVal)
            dataBlock.setClean(plug)
        else:
            return OpenMaya.kUnknownParameter
Пример #5
0
def getPointAtTime(refreshMode, mesh, vertexId, time):
    if refreshMode:
        # If refreshMode is on, mesh should be a dagPath
        # You MUST reinitialize the function set after changing time!
        fnMesh = om.MFnMesh()
        fnMesh.setObject(mesh)
    else:
        # If refreshMode is off, mesh should be a mplug
        # Get its value at the specified Time.
        meshData = mesh.asMObject(om.MDGContext(time))

        # Use its MFnMesh function set
        fnMesh = om.MFnMesh(meshData)

    # Get vertices at this time
    position = om.MPoint()
    fnMesh.getPoint(vertexId, position, om.MSpace.kWorld)
    # cmd.spaceLocator(n = 'locl%s' % frame, p = (position[0], position[1], position[2]))
    return (position[0], position[1], position[2])
Пример #6
0
    def set_world_space_rotation_and_translation_at_time(
            node_obj, time, rotation, translation):
        # get parent matrix at the given time
        mfn = OpenMaya.MFnDependencyNode(node_obj)
        plug = mfn.findPlug("parentMatrix").elementByLogicalIndex(0)
        o = plug.asMObject(OpenMaya.MDGContext(OpenMaya.MTime(time)))
        m = MayaFacadeHelper.sanitize_matrix(
            OpenMaya.MFnMatrixData(o).matrix())
        parent_matrix = OpenMaya.MMatrix()
        OpenMaya.MScriptUtil.createMatrixFromList(m, parent_matrix)

        inv_parent_matrix = parent_matrix.inverse()

        # fiding rotation
        world_rotation = OpenMaya.MQuaternion(rotation[0], rotation[1],
                                              rotation[2], rotation[3])
        lm = world_rotation.asMatrix() * inv_parent_matrix
        r = OpenMaya.MTransformationMatrix(lm).eulerRotation()
        r.reorderIt(mfn.findPlug("rotateOrder").asInt())

        # finding translation
        world_translation = OpenMaya.MPoint(translation[0], translation[1],
                                            translation[2])
        t = world_translation * inv_parent_matrix

        # Setting values on node
        d = {
            'tx': t.x,
            'ty': t.y,
            'tz': t.z,
            'rx': math.degrees(r.x),
            'ry': math.degrees(r.y),
            'rz': math.degrees(r.z)
        }

        for name, value in d.iteritems():
            plug = mfn.findPlug(name)
            kco = MayaFacade.get_keyframable_channel_object(node_obj, plug)
            # this might happen when having incoming connectiosn to this plug
            if kco is None:
                continue
            MayaFacade.set_channel_key_frame(kco, time, value)
Пример #7
0
    def getUnlockedInfluences(self):
        '''
        returns influences that are locked
        output MSelectionList
        '''

        influences = self.getInfluencesFromSkincluster()
        outputSelectionList = om.MSelectionList()

        for i in range(influences.length()):

            jointObject = influences[i].node()
            nodeFn = om.MFnDependencyNode(jointObject)
            plug = om.MPlug(jointObject,
                            nodeFn.attribute('lockInfluenceWeights'))
            value = plug.asInt(om.MDGContext())

            if value == 0:
                outputSelectionList.add(influences[i])

        return outputSelectionList
Пример #8
0
def bake_animation(plugs, frame_range=None, step=1):
    """Bake the animation to all nodes based on the passed list of plugs.

    Args:
        plugs (list): List of maya.om.MPlug Plugs to bake the animation to.
    
    Keyword Args:
        frame_range (float, float): Time range in which the keys are to be created.
        step (float): Time step for each sample.
    """

    frame_range = frame_range or timeline.Timeline.frame_range()
    start_frame, end_frame = frame_range
    step_range = get_step_range(start_frame, end_frame, step=step)

    times = om.MTimeArray(step_range, om.MTime())
    plugValues = [om.MDoubleArray(step_range, 0) for i in range(len(plugs))]
    plugsAndValues = zip(plugs, plugValues)
    for i in range(step_range):
        time = om.MTime(start_frame + i * step, om.MTime.kFilm)
        times.set(time, i)

        context = om.MDGContext(time)
        for plug, values in plugsAndValues:
            values.set(plug.asDouble(context), i)

    dg = om.MDGModifier()
    for plug in plugs:
        sources = om.MPlugArray()
        plug.connectedTo(sources, True, False)
        for i in range(sources.length()):
            dg.disconnect(sources[i], plug)

    dg.doIt()
    for plug, values in itertools.izip(plugs, plugValues):
        curve = oma.MFnAnimCurve()
        curve.create(plug, dg)
        curve.addKeys(times, values)

    dg.doIt()
Пример #9
0
def bake_transform_internal(bakes, min_frame, max_frame, progress=None):
    # Updating the progress window every frame is too slow, so we only update it
    # every 10 frames.
    update_progress_every = 10
    total_frames = max_frame - min_frame + 1

    total_progress_updates = 0
    total_progress_updates += total_frames  # frame updates
    total_progress_updates += total_frames * len(bakes)  # setting keyframes
    progress.set_total_progress_value(total_progress_updates /
                                      update_progress_every)

    # Make sure our target attributes aren't locked.  (Can we check if they're writable,
    # eg. disconnected or connected but writable?)
    failed = False
    for bake in bakes:
        attributes_to_check = []
        if bake.position:
            attributes_to_check.extend(('t', 'tx', 'ty', 'tz'))
        if bake.rotation:
            attributes_to_check.extend(('r', 'rx', 'ry', 'rz'))
        if bake.scale:
            attributes_to_check.extend(('s', 'sx', 'sy', 'sz'))

        for attr_name in attributes_to_check:
            attr = bake.dst.attr(attr_name)
            if attr.get(lock=True):
                log.error('Attribute %s is locked', attr)
                failed = True

    if failed:
        return

    # Match the transform to the target on each frame.  Don't set keyframes while in
    # an MDGContext (this confuses Maya badly).  Just store the results.
    mtime = om.MTime()
    frame_range = range(min_frame, max_frame + 1)
    values = []
    for _ in range(len(bakes)):
        values.append([])

    with maya_helpers.restores() as restores:
        # Disable stepped preview while we do this.
        restores.append(
            maya_helpers.SetAndRestoreCmd(pm.playbackOptions,
                                          key='blockingAnim',
                                          value=False))

        # Temporarily disconnect any transform connections.  If there are already keyframes
        # connected, calling pm.matchTransform will have no effect.  These connections will
        # be restored when this restores() block exits.
        for bake in bakes:

            def disconnect_attrs(attr):
                for channel in ('x', 'y', 'z'):
                    restores.append(
                        maya_helpers.SetAndRestoreAttr(
                            bake.dst.attr(attr + channel), 1))
                restores.append(
                    maya_helpers.SetAndRestoreAttr(bake.dst.attr(attr),
                                                   (1, 1, 1)))

            if bake.position: disconnect_attrs('t')
            if bake.rotation: disconnect_attrs('r')
            if bake.scale: disconnect_attrs('s')

        # Read the position on each frame.  We'll read all values, then write all results at once.
        for frame in frame_range:
            if (frame % update_progress_every) == 0:
                progress.update()

            mtime.setValue(frame)
            with MDGContextGuard(om.MDGContext(mtime)) as guard:
                for idx, bake in enumerate(bakes):
                    pm.matchTransform(bake.dst,
                                      bake.src,
                                      pos=True,
                                      rot=True,
                                      scl=True)

                    # Store the resulting transform values.
                    values[idx].append(
                        (bake.dst.t.get(), bake.dst.r.get(), bake.dst.s.get()))

    # Now that the above restores block has exited, any connections to the transform
    # will be restored.  Apply the transforms we stored now that we're no longer in
    # an MDGContext.
    with maya_helpers.restores() as restores:
        # Disable auto-keyframe while we do this.  Otherwise, a keyframe will also
        # be added at the current frame.
        restores.append(
            maya_helpers.SetAndRestoreCmd(pm.autoKeyframe,
                                          key='state',
                                          value=False))

        current_frame = pm.currentTime(q=True)

        # Set each destination node's transform on each frame.
        for idx, values_for_node in enumerate(values):
            dst = bakes[idx].dst

            for frame, (t, r, s) in zip(frame_range, values_for_node):
                if (frame % update_progress_every) == 0:
                    progress.update()

                # Work around some character set quirks.  If we set a keyframe with
                # pm.setKeyframe on the current frame, we need to also set it explicitly
                # on the attribute too, or else the keyframe won't always have the
                # correct value.
                def set_keyframe(attr, value):
                    if frame == current_frame:
                        dst.attr(attr).set(value)
                    pm.setKeyframe(dst, at=attr, time=frame, value=value)

                if bake.position:
                    set_keyframe('tx', t[0])
                    set_keyframe('ty', t[1])
                    set_keyframe('tz', t[2])
                if bake.rotation:
                    set_keyframe('rx', r[0])
                    set_keyframe('ry', r[1])
                    set_keyframe('rz', r[2])
                if bake.scale:
                    set_keyframe('sx', s[0])
                    set_keyframe('sy', s[1])
                    set_keyframe('sz', s[2])
Пример #10
0
def traceArc(space='camera'):
    '''
    The main function for creating the arc.
    '''
    
    if space != 'world' and space != 'camera':
        OpenMaya.MGlobal.displayWarning('Improper space argument.')
        return
    
    global ML_TRACE_ARC_PREVIOUS_SELECTION
    global ML_TRACE_ARC_PREVIOUS_SPACE
    
    #save for reset:
    origTime = mc.currentTime(query=True)
    
    #frame range
    frameRange = utl.frameRange()
    start = frameRange[0]
    end = frameRange[1]
    
    #get neccesary nodes
    objs = mc.ls(sl=True, type='transform')
    if not objs:
        OpenMaya.MGlobal.displayWarning('Select objects to trace')
        return
    
    ML_TRACE_ARC_PREVIOUS_SELECTION = objs
    ML_TRACE_ARC_PREVIOUS_SPACE = space
    
    cam = None
    nearClipPlane = None
    shortCam = ''
    if space=='camera':
        cam = utl.getCurrentCamera()
    
        #the arc will be placed just past the clip plane distance, but no closer than 1 unit.
        nearClipPlane = max(mc.getAttr(cam+'.nearClipPlane'),1)
        
        shortCam = mc.ls(cam, shortNames=True)[0]
    
    topGroup = 'ml_arcGroup'
    worldGrp = 'ml_arcWorldGrp'
    localGrp = 'ml_localGrp_'+shortCam
    
    #create nodes
    if not mc.objExists(topGroup):
        topGroup = mc.group(empty=True, name=topGroup)
    
    parentGrp = topGroup
    if space=='world' and not mc.objExists(worldGrp):
        worldGrp = mc.group(empty=True, name=worldGrp)
        mc.setAttr(worldGrp+'.overrideEnabled',1)
        mc.setAttr(worldGrp+'.overrideDisplayType',2)
        mc.parent(worldGrp, topGroup)
        parentGrp = mc.ls(worldGrp)[0]
    
    if space == 'camera':
        camConnections = mc.listConnections(cam+'.message', plugs=True, source=False, destination=True)
        if camConnections:
            for cc in camConnections:
                if '.ml_parentCam' in cc:
                    localGrp = mc.ls(cc, o=True)[0]
        
        if not mc.objExists(localGrp):
            localGrp = mc.group(empty=True, name=localGrp)
            mc.parentConstraint(cam, localGrp)
            mc.setAttr(localGrp+'.overrideEnabled',1)
            mc.setAttr(localGrp+'.overrideDisplayType',2)
            mc.parent(localGrp, topGroup)
            
            mc.addAttr(localGrp, at='message', longName='ml_parentCam')
            mc.connectAttr(cam+'.message', localGrp+'.ml_parentCam')
            
        parentGrp = mc.ls(localGrp)[0]
    
    #group per object:
    group = list()
    points = list()
    
    for i,obj in enumerate(objs):
        sn = mc.ls(obj,shortNames=True)[0]
        name = sn.replace(':','_')
    
        points.append(list())
        groupName = 'ml_%s_arcGrp' % name
        if mc.objExists(groupName):
            mc.delete(groupName)
        
        group.append(mc.group(empty=True, name=groupName))
        
        group[i] = mc.parent(group[i],parentGrp)[0]
        mc.setAttr(group[i]+'.translate', 0,0,0)
        mc.setAttr(group[i]+'.rotate', 0,0,0)
    
    with utl.UndoChunk():
    
        #helper locator
        loc = mc.spaceLocator()[0]
        mc.parent(loc,parentGrp)
        
        camSample = None
        if space=='camera':
            camSample = mc.spaceLocator()[0]
            mc.pointConstraint(cam, camSample)
        
        for i,obj in enumerate(objs):
            sample = mc.spaceLocator()[0]
            mc.pointConstraint(obj, sample)

            #frame loop:
            time = range(int(start),int(end+1))
            for t in time:
                objPnt = list()
                for attr in ('.tx','.ty','.tz'):
                    mSelectionList = OpenMaya.MSelectionList()
                    mSelectionList.add(sample+attr)
                    plug = OpenMaya.MPlug()
                    mSelectionList.getPlug(0, plug)
                    context = OpenMaya.MDGContext(OpenMaya.MTime(t))
                    objPnt.append(plug.asDouble(context))
                
                if space=='camera':
                    camPnt = list()
                    for attr in ('.tx','.ty','.tz'):
                        mSelectionList = OpenMaya.MSelectionList()
                        mSelectionList.add(camSample+attr)
                        plug = OpenMaya.MPlug()
                        mSelectionList.getPlug(0, plug)
                        context = OpenMaya.MDGContext(OpenMaya.MTime(t))
                        camPnt.append(plug.asDouble(context))
                        
                    #camPnt = mc.xform(cam, query=True, worldSpace=True, rotatePivot=True)

                    objVec = utl.Vector(objPnt[0],objPnt[1],objPnt[2])
                    camVec = utl.Vector(camPnt[0],camPnt[1],camPnt[2])

                    vec = objVec-camVec
                    vec.normalize()
                    #multiply here to offset from camera
                    vec=vec*nearClipPlane*1.2
                    vec+=camVec

                    mc.xform(loc, worldSpace=True, translation=vec[:])

                    trans = mc.getAttr(loc+'.translate')
                    points[i].append(trans[0]) 

                elif space=='world':
                    points[i].append(objPnt)
                    
            mc.delete(sample)

        mc.delete(loc)
        if camSample:
            mc.delete(camSample)

        #create the curves and do paint effects
        mc.ResetTemplateBrush()
        brush = mc.getDefaultBrush()
        mc.setAttr(brush+'.screenspaceWidth',1)
        mc.setAttr(brush+'.distanceScaling',0)
        mc.setAttr(brush+'.brushWidth',0.005)

        for i,obj in enumerate(objs):

            #setup brush for path
            mc.setAttr(brush+'.screenspaceWidth',1)
            mc.setAttr(brush+'.distanceScaling',0)
            mc.setAttr(brush+'.brushWidth',0.003)

            #color
            for c in ('R','G','B'):
                color = random.uniform(0.3,0.7)
                mc.setAttr(brush+'.color1'+c,color)
            
            baseCurve = mc.curve(d=3,p=points[i])
            #fitBspline makes a curve that goes THROUGH the points, a more accurate path
            curve = mc.fitBspline(baseCurve, constructionHistory=False, tolerance=0.001)
            mc.delete(baseCurve)

            #paint fx
            mc.AttachBrushToCurves(curve)
            stroke = mc.ls(sl=True)[0]
            stroke = mc.parent(stroke,group[i])[0]

            mc.setAttr(stroke+'.overrideEnabled',1)
            mc.setAttr(stroke+'.overrideDisplayType',2)

            mc.setAttr(stroke+'.displayPercent',92)
            mc.setAttr(stroke+'.sampleDensity',0.5)
            mc.setAttr(stroke+'.inheritsTransform',0)
            mc.setAttr(stroke+'.translate',0,0,0)
            mc.setAttr(stroke+'.rotate',0,0,0)

            curve = mc.parent(curve,group[i])[0]
            mc.setAttr(curve+'.translate',0,0,0)
            mc.setAttr(curve+'.rotate',0,0,0)

            mc.hide(curve)

            #setup brush for tics
            if space=='camera':
                mc.setAttr(brush+'.brushWidth',0.008)
            if space=='world':
                mc.setAttr(brush+'.brushWidth',0.005)
            mc.setAttr(brush+'.color1G',0)
            mc.setAttr(brush+'.color1B',0)

            for t in range(len(points[i])):
                frameCurve = None
                if space=='camera':
                    vec = utl.Vector(points[i][t][0],points[i][t][1],points[i][t][2])
                    vec*=0.98
                    frameCurve = mc.curve(d=1,p=[points[i][t],vec[:]])

                elif space=='world':
                    frameCurve = mc.circle(constructionHistory=False, radius=0.0001, sections=4)[0]
                    mc.setAttr(frameCurve+'.translate', points[i][t][0], points[i][t][1] ,points[i][t][2])
                    constraint = mc.tangentConstraint(curve, frameCurve, aimVector=(0,0,1), worldUpType='scene')
                    #mc.delete(constraint)

                #check for keyframe
                colorAttribute='color1G'
                if mc.keyframe(obj, time=((t+start-0.5),(t+start+0.5)), query=True):
                    mc.setAttr(brush+'.color1R',1)
                else:
                    mc.setAttr(brush+'.color1R',0)

                mc.AttachBrushToCurves(curve)

                stroke = mc.ls(sl=True)[0]
                thisBrush = mc.listConnections(stroke+'.brush', destination=False)[0]

                #setup keyframes for frame highlighting
                mc.setKeyframe(thisBrush, attribute='color1G', value=0, time=(start+t-1, start+t+1))
                mc.setKeyframe(thisBrush, attribute='color1G', value=1, time=(start+t,))

                stroke = mc.parent(stroke,group[i])[0]

                mc.hide(frameCurve)

                mc.setAttr(stroke+'.displayPercent',92)
                mc.setAttr(stroke+'.sampleDensity',0.5)

                frameCurve = mc.parent(frameCurve,group[i])[0]

                if space=='camera':
                    mc.setAttr(stroke+'.inheritsTransform',0)
                    mc.setAttr(stroke+'.pressureScale[1].pressureScale_Position', 1)
                    mc.setAttr(stroke+'.pressureScale[1].pressureScale_FloatValue', 0)
                    mc.setAttr(stroke+'.translate',0,0,0)
                    mc.setAttr(stroke+'.rotate',0,0,0)
                    mc.setAttr(frameCurve+'.translate',0,0,0)
                    mc.setAttr(frameCurve+'.rotate',0,0,0)

        mc.currentTime(origTime, edit=True)
        panel = mc.getPanel(withFocus=True)
        try:
            mc.modelEditor(panel, edit=True, strokes=True)
        except:
            pass
    
    mc.select(objs,replace=True)
Пример #11
0
def spinWheels():

    wheels = []
    namespaces = []
    l = mc.ls(sl=True, o=True) or []
    for n in l:
        namespaces.append(":".join(s for s in n.split(":")[:-1]))
    for ns in set(namespaces):
        if ns == "":
            wheels += mc.ls("wheel*_ctrl.spin", o=True, typ="transform") or []
        else:
            wheels += mc.ls(ns + ":wheel*_ctrl.spin", o=True,
                            typ="transform") or []
    if len(wheels) == 0: return

    mm.eval("source channelBoxCommand")

    d = {}
    for wheel in wheels:
        try:
            mc.setAttr(wheel + ".spin", l=False)
        except:
            continue
        mm.eval("CBdeleteConnection " + wheel + ".spin")
        if not ":" in wheel: cog = "cog_ctrl"
        else: cog = ":".join(s for s in wheel.split(":")[:-1]) + ":cog_ctrl"
        n = mc.createNode("transform", p=cog)
        mc.pointConstraint(wheel, n)
        mc.orientConstraint(cog, n)
        d[n] = [wheel, mc.getAttr(wheel + ".size")]
    if len(d) == 0: return

    sf = int(mc.playbackOptions(q=True, min=True))
    ef = int(mc.playbackOptions(q=True, max=True)) + 1

    mm.eval("paneLayout -e -vis 0 $gMainPane")

    mc.bakeResults(d.keys(),
                   sm=True,
                   t=(sf - 1, ef),
                   sb=1,
                   dic=False,
                   pok=False,
                   sac=False,
                   cp=False,
                   s=False)

    d2 = {}
    t = om.MTime()
    p = om.MPlug()
    for n in d.viewkeys():
        sl = om.MSelectionList()
        sl.add(n + ".worldMatrix")
        sl.getPlug(0, p)

        spin = 0.0
        for i in range(sf, ef):
            t.setValue(i)
            dgc = om.MDGContext(t)
            o = p.asMObject(dgc)
            m = om.MFnMatrixData(o).matrix()
            tm = om.MTransformationMatrix(m)
            cp = tm.getTranslation(om.MSpace.kObject)

            t.setValue(float(i - 1))
            dgc = om.MDGContext(t)
            o = p.asMObject(dgc)
            m = om.MFnMatrixData(o).matrix()
            tm = om.MTransformationMatrix(m)
            pp = tm.getTranslation(om.MSpace.kObject)

            spin += ((cp.z - pp.z) / d[n][1]) * 57.2957795
            try:
                mc.setKeyframe(d[n][0] + ".spin", t=float(i), v=spin)
            except:
                pass

    mc.delete(d.keys())

    mm.eval("paneLayout -e -vis 1 $gMainPane")