Reusing Scripts across Products
In a previous blog post I talked about the idea of reusing scripts across products. To do this I was experimenting with creating Python libraries that presented a new common API to different product APIs. This is a huge amount of work and isn't something one can easily do in their spare tiime.
For those feeling brave and adventurous I'll describe an unofficial (meaning: use at your own risk) scripting workflow created by my colleague Martin Ashton that appears to be a feasible way to leverage the FBX SDK to reuse scripts within Maya and MotionBuilder.
For those brave enough to continue, go put on your adventure's cap!
Getting Started
First download the FBX Python SDK 2013.1.
Next download the package fbxUtils.zip. This contains a module fbxUtils.py implemented for MotionBuilder and for Maya and a sample file: addCubeTest.py.
Steps for Maya 2013:
1) Depending on whether you have a 64-bit or 32-bit version of MotionBuilder navigate to
C:\Program Files\Autodesk\FBX\FbxPythonSdk\2013.1\lib\Python31_x64
C:\Program Files\Autodesk\FBX\FbxPythonSdk\2013.1\lib\Python31_x86
2) Copy the files (fbx.pyd, sip.pyd, and FbxCommon.py) to \PythonLib\site-packages sub-folder of your Maya installation (e.g. "C:\Program Files\Autodesk\Maya2013\Python\Lib\site-packages")
3) Copy the file "maya\fbxUtils.py" to the same location
4) Open and execute the file "addCubeTest.py"
Steps for MotionBuilder 2013:
1) Depending on whether you have a 64-bit or 32-bit version of MotionBuilder navigate to
C:\Program Files\Autodesk\FBX\FbxPythonSdk\2013.1\lib\Python31_x64
C:\Program Files\Autodesk\FBX\FbxPythonSdk\2013.1\lib\Python31_x86
2) Copy the files (fbx.pyd, sip.pyd, and FbxCommon.py) to \bin\x64\python\lib or \bin\x86\python\lib
sub-folder of your MotionBuilder installation (e.g. "C:\Program Files\Autodesk\MotionBuilder Danko (64-bit)\bin\x64\python\lib"
3) Copy the file "mobu\fbxUtil.py" to the same location
4) Open and execute the file "addCubeTest.py"
Show me the Code
Here is the source code for addCubeTest.py:
import fbx import fbxUtil # A set of vertices which we will use to create a cube in the scene. cubeVertices = [ fbx.FbxVector4( -5, -5, 5 ), # 0 - vertex index. fbx.FbxVector4( 5, -5, 5 ), # 1 fbx.FbxVector4( 5, 5, 5 ), # 2 fbx.FbxVector4( -5, 5, 5 ), # 3 fbx.FbxVector4( -5, -5, -5 ), # 4 fbx.FbxVector4( 5, -5, -5 ), # 5 fbx.FbxVector4( 5, 5, -5 ), # 6 fbx.FbxVector4( -5, 5, -5 )] # 7 # The polygons (faces) of the cube. polygonVertices = [ ( 0, 1, 2, 3 ), # Face 1 ( 4, 5, 6, 7 ), # Face 2 ( 8, 9, 10, 11 ), # Face 3 ( 12, 13, 14, 15 ), # Face 4 ( 16, 17, 18, 19 ), # Face 5 ( 20, 21, 22, 23 )] # Face 6 ############################################### # Helper Function(s). ############################################### def addCube( pScene ): ''' Adds a cubic mesh to the scene. ''' # Obtain a reference to the scene's root node. rootNode = pScene.GetRootNode() # Create a new node in the scene. newNode = fbx.FbxNode.Create( pScene, 'myNode' ) rootNode.AddChild( newNode ) # Create a new mesh node attribute in the scene, and set it as the new node's attribute newMesh = fbx.FbxMesh.Create( pScene, 'myMesh' ) newNode.SetNodeAttribute( newMesh ) # Define the new mesh's control points. Since we are defining a cubic mesh, # there are 4 control points per face, and there are six faces, for a total # of 24 control points. global cubeVertices newMesh.InitControlPoints( 24 ) # Face 1 newMesh.SetControlPointAt( cubeVertices[0], 0 ) newMesh.SetControlPointAt( cubeVertices[1], 1 ) newMesh.SetControlPointAt( cubeVertices[2], 2 ) newMesh.SetControlPointAt( cubeVertices[3], 3 ) # Face 2 newMesh.SetControlPointAt( cubeVertices[1], 4 ) newMesh.SetControlPointAt( cubeVertices[5], 5 ) newMesh.SetControlPointAt( cubeVertices[6], 6 ) newMesh.SetControlPointAt( cubeVertices[2], 7 ) # Face 3 newMesh.SetControlPointAt( cubeVertices[5], 8 ) newMesh.SetControlPointAt( cubeVertices[4], 9 ) newMesh.SetControlPointAt( cubeVertices[7], 10 ) newMesh.SetControlPointAt( cubeVertices[6], 11 ) # Face 4 newMesh.SetControlPointAt( cubeVertices[4], 12 ) newMesh.SetControlPointAt( cubeVertices[0], 13 ) newMesh.SetControlPointAt( cubeVertices[3], 14 ) newMesh.SetControlPointAt( cubeVertices[7], 15 ) # Face 5 newMesh.SetControlPointAt( cubeVertices[3], 16 ) newMesh.SetControlPointAt( cubeVertices[2], 17 ) newMesh.SetControlPointAt( cubeVertices[6], 18 ) newMesh.SetControlPointAt( cubeVertices[7], 19 ) # Face 6 newMesh.SetControlPointAt( cubeVertices[1], 20 ) newMesh.SetControlPointAt( cubeVertices[0], 21 ) newMesh.SetControlPointAt( cubeVertices[4], 22 ) newMesh.SetControlPointAt( cubeVertices[5], 23 ) # Now that the control points per polygon have been defined, we can create # the actual polygons within the mesh. global polygonVertices for i in range( 0, len( polygonVertices ) ): newMesh.BeginPolygon( i ) for j in range( 0, len( polygonVertices[i] ) ): newMesh.AddPolygon( polygonVertices[i][j] ) newMesh.EndPolygon() ##################################### # Main. ##################################### def myMainFunction( pFbxManager, pFbxScene ): ''' myMainFunction is executed in fbxUtil.doItInFBX(). ''' addCube( pFbxScene ) fbxUtil.doItInFBX( myMainFunction )
How It Works
The trick used to get the same script working in both packages lies in a single function "fbxUtil.doItInFbx()" that is implemented differently for Maya and MotionBuilder. The doItInFbx() function takes another function as an argument that must accept two arguments: an FBXManager instance and an FBXScene.
Here are the steps that the doItInFbx() takes:
1) Exports the current Maya/MotionBuilder scene to an FBX file
2) Initializes the FBX SDK
3) Loads the FBX file
4) Calls the passed function with an instance of FbxManager and FbxScene
5) Saves the modified FBX scene back to the FBX file
6) Import the new FBX file in 3ds max.
So while it is a bit heavy-handed going through the FBX import/export process, it does seem to achieve the desired goal.
You should now see a wireframe cube like the following Maya screen shot:
or the following MotionBuilder screen shot.
Note that we haven't tested this system beyond the simple sample script included. I would be very interested in hearing your feedback on whether you find uses for this workflow for less trivial tasks. Do you think it is worthwhile to have the FBX Python SDK should be accessible from all products?
Acknowledgements
This was inspired by a suggestion made by Dean Edmonds at the M&E ADN Conference at AU 2011 (you should go this year, I am!) for using the FBX SDK as a common object model between products.
And a huge thank you to Martin Ashton for writing these awesome scripts!