Browse Source

Merge pull request #6702 from TatumCreative/docs-bones

Documentation for Bones, Skeleton, and SkinnedMesh
Ricardo Cabello 10 years ago
parent
commit
0738669cf4

+ 20 - 2
docs/api/core/Geometry.html

@@ -102,12 +102,30 @@
 
 		<h3>[property:Array skinWeights]</h3>
 		<div>
-		Array of skinning weights, matching number and order of vertices.
+		Array of [page:Vector4 Vector4s] representing the skinning weights as used in a [page:SkinnedMesh],
+		The weights match the number and order of vertices in the geometry. The weighted values
+		are typically between the values of 0 and 1 and affect the amount that the individuals bones affect
+		a given vertex.
 		</div>
 
 		<h3>[property:Array skinIndices]</h3>
 		<div>
-		Array of skinning indices, matching number and order of vertices.
+		Array of [page:Vector4 Vector4s] representing the indices of individual bones in the [page:Skeleton.bones] array,
+		The indices match the number and order of vertices in the geometry.
+		<code>
+		// e.g.
+		geometry.skinIndices[15] = new THREE.Vector4(   0,   5,   9, 0 );
+		geometry.skinWeights[15] = new THREE.Vector4( 0.2, 0.5, 0.3, 0 );
+		
+		// corresponds with the following vertex
+		geometry.vertices[15];
+		
+		// these bones will be used like so:
+		skeleton.bones[0]; // weight of 0.2
+		skeleton.bones[5]; // weight of 0.5
+		skeleton.bones[9]; // weight of 0.3
+		skeleton.bones[0]; // weight of 0
+		</code>
 		</div>
 
 		<h3>[property:Object boundingBox]</h3>

+ 17 - 8
docs/api/objects/Bone.html

@@ -11,25 +11,34 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">A bone which is part of a [page:SkinnedMesh].</div>
-
+		<div class="desc">
+		A bone which is part of a [page:Skeleton]. The skeleton in turn is used by the [page:SkinnedMesh].
+		Bones are almost identical to a blank [page:Object3D].
+		</div>
+		
+		<h3>Example</h3>
+		
+		<code>
+		var root = new THREE.Bone();
+		var child = new THREE.Bone();
+		
+		root.add( child );
+		child.position.y = 5;
+		</code>
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:SkinnedMesh belongsToSkin])</h3>
-		<div>
-		belongsToSkin -- An instance of [page:SkinnedMesh].
-		</div>
+		<h3>[name]([page:SkinnedMesh skin])</h3>
 		<div>
-		This creates a new instance of a bone from the skin.
+		skin — (optional) The [page:SkinnedMesh] to which the bone belongs.
 		</div>
 
 		<h2>Properties</h2>
 
 		<h3>[property:SkinnedMesh skin]</h3>
 		<div>
-		The skin that contains this bone.
+		An optional reference to the [page:SkinnedMesh].
 		</div>
 
 

+ 128 - 0
docs/api/objects/Skeleton.html

@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<script src="../../list.js"></script>
+		<script src="../../page.js"></script>
+		<link type="text/css" rel="stylesheet" href="../../page.css" />
+	</head>
+	<body>
+		
+		<h1>[name]</h1>
+
+		<div class="desc">
+		Use an array of [page:Bone bones] to create a skeleton that can be used by a [page:SkinnedMesh].
+		WebGL only.
+		</div>
+
+		<h2>Example</h2>
+
+		<code>
+		// Create a simple "arm"
+		
+		var bones = [];
+		
+		var shoulder = new THREE.Bone();
+		var elbow = new THREE.Bone();
+		var hand = new THREE.Bone();
+		
+		shoulder.add( elbow );
+		elbow.add( hand );
+
+		bones.push( shoulder );
+		bones.push( elbow );
+		bones.push( hand );
+		
+		shoulder.position.y = -5;
+		elbow.position.y = 0;
+		hand.position.y = 5;
+		
+		var armSkeleton = THREE.Skeleton( bones );
+		
+		// See THREE.SkinnedMesh for an example of usage with a mesh
+		</code>
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:Array bones], [page:Array boneInverses], [page:Boolean useVertexTexture]  )</h3>
+		<div>
+		bones — The array of [page:bone bones]<br/>
+		boneInverses — (optional) An array of [page:Matrix4 Matrix4s]<br/>
+		useVertexTexture — (optional) Whether or not to use a vertex texture in the shader.
+		</div>
+		<div>
+		The constructor automatically sets up all of the properties below.
+		</div>
+
+
+		<h2>Properties</h2>
+		
+		<h3>[property:Array bones]</h3>
+		<div>
+		The array of [page:bone bones]
+		</div>
+		
+		
+		<h3>[property:Boolean useVertexTexture]</h3>
+		<div>
+		Whether or not to use a vertex texture in the shader, set in the constructor. Not all devices
+		support floating point pixel textures. If this option is set then the bone matrices will be packed into
+		a texture and sent to the shader. This method allows a much larger set of bones to be used. Otherwise
+		the vertex shader will use uniforms, which do not allow for as many bones to be used. The exact
+		numbers vary between devices.
+		</div>
+		
+		
+		<h3>[property:Array boneInverses]</h3>
+		<div>
+		An array of [page:Matrix4 Matrix4s] that represent the inverse of the matrixWorld of the individual bones.
+		</div>
+		
+		
+		<h3>[property:Integer boneTextureWidth]</h3>
+		<div>
+		The width of the vertex data texture.
+		</div>
+		
+		
+		<h3>[property:Integer boneTextureHeight]</h3>
+		<div>
+		The height of the vertex data texture.
+		</div>
+		
+		
+		<h3>[property:Float32Array boneMatrices]</h3>
+		<div>
+		The array buffer holding the bone data when using a vertex texture.
+		</div>
+		
+		
+		<h3>[property:DataTexture boneTexture]</h3>
+		<div>
+		The [page:DataTexture] holding the bone data when using a vertex texture.
+		</div>
+		
+
+		<h2>Methods</h2>
+
+		<h3>[method:null calculateInverses]()</h3>
+		<div>Generates the boneInverses.</div>
+		
+		
+		<h3>[method:null pose]()</h3>
+		<div>Returns the skeleton to the base pose.</div>
+		
+		
+		<h3>[method:null update]()</h3>
+		<div>
+		Updates the [page:Float32Array boneMatrices] and [page:DataTexture boneTexture] after changing the bones.
+		This is called automatically by the [page:WebGLRenderer] if the skeleton is used with a [page:SkinnedMesh].
+		</div>
+		
+		
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 76 - 12
docs/api/objects/SkinnedMesh.html

@@ -11,27 +11,60 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">An 3d object that has bones data. These Bones can then be used to animate the vertices of the object.</div>
-
+		<div class="desc">A mesh that has a [page:Skeleton] with [page:Bone bones] that can then be used to animate the vertices of the geometry.</div>
+
+		<h2>Example</h2>
+		
+		<iframe src='../../scenes/bones-browser.html'></iframe>
+		
+		<code>
+		var geometry = new THREE.CylinderGeometry( 5, 5, 5, 5, 15, 5, 30 );
+		
+		//Create the skin indices and skin weights
+		for ( var i = 0; i < geometry.vertices.length; i ++ ) {
+			
+			// Imaginary functions to calculate the indices and weights
+			var skinIndex = calculateSkinIndex( geometry.vertices, i );
+			var skinWeight = calculateSkinWeight( geometry.vertices, i );
+			
+			// Ease between each bone
+			geometry.skinIndices.push( new THREE.Vector4( skinIndex, skinIndex + 1, 0, 0 ) );
+			geometry.skinWeights.push( new THREE.Vector4( 1 - skinWeight, skinWeight, 0, 0 ) );		
+			
+		}
+		
+		var mesh = THREE.SkinnedMesh( geometry, material );
+
+		// See example from THREE.Skeleton for the armSkeleton
+		var rootBone = armSkeleton.bones[ 0 ];
+		mesh.add( rootBone );
+		
+		// Bind the skeleton to the mesh
+		mesh.bind( armSkeleton );
+		
+		// Update the inverse matrices in the skeleton to reflect the newly bound skeleton
+		armSkeleton.calculateInverses();
+		
+		// Move the bones and manipulate the model
+		armSkeleton.bones[ 0 ].rotation.x = -0.1;
+		armSkeleton.bones[ 1 ].rotation.x = 0.2;
+		</code>
+		
 
 		<h2>Constructor</h2>
 
 
 		<h3>[name]([page:Geometry geometry], [page:Material material], [page:boolean useVertexTexture])</h3>
 		<div>
-                geometry — An instance of [page:Geometry].<br />
-                material — An instance of [page:Material] (optional).<br />
-		useVertexTexture -- Defines wether a vertex texture can be used (optional).
-		</div>
-		<div>
-		This Creates a new instance of skinnedMesh.
+        geometry — An instance of [page:Geometry]. [page:Geometry.skinIndices] and [page:Geometry.skinWeights] should be set.<br />
+        material — An instance of [page:Material] (optional).<br />
+		useVertexTexture -- Defines whether a vertex texture can be used (optional).
 		</div>
 
 
 		<h2>Properties</h2>
 
 
-
 		<h3>[property:array bones]</h3>
 		<div>
 		This contains the array of bones for this mesh. These should be set in the constructor.
@@ -52,19 +85,50 @@
 		This array of matrices contains the matrices of the bones. These get calculated in the constructor.
 		</div>
 
+		<h3>[property:string bindMode]</h3>
+		<div>
+		Either "attached" or "detached". "attached" uses the [page:SkinnedMesh.matrixWorld] property for the base transform
+		matrix of the bones. "detached" uses the [page:SkinnedMesh.bindMatrix].
+		</div>
+		
+		<h3>[property:Matrix4 bindMatrix]</h3>
+		<div>
+		The base matrix that is used for the bound bone transforms.
+		</div>
+
+		<h3>[property:Matrix4 inverseBindMatrix]</h3>
+		<div>
+		The inverse of the bindMatrix.
+		</div>
+
 		<h2>Methods</h2>
 
+		<h3>[method:null bind]([page:Skeleton skeleton], [page:Matrix4 bindMatrix])</h3>
+		<div>
+		skeleton — [page:Skeleton]<br/>
+		bindMatrix — [page:Matrix4] that represents the base transform of the skeleton
+		</div>
+		<div>
+		Bind a skeleton to the skinned mesh. The bindMatrix gets saved to .bindMatrix property and the .bindMatrixInverse
+		gets calculated.
+		</div>
+		
+		<h3>[method:null normalizeSkinWeights]()</h3>
+		<div>
+		Normalizes the [page:Geometry.skinWeights] vectors. Does not affect [page:BufferGeometry].
+		</div>
+
 		<h3>[method:null pose]()</h3>
 		<div>
-		This method sets the skinnedmesh in the rest pose.
+		This method sets the skinned mesh in the rest pose.
 		</div>
 
 		<h3>[method:Bone addBone]([page:Bone bone])</h3>
 		<div>
-		bone -- This is the bone that needs to be added. (optional)
+		bone  This is the bone that needs to be added. (optional)
 		</div>
 		<div>
-		This method adds the bone to the skinnedmesh when it is provided. It creates a new bone and adds that when no bone is given.
+		This method adds the bone to the skinned mesh when it is provided. It creates a new bone and adds that when no bone is given.
 		</div>
 
 		<h2>Source</h2>

+ 1 - 0
docs/list.js

@@ -116,6 +116,7 @@ var list = {
 			[ "MorphAnimMesh", "api/objects/MorphAnimMesh" ],
 			[ "PointCloud", "api/objects/PointCloud" ],
 			[ "SkinnedMesh", "api/objects/SkinnedMesh" ],
+			[ "Skeleton", "api/objects/Skeleton" ],
 			[ "Sprite", "api/objects/Sprite" ]
 		],
 

+ 272 - 0
docs/scenes/bones-browser.html

@@ -0,0 +1,272 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<title>Three.js Bones Browser</title>
+		<style>
+			@font-face {
+				font-family: 'inconsolata';
+				src: url('../files/inconsolata.woff') format('woff');
+				font-weight: normal;
+				font-style: normal;
+			}
+			
+			body {
+				margin:0;
+				font-family: 'inconsolata';
+				font-size: 15px;
+				line-height: 18px;
+				overflow: hidden;
+			}
+			
+			canvas { width: 100%; height: 100% }
+			
+			#newWindow {
+				display: block;
+				position: absolute;
+				bottom: 0.3em;
+				left: 0.5em;
+				color: #fff;
+			}
+		</style>
+	</head>
+	<body>
+		
+		<a id='newWindow' href='./bones-browser.html' target='_blank'>Open in New Window</a>
+		
+		<script src="../../build/three.min.js"></script>
+		<script src='../../examples/js/libs/dat.gui.min.js'></script>
+		<script src="../../examples/js/controls/OrbitControls.js"></script>
+		
+		<script>
+
+			var gui, scene, camera, renderer, orbit, ambientLight, lights, mesh, bones, skeletonHelper;
+
+			var state = {
+
+				animateBones : false
+
+			};
+
+			function initScene () {
+
+				gui = new dat.GUI();
+				scene = new THREE.Scene();
+				camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 200 );
+				camera.position.z = 30;
+				camera.position.y = 30;
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				orbit = new THREE.OrbitControls( camera, renderer.domElement );
+				orbit.noZoom = true;
+
+				ambientLight = new THREE.AmbientLight( 0x000000 );
+				scene.add( ambientLight );
+
+				lights = [];
+				lights[ 0 ] = new THREE.PointLight( 0xffffff, 1, 0 );
+				lights[ 1 ] = new THREE.PointLight( 0xffffff, 1, 0 );
+				lights[ 2 ] = new THREE.PointLight( 0xffffff, 1, 0 );
+
+				lights[ 0 ].position.set( 0, 200, 0 );
+				lights[ 1 ].position.set( 100, 200, 100 );
+				lights[ 2 ].position.set( -100, -200, -100 );
+
+				scene.add( lights[ 0 ] );
+				scene.add( lights[ 1 ] );
+				scene.add( lights[ 2 ] );
+
+				window.addEventListener( 'resize', function () {
+
+					camera.aspect = window.innerWidth / window.innerHeight;
+					camera.updateProjectionMatrix();
+
+					renderer.setSize( window.innerWidth, window.innerHeight );
+
+				}, false );
+
+				initBones();
+				setupDatGui();
+
+			}
+
+			function createGeometry ( sizing ) {
+
+				var geometry = new THREE.CylinderGeometry(
+					5,                       // radiusTop
+					5,                       // radiusBottom
+					sizing.height,           // height
+					8,                       // radiusSegments
+					sizing.segmentCount * 3, // heightSegments
+					true                     // openEnded
+				);
+
+				for ( var i = 0; i < geometry.vertices.length; i ++ ) {
+
+					var vertex = geometry.vertices[ i ];
+					var y = ( vertex.y + sizing.halfHeight );
+
+					var skinIndex = Math.floor( y / sizing.segmentHeight );
+					var skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight;
+
+					geometry.skinIndices.push( new THREE.Vector4( skinIndex, skinIndex + 1, 0, 0 ) );
+					geometry.skinWeights.push( new THREE.Vector4( 1 - skinWeight, skinWeight, 0, 0 ) );
+
+				}
+
+				return geometry;
+
+			};
+
+			function createBones ( sizing ) {
+
+				bones = [];
+
+				var prevBone = new THREE.Bone();
+				bones.push( prevBone );
+				prevBone.position.y = -sizing.halfHeight;
+
+				for ( var i = 0; i < sizing.segmentCount; i ++ ) {
+
+					var bone = new THREE.Bone();
+					bone.position.y = sizing.segmentHeight;
+					bones.push( bone );
+					prevBone.add( bone );
+					prevBone = bone;
+
+				}
+
+				return bones;
+
+			};
+
+			function createMesh ( geometry, bones ) {
+
+				var material = new THREE.MeshPhongMaterial( {
+					skinning : true,
+					color: 0x156289,
+					emissive: 0x072534,
+					side: THREE.DoubleSide,
+					shading: THREE.FlatShading
+				} );
+
+				var mesh = new THREE.SkinnedMesh( geometry,	material );
+				var skeleton = new THREE.Skeleton( bones );
+	
+				mesh.add( bones[ 0 ] );
+	
+				mesh.bind( skeleton );
+				skeleton.calculateInverses();
+
+				skeletonHelper = new THREE.SkeletonHelper( mesh );
+				skeletonHelper.material.linewidth = 2;
+				scene.add( skeletonHelper );
+
+				return mesh;
+	
+			};
+
+			function setupDatGui () {
+	
+				var folder = gui.addFolder( "General Options" );
+	
+				folder.add( state, "animateBones" );
+				folder.__controllers[ 0 ].name( "Animate Bones" );
+
+				folder.add( mesh, "pose" );
+				folder.__controllers[ 1 ].name( ".pose()" );
+	
+				var bones = mesh.skeleton.bones;
+	
+				for ( var i = 0; i < bones.length; i ++ ) {
+		
+					var bone = bones[ i ];
+		
+					folder = gui.addFolder( "Bone " + i );
+	
+					folder.add( bone.position, 'x', -10 + bone.position.x, 10 + bone.position.x );
+					folder.add( bone.position, 'y', -10 + bone.position.y, 10 + bone.position.y );
+					folder.add( bone.position, 'z', -10 + bone.position.z, 10 + bone.position.z );
+
+					folder.add( bone.rotation, 'x', -Math.PI * 0.5, Math.PI * 0.5 );
+					folder.add( bone.rotation, 'y', -Math.PI * 0.5, Math.PI * 0.5 );
+					folder.add( bone.rotation, 'z', -Math.PI * 0.5, Math.PI * 0.5 );
+		
+					folder.add( bone.scale, 'x', 0, 2 );
+					folder.add( bone.scale, 'y', 0, 2 );
+					folder.add( bone.scale, 'z', 0, 2 );
+
+					folder.__controllers[ 0 ].name( "position.x" );
+					folder.__controllers[ 1 ].name( "position.y" );
+					folder.__controllers[ 2 ].name( "position.z" );
+		
+					folder.__controllers[ 3 ].name( "rotation.x" );
+					folder.__controllers[ 4 ].name( "rotation.y" );
+					folder.__controllers[ 5 ].name( "rotation.z" );
+		
+					folder.__controllers[ 6 ].name( "scale.x" );
+					folder.__controllers[ 7 ].name( "scale.y" );
+					folder.__controllers[ 8 ].name( "scale.z" );
+		
+				}
+				
+			}
+
+			function initBones () {
+	
+				var segmentHeight = 8;
+				var segmentCount = 4;
+				var height = segmentHeight * segmentCount;
+				var halfHeight = height * 0.5;
+
+				var sizing = {
+					segmentHeight : segmentHeight,
+					segmentCount : segmentCount,
+					height : height,
+					halfHeight : halfHeight
+				};
+
+				var geometry = createGeometry( sizing );
+				var bones = createBones( sizing );
+				mesh = createMesh( geometry, bones );
+
+				mesh.scale.multiplyScalar( 1 );
+				scene.add( mesh );
+
+			};
+
+			function render () {
+	
+				requestAnimationFrame( render );
+
+				var time = Date.now() * 0.001;
+	
+				var bone = mesh;
+
+	
+				//Wiggle the bones
+				if ( state.animateBones ) {
+		
+					for ( var i = 0; i < mesh.skeleton.bones.length; i ++ ) {
+		
+						mesh.skeleton.bones[ i ].rotation.z = Math.sin( time ) * 2 / mesh.skeleton.bones.length;
+		
+					}
+		
+				}
+
+				skeletonHelper.update();
+	
+				renderer.render( scene, camera );
+	
+			};
+
+			initScene();
+			render();
+			
+		</script>
+	</body>
+</html>