Creating Canvas Ops and setting values with RTVals via Python

EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

Hello everyone,

I've been meaning to make a video for this but haven't had much time but I have a snippet that I created as a test for some recent Kraken work and thought it'd be useful to post here.

What's awesome about Fabric is that you can create KL types in Python. You can even call methods on these types as well. Imagine you had an IK Solver KL type and needed to set it's arguments and evaluate the solver and get it's solved outputs, all from Python. Well, YOU CAN! :)

For this example though I'm just going to show how to create simple types, create a Canvas Op, and set it's ports via RTVals. Users can expand on this as a lesson.

# ===============================
# Softimage Python Script, easily converted to Maya
# ===============================
import json
import FabricEngine.Core as fabric

si = Application
log = LogMessage
sel = si.Selection

# Create Null to host Canvas Op on
scnRoot = si.ActiveProject3.ActiveScene.Root
null = scnRoot.AddNull("myCanvasHost")

# Create Canvas Op and port of Scalar Array type
canvasOpPath = si.FabricCanvasOpApply(null.FullName, "", True, "", "")
si.FabricCanvasAddPort(canvasOpPath, "", "inScalar", "In", "Scalar[]", "", "")

# Load Fabric Client in Python, load Math Ext, and create RTVal Scalar Array
client = fabric.createClient()
client.loadExtension("Math")

# Create RTVal Scalar array and fill it with a Python list
rtVal = client.RT.types.Scalar.createArray([3, 2, 1])

# Convert RTVal to JSON object type and then to string
scalarArray = rtVal.getJSON().getSimpleType()

# Set the Scalar Array on the Canvas Port with string
si.FabricCanvasSetArgValue(canvasOpPath, "inScalar", "Scalar[]", scalarArray)

client.close()

With the above script you can replace the Python list [3,21] with a list that is generated from values inside Softimage, say some Envelope weights for a bone that is driving a mesh, and feed that data back into a Canvas Op on a port. :)

Hope this helps people tinker a bit.

Eric Thivierge
Kraken Developer
Kraken Rigging Framework

Comments

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭
    edited March 2016

    Thanks for sharing. The example seems heavily simplified, so something more powerful really showing the strengths of this would be greatly welcome as well.

    I had a go at converting this to Maya:

    # ========================================
    # Maya Python Script, easily converted to Softimage (pun intended)
    # ========================================
    import json
    import FabricEngine.Core as fabric
    import maya.cmds as cmds
    
    # Create Canvas Node and port of Scalar Array type
    canvasNode = cmds.createNode("canvasNode")
    cmds.FabricCanvasAddPort(mayaNode=canvasNode, execPath="", desiredPortName="inScalar", p="In", typeSpec="Scalar[]")
    
    # Load Fabric Client in Python, load Math Ext, and create RTVal Scalar Array
    client = fabric.createClient()
    client.loadExtension("Math")
    
    # Create RTVal Scalar array and fill it with a Python list
    rtVal = client.RT.types.Scalar.createArray([3, 2, 1])
    
    # Convert RTVal to JSON object type and then to string
    scalarArray = rtVal.getJSON().getSimpleType()
    
    # Set the Scalar Array on the Canvas Port with string
    cmds.FabricCanvasSetArgValue(mayaNode=canvasNode, n="inScalar", type="Scalar[]", value=scalarArray)
    
    client.close()
    

    Even though this runs fine (no errors) it doesn't seem to actually set the input value. Probably because the attribute that gets created as an in port there is actually a Maya attribute. So instead I suppose there's two ways to make this work. Either make a node within the Canvas graph to actually set the value there and retrieve the results, or maybe make the port opaque in the DCC?

    Note that I used long names for the keyword arguments to be more descriptive except for the name argument with cmds.FabricCanvasSetArgValue. It seems name is an invalid keyword argument (getting errors) even though the documentation states that it would be the full name for the keyword argument.

    I also noticed that the Softimage code doesn't use keyword arguments, where Maya version of these arguments only work with keyword arguments. Is this deliberate?

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    I left out keywords for no reason. It can be put back in. Yes for Maya you would make the ports not exposed add Maya ports.

    I'll expand when I have time to make a more complex example.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭

    I left out keywords for no reason.

    I meant more that I felt it was odd that in Maya doing it without keywords doesn't actually work. In Maya it requires the keyword arguments. ;) Probably a DCC plug-in API thing.

    Yes for Maya you would make the ports not exposed add Maya ports.

    Hope my code helps others getting started even though in this particular state mine doesn't actually trigger setting a value. It would be pretty close though.

    I'll expand when I have time to make a more complex example.

    Sure. I'll try to follow along with Maya conversions of it if needed and I also can find the time on my hands.

    Thanks for sharing the tips!

  • borjaborja Administrator, Fabric Employee Posts: 480 admin

    About the long name not working, I have filed FE-6277 to correct the documentation (you can use -argName instead of -name)

    Borja Morales
    Technical Product Manager
    Fabric Software Inc.

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭

    About the long name not working, I have filed FE-6277 to correct the documentation (you can use -argName instead of -name)

    Thanks. :smile:

  • duncanruddduncanrudd Posts: 22

    These are great examples, thanks guys :)

    I was wondering. Is it possible to use rtVals to read a value from a canvas argument via python? For example, I have an Xfo[] and I want to create a new Xfo preset and set its value to a specific index from the Xfo[]. Or is there a better method for doing this in python?

    Not sure if it makes a difference but I'm working inside Maya

    Thanks

    Dunc

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    Yes you can! I'll need to dig up some more code on how to access an operator and it's ports and grab port values when I have time (I don't right now).

    You can even call methods on objects that are set as ports as I said above. If you have a solver, or other object with methods you can call those and get the RTVal results and convert them to Python types (if they can map) using the getSimpleType() call.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • duncanruddduncanrudd Posts: 22

    @Eric - "when I have time (I don't right now)" - of course, no worries at all!

    I'm more than happy to go digging through documentation / code. Do you have any tips on places to look for this kind of stuff beyond the fabric documentation?

    Thanks again, the support on this forum is incredible - you guys are doing an amazing job!

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    Just the docs really. RTVals and Canvas API

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    Found some time and got some assistance from @Helge for this. This is Softimage code but can be easily translated over:

    from rigging.dcc.si.utils import *
    
    import FabricEngine.Core as fabric
    
    PORT_TYPE_MAP = {
        0: 'In',
        1: 'IO',
        2: 'Out'
    }
    
    
    
    # Get Client
    contextID = si.fabricSplice('getClientContextID')
    if contextID == '':
        si.fabricSplice('constructClient')
        contextID = si.fabricSplice('getClientContextID')
    
    client = fabric.createClient({"contextID": contextID})
    host = client.DFG.host
    
    # Get Binding for Operator
    opBindingID = si.FabricCanvasGetBindingID("null.kine.global.CanvasOp")
    opBinding = host.getBindingForID(opBindingID)
    node = opBinding.getExec()
    
    # Iterate over ports
    for i in xrange(node.getExecPortCount()):
        portName = node.getExecPortName(i)
        portConnectionType = PORT_TYPE_MAP[node.getExecPortType(i)]
        rtVal = opBinding.getArgValue(portName)
        portDataType = rtVal.getTypeName().getSimpleType()
    
        log("==================")
        log(portName)
        log(portConnectionType)
        log(rtVal)
        log(portDataType)
    

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • duncanruddduncanrudd Posts: 22

    @EricT
    Thanks so much for this - I was struggling with the api docs as my programming skills are kind of limited to python. Here's a working Maya version based on what @Roy Nieterau posted above.

    import json
    import FabricEngine.Core as fabric
    import maya.cmds as cmds
    
    PORT_TYPE_MAP = {
        0: 'In',
        1: 'IO',
        2: 'Out'
    }
    
    # Create Canvas Node and port of Scalar Array type
    canvasNode = cmds.createNode("canvasNode")
    cmds.FabricCanvasAddPort(mayaNode=canvasNode, execPath="", desiredPortName="inScalar", p="In", typeSpec="Scalar[]")
    
    # Get Client
    contextID = cmds.fabricSplice('getClientContextID')
    if contextID == '':
        cmds.fabricSplice('constructClient')
        contextID = cmds.fabricSplice('getClientContextID')
    client = fabric.createClient({"contextID": contextID})
    host = client.DFG.host
    
    # Get Binding for Operator
    opBindingID = cmds.FabricCanvasGetBindingID(n=canvasNode)
    opBinding = host.getBindingForID(opBindingID)
    node = opBinding.getExec()
    
    # Create RTVal Scalar array and fill it with a Python list
    rtVal = client.RT.types.Scalar.createArray([3, 2, 1])
    
    # Convert RTVal to JSON object type and then to string
    scalarArray = rtVal.getJSON().getSimpleType()
    
    # Set the Scalar Array on the Canvas Port with string
    cmds.FabricCanvasSetArgValue(mayaNode=canvasNode, n="inScalar", type="Scalar[]", value=scalarArray)
    
    # Iterate over ports
    for i in xrange(node.getExecPortCount()):
        portName = node.getExecPortName(i)
        portConnectionType = PORT_TYPE_MAP[node.getExecPortType(i)]
        rtVal = opBinding.getArgValue(portName)
        portDataType = rtVal.getTypeName().getSimpleType()
    
        print"=================="
        print portName
        print portConnectionType
        print rtVal
        print portDataType
    
    client.close()
    

    Hopefully I can get this to work for my specific application now!

    Thanks again.

    Dunc

Sign In or Register to comment.