- Main Page
- Related Pages
- Modules
- Classes
- Examples

# slopeShader.py

    #-
    # ==========================================================================
    # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All
    # rights reserved.
    #
    # The coded instructions, statements, computer programs, and/or related
    # material (collectively the "Data") in these files contain unpublished
    # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
    # licensors, which is protected by U.S. and Canadian federal copyright
    # law and by international treaties.
    #
    # The Data is provided for use exclusively by You. You have the right
    # to use, modify, and incorporate this Data into other products for
    # purposes authorized by the Autodesk software license agreement,
    # without fee.
    #
    # The copyright notices in the Software and this entire statement,
    # including the above license grant, this restriction and the
    # following disclaimer, must be included in all copies of the
    # Software, in whole or in part, and all derivative works of
    # the Software, unless such copies or derivative works are solely
    # in the form of machine-executable object code generated by a
    # source language processor.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
    # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
    # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
    # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
    # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
    # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
    # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
    # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
    # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
    # OR PROBABILITY OF SUCH DAMAGES.
    #
    # ==========================================================================
    #+

    # import maya
    # maya.cmds.loadPlugin("slopeShader.py")
    #
    # Create a sphere and assign a shader such as lambert to it.
    # Select the color channel and link it to Utilities/Sp SlopeShader
    # Render the scene

    # imports
    import maya.OpenMaya as OpenMaya
    import maya.OpenMayaUI as OpenMayaUI
    import maya.OpenMayaMPx as OpenMayaMPx
    import math, sys

    # consts
    kSlopeShaderBehaviourName = "slopeShaderBehaviour"
    kSlopeNodeName = "spSlopeShader"
    kSlopeNodeClassify = "utility/color"
    kSlopeNodeId = OpenMaya.MTypeId(0x87001)

    # Node definition
    class slopeShader(OpenMayaMPx.MPxNode):
        # class variables
        aAngle = OpenMaya.MObject()
        aColor1 = OpenMaya.MObject()
        aColor2 = OpenMaya.MObject()
        aTriangleNormalCamera = OpenMaya.MObject()
        aOutColor = OpenMaya.MObject()
        aMatrixEyeToWorld = OpenMaya.MObject()
        aDirtyShaderAttr = OpenMaya.MObject()

        def __init__(self):
            OpenMayaMPx.MPxNode.__init__(self)

        def compute(self, plug, dataBlock):
            if plug == slopeShader.aOutColor or plug.parent() == slopeShader.aOutColor:

                resultColor = OpenMaya.MFloatVector(0.0,0.0,0.0)

                try:
                    dataHandle = dataBlock.inputValue( slopeShader.aColor1 )
                except:
                    sys.stderr.write( "Failed to get inputValue aColor1" )
                    raise
                walkable = dataHandle.asFloatVector()

                try:
                    dataHandle = dataBlock.inputValue( slopeShader.aColor2 )
                except:
                    sys.stderr.write( "Failed to get inputValue aColor2" )
                    raise
                nonWalkable = dataHandle.asFloatVector()

                try:
                    dataHandle = dataBlock.inputValue( slopeShader.aTriangleNormalCamera )
                except:
                    sys.stderr.write( "Failed to get inputValue aTriangleNormalCamera" )
                    raise
                surfaceNormal = dataHandle.asFloatVector()

                try:
                    dataHandle = dataBlock.inputValue( slopeShader.aMatrixEyeToWorld )
                except:
                    sys.stderr.write( "Failed to get inputValue aMatrixEyeToWorld" )
                    raise
                viewMatrix = dataHandle.asFloatMatrix()

                # Get the angle
                try:
                    dataHandle = dataBlock.inputValue( slopeShader.aAngle )
                except:
                    sys.stderr.write( "Failed to get inputValue aAngle" )
                    raise
                angle = dataHandle.asFloat()

                yVector = OpenMaya.MFloatVector(0, 1, 0)

                # Normalize the view vector
                #
                surfaceNormal.normalize()
                WSVector = surfaceNormal * viewMatrix

                # find dot product
                #
                scalarNormal = WSVector * yVector

                # take the absolute value
                #
                if scalarNormal < 0.0:
                    scalarNormal *= -1.0

                radianAngle = math.radians(angle)
                cosOfAngle = math.cos(radianAngle)
                if cosOfAngle < scalarNormal:
                    resultColor = walkable
                else:
                    resultColor = nonWalkable

                # set output color attribute
                #
                try:
                    outColorHandle = dataBlock.outputValue( slopeShader.aOutColor )
                except:
                    sys.stderr.write( "Failed to get outputValue aOutColor" )
                    raise

                outColorHandle.setMFloatVector(resultColor)
                outColorHandle.setClean()

            else:
                return OpenMaya.kUnknownParameter

        def postConstructor(self):
            print "In slopeShader.postConstructor"
            # OpenMayaMPx.MPxNode.setMPSafe(self, 1)

    class slopeShaderBehavior(OpenMayaMPx.MPxDragAndDropBehavior):
        def __init__(self):
            OpenMayaMPx.MPxDragAndDropBehavior.__init__(self)

        def shouldBeUsedFor(self, sourceNode, destinationNode, sourcePlug, destinationPlug):
            """
            Overloaded function from MPxDragAndDropBehavior
            this method will return True if it is going to handle the connection
            between the two nodes given.
            """
            result = False

            if sourceNode.hasFn(OpenMaya.MFn.kLambert):
                #if the source node was a lambert
                #than we will check the downstream connections to see
                #if a slope shader is assigned to it.
                #
                shaderNode = None
                src = OpenMaya.MFnDependencyNode(sourceNode)
                connections = OpenMaya.MPlugArray()
                src.getConnections(connections)

                for i in range(connections.length()):
                    #check the incoming connections to this plug
                    #
                    connectedPlugs = OpenMaya.MPlugArray()
                    connections[i].connectedTo(connectedPlugs, True, False)
                    for j in range(connectedPlugs.length()):
                        #if the incoming node is a slope shader than
                        #set shaderNode equal to it and break out of the inner
                        #loop
                        #
                        testNode = OpenMaya.MFnDependencyNode(connectedPlugs[j].node())
                        if testNode.typeName() == kSlopeNodeName:
                            shaderNode = connectedPlugs[j].node()
                            break

                    #if the shaderNode is not None
                    #than we have found a slopeShader
                    #
                    if shaderNode is not None:
                        #if the destination node is a mesh than we will take
                        #care of this connection so set the result to True
                        #and break out of the outer loop
                        #
                        if destinationNode.hasFn(OpenMaya.MFn.kMesh):
                            result = True
                        break

            node = OpenMaya.MFnDependencyNode(sourceNode)
            if node.typeName() == kSlopeNodeName:
                #if the sourceNode is a slope shader than check what we
                #are dropping on to
                #
                if destinationNode.hasFn(OpenMaya.MFn.kLambert):
                    result = True
                elif destinationNode.hasFn(OpenMaya.MFn.kMesh):
                    result = True

            return result

        def connectNodeToNode(self, sourceNode, destinationNode, force):
            """
            Overloaded function from MPxDragAndDropBehavior
            this method will handle the connection between the slopeShader and the shader it is
            assigned to as well as any meshes that it is assigned to.
            """
            src = OpenMaya.MFnDependencyNode(sourceNode)

            #if we are dragging from a lambert
            #we want to check what we are dragging
            #onto.
            if sourceNode.hasFn(OpenMaya.MFn.kLambert):
                shaderNode = OpenMaya.MObject()
                connections = OpenMaya.MPlugArray()
                shaderNodes = OpenMaya.MObjectArray()
                shaderNodes.clear()

                #if the source node was a lambert
                #than we will check the downstream connections to see
                #if a slope shader is assigned to it.
                #
                src.getConnections(connections)
                for i in range(connections.length()):
                    #check the incoming connections to this plug
                    #
                    connectedPlugs = OpenMaya.MPlugArray()
                    connections[i].connectedTo(connectedPlugs, True, False)
                    for j in range(connectedPlugs.length()):
                        #if the incoming node is a slope shader than
                        #append the node to the shaderNodes array
                        #
                        currentnode = connectedPlugs[j].node()
                        if OpenMaya.MFnDependencyNode(currentnode).typeName() == kSlopeNodeName:
                            shaderNodes.append(currentnode)

                #if we found a shading node
                #than check the destination node
                #type to see if it is a mesh
                #
                if shaderNodes.length() > 0:
                    dest = OpenMaya.MFnDependencyNode(destinationNode)
                    if destinationNode.hasFn(OpenMaya.MFn.kMesh):
                        #if the node is a mesh than for each slopeShader
                        #connect the worldMesh attribute to the dirtyShaderPlug
                        #attribute to force an evaluation of the node when the mesh
                        #changes
                        #
                        for i in range(shaderNodes.length()):
                            srcPlug = dest.findPlug("worldMesh")
                            destPlug = OpenMaya.MFnDependencyNode(shaderNodes[i]).findPlug("dirtyShaderPlug")

                            if (not srcPlug.isNull()) and (not destPlug.isNull()):
                                cmd = "connectAttr -na %s %s" % (srcPlug.name(), destPlug.name())
                                OpenMaya.MGlobal.executeCommand(cmd)

                        #get the shading engine so we can assign the shader
                        #to the mesh after doing the connection
                        #
                        shadingEngine = self._findShadingEngine(sourceNode)

                        #if there is a valid shading engine than make
                        #the connection
                        #
                        if not shadingEngine.isNull():
                            cmd = "sets -edit -forceElement %s %s" % (
                                            OpenMaya.MFnDependencyNode(shadingEngine).name(),
                                            OpenMaya.MFnDagNode(destinationNode).partialPathName()
                                                                    )
                            OpenMaya.MGlobal.executeCommand(cmd)

            elif src.typeName() == kSlopeNodeName:
                #if we are dragging from a slope shader
                #than we want to see what we are dragging onto
                #
                if destinationNode.hasFn(OpenMaya.MFn.kMesh):
                    #if the user is dragging onto a mesh
                    #than make the connection from the worldMesh
                    #to the dirtyShader plug on the slopeShader
                    #
                    dest = OpenMaya.MFnDependencyNode(destinationNode)
                    srcPlug = dest.findPlug("worldMesh")
                    destPlug = src.findPlug("dirtyShaderPlug")
                    if (not srcPlug.isNull()) and (not destPlug.isNull()):
                        cmd = "connectAttr -na %s %s" % (srcPlug.name(), destPlug.name())
                        OpenMaya.MGlobal.executeCommand(cmd)

                elif destinationNode.hasFn(OpenMaya.MFn.kLambert):
                    dest = OpenMaya.MFnDependencyNode(destinationNode)
                    srcPlug = src.findPlug("outColor")
                    destPlug = dest.findPlug("color")
                    if (not srcPlug.isNull()) and (not destPlug.isNull()):
                        if force:
                            cmd = "connectAttr -f %s %s" % (srcPlug.name(), destPlug.name())
                        else:
                            cmd = "connectAttr %s %s" % (srcPlug.name(), destPlug.name())
                        OpenMaya.MGlobal.executeCommand(cmd)

        def connectNodeToAttr(self, sourceNode, destinationPlug, force):
            """
            Overloaded function from MPxDragAndDropBehavior
            this method will assign the correct output from the slope shader
            onto the given attribute.
            """
            src = OpenMaya.MFnDependencyNode(sourceNode)

            #if we are dragging from a slopeShader
            #to a shader than connect the outColor
            #plug to the plug being passed in
            #
            if destinationPlug.node().hasFn(OpenMaya.MFn.kLambert):
                if src.typeName() == kSlopeNodeName:
                    srcPlug = src.findPlug("outColor")
                    if (not srcPlug.isNull()) and (not destinationPlug.isNull()):
                        cmd = "connectAttr %s %s" % (srcPlug.name(), destinationPlug.name())
                        OpenMaya.MGlobal.executeCommand(cmd)
            else:
                #in all of the other cases we do not need the plug just the node
                #that it is on
                #
                destinationNode = destinationPlug.node()
                self.connectNodeToNode(sourceNode, destinationNode, force)

        def _findShadingEngine(self, node):
            """
            Given the material MObject this method will
            return the shading group that it is assigned to.
            if there is no shading group associated with
            the material than a null MObject is passed back.
            """
            nodeFn = OpenMaya.MFnDependencyNode (node)
            srcPlug = nodeFn.findPlug("outColor")
            nodeConnections = OpenMaya.MPlugArray()
            srcPlug.connectedTo(nodeConnections, False, True)
            #loop through the connections
            #and find the shading engine node that
            #it is connected to
            #
            for i in range(nodeConnections.length()):
                theNode = nodeConnections[i].node()
                if theNode.hasFn(OpenMaya.MFn.kShadingEngine):
                    return theNode

            #no shading engine associated so return a
            #null MObject
            #
            return OpenMaya.MObject()

    ##################################################################

    def nodeCreator():
        return OpenMayaMPx.asMPxPtr(slopeShader())

    def behaviourCreator():
        return OpenMayaMPx.asMPxPtr(slopeShaderBehavior())

    def nodeInitializer():
        nAttr = OpenMaya.MFnNumericAttribute()
        nMAttr = OpenMaya.MFnMatrixAttribute()
        nTAttr = OpenMaya.MFnTypedAttribute()
        nGAttr = OpenMaya.MFnGenericAttribute()
        # input
        slopeShader.aAngle = nAttr.create( "angle", "ang", OpenMaya.MFnNumericData.kFloat )
        nAttr.setDefault(30.0)
        nAttr.setMin(0.0)
        nAttr.setMax(100.0)
        nAttr.setKeyable(1)
        nAttr.setStorable(1)
        nAttr.setReadable(1)
        nAttr.setWritable(1)

        slopeShader.aColor1 = nAttr.createColor("walkableColor", "w")
        nAttr.setDefault(0.0, 1.0, 0.0)
        nAttr.setKeyable(1)
        nAttr.setStorable(1)
        nAttr.setUsedAsColor(1)
        nAttr.setReadable(1)
        nAttr.setWritable(1)

        slopeShader.aColor2 = nAttr.createColor( "nonWalkableColor", "nw" )
        nAttr.setDefault(1.0, 0.0, 0.0)
        nAttr.setKeyable(1)
        nAttr.setStorable(1)
        nAttr.setUsedAsColor(1)
        nAttr.setReadable(1)
        nAttr.setWritable(1)

        # Surface Normal supplied by the render sampler
        #
        slopeShader.aTriangleNormalCamera = nAttr.createPoint( "triangleNormalCamera", "n" )
        nAttr.setStorable(0)
        nAttr.setHidden(1)
        nAttr.setReadable(1)
        nAttr.setWritable(1)

        # View matrix from the camera into world space
        #
        slopeShader.aMatrixEyeToWorld = nMAttr.create( "matrixEyeToWorld", "mew", OpenMaya.MFnMatrixAttribute.kFloat )
        nAttr.setHidden(1)
        nMAttr.setWritable(1)

        # Output Attributes
        #
        slopeShader.aOutColor  = nAttr.createColor( "outColor", "oc" )
        nAttr.setStorable(0)
        nAttr.setHidden(0)
        nAttr.setReadable(1)
        nAttr.setWritable(0)

        meshTypeId = OpenMaya.MTypeId(OpenMaya.MFnData.kMesh)

        # dummy plug for forcing evaluation
        #
        slopeShader.aDirtyShaderAttr = nGAttr.create( "dirtyShaderPlug", "dsp")
        nGAttr.setArray(1)
        nGAttr.setHidden(0)
        nGAttr.setUsesArrayDataBuilder(1)
        nGAttr.setReadable(0)
        nGAttr.setStorable(1)
        nGAttr.setIndexMatters(0)
        # nGAttr.addAccept(meshTypeId)

        # Add attribues
        #
        slopeShader.addAttribute(slopeShader.aAngle)
        slopeShader.addAttribute(slopeShader.aColor1)
        slopeShader.addAttribute(slopeShader.aColor2)
        slopeShader.addAttribute(slopeShader.aTriangleNormalCamera)
        slopeShader.addAttribute(slopeShader.aOutColor)
        slopeShader.addAttribute(slopeShader.aMatrixEyeToWorld)
        slopeShader.addAttribute(slopeShader.aDirtyShaderAttr)

        slopeShader.attributeAffects (slopeShader.aAngle,  slopeShader.aOutColor)
        slopeShader.attributeAffects (slopeShader.aColor1, slopeShader.aOutColor)
        slopeShader.attributeAffects (slopeShader.aColor2, slopeShader.aOutColor)
        slopeShader.attributeAffects (slopeShader.aTriangleNormalCamera,  slopeShader.aOutColor)
        slopeShader.attributeAffects (slopeShader.aDirtyShaderAttr, slopeShader.aOutColor)

    # initialize the script plug-in
    def initializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject)

        # register node
        try:
            mplugin.registerNode(kSlopeNodeName, kSlopeNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, kSlopeNodeClassify)
        except:
            sys.stderr.write("Failed to register node: %s" % kSlopeNodeName )
            raise

        # register behaviors
        try:
            mplugin.registerDragAndDropBehavior(kSlopeShaderBehaviourName, behaviourCreator)
        except:
            sys.stderr.write("Failed to register behaviour: %s" % kSlopeShaderBehaviourName)

        postCmd = "if( `window -exists createRenderNodeWindow` )  {refreshCreateRenderNodeWindow(\"%s\");}\n" % kSlopeNodeClassify
        OpenMaya.MGlobal.executeCommand(postCmd)

    # uninitialize the script plug-in
    def uninitializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
        try:
            mplugin.deregisterNode(kSlopeNodeId)
        except:
            sys.stderr.write( "Failed to unregister node: %s" % kSlopeNodeName )
            raise

        try:
            plugin.deregisterDragAndDropBehavior(kSlopeShaderBehaviourName)
        except:
            sys.stderr.write("Failed to deregister behaviour: %s" % kSlopeShaderBehaviourName)

        postCmd = "if( `window -exists createRenderNodeWindow` )  {refreshCreateRenderNodeWindow(\"%s\");}\n" % kSlopeNodeClassify
        OpenMaya.MGlobal.executeCommand(postCmd)