Source: animation/armature/Skeleton.js

import { Matrix4 } from '../../math/Matrix4.js';

var _offsetMatrix = new Matrix4();

/**
 * Use an array of bones to create a skeleton that can be used by a SkinnedMesh.
 * @constructor
 * @memberof zen3d
 */
function Skeleton(bones) {
	// bones in array
	bones = bones || [];

	/**
     * The array of bones.
     * @type {zen3d.Bone[]}
     * @default []
     */
	this.bones = bones.slice(0);

	/**
     * The array buffer holding the bone data.
     * @type {Float32Array}
     */
	this.boneMatrices = new Float32Array(16 * this.bones.length);

	/**
     * The {@link zen3d.Texture2D} holding the bone data when using a vertex texture.
     * Use vertex texture to update boneMatrices, by that way, we can use more bones on phone.
     * @type {zen3d.Texture2D|undefined}
     * @default undefined
     */
	this.boneTexture = undefined;
}

Object.assign(Skeleton.prototype, /** @lends zen3d.Skeleton.prototype */{

	/**
     * Returns the skeleton to the base pose.
     * @method
     */
	pose: function() {
		for (var i = 0; i < this.bones.length; i++) {
			var bone = this.bones[i];
			bone.worldMatrix.getInverse(bone.offsetMatrix);
		}

		for (var i = 0; i < this.bones.length; i++) {
			var bone = this.bones[i];
			if (bone.parent && bone.parent.type == "bone") {
				bone.matrix.getInverse(bone.parent.worldMatrix);
				bone.matrix.multiply(bone.worldMatrix);
			} else {
				bone.matrix.copy(bone.worldMatrix);
			}

			bone.matrix.decompose(bone.position, bone.quaternion, bone.scale);
		}
	},

	/**
     * Updates the boneMatrices and boneTexture after changing the bones.
     * This is called automatically if the skeleton is used with a SkinnedMesh.
     * @method
     * @param {string} name -- String to match to the Bone's .name property.
     * @return {zen3d.Bone}
     */
	updateBones: function() {
		for (var i = 0; i < this.bones.length; i++) {
			var bone = this.bones[i];
			_offsetMatrix.multiplyMatrices(bone.worldMatrix, bone.offsetMatrix);
			_offsetMatrix.toArray(this.boneMatrices, i * 16);
		}

		if (this.boneTexture !== undefined) {
			this.boneTexture.version++;
		}
	},

	/**
     * Searches through the skeleton's bone array and returns the first with a matching name.
     * @param {string} name -- String to match to the Bone's .name property.
     * @return {zen3d.Bone}
     */
	getBoneByName: function(name) {
		for (var i = 0, il = this.bones.length; i < il; i++) {
			var bone = this.bones[i];
			if (bone.name === name) {
				return bone;
			}
		}

		return undefined;
	}

});

export { Skeleton };