import { Mesh } from './Mesh.js'; import { Box3 } from '../math/Box3.js'; import { Matrix4 } from '../math/Matrix4.js'; import { Sphere } from '../math/Sphere.js'; import { Vector3 } from '../math/Vector3.js'; import { Vector4 } from '../math/Vector4.js'; import { Ray } from '../math/Ray.js'; const _basePosition = /*@__PURE__*/ new Vector3(); const _skinIndex = /*@__PURE__*/ new Vector4(); const _skinWeight = /*@__PURE__*/ new Vector4(); const _vector3 = /*@__PURE__*/ new Vector3(); const _matrix4 = /*@__PURE__*/ new Matrix4(); const _vertex = /*@__PURE__*/ new Vector3(); const _sphere = /*@__PURE__*/ new Sphere(); const _inverseMatrix = /*@__PURE__*/ new Matrix4(); const _ray = /*@__PURE__*/ new Ray(); class SkinnedMesh extends Mesh { constructor( geometry, material ) { super( geometry, material ); this.isSkinnedMesh = true; this.type = 'SkinnedMesh'; this.bindMode = 'attached'; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); this.boundingBox = null; this.boundingSphere = null; } computeBoundingBox() { const geometry = this.geometry; if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } this.boundingBox.makeEmpty(); const positionAttribute = geometry.getAttribute( 'position' ); for ( let i = 0; i < positionAttribute.count; i ++ ) { _vertex.fromBufferAttribute( positionAttribute, i ); this.applyBoneTransform( i, _vertex ); this.boundingBox.expandByPoint( _vertex ); } } computeBoundingSphere() { const geometry = this.geometry; if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } this.boundingSphere.makeEmpty(); const positionAttribute = geometry.getAttribute( 'position' ); for ( let i = 0; i < positionAttribute.count; i ++ ) { _vertex.fromBufferAttribute( positionAttribute, i ); this.applyBoneTransform( i, _vertex ); this.boundingSphere.expandByPoint( _vertex ); } } copy( source, recursive ) { super.copy( source, recursive ); this.bindMode = source.bindMode; this.bindMatrix.copy( source.bindMatrix ); this.bindMatrixInverse.copy( source.bindMatrixInverse ); this.skeleton = source.skeleton; if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); return this; } raycast( raycaster, intersects ) { const material = this.material; const matrixWorld = this.matrixWorld; if ( material === undefined ) return; // test with bounding sphere in world space if ( this.boundingSphere === null ) this.computeBoundingSphere(); _sphere.copy( this.boundingSphere ); _sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; // convert ray to local space of skinned mesh _inverseMatrix.copy( matrixWorld ).invert(); _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); // test with bounding box in local space if ( this.boundingBox !== null ) { if ( _ray.intersectsBox( this.boundingBox ) === false ) return; } // test for intersections with geometry this._computeIntersections( raycaster, intersects, _ray ); } getVertexPosition( index, target ) { super.getVertexPosition( index, target ); this.applyBoneTransform( index, target ); return target; } bind( skeleton, bindMatrix ) { this.skeleton = skeleton; if ( bindMatrix === undefined ) { this.updateMatrixWorld( true ); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy( bindMatrix ); this.bindMatrixInverse.copy( bindMatrix ).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { vector.fromBufferAttribute( skinWeight, i ); const scale = 1.0 / vector.manhattanLength(); if ( scale !== Infinity ) { vector.multiplyScalar( scale ); } else { vector.set( 1, 0, 0, 0 ); // do something reasonable } skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); } } updateMatrixWorld( force ) { super.updateMatrixWorld( force ); if ( this.bindMode === 'attached' ) { this.bindMatrixInverse.copy( this.matrixWorld ).invert(); } else if ( this.bindMode === 'detached' ) { this.bindMatrixInverse.copy( this.bindMatrix ).invert(); } else { console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); } } applyBoneTransform( index, vector ) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); _basePosition.copy( vector ).applyMatrix4( this.bindMatrix ); vector.set( 0, 0, 0 ); for ( let i = 0; i < 4; i ++ ) { const weight = _skinWeight.getComponent( i ); if ( weight !== 0 ) { const boneIndex = _skinIndex.getComponent( i ); _matrix4.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); vector.addScaledVector( _vector3.copy( _basePosition ).applyMatrix4( _matrix4 ), weight ); } } return vector.applyMatrix4( this.bindMatrixInverse ); } boneTransform( index, vector ) { // @deprecated, r151 console.warn( 'THREE.SkinnedMesh: .boneTransform() was renamed to .applyBoneTransform() in r151.' ); return this.applyBoneTransform( index, vector ); } } export { SkinnedMesh };