How to Export a Rigged, Animated Model From 3ds Max to Three.js

3ds max to Three.js

See the demo running here.

After about six months, I've finally figured out how to export a boned, rigged, animated model from 3ds max and import it into Three.js.

Requirements

  • Three.js r68 or higher
  • Intermediate knowledge of 3ds max (this is not a rigging tutorial)
  • Use the Skin modifier to rig your mesh. Not a strict requirement, but this tutorial is Skin-modifier focused
  • A browser capable of running WebGL (duh)

Warning: This process is awful. There are multiple gotchas, and I will attempt to document them all.

This tutorial is only for a model that is rigged (with bones), and animated with one animation. In a later blog post we will explore multiple animations and blending.

Create and Rig Your Model

I can't help you very much here, but I can give you some pointers to make the process easier.

3ds max bone name interface

Tip: Name your bones specifically, like "JawBoneUpper," so that you can find them easily when skinning.

XForm modifier in stack

Tip: Add an XForm modifier to the stack before you start rigging. I don't know if this is strictly required, but it will reset your object's position, rotation, etc to the origin, minimizing import problems.

Edit Mesh modifier in stack

Requirement: YOUR MODEL MUST BE A MESH. What this means is you cannot export an Editable Poly or anything else using this technique. Before you apply the Skin modifier add an "Edit Mesh" modifier to the stack. This isn't required if your base object is already an editable mesh, but if you're modeling with editable meshes you should reconsider your life priorities.

If you've already rigged your object with the Skin modifier, you might be able to click on the entry below it in the stack (in this case, Unwrap UVW), accept the warning, add the Edit Mesh modifier, and then jump back up the stack (de-select Edit Mesh / select Skin). This worked for me, but your results may vary. In general, get in the habit of adding "Edit Mesh" as part of your workflow before adding Skin.

Vertices missing weights in rigged 3ds max model

Requirement: ALL VERTICES MUST HAVE WEIGHTS if their respective bone is being animated! This one is a big gotcha, and difficult to address in your mesh. Look at the above image. I have CoreBone selected, but you can see that there are vertices (circled) on the head that have no weights associated. You must visually inspect each bone in the Skin modifier to make sure all vertices are accounted for.

If you don't weight all vertices then you will get holes in your model, or worse. See the below image. If you see this, go back to your model, add appropriate weights, then re-export.

Set Up 3ds max for Export

1. Install the Three.js export plugin file ThreeJSAnimationExporter.ms found in this folder (direct link). You do not need the other plugins. It's confusing that they exist.

Place the plugin in your 3ds max plugins folder, found by going to Customize > Configure System Paths > "3rd Party Plug-Ins" tab. It's traditionally "3ds max install location/Plugins".

2. Restart 3ds max. On restart, you will get a new dialog. DO NOT CLOSE IT, because you cannot reopen it! This is a nightmare, I know. Just move it to the side for now. Having multiple monitors helps.

Export your model

Three.js model export plugin dialog

Select your MODEL ONLY, NOT the bones. The script will error if you select the bones. You can leave the frame rate at 25 unless you have a specific reason to change it. Then click the Export button. Your animation will auto-play a few times (very jarring!). Don't touch it.

Warning: If the script errors, you cannot repeat the process until you restart 3ds max. The button just stops working if the export fails. Great! If you get an error "Runtime error: Mesh operation on non-mesh: Editable Poly," then see the above steps for making your model an Editable Mesh, and restart.

If there are no errors, you're almost there!

Load Your Model in Three.js

The commented code is below.

var loader = new THREE.JSONLoader(),  
    animatedMesh;

loader.load( './YOUR-EXPORTED-FILE-NAME.js', function ( geometry, materials ) {

    // Tell the material that it has bone weights
    var originalMaterial = materials[ 0 ];
    originalMaterial.skinning = true;

    // Create a new SkinnedMesh (important! Not a animatedMesh!)
    animatedMesh = new THREE.SkinnedMesh( geometry, originalMaterial );

    // Optional scale step, your results may vary
    animatedMesh.scale.set( 0.1, 0.1, 0.1 );

    // Instantiate the animation
    var animation = new THREE.Animation(
        animatedMesh,
        geometry.animation
    );

    // Start playing the animation
    animation.play();

});

// ... Your scene code here ...

// Add animatedMesh to scene

scene.add( animatedMesh );

Add the update function to your animation loop. You'll need to instantiate a clock outside the render function to get the elapsed time.

var clock = new THREE.Clock();

// Your render function
function render() {

    var delta = clock.getDelta();

    THREE.AnimationHandler.update( delta );

    // ... The rest of your animation code ...

}

You Did It!

Look at the horrible thing you've done.

The working demo is found here. In the next installment of "Andy fights through a web of manure so you don't have to," we'll look at multiple animations on a model, and blending between them.

Resources

Several other pages gave me some insight while dealing with this pile of baloney.

If this tutorial helped you, consider following me on Twitter or buying me a coffee :).

comments powered by Disqus