210 lines
3.9 KiB
JavaScript
210 lines
3.9 KiB
JavaScript
|
import { Mesh } from './Mesh.js';
|
||
|
import { Vector4 } from '../math/Vector4.js';
|
||
|
import { Skeleton } from './Skeleton.js';
|
||
|
import { Bone } from './Bone.js';
|
||
|
import { Matrix4 } from '../math/Matrix4.js';
|
||
|
|
||
|
/**
|
||
|
* @author mikael emtinger / http://gomo.se/
|
||
|
* @author alteredq / http://alteredqualia.com/
|
||
|
* @author ikerr / http://verold.com
|
||
|
*/
|
||
|
|
||
|
function SkinnedMesh( geometry, material ) {
|
||
|
|
||
|
Mesh.call( this, geometry, material );
|
||
|
|
||
|
this.type = 'SkinnedMesh';
|
||
|
|
||
|
this.bindMode = 'attached';
|
||
|
this.bindMatrix = new Matrix4();
|
||
|
this.bindMatrixInverse = new Matrix4();
|
||
|
|
||
|
var bones = this.initBones();
|
||
|
var skeleton = new Skeleton( bones );
|
||
|
|
||
|
this.bind( skeleton, this.matrixWorld );
|
||
|
|
||
|
this.normalizeSkinWeights();
|
||
|
|
||
|
}
|
||
|
|
||
|
SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
|
||
|
|
||
|
constructor: SkinnedMesh,
|
||
|
|
||
|
isSkinnedMesh: true,
|
||
|
|
||
|
initBones: function () {
|
||
|
|
||
|
var bones = [], bone, gbone;
|
||
|
var i, il;
|
||
|
|
||
|
if ( this.geometry && this.geometry.bones !== undefined ) {
|
||
|
|
||
|
// first, create array of 'Bone' objects from geometry data
|
||
|
|
||
|
for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {
|
||
|
|
||
|
gbone = this.geometry.bones[ i ];
|
||
|
|
||
|
// create new 'Bone' object
|
||
|
|
||
|
bone = new Bone();
|
||
|
bones.push( bone );
|
||
|
|
||
|
// apply values
|
||
|
|
||
|
bone.name = gbone.name;
|
||
|
bone.position.fromArray( gbone.pos );
|
||
|
bone.quaternion.fromArray( gbone.rotq );
|
||
|
if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
|
||
|
|
||
|
}
|
||
|
|
||
|
// second, create bone hierarchy
|
||
|
|
||
|
for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {
|
||
|
|
||
|
gbone = this.geometry.bones[ i ];
|
||
|
|
||
|
if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {
|
||
|
|
||
|
// subsequent bones in the hierarchy
|
||
|
|
||
|
bones[ gbone.parent ].add( bones[ i ] );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// topmost bone, immediate child of the skinned mesh
|
||
|
|
||
|
this.add( bones[ i ] );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// now the bones are part of the scene graph and children of the skinned mesh.
|
||
|
// let's update the corresponding matrices
|
||
|
|
||
|
this.updateMatrixWorld( true );
|
||
|
|
||
|
return bones;
|
||
|
|
||
|
},
|
||
|
|
||
|
bind: function ( skeleton, bindMatrix ) {
|
||
|
|
||
|
this.skeleton = skeleton;
|
||
|
|
||
|
if ( bindMatrix === undefined ) {
|
||
|
|
||
|
this.updateMatrixWorld( true );
|
||
|
|
||
|
this.skeleton.calculateInverses();
|
||
|
|
||
|
bindMatrix = this.matrixWorld;
|
||
|
|
||
|
}
|
||
|
|
||
|
this.bindMatrix.copy( bindMatrix );
|
||
|
this.bindMatrixInverse.getInverse( bindMatrix );
|
||
|
|
||
|
},
|
||
|
|
||
|
pose: function () {
|
||
|
|
||
|
this.skeleton.pose();
|
||
|
|
||
|
},
|
||
|
|
||
|
normalizeSkinWeights: function () {
|
||
|
|
||
|
var scale, i;
|
||
|
|
||
|
if ( this.geometry && this.geometry.isGeometry ) {
|
||
|
|
||
|
for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) {
|
||
|
|
||
|
var sw = this.geometry.skinWeights[ i ];
|
||
|
|
||
|
scale = 1.0 / sw.manhattanLength();
|
||
|
|
||
|
if ( scale !== Infinity ) {
|
||
|
|
||
|
sw.multiplyScalar( scale );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
sw.set( 1, 0, 0, 0 ); // do something reasonable
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else if ( this.geometry && this.geometry.isBufferGeometry ) {
|
||
|
|
||
|
var vec = new Vector4();
|
||
|
|
||
|
var skinWeight = this.geometry.attributes.skinWeight;
|
||
|
|
||
|
for ( i = 0; i < skinWeight.count; i ++ ) {
|
||
|
|
||
|
vec.x = skinWeight.getX( i );
|
||
|
vec.y = skinWeight.getY( i );
|
||
|
vec.z = skinWeight.getZ( i );
|
||
|
vec.w = skinWeight.getW( i );
|
||
|
|
||
|
scale = 1.0 / vec.manhattanLength();
|
||
|
|
||
|
if ( scale !== Infinity ) {
|
||
|
|
||
|
vec.multiplyScalar( scale );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
vec.set( 1, 0, 0, 0 ); // do something reasonable
|
||
|
|
||
|
}
|
||
|
|
||
|
skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
updateMatrixWorld: function ( force ) {
|
||
|
|
||
|
Mesh.prototype.updateMatrixWorld.call( this, force );
|
||
|
|
||
|
if ( this.bindMode === 'attached' ) {
|
||
|
|
||
|
this.bindMatrixInverse.getInverse( this.matrixWorld );
|
||
|
|
||
|
} else if ( this.bindMode === 'detached' ) {
|
||
|
|
||
|
this.bindMatrixInverse.getInverse( this.bindMatrix );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode );
|
||
|
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
clone: function () {
|
||
|
|
||
|
return new this.constructor( this.geometry, this.material ).copy( this );
|
||
|
|
||
|
}
|
||
|
|
||
|
} );
|
||
|
|
||
|
|
||
|
export { SkinnedMesh };
|