three-stdlib
Version:
stand-alone library of threejs examples
271 lines (270 loc) • 9.14 kB
JavaScript
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const _q = /* @__PURE__ */ new THREE.Quaternion();
const _targetPos = /* @__PURE__ */ new THREE.Vector3();
const _targetVec = /* @__PURE__ */ new THREE.Vector3();
const _effectorPos = /* @__PURE__ */ new THREE.Vector3();
const _effectorVec = /* @__PURE__ */ new THREE.Vector3();
const _linkPos = /* @__PURE__ */ new THREE.Vector3();
const _invLinkQ = /* @__PURE__ */ new THREE.Quaternion();
const _linkScale = /* @__PURE__ */ new THREE.Vector3();
const _axis = /* @__PURE__ */ new THREE.Vector3();
const _vector = /* @__PURE__ */ new THREE.Vector3();
const _matrix = /* @__PURE__ */ new THREE.Matrix4();
class CCDIKSolver {
/**
* @param {THREE.SkinnedMesh} mesh
* @param {Array<Object>} iks
*/
constructor(mesh, iks = []) {
this.mesh = mesh;
this.iks = iks;
this._valid();
}
/**
* Update all IK bones.
*
* @return {CCDIKSolver}
*/
update() {
const iks = this.iks;
for (let i = 0, il = iks.length; i < il; i++) {
this.updateOne(iks[i]);
}
return this;
}
/**
* Update one IK bone
*
* @param {Object} ik parameter
* @return {CCDIKSolver}
*/
updateOne(ik) {
const bones = this.mesh.skeleton.bones;
const math = Math;
const effector = bones[ik.effector];
const target = bones[ik.target];
_targetPos.setFromMatrixPosition(target.matrixWorld);
const links = ik.links;
const iteration = ik.iteration !== void 0 ? ik.iteration : 1;
for (let i = 0; i < iteration; i++) {
let rotated = false;
for (let j = 0, jl = links.length; j < jl; j++) {
const link = bones[links[j].index];
if (links[j].enabled === false)
break;
const limitation = links[j].limitation;
const rotationMin = links[j].rotationMin;
const rotationMax = links[j].rotationMax;
link.matrixWorld.decompose(_linkPos, _invLinkQ, _linkScale);
_invLinkQ.invert();
_effectorPos.setFromMatrixPosition(effector.matrixWorld);
_effectorVec.subVectors(_effectorPos, _linkPos);
_effectorVec.applyQuaternion(_invLinkQ);
_effectorVec.normalize();
_targetVec.subVectors(_targetPos, _linkPos);
_targetVec.applyQuaternion(_invLinkQ);
_targetVec.normalize();
let angle = _targetVec.dot(_effectorVec);
if (angle > 1) {
angle = 1;
} else if (angle < -1) {
angle = -1;
}
angle = math.acos(angle);
if (angle < 1e-5)
continue;
if (ik.minAngle !== void 0 && angle < ik.minAngle) {
angle = ik.minAngle;
}
if (ik.maxAngle !== void 0 && angle > ik.maxAngle) {
angle = ik.maxAngle;
}
_axis.crossVectors(_effectorVec, _targetVec);
_axis.normalize();
_q.setFromAxisAngle(_axis, angle);
link.quaternion.multiply(_q);
if (limitation !== void 0) {
let c = link.quaternion.w;
if (c > 1)
c = 1;
const c2 = math.sqrt(1 - c * c);
link.quaternion.set(limitation.x * c2, limitation.y * c2, limitation.z * c2, c);
}
if (rotationMin !== void 0) {
link.rotation.setFromVector3(_vector.setFromEuler(link.rotation).max(rotationMin));
}
if (rotationMax !== void 0) {
link.rotation.setFromVector3(_vector.setFromEuler(link.rotation).min(rotationMax));
}
link.updateMatrixWorld(true);
rotated = true;
}
if (!rotated)
break;
}
return this;
}
/**
* Creates Helper
*
* @return {CCDIKHelper}
*/
createHelper() {
return new CCDIKHelper(this.mesh, this.iks);
}
// private methods
_valid() {
const iks = this.iks;
const bones = this.mesh.skeleton.bones;
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const effector = bones[ik.effector];
const links = ik.links;
let link0, link1;
link0 = effector;
for (let j = 0, jl = links.length; j < jl; j++) {
link1 = bones[links[j].index];
if (link0.parent !== link1) {
console.warn("THREE.CCDIKSolver: bone " + link0.name + " is not the child of bone " + link1.name);
}
link0 = link1;
}
}
}
}
function getPosition(bone, matrixWorldInv) {
return _vector.setFromMatrixPosition(bone.matrixWorld).applyMatrix4(matrixWorldInv);
}
function setPositionOfBoneToAttributeArray(array, index, bone, matrixWorldInv) {
const v = getPosition(bone, matrixWorldInv);
array[index * 3 + 0] = v.x;
array[index * 3 + 1] = v.y;
array[index * 3 + 2] = v.z;
}
class CCDIKHelper extends THREE.Object3D {
constructor(mesh, iks = [], sphereSize = 0.25) {
super();
this.root = mesh;
this.iks = iks;
this.matrix.copy(mesh.matrixWorld);
this.matrixAutoUpdate = false;
this.sphereGeometry = new THREE.SphereGeometry(sphereSize, 16, 8);
this.targetSphereMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(16746632),
depthTest: false,
depthWrite: false,
transparent: true
});
this.effectorSphereMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(8978312),
depthTest: false,
depthWrite: false,
transparent: true
});
this.linkSphereMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(8947967),
depthTest: false,
depthWrite: false,
transparent: true
});
this.lineMaterial = new THREE.LineBasicMaterial({
color: new THREE.Color(16711680),
depthTest: false,
depthWrite: false,
transparent: true
});
this._init();
}
/**
* Updates IK bones visualization.
*/
updateMatrixWorld(force) {
const mesh = this.root;
if (this.visible) {
let offset = 0;
const iks = this.iks;
const bones = mesh.skeleton.bones;
_matrix.copy(mesh.matrixWorld).invert();
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const targetBone = bones[ik.target];
const effectorBone = bones[ik.effector];
const targetMesh = this.children[offset++];
const effectorMesh = this.children[offset++];
targetMesh.position.copy(getPosition(targetBone, _matrix));
effectorMesh.position.copy(getPosition(effectorBone, _matrix));
for (let j = 0, jl = ik.links.length; j < jl; j++) {
const link = ik.links[j];
const linkBone = bones[link.index];
const linkMesh = this.children[offset++];
linkMesh.position.copy(getPosition(linkBone, _matrix));
}
const line = this.children[offset++];
const array = line.geometry.attributes.position.array;
setPositionOfBoneToAttributeArray(array, 0, targetBone, _matrix);
setPositionOfBoneToAttributeArray(array, 1, effectorBone, _matrix);
for (let j = 0, jl = ik.links.length; j < jl; j++) {
const link = ik.links[j];
const linkBone = bones[link.index];
setPositionOfBoneToAttributeArray(array, j + 2, linkBone, _matrix);
}
line.geometry.attributes.position.needsUpdate = true;
}
}
this.matrix.copy(mesh.matrixWorld);
super.updateMatrixWorld(force);
}
/**
* Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
*/
dispose() {
this.sphereGeometry.dispose();
this.targetSphereMaterial.dispose();
this.effectorSphereMaterial.dispose();
this.linkSphereMaterial.dispose();
this.lineMaterial.dispose();
const children = this.children;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.isLine)
child.geometry.dispose();
}
}
// private method
_init() {
const scope = this;
const iks = this.iks;
function createLineGeometry(ik) {
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array((2 + ik.links.length) * 3);
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
return geometry;
}
function createTargetMesh() {
return new THREE.Mesh(scope.sphereGeometry, scope.targetSphereMaterial);
}
function createEffectorMesh() {
return new THREE.Mesh(scope.sphereGeometry, scope.effectorSphereMaterial);
}
function createLinkMesh() {
return new THREE.Mesh(scope.sphereGeometry, scope.linkSphereMaterial);
}
function createLine(ik) {
return new THREE.Line(createLineGeometry(ik), scope.lineMaterial);
}
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
this.add(createTargetMesh());
this.add(createEffectorMesh());
for (let j = 0, jl = ik.links.length; j < jl; j++) {
this.add(createLinkMesh());
}
this.add(createLine(ik));
}
}
}
exports.CCDIKHelper = CCDIKHelper;
exports.CCDIKSolver = CCDIKSolver;
//# sourceMappingURL=CCDIKSolver.cjs.map