Showing posts with label 3d. Show all posts
Showing posts with label 3d. Show all posts

Thursday, May 3, 2018

Customizing Radical Heights - Part 3


In this post I will go over the process of importing and implementing the customization item into game using Unreal Engine 4.

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.


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



Thursday, April 26, 2018

Customizing Radical Heights - Part 2


In this post I will step over the process the artist will run through to get the assets ready for game. The artists can import the mesh from any DCC they choose to create the assets within. The setup process itself is handled entirely in Maya.

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 selected mesh
  • Create a unique item name
  • Choose the body part section

Rigging the mesh

In this stage we import a clean skinned base body mesh. We locate the flagged gender specific body mesh that was imported and copy the skinweights from the body to the clothing mesh. Additionally, the skinweights and bones are pruned based on the body part chosen, to use the least required amount possible.



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

Exporting the mesh
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




Wednesday, April 25, 2018

Customizing Radical Heights - Part 1



My work on Boss Key Productions most recent project, Radical Heights, focused a lot on character customization. In this post I'm going to discuss the process and creation of a customizable character for Radical Heights.

Having previously worked on Lawbreakers and the Saints Row franchise, much of my time has been focused on creating processes for constructing customization for game characters. The methods described below were no doubt influenced by my past experiences and lessons learned working on these titles.

For customization purposes it is important that your visual goals are outlined initially and the "known" technicalities are addressed before jumping in and creating clothing assets. If you change any of the base character assets during production, you will lose a great deal of time re-working any of the existing clothing assets you may have already created. Understanding the problems up front is key to defining the customization process. You want to minimize the work on the art team and maximize the quality and quantity of assets that can be created.


Character Scale


First and foremost the Character Artist and the Character TD will need to work with the level and environment team to ensure you character height will work properly in the scale of the world. In most cases it is a good idea to keep close to real world units for scale. It's likely easier to create real world weapons that work correctly with your character, if you follow this rule as well.

Play-testing your character in game running around, even early in a rough state will help you identify issues with character and world scale. It's important to nail this down early in your process.



Character Mesh Topology


We started with a fairly standard male mesh until we fully determined how we wanted to approach the semi-stylistic look we wanted the characters to have. Here you can see some stages the proportions of the character went through before we came to our current version. The overall height and proportions didn't have to change drastically from our initial construction. However, some aspects changed enough for us to have to modify the skeleton and in turn adjust and re-export animations.You can also see some of the topology was reduced to minimize the work that it would take to project onto future clothing assets and to lower the polycount overall.



From my past experiences, I found its best to have the character mesh topology to be created in a uniform manner. As you can see here, it's almost a grid-like pattern. This actually makes it easy to define notable landmarks, where clothing assets will generally start and end. If you know the type of clothing and outfits the character may wear, its usually a good idea to make sure edge loops can border these common areas. This will be necessary for hiding the geo of the underlying character mesh at run-time, using a technique I will describe below.

When optimizing the mesh, it's important to remove unnecessary edge loops where the detail doesn't need to project onto clothing assets or the detail doesn't support deformation. You may notice the nipples were removed from the topology as they may only be seen in very few assets and projecting that topology onto most of the clothing would just be too time consuming and wasteful. Only add edge loops to areas that need to support deformation and where the silhouettes of clothing would benefit from that detail.

Character model by Chris Wells



Customization Asset Topology


In the following image you will see how the clothing topology mimics that of the body mesh. This is important for deformation consistency. If the body can deform fine with the existing skeleton then the overlapping clothing assets with the same edge loops, will usually deform the same. You don't want to have issues with the body tearing through the clothing and sticking out. If the topology was different between the meshes then you would likely have to add bones to correct for the tearing and intersecting issues as a result. This is not ideal when you are creating a game that needs to run as optimal as possible and extra bones increases processing time. Fixing visual issues with solutions that would hinder performance is not the best route to take.


Clothing meshes mimic the body topology



Character UVs & Mesh Hiding


The first uv channel is fairly standard as you can see here. The second uv channel is what we use to hide the faces of the body mesh. We first identified the common clothing areas and broke them down into subsections depending on the types of clothing. Next we generated uv shells based on those subsections and smashed the coordinates of those uvs to a singular value in the UV space. This coordinate can then be mapped to a game-side data file and referenced to hide when marked up with corresponding clothing assets. The material on the body skeletal mesh will have a technique that can then hide the triangles, or set them to not render when the coordinates have been flagged to be hidden.

For example, if the character is wearing a shirt, we would flag the first couple of coordinates being selected below to hide, ( U 0.1, 0.2, 0.3 ) Keep in mind the mesh assets need to be created so that the overlying mesh crosses over the edge boundaries, so that holes in the body will not be visible, when the body mesh faces are hidden.



UV Channel 1
UV channel 2 coordinates used to hide body mesh faces in game.



Customization Parts


When determining how complex you want your customization system to be you have to understand the amount of work you will be taking on. In Lawbreakers we did full mesh customization. This is essentially a mesh swap for each version of customization you want to have for the character. The creation is straight-forward to build as long as the meshes follow the conventions of the original character. The in game system is not complex at all, as it is just a mesh reference change, so the programming effort to implement this system was minimal.

For Radical Heights we decided to do a relatively basic "Paper Doll" setup. We researched the type of clothing we expected the character to wear and defined the most notable areas that we want to be able to customize so we could plan our asset creation accordingly. On the surface this looks easy to create for but it can spiral out of control very quickly from asset creation to in game implementation.


Clothing Boundaries

The things to be aware when creating the clothing, are asset parts that cross the boundaries of other parts and the volume/thickness of those assets. For example, if you create a shirt and pants with the same thickness and the shirt crosses over the boundary of the top of the pants, you will get an unwanted intersection. Determining how to create these assets consistently while avoiding these issues need to be decided up front.

Clothing Layers

Layered clothing, such as the UpperBodyArmor here, has to work relative to varying levels of volume/thickness of the upperbody assets. To correct for this instance, we have a morph target on the bulletproof vest mesh, that can be adjusted per upperbody asset at run-time. Addressing a singular asset in this case to correct an issue, works out far better than generating morph targets for each and every shirt asset. Always look for the cheapest way to solve a problem.

Clothing Materials & Draw Calls

Radical Heights can have up to 100 characters and the body mesh for the character already has three draw calls ( head, body and the eyes). For customization purposes we have separated out the clothing to specific parts, and each part on its own becomes a singular draw call, this means by default a character can be upwards of ten draw calls. This is not a great start, which means we need to keep the material count low for each clothing asset. We try to make sure they sure each asset has only a single material and two in the worst case.

Customization Fixes

When you have assets crossing boundaries or overlapping other parts you also have to handle how you want to deal with other crashing issues. In extreme cases we will hide the offending assets, large helmets will hide the hair parts. In other situations, in which we identify a common issue, we will create named morph targets to fix problems. Similar to what we did with the armor we will create a common morph target for each hair asset when a standard hat is enabled. This morph target will scrunch the hair down to work better with the hat. We cannot create a custom morph target for every hat asset, that amount of work and data loading would be ridiculous. Therefore, when we create the hat model, they fit to a predefined location on the head, so that a singular hair morph will work with every hat.


We did later determine that we wanted to have headbands for our characters to customize, as they fit the time period and aesthetic we were going for. This meant we had to circle back and create new morph targets for each of the hair assets to accommodate the headband location. This was an unknown initially but it was important for us to implement this specific data fix for each asset.

These rules for how hats, headbands and every other customization part can work together are important in defining creation guidelines. These guidelines are necessary to prevent data correction bloat from permutation issues. Understanding the volumes and boundaries for each asset is helpful to reducing these data fixes and ensuring compatibility with future assets.



  Character Skeleton


Radical Heights is an online Multiplayer game with up to 100 players. This means the character itself has to be fairly simplistic in terms of run-time complexity. The number of processing skeleton joints should be fairly small even at the lowest Level of Detail (LOD). The deforming corrective deforming joints start to drop off quickly as the character starts to LOD out. The twist and corrective joints are usually the first to go, ultimately leaving only the primary body joints, when deforming characters in the furthest distances on screen.

The supplemental joints here are comprised of joints used for IK hands and feet as well as joints only used when the character is in First Person/Aim Down Sights (ADS) mode.




  Character Rig


We continued to use Epic's ART tools, created by Jeremy Ernst, to construct the player character rig for animation. The tool set allows you team to hit the ground running with a fully animation friendly rig and accompanying animation tools. I use most of the rig construction out of the box with additional custom rigging on top to satisfy our specific needs for animation and deformation.


Shared Skeletal Mesh

On Lawbreakers we chose to have two separate skeletons for First and Third Person. This required us to duplicate the customization assets work done by the character artists and double the amount of data that needed to be loaded by a character in game. The fidelity of the assets had also changed enough to necessitate this choice for that project. However, the speed at which we needed to create assets and the bandwidth we had on Radical Heights, meant we needed to be a bit more conservative and efficient with our time in asset creation. By having a single skeleton for first and third person it meant we should only have to create the clothing assets once and the same assets could be visible for both perspectives in game.
I modified the rig and skeleton hierarchy to be able to handle both aspects for animation purposes. The core of the third person skeleton remains untouched aside from various supplemental joints for controlling the camera and upperbody pitching in first person. This is handled by the rig using a single switch that modifies the visibility of various animation controls and changes multiple constraints that affect the behavior of the rig.





  Character Animation



In First Person, the camera is positioned relative to the head location, so we need to move the head out of the way for animation purposes. In the animation file we just rotate it backwards. In game we also hide the faces on the head and parts of the torso, using the UV method mentioned above, to prevent the them from rendering in the players view.

Animation by Ryan Palser

Checking Animation with Clothing Assets


The pipeline and tool set I created to work with all of the assets on Radical Heights, allows the animator to view customization items in their scene and correct poses where needed. Not every item will work perfectly with every pose, but this gives the animators the opportunity to easily address some of the worst cases scenarios as they are bugged by our QA team.

Animation by Nick Maw-Naing



Coming up Next

In the next post I will be going over the customization tool used by the character artists to setup assets for export and implementation in game.

Part 2




Sunday, April 22, 2018

Fixing Mechanical joints For Rigging


For Lawbreakers we had a juggernaut class that featured two bipedal robots. The robots had a shared base skeleton hierarchy for animation purposes. However, they had custom joints to handle the visual differences in their armor and armor plating. In most cases the base bipedal skeleton worked just fine, but there were issues when the mechanical mesh joint visual pivots and the actual skeleton joints were inconsistent. We had a couple levels of customization and we had to make sure the joints were modeled consistently to make sure they would work with the rig and animation properly.

In the video below, I go over a process on how to correct the meshes for these issues specifically on the fingers joints.



The only thing missing in the video is the clean up of the temporary joints and freezing the mesh to preserve its new modified transform. Once that is done, you can bind the meshes back to the original skeletal hierarchy.




Friday, March 6, 2015

Maya Callbacks and References

Working Scenario
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.

Callback
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.

The Actual 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

The Solution
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!

Tuesday, January 29, 2013

Building a Scripted Plugin Modifier for Game Characters

In response to  +David Moulder I have detailed a little more of our character "referencing" and scene building process, starting with our custom character modifier. I hope this is helpful.

All of our characters and animated objects have a custom scripted plugin modifier that sits on top of the character mesh modifier stack. It stores references to the skeleton and helper objects in the parameter blocks of the modifier. To store these object references you will see below the parameter type should probably be a #maxObjectTab type.  If you are looking for something similar to the message attributes in Maya the #maxObject parameter type, or what 3dsmax calls weak referencing, is as close as you are going to get. Paul Neale has a pretty good tutorial for weak referencing.

Weak References

I went ahead and wrote up some functional code below to help provide a basic understanding of scripted plugin modifiers and weak referencing. If you evaluate this code a new modifier will appear in the modifier list named "AnimExportTest".  It does not modify your mesh/object it is only a container for storing references and variables relative to your character.
plugin Modifier AnimExportTest
name:"AnimExportTest"
extends:EmptyModifier
 
--- The id here must be unique run genclassid() to generate a unique id for your plugin
classId:#(0x2c608fe8, 0x14804a99)
 
version:1
(
 -- set the file path parameter
 fn set_path &path =
 (
  local file_path = getSaveFilename filename:"C:" types:"Meshx(*.meshx)|*.meshx|ALL|*.*" 
  if file_path != undefined do (
   path = file_path
  )
 )
  
 -- add object weak references to the parameters
 fn add_bones objs =
 (
  for obj in objs do (
   -- this is how you create a weak reference to an object/node
   add_obj = (nodeTransformMonitor node:obj forwardTransformChangeMsgs:false)
   append this.p_bones add_obj
  )
 )
  
 -- object selection filter
 -- boolean check to see if the incoming object has what you are looking for
 fn bone_filter obj =
 (
  is_valid = True

  -- make sure the object is our type of bone
  if ( not superclassof obj == helper ) do ( 
   is_valid = False 
  )

  -- make sure object name has namespace identifier ":"
  if ( not matchpattern obj.name pattern:"*:*") do ( 
   is_valid = False 
  )

  -- make sure the object or object name doesnt already exist in our list
  for bone_obj in this.p_bones do (
   -- since this is a weak reference get the .node to query the actual object
   if ( bone_obj.node == obj ) do ( 
    is_valid = False
    exit    
   )
  )

  -- return bool
  is_valid
 )
  
 --- Store any variable,object references, export paths, values necessary to this modifier
 Parameters export_params
 (
  p_export_mesh type:#string   default:""
  p_export_rig type:#string   default:""

  p_bones   type:#maxObjectTab  tabSize:0  tabSizeVariable:true
  p_bone_names type:#stringTab     tabSize:0  tabSizeVariable:true
 )
 
 -- rollout ui creation
 rollout ro_export "Export Paths"
 (
  Group "Export Paths" (
   Button bn_export_mesh "Export Mesh" width:100
   Button bn_export_rig  "Export Rig"  width:100
  )

  --- Functions to set paths
  On bn_export_mesh pressed do (
   set_path &this.p_export_mesh
  ) 

  --- Functions to set paths
  On bn_export_rig pressed do (
   set_path &this.p_export_rig
  ) 

 )
  
 -- export objects menu
 rollout ro_export_bones "Export Objects"
 (
  Group "Export Bones" (
   MultiListBox lbox_bones "" width:140 height:20
   Button bn_add_bones "+" across:2
   Button bn_remove_bones "-"
  )  
   
  -- Update the rollout interface
  fn update_ui =
  (
   local bone_names = #()
   for obj in this.p_bones do (
    -- since this is a weak reference get the .node to query the actual object name
    append bone_names obj.node.name
   )

   lbox_bones.items = bone_names
  )
    
  --- Functions to add and remove bones
  on bn_add_bones pressed do (
   
   --- object picker dialog
   new_bones = selectByName filter:bone_filter
   if ( new_bones != undefined ) do (
    add_bones new_bones
   )
   
   -- update the interface
   update_ui()
  ) 
  
  -- Update the interface when the rollout is opened
  on ro_export_bones open do (
   update_ui()
  )
 )  
)

Custom Test Modifier
Custom Character Modifier
by  +Nathaniel Albright 
Our version of this modifier has many more methods, attributes and options for storing animation and other references. This is more or less the core interface for evaluating incoming animatable assets to make sure they are properly setup for animation and exporting when building an animation scene, as noted in my previous post.

You may chose to build a similar interface but rather than a modifier it can be a custom shape object that lives in your scene. It's pretty much the same thing just where it lives is different.


Custom Attributes & adding Assets
When adding an asset I will merge the entire scene and inspect this custom modifier to make sure it has everything I need for animation. If it passes the validation process I will collect a list of only the objects necessary for animation and delete the remaining unnecessary objects that came into the scene. Once I have identified the valid objects I will apply a "namespace" prefix to them based on the supplied asset name. I will then stamp additional attributes onto all of the valid objects to sort them and identify them as a unique asset within the scene. You can see my post on Tech-Artists.Org for more on custom attributes. Below is an example of setting the custom asset attributes on the incoming objects.

-- Asset Attributes
-- This attribute block should probably live in your library code
--- so that it is always accessible.
my_asset_data = attributes asset_data
attribid:#(0x6664296c, 0x5dec7a0)
version:1
(
 parameters asset_data
 (
  asset_name  type:#string  default:"none" 
      asset_id  type:#integer  default:0
      asset_type type:#string  default:"Character"
 )
)

-- Loop through all the incoming objects and apply the custom attribute

-- Apply attribute class to an object
add_attribute = custAttributes.add obj my_asset_data baseobject:True

-- update attribute data with your unique asset name, asset Id
-- and anything additional you need to track
obj.asset_name = obj.supplied_asset_name
obj.asset_id = unique_asset_id


A Bulk of the Work
Now that you have brought in a character asset into your scene in a clean and managed fashion the heavy lifting is done by your scene manager. Adding and Deleting should then become fairly trivial as your asset objects should be clearly identified. However, the swapping of your animated characters requires your tool to do much more in the way of saving animation, link constraints and export data defined in your scene. This of course relies heavily on your control rig system be it CAT, custom, or god forbid Biped +Brad Clark.  Once you have a proper save and load, swapping assets should become a "breeze". I will note that 80% of my Asset Manager code is handling the saving and loading of animation. You may find many cases where you have to do a lot of code to preserve the state of your animation in relation to other objects or characters in your scene.

Feel free to ask any additional questions. I will try to follow up.
Until next time.

UPDATE: Fixed some script errors in the plugin modifier. It will now refresh the ui when you reopen it.

Saturday, January 28, 2012

Game Skeleton Builder via Biped

Animators using 3dsmax may want to use Biped as their control skeleton but for games you do not necessarily want to use this as your exported skeleton. Ryan Griffin on G+ has recently posed a question regarding building a skeleton based off of a biped. As we have done this for quite some time at Volition I felt I could offer some assistance here using Maxscript.

First we want to create a structure representing the bone data that will be used to create the skeleton. A struct in maxscript is somewhat similar to class objects that exists in other languages such as python or C#. Check out Duber's Blog for a more in depth look on maxscript structs.

struct bone_info (bone_name, parent_name, bone_xform)

Next we create a recursive function that will return the data representing each biped bone name, the parent bone name, and the bone transform. Notice that I am ignoring certain biped helper objects that don't necessarily need to become bones in our skeleton.
-- recursive function to get biped children objects
fn get_bip_children bip_obj bone_data =
(
  -- make sure the current biped object has children
  if (bip_obj.children != undefined) do (
 
    -- loop through all of the current bone's children
    for child in bip_obj.children do (
  
      -- ignore these biped objects
      match_footsteps = matchpattern child.name pattern:"*footsteps"
      match_nubs = matchpattern child.name pattern:"*nub"
   
      -- do not add certain objects as bones, this is a preference
      if (not match_footsteps) and (not match_nubs) do (   
   
        -- create an object for the current child bone
        new_bone = bone_info child.name bip_obj.name child.transform
    
     -- update the bone_data array with the current child
     append bone_data new_bone
    
     -- see if the current child has and children
     if (child.children != undefined) do (
       get_bip_children child bone_data
     )
      ) 
    ) 
  )
 
  return bone_data
)
Next up is the main function that collects the biped bone data, creates the new bones and rebuilds the hierarchy. Something that I should point out here. I am creating the actual bones using point helper objects. Almost any object in max can be used as an animatable bone and work with the Skin modifier to deform meshes. One thing to keep in mind is that I avoid creating max bones using the bone() creation method. This tends to be slow and I have found to crash Skin. You may choose to use bonesys.createbone as an alternative.
-- create a skeleton from a biped skeleton
fn create_skeleton bip prefix =
(
  -- get the biped root node
  bip_root = bip.controller.rootnode

  local bone_data = #()
  
  -- create an object for the root bone
  root_bone = bone_info bip_root.name undefined bip_root.transform
  append bone_data root_bone
 
  -- get bone data (names and hierarchy) from the biped
  bone_data = get_bip_children bip_root bone_data
 
  -- make sure bone data is valid
  if (bone_data.count > 0) do (

  -- create the bones
  for b in bone_data do (
   bone_name = substitutestring b.bone_name "Bip" prefix
   -- using a point object here, this can be changed to any object type
   -- do not use bone()!, that is really slow and crashes skin
   new_bone = point name:bone_name size:5 box:on cross:off
  )

  -- update the hierarchy and set the transforms on the newly created bones
  for b in bone_data do (
   
   -- only update the bone parent if it actually has a parent
   if (b.parent_name != undefined) then (
    
    bone_name = substitutestring b.bone_name "Bip" prefix
    parent_name = substitutestring b.parent_name "Bip" prefix
   
    bone_node = getnodebyname bone_name
    parent_node = getnodebyname parent_name
    
    -- set the bone parent then set the transform
    if (isvalidnode bone_node) and (isvalidnode parent_node) do (
     bone_node.parent = parent_node
     bone_node.transform = b.bone_xform
    )
    
   ) else (
    
    -- get the bone object
    bone_name = substitutestring b.bone_name "Bip" prefix
    bone_node = getnodebyname bone_name
    
    -- set the bone transform without a parent
    if (isvalidnode bone_node) do (
     bone_node.transform = b.bone_xform
    )
    
   )
   
  )

  messageBox "Creating Skeleton Complete!" title:"Create Skeleton"

 )
)
This is the opening of the script that will make sure the user has a single biped object selected and provide the prefix name that will precede the new bone names.
if (selection.count == 1) and (classof selection[1] == Biped_Object) then (
 -- create the skeleton with a new prefix
 create_skeleton selection[1] "Guy"
) else (
 messageBox "Select a single biped node" title:"Create Skeleton"
)
Now since we used point helpers to create the bones you may want to display these objects with links and make them look more like max bones. To do this you can go to the display panel under link display at the bottom and check both options.
The final result with a little object coloring should look like this.

There are many other things you can do once you create this skeleton such as constrain these bones directly to the biped, create additional helper bones to represent bone twists or add your own custom constraints to transform these bones any way you like.

You can download the full script here.
biped_create_skeleton.ms

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:
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:
   return
With 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!