提交 0738669c 编写于 作者: R Ricardo Cabello

Merge pull request #6702 from TatumCreative/docs-bones

Documentation for Bones, Skeleton, and SkinnedMesh
......@@ -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>
......
......@@ -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>
......
<!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>
......@@ -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>
......
......@@ -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" ]
],
......
<!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>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册