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

4 comments:

  1. Great post man! I actually use bone helpers, but only after first skinning the mesh to boxes/cubes b/c of the strange effect it has on the bone list entries of the skin. Then I swap the boxes for bone helpers, b/c, well, personal preference I guess, but it just seemed natural for the linked hierarchy, and also b/c I wanted my skeleton as a different class than the mesh. Also we were using 2009. I'm not sure how much things have changed. Regardless, bone helpers are a pain for skeletons, I think I was just too stubborn to let it go, and obviously had to go through more steps to achieve it.

    ReplyDelete
  2. Nice! I'm sure I can use this to get animation into modo while I wait for Luxology to add a real character animation system.

    ReplyDelete
  3. Modo animation is not robust enough for you? I'm not that familiar with it, but we are totally looking at XSI for animation more closely. So many tools, so little time...

    ReplyDelete
    Replies
    1. They have an amazing animation/rigging system so far, but they have specifically mentioned that they haven't been focusing on character animation, it's more for mechanical objects. Any rigging/setup in modo for characters is really just bending the system to work for you (which works pretty well, but setup can be a pain).

      Delete