New skinned mesh animation system in three.js r73 and r74

Posted :

The animation system for Skinned Mesh of three.js has been changed alot at r73 and the documentation for it is still not added. Therefore, getting it might be a little bit difficult for now. Also Blender Exporter has been renewed recently. This blog post is a quick note of a basic usage of the new animation system.

First, you need to prepare a 3D model in three.js JSON format. To publish it, I personally recommend Blender (Blender Exporter is well maintained as well as three.js). Make sure to apply rest pose before publishing. To apply it,

Clear all pose and hit [CTRL + A] in Pose Mode (might be unnecessary)

Hit [CTRL + A] in Object Mode and check Location, Rotation and Scale in the left pane (necessary)

See also this video

When you publish the model, check skinned mesh related options.

If you see this message below, select the mesh in 3D View before export. FYI: Multiple meshes must be merged to one mesh.

And then, write some code as usual. After loaded the model file, make THREE.AnimationAction instances which is a kinda container for an animation, and then add them to a THREE.AnimationMixer instance which manages animations. The Mixer will mix all animations together as one mixed animation, and you can set how much influence from each animations. Generally set weight to 1 for the active animation and 0 for others. To change the animation smoothly, there is crossFade method of the mixer. It will cross-fade weight values of 2 animations.

Here is the code and a demo down below.

demo

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
'use strict';

var width, height, clock, scene, camera, renderer;
var loader = new THREE.JSONLoader();
var ambientLight, mesh, action = {}, mixer, fadeAction;

width = window.innerWidth;
height = window.innerHeight;
clock = new THREE.Clock();
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 40, width / height, 1, 100 );
camera.position.set( 0, 1, 4 );
renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );

ambientLight = new THREE.AmbientLight( 0xffffff );
scene.add( ambientLight );

loader.load( './rin.json', function( geometry, materials ) {

materials.forEach( function ( material ) {

material.skinning = true;

} );

mesh = new THREE.SkinnedMesh(
geometry,
new THREE.MeshFaceMaterial( materials )
);

action.idle = new THREE.AnimationAction( geometry.animations[ 0 ] );
action.run = new THREE.AnimationAction( geometry.animations[ 1 ] );
action.jump = new THREE.AnimationAction( geometry.animations[ 2 ] );
action.slide = new THREE.AnimationAction( geometry.animations[ 3 ] );

action.idle.weight = 1;
action.run.weight = 0;
action.jump.weight = 0;
action.slide.weight = 0;
// action.jump.loop = THREE.LoopOnce;

mixer = new THREE.AnimationMixer( mesh );
mixer.addAction( action.idle );
mixer.addAction( action.run );
mixer.addAction( action.jump );
mixer.addAction( action.slide );

scene.add( mesh );

} );


fadeAction = function () {

var activeActionName = 'idle';

return function ( name ) {

mixer.crossFade( action[ activeActionName ], action[ name ], .3 );
activeActionName = name;

}

}();


;( function update () {

requestAnimationFrame( update );

var delta = clock.getDelta();
var theta = clock.getElapsedTime();

if ( mixer ) { mixer.update( delta ); }

renderer.render( scene, camera );

} )();

You can change the animation, using crossFade. The animation will cross-fade from actionInstanceA to actionInstanceB over 0.5 sec.

1
mixerInstance.crossFade( actionInstanceA, actionInstanceB, 0.5 );

Updated and added below on Feb 7th, 2016

three.js r74

Animation System was changed again at r74, for performance optimization.

AnimationAction class has been removed, you need to make instant instances with mixer.clipAction().

fadeIn() and fadeOut() are now clip instance’s methods, and previous crossFade() has been split into crossFadeFrom() and crossFadeTo().

If you would like to set LoopOnce, use setLoop() and clampWhenFinished prop.

Here is an example for r74. check the code.