Spaces:
Running
Running
| import { RGBAFormat, FloatType } from '../constants.js'; | |
| import { Bone } from './Bone.js'; | |
| import { Matrix4 } from '../math/Matrix4.js'; | |
| import { DataTexture } from '../textures/DataTexture.js'; | |
| import * as MathUtils from '../math/MathUtils.js'; | |
| const _offsetMatrix = /*@__PURE__*/ new Matrix4(); | |
| const _identityMatrix = /*@__PURE__*/ new Matrix4(); | |
| class Skeleton { | |
| constructor(bones = [], boneInverses = []) { | |
| this.uuid = MathUtils.generateUUID(); | |
| this.bones = bones.slice(0); | |
| this.boneInverses = boneInverses; | |
| this.boneMatrices = null; | |
| this.boneTexture = null; | |
| this.boneTextureSize = 0; | |
| this.frame = -1; | |
| this.init(); | |
| } | |
| init() { | |
| const bones = this.bones; | |
| const boneInverses = this.boneInverses; | |
| this.boneMatrices = new Float32Array(bones.length * 16); | |
| // calculate inverse bone matrices if necessary | |
| if (boneInverses.length === 0) { | |
| this.calculateInverses(); | |
| } else { | |
| // handle special case | |
| if (bones.length !== boneInverses.length) { | |
| console.warn('THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.'); | |
| this.boneInverses = []; | |
| for (let i = 0, il = this.bones.length; i < il; i++) { | |
| this.boneInverses.push(new Matrix4()); | |
| } | |
| } | |
| } | |
| } | |
| calculateInverses() { | |
| this.boneInverses.length = 0; | |
| for (let i = 0, il = this.bones.length; i < il; i++) { | |
| const inverse = new Matrix4(); | |
| if (this.bones[i]) { | |
| inverse.copy(this.bones[i].matrixWorld).invert(); | |
| } | |
| this.boneInverses.push(inverse); | |
| } | |
| } | |
| pose() { | |
| // recover the bind-time world matrices | |
| for (let i = 0, il = this.bones.length; i < il; i++) { | |
| const bone = this.bones[i]; | |
| if (bone) { | |
| bone.matrixWorld.copy(this.boneInverses[i]).invert(); | |
| } | |
| } | |
| // compute the local matrices, positions, rotations and scales | |
| for (let i = 0, il = this.bones.length; i < il; i++) { | |
| const bone = this.bones[i]; | |
| if (bone) { | |
| if (bone.parent && bone.parent.isBone) { | |
| bone.matrix.copy(bone.parent.matrixWorld).invert(); | |
| bone.matrix.multiply(bone.matrixWorld); | |
| } else { | |
| bone.matrix.copy(bone.matrixWorld); | |
| } | |
| bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); | |
| } | |
| } | |
| } | |
| update() { | |
| const bones = this.bones; | |
| const boneInverses = this.boneInverses; | |
| const boneMatrices = this.boneMatrices; | |
| const boneTexture = this.boneTexture; | |
| // flatten bone matrices to array | |
| for (let i = 0, il = bones.length; i < il; i++) { | |
| // compute the offset between the current and the original transform | |
| const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; | |
| _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); | |
| _offsetMatrix.toArray(boneMatrices, i * 16); | |
| } | |
| if (boneTexture !== null) { | |
| boneTexture.needsUpdate = true; | |
| } | |
| } | |
| clone() { | |
| return new Skeleton(this.bones, this.boneInverses); | |
| } | |
| computeBoneTexture() { | |
| // layout (1 matrix = 4 pixels) | |
| // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) | |
| // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) | |
| // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) | |
| // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) | |
| // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) | |
| let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix | |
| size = MathUtils.ceilPowerOfTwo(size); | |
| size = Math.max(size, 4); | |
| const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel | |
| boneMatrices.set(this.boneMatrices); // copy current values | |
| const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); | |
| boneTexture.needsUpdate = true; | |
| this.boneMatrices = boneMatrices; | |
| this.boneTexture = boneTexture; | |
| this.boneTextureSize = size; | |
| return this; | |
| } | |
| getBoneByName(name) { | |
| for (let i = 0, il = this.bones.length; i < il; i++) { | |
| const bone = this.bones[i]; | |
| if (bone.name === name) { | |
| return bone; | |
| } | |
| } | |
| return undefined; | |
| } | |
| dispose() { | |
| if (this.boneTexture !== null) { | |
| this.boneTexture.dispose(); | |
| this.boneTexture = null; | |
| } | |
| } | |
| fromJSON(json, bones) { | |
| this.uuid = json.uuid; | |
| for (let i = 0, l = json.bones.length; i < l; i++) { | |
| const uuid = json.bones[i]; | |
| let bone = bones[uuid]; | |
| if (bone === undefined) { | |
| console.warn('THREE.Skeleton: No bone found with UUID:', uuid); | |
| bone = new Bone(); | |
| } | |
| this.bones.push(bone); | |
| this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); | |
| } | |
| this.init(); | |
| return this; | |
| } | |
| toJSON() { | |
| const data = { | |
| metadata: { | |
| version: 4.5, | |
| type: 'Skeleton', | |
| generator: 'Skeleton.toJSON', | |
| }, | |
| bones: [], | |
| boneInverses: [], | |
| }; | |
| data.uuid = this.uuid; | |
| const bones = this.bones; | |
| const boneInverses = this.boneInverses; | |
| for (let i = 0, l = bones.length; i < l; i++) { | |
| const bone = bones[i]; | |
| data.bones.push(bone.uuid); | |
| const boneInverse = boneInverses[i]; | |
| data.boneInverses.push(boneInverse.toArray()); | |
| } | |
| return data; | |
| } | |
| } | |
| export { Skeleton }; | |