Saturday, April 5, 2014

Daz3D -> 3DS Max Biped for Skin Wrapping

Instead of whining about my feelings for a change, I wanted to write about something I'm excited about that actually has to do with game development!  In this case, the process of taking a skinned model from Daz 3D, transferring the bone weight information properly to 3DS Max's biped, and using this new configuration to skin characters!

The Problem

Since I'm a fan of Unity3D, I want to actually create 3D assets for a game.  With the release of their 2D tools, I know I could just make a sprite-based game, which technically I am with Battle High 2 despite not using this new tool set, but I don't want to do this.  I want to eventually make a 3D game with animated characters.
Since I technically was trained to be an artist, I feel this muscle is constantly atrophying, and I the only way to build it back up is to create more art.  Also, I always enjoyed 3D character modeling and animating, and though I know my work will never be the level of quality created by AAA studios -- for one, I don't touch ZBrush -- I'd rather create something than nothing.
Since I'm employed and don't have tons of times to dedicate modeling the perfect nose or every strand of hair on someone's head, I look for shortcuts, especially when it comes to tedious task.  Utilizing these shortcuts has caused some issues though, but there is a crossroad that I reach.  I can either take the time to learn something new, something that'll probably be replaced by a better technique, program, or method, or just use what I know and make something.  Also, as a kinesthetic learner -- I think anyway -- I learn and improve best by actually doing.  This being said, one of my major problems is 3D Studio Max is my modeling suite of choice.  There are many problems associated with this:
  • It's expensive.  A single license cost about $4,000.  Up until recently, they've introduced a subscription model, but personally, because of game development's unpredictable nature, I try to avoid subscriptions, finding that eventually buying the static license would have been cheaper.  Maya LT and Blender are cheaper -- Blender being free -- but again, despite this, I really don't want to take the time to learn these other modeling suites when I have one I am capable of doing the same things in.
  • It's skinning capabilities are dreadful.  Skinning refers to binding a mesh's vertices to bones; you'll be hearing a lot more of that later in this post.  Both Maya and Blender utilize heatmapping, a technique I believe was created or at least inspired by the M.I.T. Pinocchio, auto-rigging project.  What's even more irritating is that a script has been written to utilize this, but the user doesn't have it uploaded anywhere.  You can see it in this video:  
  • It has tons of tools I will never use.  This correlates to the high expense.  It would be like buying a 90 piece knife set but only using 3 knives all the time.
Anyway, in my search of shortcuts, one process I definitely find tedious, as mentioned earlier, is skinning, so I set out to find a better solution.  I reached a lot of deadends and decided to eventually form my own.

The Solution in Genesis

3DS Max does have one tool that I really like to make skinning a bit easier.  This tool is known as Skin Wrap.  I believe I talked about it in an earlier post.  This video explains more about it:

Skin wrap comes with two giant caveats though.  You need a model that is already skinned for one.  This isn't impossible to find, but can be irritating.  The second issue is that with skin wrap, the rig of what is being used becomes your new model's rig.
To solve solution one, I found a 3D modeling program, Daz 3D, that comes with an already, rather well skinned character known as Genesis.
Those weird gray shapes are the dummies / bones.

This model can be exported from Daz3D with its skinning information intact as an FBX.  This is great, but I don't want to use the rig it comes with.  Nothing is linked up properly, and all of the rotations are at identity quaternions.  This can be extremely problematic when animating.
This also causes an issue with another shortcut I use, this being the use of 3DS Max's Biped.  The biped is a built-in rig.  It is a little dated, but it still is useful for animation, especially due to its ability to save and load animations and poses.
Oh Biped, you so crazy looking!

I could just size the biped in figure mode and make it close to the Daz Rig, but I don't like this approach.  Also, the Genesis rig has extra bones that I simply don't need such as a big toe bone and two carpal bones -- which were the most troublesome.  Here are the two rigs side-by side:
The different poses aren't the issue, but the different orientation of bones.
Here is a more detailed list of differences:
  • Biped assumes the legs are at the same height as its pelvis
  • Genesis has two carpal bones in each hand, a big toe bone, and pectoral bones
  • Genesis has a lot of extra bones in the head that using morphing for facial expressions could replace
  • Genesis doesn't have forearm twist, which can be added to the Biped.
So, in my process of transferring the Daz model to the biped rig, I had to resolve these.

Removing Unnecessary Bones

Normally, more bones can create a nicer, more realistic rig, but it can also become more difficult for the game engine to process and more time-consuming to animate.  This being said, I had to delete these unneeded bones, but unfortunately, when removing bones from the skin modifier, 3DS Max doesn't do a great job reconfiguring skin weights.
I could delete the carpals, but there is no guarantee these weights would be distributed to the hand properly.

I could have tried deleting this and fixing it by hand or use the weight table to transfer these values to the hand bones, but this would have taken a very long time.  Instead, I decided to write a maxscript!  Unfortunately, I don't know much about manipulating skin values with maxscript, so my solution is rather slow and tedious.  It also uses two functions which actually aren't documented, which made this process rather irritating:
-- Swaps selected indices between bones.

rollout skinTransferRollout "Skin Transfer"
(
    edittext boneIndexA "Bone Index A" readonly:true
    edittext boneIndexB "Bone Index B" readonly:true
  
    checkbox canCheck "Add Bone Weight"
  
    button setBoneA "Set Bone A"
    button setBoneB "Set Bone B"
  
    on setBoneA pressed do
    (
        _mySkin = $.modifiers[#Skin]
        boneIndexA.text = (skinOps.GetSelectedBone _mySkin) as string;
    )
  
    on setBoneB pressed do
    (
        _mySkin = $.modifiers[#Skin]
        boneIndexB.text = (skinOps.GetSelectedBone _mySkin) as string;
    )
  
    button doit "Process Scene" -- button to start processing
    progressbar doit_prog color:red -- a red progress bar
    on doit pressed do -- when the button is pressed...
    (
        bA = boneIndexA.text as integer;
        bB = boneIndexB.text as integer;
      
        _mySkin = $.modifiers[#Skin]

        -- Gets the number of vertices
        vertCount = skinOps.GetNumberVertices _mySkin

        print vertCount;

        selectedBone  = skinOps.GetSelectedBone _mySkin
        print selectedBone

        selectedVertexArray = #()

        for i = 1 to vertCount do
        (
            if (skinOps.IsVertexSelected _mySkin  i) == 1 then
            (
                append selectedVertexArray i
            )
        )

        finVertCount = selectedVertexArray.count;
      
        for i = 1 to finVertCount do
        (
            doit_prog.value = 100.0*i / finVertCount
          
            v = selectedVertexArray[i];
  
            skinOps.selectVertices _mySkin v
  
            weightCount = skinOps.GetVertexWeightCount _mySkin v;
  
            weight = -1.0
            for b = 1 to weightCount do
            (
                boneIndex = skinOps.GetVertexWeightBoneID _mySkin v b;
      
                if boneIndex == bA then
                (
                    weight = skinOps.GetVertexWeight _mySkin v b;
                )
            )
  
            if weight >= 0 then
            (
                if canCheck.checked == false then
                (
                    skinOps.SelectBone _mySkin bA
                    skinOps.setWeight _mySkin 0
      
                    skinOps.SelectBone _mySkin bB
                    skinOps.setWeight _mySkin weight
                )
                else
                (
                    skinOps.SelectBone _mySkin bA
                    skinOps.setWeight _mySkin 0
      
                    skinOps.SelectBone _mySkin bB
                    skinOps.addWeight _mySkin weight
                )
            )
        )
        doit_prog.value = 0
    )
)
createDialog skinTransferRollout-- create a dialog to test 



This script was written rather quickly and could probably be (a lot) better.  Essentially, the user defines two bones -- the first is the bone whose vertex weights will be transferred, the other is the bone they will be transferred to.  Then, the user specifies if the weights will be added or set.  I had to make this differentiation because the hand was taking information from two bones and simply setting using setWeight would cause the bones to the values to overwrite.  Also, I have to do the extensive looping of each vertex because setWeight and addWeight only work upon the selected vertices.
This was great for transferring these bones.  I was able to take the carpal weights and add them to the hands.  I also did this to the big toe bone.  I didn't do this to the pectoral because the pectoral weighting covered a large region.  I could probably figure out a way to do this, but I decided to leave them as they would just serve as additions to the biped skeleton and not interruptions the rig like the carpals did.

Align those hips!

So now that I can transfer bone weights, the next issue had to be solved.  The Genesis rig's hips and legs are not aligned while the bipeds are.  Honestly, the Gensis rig is probably more realistic or better, but to transfer the rig over, beause the biped's legs cannot be detached from the pelvis, I had to realign the genesis rig.

Fortunately, the skin modifier can be temporarily turned off to make these changes.  The Genesis's rigs hip, pelvis and leg bones were moved to align with the bipeds more properly.

 While "Always Deform" is off, the bones can be adjusted.  I had to make sure that the legs would end up being in their original positions as moving the pelvis and hips causes them to moved as well.

Aligning the Biped

The next phase in this journey is aligning the biped to the original daz rig.  Now, I could do by carefully eyeballing, but that is risky and can cause things to be misaligned, which would cause strange deformation later.  I, instead, decided to write a script to at least make sure the bone sizes were correct.

rollout bipedAligner "Biped Aligner"
(   
    local bone_selection = undefined;
    local dummy_selection = undefined;
   
    group "Biped Group"
    (
        pickbutton SelectBipedBone "Select Biped Limb"  across:2 align:#left
        edittext BipedText "" text:"undefined" align:#right readonly:true
    )
   
    group "Dummy Group"
    (
        pickbutton SelectDummy "Select Dummy" across:2 align:#left
        edittext DummyText "" text:"undefined" align:#right readonly:true
    )
   
    button ScaleX "Scale X"

    button ScaleZ "Scale Z"
   
    on SelectBipedBone picked obj do
    (
        if (obj != undefined) then
        (
            bipedAligner.bone_selection = obj;
            BipedText.text = obj.name;
        )
        else
        (
            BipedText.text = "undefined";
        )
    )
   
    on SelectDummy picked obj do
    (
        if (obj != undefined) then
        (
            bipedAligner.dummy_selection = obj
            DummyText.text = obj.name
        )
        else
        (
            DummyText.text = "undefined"
        )
    )
   
    on ScaleX pressed do
    (
        bone_dist = distance bipedAligner.bone_selection.transform.pos bipedAligner.bone_selection.children[1].transform.pos
        dummy_dist = distance bipedAligner.dummy_selection.pos bipedAligner.dummy_selection.children[1].pos;
       
        scale bipedAligner.bone_selection [dummy_dist /bone_dist , 1, 1]

    )
   
   
    on ScaleZ pressed do
    (
        bone_dist = distance bipedAligner.bone_selection.transform.pos bipedAligner.bone_selection.children[2].transform.pos
        dummy_dist = distance bipedAligner.dummy_selection.pos bipedAligner.dummy_selection.children[2].pos;
       
        scale bipedAligner.bone_selection [1, 1, dummy_dist /bone_dist]
    )
)
createdialog bipedAligner 900 200

Again, not the most elegant or clean or efficient code, but it did what I wanted.  Essentially, I pick one of the biped bones and one of the Genesis rig dummies.  I then  run a script that figures out the distance between the selected dummy and one of its children in the genesis rig and then the same in the biped rig and scales the biped bone on either the x or z axis.  Almost every bone used x except for the hips which had to use z.  Also, I use the child at index 2 for the z since the first child in the hips is actually the spine and not the legs.  By making the distances match, I'm guaranteed that the bones are the proper sizes.  Unfortunately, I still had to eyeball rotation.  I know there is a way to use the distance and get a vector and convert that to a rotation somehow -- and if you have a clear way to do so, I'd love to hear it -- but at 1:30 AM, which is when I was working on this, eyeballing was faster.

Genesis Transfer

After finally getting the rigs aligned and adding my own forearm twist bones, I could transfer the weights.  Skin Envelopes and vertex weights can be saved out to a file, which I did.  I also renamed all the biped bones so they would match the Genesis rig.  The loading process for envelopes requires the names to match; otherwise, a list of current bones has to be adjusted in a rather slow process:
This process is outrageously slow if the names don't match.
After this, the Daz model was now rigged to the biped!  Now for the wrapping to begin!

Wrap It Up!

As mentioned earlier, Skin Wrap is a nice method for transferring skin weights from one mesh to another.  One problem was I had to resize the biped for now meshes.  You can change the height of a biped in figure mode, but it looks rather silly and can probably cause inaccuracies when skin wrapping.
I was going to try and just use this, but fortunately after a quick Google search, I found biped resizer script.  Essentially it allowed me to resize a biped and keep anything skinned to it properly resized.  Anyway, after all of this madness, I made a video of me aligning the biped to a character.


(A little upset how the video isn't the recorded resolution, but hopefully it gets the point across.)

In the video, I transfer the Daz skin, after resizing it to an old character.  I don't take the time to position the biped perfectly since this is for demonstration purposes, but with my quick job, the character is skinned relatively well to the new bone structure.
This was a lengthy process, but I'm happy I got it working and it took much less time than learning a new modeling suite.  Hopefully I can use my new skin wrapping pipeline to create 3D humanoid characters for my next 3D game after Battle High 2, which I still am working on.  If you have suggestions on how to better my process, I'd love to hear it as I know these are all very quick approaches that could probably be better.


1 comment: