I spent some time this weekend trying to figure out how to run an external process and send any updates back to Maya. I'm not too familiar with network programming but with some scrounging around on blogs and stack-overflow of course, I started to get a reasonable understanding of socket communication. After writing and debugging many examples I have a working server, an external process updating that server and several clients that receive the process updates from the server.
My last goal was to have this external process inform Maya and the tool that resides in Maya, of any updates that may need to be made. If you've written any Python and user interfaces with Python you are likely familiar with GIL (global interpreter lock). In a simplified explanation you could being running a process that will lock up your tool and any parent of the tool, in this case Maya. By using threads you can run your processes in the background and not lock up your tool or Maya. Well, it's less likely to happen. Now that I can communicate with Maya from an external source, I should be able to update any tools within as needed.
Many thanks to the TDAnon guys, including @DhruvGovil for pointing me in the right direction.
Here is an example of me sending external messages to Maya. The scripts I use can be found below.
Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts
Sunday, September 29, 2019
Saturday, September 29, 2018
Maya Create Bone at Custom Pivot
When rigging weapons or hard surface meshes with specific angles or orientations, I want to be able to quickly create a bone with the same orient of a selected mesh component. I want to ensure I have as precise a position and orientation as possible.
With the latest versions of Maya creating and manipulating pivots have become far easier with native tools. Though, I've yet to see a process to do exactly what I've been looking for. I've written a fairly simple script to achieve what I've found to be the most intuitive approach to creating a bone with the position and orientation that I desire.
Here is a brief walk through of how I create bones using a custom pivot. The script I use can be found below.
Character Model by Chris Wells
With the latest versions of Maya creating and manipulating pivots have become far easier with native tools. Though, I've yet to see a process to do exactly what I've been looking for. I've written a fairly simple script to achieve what I've found to be the most intuitive approach to creating a bone with the position and orientation that I desire.
Here is a brief walk through of how I create bones using a custom pivot. The script I use can be found below.
Character Model by Chris Wells
Thursday, May 3, 2018
Customizing Radical Heights - Part 3
Part 2 can be found here:
https://techanimator.blogspot.com/2018/04/customizing-radical-heights-part-2.html
Import the mesh
The mesh we are importing must have the same skeletal mesh hierarchy as the base body. So, when importing we will choose the base_body_skeleton. We share skeletons in UE4 so that the skeletal meshes can share any animation transformations that may occur on the given skeleton.
2. Part VIDs to Hide - Mesh face groups on the body part that will be hidden when this item is active.
3. Part Morphs - Given body part item will activate a morph target with a given value when this item is active.
4. Bodies - These are the bodies and part references that this data container represents.
Below are some examples of item config data to make sure items will work, look visually correct, in relation to other items that may be active.
I hope this was an informative and helpful look into a our customization process.
If you have any questions or comments don't hesitate to post them here hit me up on Twitter.
@Meatplowz
| Imported Skeletal Mesh |
Customization Data
There are several data files that the customization system in Radical Heights needs to populate items on the character. The first is the PlayerPawn, this is the main data container that represents the player character in game, the components it needs to function and of course the skeletal meshes for customization purposes. When the meshes are chosen for customization in the game front end, they are slotted into the named mesh components seen below.| Player Character Pawn |
Customization Data Assets
The data assets here mostly represent each "slot" or body part on the character that can be customized. The main customization data asset references the specific body part customization data files. Each body part data file will hold all of the items that the character can wear on that area of the body. For example, the CustomizationUpperBody data file will contain reference & markup data for all shirts, coats, etc that can be worn on the UpperBody location of the character.| Customization Data Assets |
| UpperBody Customization Data |
Customization Body Vids
In Part 1, it was mentioned that we use UV channel 2 on the body mesh to define face group sections that will be used for hiding. Inside the CustomizationBody data file, we define the mapping of UV coordinates to indices. If an item blueprint has parameters that flag certain PartVidsToHide indices, then the associated faces for the body mesh will hide in game. Some examples can be seen below.Customization Blueprint
After the mesh has been imported it can be assigned to a new customization blueprint. The blueprint is a visual container for the artist to reference the meshes and materials that will define a customization asset. Here the user can also mark up specific parameters for controlling how the asset interacts and functions in relation to other items within the customization system. Below is an UpperBody specific blueprint that has mappings to inform the customization system this item can only be applied to the UpperBody section of the character. When the user saves this file it will populate the associated customization data part file, seen above, with all the references and modified parameters in the found in the item config.| Item Customization Blueprint |
| Item Config Data |
Config Data
1. Incompatible Parts - Given body part item will be hidden when this item is active.2. Part VIDs to Hide - Mesh face groups on the body part that will be hidden when this item is active.
3. Part Morphs - Given body part item will activate a morph target with a given value when this item is active.
4. Bodies - These are the bodies and part references that this data container represents.
Below are some examples of item config data to make sure items will work, look visually correct, in relation to other items that may be active.
| When a full helmet is active we hide the hair part |
![]() |
| Helmet item hides the Hair part |
| When a hat is active a morph is applied to the hair part |
![]() |
| Hair item is morphed to scrunch inside the Hat |
| Face groups flagged to hide on the base body |
![]() |
| Body face groups for the feet are hidden |
Conclusion
At a pretty high level this covers the customization pipeline and process we used for Radical Heights. It should be noted that the core of the editor implementation and runtime system was engineered by Jim Ashcraft. Many systems like these require a solid engineer to get it going and Jim is one of the best!I hope this was an informative and helpful look into a our customization process.
If you have any questions or comments don't hesitate to post them here hit me up on Twitter.
@Meatplowz
Labels:
3d,
3dsmax,
Animation,
BossKey,
Character,
Character TD,
Customization,
Export,
FBX,
GameDev,
import,
Maya,
Pipeline,
Programming,
Python,
References,
Rigging,
TechAnim,
UE4,
Unreal
Thursday, April 26, 2018
Customizing Radical Heights - Part 2
Part 1 can be found here:
https://techanimator.blogspot.com/2018/04/customizing-radical-heights-part-1.html
Setting up the item
We need to first import the artist created meshes into a clean Maya scene. As mentioned previously the clothing is created for specific sections of the character. In this scene we have a shirt/upperbody and pants/lowerbody.
Assign the mesh
The artist runs the Customization Builder tool, selects the mesh and plugs the selection into the gender specific field. This field is used to determine which rig/skinned mesh the item will be constructed upon. Upon assigning the mesh it is evaluated for poly count, number of uv channels and number of material assignments. It also makes sure the transforms are correct on the mesh before attempting to bind to a skeleton.
Name Item & Associate Slot
Next we set a unique item name and then we assign the body slot/section for the clothing item. This will rename the mesh accordingly, create an export hierarchy and apply attributes to the groups and meshes used to rig and export the item. These attributes assist the tool when opened later and will also inform the export process how to handle the current asset.
Assign the mesh
The artist runs the Customization Builder tool, selects the mesh and plugs the selection into the gender specific field. This field is used to determine which rig/skinned mesh the item will be constructed upon. Upon assigning the mesh it is evaluated for poly count, number of uv channels and number of material assignments. It also makes sure the transforms are correct on the mesh before attempting to bind to a skeleton.
Name Item & Associate Slot
Next we set a unique item name and then we assign the body slot/section for the clothing item. This will rename the mesh accordingly, create an export hierarchy and apply attributes to the groups and meshes used to rig and export the item. These attributes assist the tool when opened later and will also inform the export process how to handle the current asset.
- Assign the selected mesh
- Create a unique item name
- Choose the body part section
Rigging the mesh
| The base body mesh is flagged for skinweight copying |
| Rigged body mesh is versioned to handle updates |
Importing not Referencing
It's important to note that I am not referencing the base body mesh here. While there are benefits to references they don't apply very well here. If I update the body mesh or skeleton, I want to know how to propagate those changes to the meshes in the customization files. If I allow referencing to just update naturally I cannot control the changes. After opening an existing customization file a callback will check the local mesh/rig version and compare to the latest one in the depot. I use the version numbers to inform how to process any updates. Some updates happen without user intervention while others require notification. The callback code for the tool also has versioning so I can run code specific to the version number where applicable. Once the scene version is up to date I can tick the local version attributes to the proper index.
Rules for version updates:
- Major: This requires a complete replacement of the rig in the scene & skin copy update.
- Minor: The tool can update specific aspects of the mesh/rig without requiring a full update.
- Patch: These small changes can generally be ignored and likely require no updates.
Range of Motion
The artist can optionally import the skeleton range of motion to check how the skinning worked out. A rom skeleton with baked animation is imported into the scene and the scene skeleton is constrained to the that skeleton. The artist can then apply some quick skinning fixes to the mesh or let the Character TD address any issues later in a polish pass.
![]() |
| View mesh skinning with Range of Motion |
Once everything looks good to go we can move forward with exporting the item. As was mentioned before the exporter handles all of the file naming and determines where to export relative to the project and the item slot. The file must also be saved and added to the depot in a proper location before exporting will occur. This ensures an artist that is putting assets into game will have the source file available as well.
On export the item FBX file is written out, a post-process is run on the FBX file to remove unnecessary nodes and correct the hierarchy where needed. A thumbnail is generated for our asset tool and all of the files are checked out and/or added to perforce automatically.
![]() |
| Exporting the mesh |
![]() |
| The clean processed FBX file |
Completed Process
Below is a video showing how easy the entire process is for the artist setting up the asset for use in game. The artist can further edit the existing meshes or update with another internal tool that will preserve all of the settings and replace the mesh, uv's, etc, all with updated skinning as needed.
Coming up Next
In the next and final part I will go over how the customization items are implemented in game using Unreal Engine 4.
Part 3
https://techanimator.blogspot.com/2018/05/customizing-radical-heights-part-3.html
Labels:
3d,
3dsmax,
Animation,
BossKey,
Character,
Character TD,
Customization,
Export,
FBX,
GameDev,
import,
Maya,
Pipeline,
Programming,
Python,
References,
Rigging,
TechAnim,
UE4,
Unreal
Tuesday, April 4, 2017
Flexible FBX with FBX Python SDK
FBX issues and such
Python FBX SDK Uses
Listed below are just a few things that you can modify in an FBX file with the SDK- Adding or removing properties/attributes
- Removing Namespaces before importing or after exporting
- Removing objects that were not a part of the specifically selected objects but were associated
- Removing textures or renaming paths
- Rename objects in fbx scenes without having to re-export the file from the source
- You can modify most anything in an FBX scene, external from a DCC application
Setting up the Python FBX SDK
http://www.autodesk.com/products/fbx/overviewHit the "Get FBX SDK" link and on the next page look for Python Binding. Choose your flavor, Windows, Linux, or Mac, download and install/extract the package. If you need an earlier version for compatibility reasons go to the "SDK Archive" link. I'm still using 2014.1 and it works fine with Maya 2014/2015/2016 and UnrealEngine 4.x.
You will need to install the package you have chosen to download. This package has a lot of incredibly useful samples for getting started with FBX in python and getting an understanding of the FBX scene in general. If you are at all familiar working with Python in Motionbuilder, pyfbxsdk, it is almost exactly the same and this should be quite easy to pick up.
Installing for Maya
If you are working in the latest versions of Maya 2014 and higher you will want the files in lib/Python27_x64 or x86. There are three main files that you will want to point to or add to your existing python paths for Maya.Here is a snippet to add to a script or your startup for Maya.
import sys sys.path.append(r'/FBXPath')These are the files that are needed:
fbx.pyd
FbxCommon.py
fbxsip.pyd
Here is some startup code for the FBX Scene Wrapper Class.
import fbx import FbxCommon class FBX_Class(object): def __init__(self, filename): """ FBX Scene Object """ self.filename = filename self.scene = None self.sdk_manager = None self.sdk_manager, self.scene = FbxCommon.InitializeSdkObjects() FbxCommon.LoadScene(self.sdk_manager, self.scene, filename) self.root_node = self.scene.GetRootNode() self.scene_nodes = self.get_scene_nodes() fbx_scene = FBX_Class(r'c:\my_path\character.fbx') # instantiate the classThe main thing to be aware of with most classes, is that you have to instantiate them to access their internal methods. Here we need to pass an argument that is a string of the fbx file path and name. The _init_ method will automatically parse and load the fbx scene. The one thing when parsing a file that you care about is speed, luckily when working with the Fbx SDK, even in python, I find it to be incredibly fast even on large Fbx files.
Now that the fbx scene is loaded we can go nuts changing the fbx file however we want. At Boss Key, after exporting I run a post process to modify all the fbx files, be it a character skeletal mesh, a weapon, or an animation file. I remove namespaces, empty display layers, remove objects that are in exported hierarchies that I don't want importing into UE4, or cleaning up any properties when necessary.
import FBX_Scene
def clean_character_scene(fbx_file):
"""
Clean up character fbx file
"""
# open the fbx scenes and get the scene nodes
fbx_scene = FBX_Class(fbx_file)
if not fbx_scene:
return False
remove_names = []
keep_names = []
# remove invalid nodes noted by properties assigned in the DCC application
all_nodes = fbx_scene.get_scene_nodes()
for node in all_nodes:
export_property = fbx_scene.get_property(node, 'no_export')
if export_property:
property_value = fbx_scene.get_property_value(node, 'no_export')
if property_value == True:
node_name = node.GetName()
fbx_scene.scene.DisconnectSrcObject(node)
remove_names.append(node_name)
else:
node_name = node.GetName()
keep_names.append(node_name)
# remove the nodes from the scene by name
fbx_scene.remove_nodes_by_names(remove_names)
# remove display layers
# For some reason these change FbxCollection ID and NodeName
layer_objs = fbx_scene.get_class_nodes(fbx.FbxCollectionExclusive.ClassId)
if layer_objs:
remove_layers(fbx_scene, layer_objs)
# remove FbxContainers
nodes = fbx_scene.get_class_nodes(fbx.FbxObject.ClassId)
if nodes in nodes:
if node.GetClassId().GetName() == 'FbxContainer':
# disconnect the layer from the scene
node.DisconnectAllDstObject()
node.DisconnectAllSrcObject()
fbx_scene.scene.RootProperty.DisconnectSrcObject(node)
# remove display layers
display_layers = fbx_scene.get_type_objects(u'DisplayLayer')
if display_layers:
remove_layers(fbx_scene, display_layers)
# save the modified fbx scene
fbx_scene.save()
return True
Right after I run my custom fbx export method in Maya I will call a method, such as the one above, all in the same process. So when the artist is finished exporting they won't even know I opened up the fbx and modified it for the better.
The great thing about the FBX Python SDK is that you don't need a DCC application to run it. You can run standalone processes and modify all of your mocap or skeletal mesh files, renaming nodes, changing hierarchies or adding properties, all without having to return to the original DCC to reexport. Once you start working with the Fbx Python SDK you may never stop.
![]() |
| The Python SDK can process importing mocap before you actually import it into your scene and remove namespace issues. |
Once I got familiar enough with FBX SDK I started a project which became the Saints Row Mod Tools. I wrote the tool that converted fbx scenes into the file formats needed for Saints Row 3, and Saints Row IV for for modding purposes. Before we used custom plugins, tools and processes that would be far too complex to release to the community and support for multiple DCCs. Fbx covers all the bases so this was a worthwhile tool and the community seemed to appreciate it. If you want to download the python script that really digs into breaking down the fbx scene, feel free to hop over to the saintsrowmods forums and grab it.
My Public Gists (full examples from this post)
https://gist.github.com/Meatplowz
On a side note if you want to actually extend the plugins with C++ you can do so.
FBX SDK Plugin Extension
Let me know if you have any questions on the Fbx Python SDK. If you have any suggestions or tips yourself please share them as well.
Friday, March 6, 2015
Maya Callbacks and References
The scenario that I'm working with in Maya involves two referenced rigs and creating constraints and message attributes between them. Now since i'm using references in Maya, additional bones, controls or what have you may be created in either reference at any point in time. What I'm doing is creating parent constraints from one rig to the other and anytime new bones or controls are added to the on rig, I need to constrain it to the other rig with relative names automatically. This is where callbacks come into play.
After the scene is fully Open and all the references are loaded I need to check message attributes to see if one rig is "connected" to another. Here I'm using message attributes to point each rig to one another. The callback looks for this message, if found it will update the connections by creating new constraints for similarly named objects. Now I was originally using the kAfterOpen callback which should fire off after all references are loaded. It does work, but I changed to the scriptJob version, "SceneOpened", that seems to run later than the callback variant, even after viewport processing. The method being called in the callback runs and connects everything just fine. While the scene appeared to be functioning correctly, after saving and reloading the file later, all the constraints were broken and the attributes were missing. I started to panic thinking this was a new bug with saving and references. I posted on the Autodesk beta boards after trying multiple approaches to work around my problem.
I spent quite a while doing diffs on the maya ascii files. The file before the callback and the file after the callback. Here I started to notice differences in the referenceEdits. I didn't understand all the formatting so I went on a hunt for the information there. Sadly, the actual documentation that I found had no real detail in the places that I had question about. This archived Post from PosingMantis on cgsociety helped greatly. What I started to realize was that all the operations I was doing during callbacks, while still functioning in the scene afterwards, they were not saving in the referenceEdits.
Here's an example of what the connections in the refEdits look like before the callbacks. The referenced ball is parentConstrained to the referenced box.
After the callback ran, all of those reference edits were now missing, but like I said everything was still operating and working properly in the scene. This was incredibly misleading. I looked for some more documentation and I found the root of my problems.
File I/O callbacks don't record reference edits
If I was a good little tech artist I would have read ALL of the documentation on callbacks. I know what callbacks do, why would I think methods called from them wouldn't record referenceEdits. Sigh. There are specific callbacks you can run after a reference loads and you can record referenceEdits. kAfterSceneReadAndRecordEdits This one probably does precisely what I needed, the others fire off after every referenceLoads which I didn't need. However, after chatting with Brad Clark cofounder of @RigginDojo, I opted for creating a scriptNode that runs the same methods OnDemand after a "SceneOpened" scriptJob, and the scriptNode has a flag to "RecordReferenceEdits". I can also only create this in the scenes that need them.
This still may not be the best way to do it but right now it's what I needed to move forward. I hope this will be helpful to someone as none of the issues I noticed were directly obvious. Good luck!
Labels:
3d,
Animation,
api,
Art,
Autodesk,
callback,
Maya,
Python,
refEdits,
References,
Rigging,
scriptJob,
scriptNode,
Technical,
Tool,
Video Game
Location:
North Carolina, USA
Monday, October 10, 2011
MotionBuilder Python & Window Focusing
I recently had to do some window handle trickery with Python and MotionBuilder. I have an external python tool that is communicating with MotionBuilder, 3dsmax, and in the near future Maya. For this post I will only be touching on the MotionBuilder implementation of the tool.
When I run the tool it will return a list of scene objects within a list view. If I select on a item in the list view it should select the object within MotionBuilder. While the selection in the scene navigator is updated, since the tool is not a child of the MotionBuilder application, MotionBuilder will not redraw the viewport. I'm quite new to MoBu but the only code I saw to potentially force redraw the scene was.
FBSystem().Scene.Evaluate()
It is likely that the Mobu viewport/rendering is on a separate thread from normal scene processing. Which would explain why the scene navigator updates and the viewport does not. The viewport only seems to update if the application is the focused application. With this in mind I decided to force change the application focus to get the results I wanted.
When the python tool makes a connection to the specific dcc app I will call the following:
Now that I have stored the window handle when I select an object in my listview it will call the following:
UPDATE:
In the comments below, Kevin pointed out that you can skip all of this and modify the application.txt by adding the following line under [Timing]
NoFastIdleOnDeactivate = No
Thanks Kevin!
FBSystem().Scene.Evaluate()
It is likely that the Mobu viewport/rendering is on a separate thread from normal scene processing. Which would explain why the scene navigator updates and the viewport does not. The viewport only seems to update if the application is the focused application. With this in mind I decided to force change the application focus to get the results I wanted.
When the python tool makes a connection to the specific dcc app I will call the following:
import win32gui, win32con def enum_callback(self, hwnd, results): self.winlist.append((hwnd, win32gui.GetWindowText(hwnd))) def get_dcc_hwnd(self, app_name): self.toplist = [] self.winlist = [] win32gui.EnumWindows(self.enum_callback, self.toplist) # look for the specific application window name dcc = [(hwnd, title) for hwnd, title in self.winlist if app_name in title.lower()] if dcc != None and dcc[0] != None: dcc = dcc[0] self.dcc_hwnd = dcc[0] get_dcc_hwnd( 'motionbuilder')This will return self.dcc_hwnd and all that really is is a window process handle or long integer. In this example it is. DCC Handle: 3214530
Now that I have stored the window handle when I select an object in my listview it will call the following:
def set_dcc_focus(self): if self.dcc_hwnd != None: # store the current python tool window handle current_hwnd = win32gui.GetForegroundWindow() # use the window handle to set focus win32gui.SetForegroundWindow(self.dcc_hwnd) # important to sleep here as to allow Mobu time to draw time.sleep(0.03) try: # set the focus back to the python tool win32gui.SetForegroundWindow(current_hwnd) except: returnWith this I can click on all the objects in my external tool and see the select update 'realtime' within the MotionBuilder viewport. The code will change the window focus momentarily and just long enough to redraw the MotionBuilder viewport then immediately return focus back to the python tool. With the sleep time I have set here it is fast enough that I can be pressing down on the keyboard to step between items in the listview without noticing the app focus switching. However, I have noticed that if Motionbuilder doesn't have enough time to redraw, some things have the possibility of not rendering when the viewport is redrawn. So the sleep here could become variable and dependent on the amount of objects within the viewport.
UPDATE:
In the comments below, Kevin pointed out that you can skip all of this and modify the application.txt by adding the following line under [Timing]
NoFastIdleOnDeactivate = No
Thanks Kevin!
Labels:
3d,
Art,
Game,
Motionbuilder,
Programming,
Python,
Technical,
Tool
Subscribe to:
Posts (Atom)










