Three.JS Multiple SkinnedMesh w/ Shared Skeleton
Demonstrates a multi-part character with a shared animated skeleton
by Adi Jaya
HTML
<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.js"></script>
<canvas id="viewport"></canvas>
<div id="controls">
<button id="toggle-body">Toggle Body</button>
<button id="toggle-clothes">Toggle Clothes</button>
<button id="toggle-skeleton">Toggle Skeleton</button>
<button id="arms-down">Arms Down</button>
<button id="arms-up">Arms Up</button>
<button id="toggle-animation">Toggle Animation</button>
<p>Multiple THREE.SkinnedMesh (Indexed BufferGeometry) instances sharing a single THREE.Skeleton w/ THREE.AnimationClip</p>
</div>
CSS
html, body { margin: 0; height: 100%; color: #999; font-family: monospace }
#viewport { display: block; position: absolute; width: 100%; height: 100%; }
#controls { position: absolute; top: 0; left: 0; padding: .25em; z-index: 9999; }
JavaScript
// This demonstrates creating and binding a shared THREE.Skeleton to multiple THREE.SkinnedMesh instances.
//
// A THREE.Group instance is used as an armature root object.
// Body and Clothes THREE.SkinnedMesh instances, and a THREE.Bone
// heirarchy are parented to the root object.
//
// NOTE: This is using unpatched dev branch 81dev
const SCENE_URL = 'https://gist.githubusercontent.com/satori99/6f642d37999d73fafbdecd5deee0a93b/raw/human.json'
var viewport, renderer, clock, scene, camera
var root, skeleton, body, clothes
var mixer, action
init()
function init() {
viewport = document.querySelector('#viewport')
renderer = new THREE.WebGLRenderer( { canvas: viewport, antialias: true } )
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
renderer.setSize( viewport.clientWidth, viewport.clientHeight )
renderer.clear()
clock = new THREE.Clock()
document.body.appendChild( renderer.domElement )
new THREE.ObjectLoader().load( SCENE_URL, result => {
scene = result
// find camera
camera = scene.getObjectByName( 'Camera' )
camera.aspect = viewport.clientWidth / viewport.clientHeight
camera.updateProjectionMatrix()
// find armature root object.
// This is a group instance with a userData.bones property
// containing bones in the same format as would normally be
// found on a SkinnedMesh Geometry instance
root = scene.getObjectByName( 'Human' )
// manually create bones and parent them to the root object
// NOTE: This is normally done in the SkinnedMesh constructor
const bones = createBones( root, root.userData.bones )
// Important! must update world matrices before creating skeleton instance
root.updateMatrixWorld()
// create skeleton
skeleton = new THREE.Skeleton( bones, undefined, true )
// create SkinnedMesh from static mesh geometry
body = createSkinnedMesh( root.getObjectByName( 'Body' ), skeleton )
clothes = createSkinnedMesh(...