@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
276 lines (224 loc) • 6.78 kB
JavaScript
import { Bone } from "three";
import { array_pick_best_element } from "../../../../core/collection/array/array_pick_best_element.js";
import { string_compute_similarity } from "../../../../core/primitives/strings/string_compute_similarity.js";
import { extractName } from "../../../../extractName.js";
import { BoneMapping } from "./skeleton/BoneMapping.js";
/**
*
* @param {Mesh} component
* @param {string} boneName
* @returns {THREE.Bone | null}
*/
export function getSkeletonBone(component, boneName) {
const mesh = component.mesh;
if (mesh === undefined) {
throw new Error("No mesh data");
}
if (mesh.isSkinnedMesh !== true) {
throw new Error("Not a skinned mesh, no bones");
}
const skeleton = mesh.skeleton;
if (skeleton === undefined) {
throw new Error("No skeleton data");
}
const bones = skeleton.bones;
if (bones === undefined) {
throw new Error("No bones (undefined)");
}
if (bones.length === 0) {
throw new Error("No bones (length === 0)");
}
//find the right bone
for (let i = 0, l = bones.length; i < l; i++) {
const bone = bones[i];
if (bone.name === boneName) {
//found the right bone
return bone;
}
}
//bone not found
//try to find similar bones
const bestMatch = array_pick_best_element(bones, function (bone) {
return string_compute_similarity(bone.name, boneName);
});
throw new Error("Bone '" + boneName + "' not found, did you mean '" + bestMatch.name + "'");
}
/**
*
* @param {Object3D} object
* @returns {Skeleton|undefined}
*/
function extractSkeletonFromObject3D(object) {
if (object.isSkinnedMesh === true) {
const skeleton = object.skeleton;
return skeleton;
}
const children = object.children;
const n = children.length;
for (let i = 0; i < n; i++) {
const child = children[i];
const sk = extractSkeletonFromObject3D(child);
if (sk !== undefined) {
return sk;
}
}
}
/**
*
* @param {Mesh} component
* @returns {Skeleton|undefined}
*/
export function extractSkeletonFromMeshComponent(component) {
const mesh = component.mesh;
if (mesh === null) {
// no mesh data
return null;
}
if (mesh === undefined) {
// No mesh data
return null;
}
return extractSkeletonFromObject3D(mesh);
}
/**
*
* @param {Skeleton} skeleton
* @param {HumanoidBoneType|String} boneType
* @return {Bone|null}
*/
export function findSkeletonBoneByType(skeleton, boneType) {
const bones = skeleton.bones;
if (bones === undefined) {
// No bones (undefined)
return null;
}
if (bones.length === 0) {
// No bones (length === 0)
return null;
}
if (bones[0].boneType === undefined) {
//build mapping
const boneMapping = new BoneMapping();
boneMapping.build(bones.map(extractName));
boneMapping.apply(bones);
}
//find the right bone
for (let i = 0, l = bones.length; i < l; i++) {
const bone = bones[i];
if (bone.boneType === boneType) {
//found the right bone
return bone;
}
}
return null;
}
/**
*
* @param {Mesh} component
* @param {HumanoidBoneType|String} boneType
* @returns {Bone | null}
*/
export function getSkeletonBoneByType(component, boneType) {
const skeleton = extractSkeletonFromMeshComponent(component);
if (skeleton === undefined) {
return null;
}
return findSkeletonBoneByType(skeleton, boneType);
}
/**
*
* @param {Mesh} component
* @param {String} name
* @returns {Bone | null}
*/
export function getSkeletonBoneByName_old(component, name) {
const skeleton = extractSkeletonFromMeshComponent(component);
if (skeleton === undefined) {
return null;
}
const bones = skeleton.bones;
if (bones === undefined) {
// No bones (undefined)
return null;
}
if (bones.length === 0) {
// No bones (length === 0)
return null;
}
//find the right bone
for (let i = 0, l = bones.length; i < l; i++) {
const bone = bones[i];
if (bone.name === name) {
//found the right bone
return bone;
}
}
return null
}
const stack = [];
/**
*
* @param {Mesh} component
* @param {String} name
* @returns {Bone | null}
*/
export function getSkeletonBoneByName(component, name) {
if (component.mesh === null) {
return null;
}
const m = component.mesh;
let stack_top = 0;
stack[stack_top++] = m;
while (stack_top > 0) {
stack_top--;
const top = stack[stack_top];
if (top.isBone && top.name === name) {
return top;
}
const children = top.children;
const n = children.length;
for (let i = 0; i < n; i++) {
stack[stack_top++] = children[i];
}
}
return null;
}
/**
*
* @param {Mesh} mesh
* @returns {Array}
*/
function initBones(mesh) {
var geometry = mesh.geometry;
var bones = [], bone, gbone;
var i, il;
if (geometry && geometry.bones !== undefined) {
// first, create array of 'Bone' objects from geometry data
for (i = 0, il = geometry.bones.length; i < il; i++) {
gbone = 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 = geometry.bones.length; i < il; i++) {
gbone = 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
mesh.add(bones[i]);
}
}
}
// now the bones are part of the scene graph and children of the skinned mesh.
// let's update the corresponding matrices
mesh.updateMatrixWorld(true);
return bones;
}