123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- import { Vector3, Quaternion, Matrix } from "../Maths/math.vector.js";
- import { Space } from "../Maths/math.axis.js";
- import { Logger } from "../Misc/logger.js";
- /**
- * Class used to apply inverse kinematics to bones
- * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/bonesSkeletons#boneikcontroller
- */
- export class BoneIKController {
- /**
- * Gets or sets maximum allowed angle
- */
- get maxAngle() {
- return this._maxAngle;
- }
- set maxAngle(value) {
- this._setMaxAngle(value);
- }
- /**
- * Creates a new BoneIKController
- * @param mesh defines the TransformNode to control
- * @param bone defines the bone to control. The bone needs to have a parent bone. It also needs to have a length greater than 0 or a children we can use to infer its length.
- * @param options defines options to set up the controller
- * @param options.targetMesh
- * @param options.poleTargetMesh
- * @param options.poleTargetBone
- * @param options.poleTargetLocalOffset
- * @param options.poleAngle
- * @param options.bendAxis
- * @param options.maxAngle
- * @param options.slerpAmount
- */
- constructor(mesh, bone, options) {
- /**
- * Gets or sets the target position
- */
- this.targetPosition = Vector3.Zero();
- /**
- * Gets or sets the pole target position
- */
- this.poleTargetPosition = Vector3.Zero();
- /**
- * Gets or sets the pole target local offset
- */
- this.poleTargetLocalOffset = Vector3.Zero();
- /**
- * Gets or sets the pole angle
- */
- this.poleAngle = 0;
- /**
- * The amount to slerp (spherical linear interpolation) to the target. Set this to a value between 0 and 1 (a value of 1 disables slerp)
- */
- this.slerpAmount = 1;
- this._bone1Quat = Quaternion.Identity();
- this._bone1Mat = Matrix.Identity();
- this._bone2Ang = Math.PI;
- this._maxAngle = Math.PI;
- this._rightHandedSystem = false;
- this._bendAxis = Vector3.Right();
- this._slerping = false;
- this._adjustRoll = 0;
- this._notEnoughInformation = false;
- this._bone2 = bone;
- const bone1 = bone.getParent();
- if (!bone1) {
- this._notEnoughInformation = true;
- Logger.Error("BoneIKController: bone must have a parent for IK to work.");
- return;
- }
- this._bone1 = bone1;
- if (this._bone2.children.length === 0 && !this._bone2.length) {
- this._notEnoughInformation = true;
- Logger.Error("BoneIKController: bone must not be a leaf or it should have a length for IK to work.");
- return;
- }
- this.mesh = mesh;
- bone.getSkeleton().computeAbsoluteMatrices();
- const bonePos = bone.getPosition();
- if (bone.getAbsoluteMatrix().determinant() > 0) {
- this._rightHandedSystem = true;
- this._bendAxis.x = 0;
- this._bendAxis.y = 0;
- this._bendAxis.z = -1;
- if (bonePos.x > bonePos.y && bonePos.x > bonePos.z) {
- this._adjustRoll = Math.PI * 0.5;
- this._bendAxis.z = 1;
- }
- }
- if (this._bone1.length && this._bone2.length) {
- const boneScale1 = this._bone1.getScale();
- const boneScale2 = this._bone2.getScale();
- this._bone1Length = this._bone1.length * boneScale1.y * this.mesh.scaling.y;
- this._bone2Length = this._bone2.length * boneScale2.y * this.mesh.scaling.y;
- }
- else if (this._bone2.children[0]) {
- mesh.computeWorldMatrix(true);
- const pos1 = this._bone2.children[0].getAbsolutePosition(mesh);
- const pos2 = this._bone2.getAbsolutePosition(mesh);
- const pos3 = this._bone1.getAbsolutePosition(mesh);
- this._bone2Length = Vector3.Distance(pos1, pos2);
- this._bone1Length = Vector3.Distance(pos2, pos3);
- }
- else {
- mesh.computeWorldMatrix(true);
- const boneScale2 = this._bone2.getScale();
- this._bone2Length = this._bone2.length * boneScale2.y * this.mesh.scaling.y;
- const pos2 = this._bone2.getAbsolutePosition(mesh);
- const pos3 = this._bone1.getAbsolutePosition(mesh);
- this._bone1Length = Vector3.Distance(pos2, pos3);
- }
- this._bone1.getRotationMatrixToRef(Space.WORLD, mesh, this._bone1Mat);
- this.maxAngle = Math.PI;
- if (options) {
- if (options.targetMesh) {
- this.targetMesh = options.targetMesh;
- this.targetMesh.computeWorldMatrix(true);
- }
- if (options.poleTargetMesh) {
- this.poleTargetMesh = options.poleTargetMesh;
- this.poleTargetMesh.computeWorldMatrix(true);
- }
- else if (options.poleTargetBone) {
- this.poleTargetBone = options.poleTargetBone;
- }
- else if (this._bone1.getParent()) {
- this.poleTargetBone = this._bone1.getParent();
- }
- if (options.poleTargetLocalOffset) {
- this.poleTargetLocalOffset.copyFrom(options.poleTargetLocalOffset);
- }
- if (options.poleAngle) {
- this.poleAngle = options.poleAngle;
- }
- if (options.bendAxis) {
- this._bendAxis.copyFrom(options.bendAxis);
- }
- if (options.maxAngle) {
- this.maxAngle = options.maxAngle;
- }
- if (options.slerpAmount) {
- this.slerpAmount = options.slerpAmount;
- }
- }
- }
- _setMaxAngle(ang) {
- if (ang < 0) {
- ang = 0;
- }
- if (ang > Math.PI || ang == undefined) {
- ang = Math.PI;
- }
- this._maxAngle = ang;
- const a = this._bone1Length;
- const b = this._bone2Length;
- this._maxReach = Math.sqrt(a * a + b * b - 2 * a * b * Math.cos(ang));
- }
- /**
- * Force the controller to update the bones
- */
- update() {
- if (this._notEnoughInformation) {
- return;
- }
- const target = this.targetPosition;
- const poleTarget = this.poleTargetPosition;
- const mat1 = BoneIKController._TmpMats[0];
- const mat2 = BoneIKController._TmpMats[1];
- if (this.targetMesh) {
- target.copyFrom(this.targetMesh.getAbsolutePosition());
- }
- if (this.poleTargetBone) {
- this.poleTargetBone.getAbsolutePositionFromLocalToRef(this.poleTargetLocalOffset, this.mesh, poleTarget);
- }
- else if (this.poleTargetMesh) {
- Vector3.TransformCoordinatesToRef(this.poleTargetLocalOffset, this.poleTargetMesh.getWorldMatrix(), poleTarget);
- }
- const bonePos = BoneIKController._TmpVecs[0];
- const zaxis = BoneIKController._TmpVecs[1];
- const xaxis = BoneIKController._TmpVecs[2];
- const yaxis = BoneIKController._TmpVecs[3];
- const upAxis = BoneIKController._TmpVecs[4];
- const tmpQuat = BoneIKController._TmpQuat;
- this._bone1.getAbsolutePositionToRef(this.mesh, bonePos);
- poleTarget.subtractToRef(bonePos, upAxis);
- if (upAxis.x == 0 && upAxis.y == 0 && upAxis.z == 0) {
- upAxis.y = 1;
- }
- else {
- upAxis.normalize();
- }
- target.subtractToRef(bonePos, yaxis);
- yaxis.normalize();
- Vector3.CrossToRef(yaxis, upAxis, zaxis);
- zaxis.normalize();
- Vector3.CrossToRef(yaxis, zaxis, xaxis);
- xaxis.normalize();
- Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, mat1);
- const a = this._bone1Length;
- const b = this._bone2Length;
- let c = Vector3.Distance(bonePos, target);
- if (this._maxReach > 0) {
- c = Math.min(this._maxReach, c);
- }
- let acosa = (b * b + c * c - a * a) / (2 * b * c);
- let acosb = (c * c + a * a - b * b) / (2 * c * a);
- if (acosa > 1) {
- acosa = 1;
- }
- if (acosb > 1) {
- acosb = 1;
- }
- if (acosa < -1) {
- acosa = -1;
- }
- if (acosb < -1) {
- acosb = -1;
- }
- const angA = Math.acos(acosa);
- const angB = Math.acos(acosb);
- let angC = -angA - angB;
- if (this._rightHandedSystem) {
- Matrix.RotationYawPitchRollToRef(0, 0, this._adjustRoll, mat2);
- mat2.multiplyToRef(mat1, mat1);
- Matrix.RotationAxisToRef(this._bendAxis, angB, mat2);
- mat2.multiplyToRef(mat1, mat1);
- }
- else {
- const _tmpVec = BoneIKController._TmpVecs[5];
- _tmpVec.copyFrom(this._bendAxis);
- _tmpVec.x *= -1;
- Matrix.RotationAxisToRef(_tmpVec, -angB, mat2);
- mat2.multiplyToRef(mat1, mat1);
- }
- if (this.poleAngle) {
- Matrix.RotationAxisToRef(yaxis, this.poleAngle, mat2);
- mat1.multiplyToRef(mat2, mat1);
- }
- if (this._bone1) {
- if (this.slerpAmount < 1) {
- if (!this._slerping) {
- Quaternion.FromRotationMatrixToRef(this._bone1Mat, this._bone1Quat);
- }
- Quaternion.FromRotationMatrixToRef(mat1, tmpQuat);
- Quaternion.SlerpToRef(this._bone1Quat, tmpQuat, this.slerpAmount, this._bone1Quat);
- angC = this._bone2Ang * (1.0 - this.slerpAmount) + angC * this.slerpAmount;
- this._bone1.setRotationQuaternion(this._bone1Quat, Space.WORLD, this.mesh);
- this._slerping = true;
- }
- else {
- this._bone1.setRotationMatrix(mat1, Space.WORLD, this.mesh);
- this._bone1Mat.copyFrom(mat1);
- this._slerping = false;
- }
- this._updateLinkedTransformRotation(this._bone1);
- }
- this._bone2.setAxisAngle(this._bendAxis, angC, Space.LOCAL);
- this._updateLinkedTransformRotation(this._bone2);
- this._bone2Ang = angC;
- }
- _updateLinkedTransformRotation(bone) {
- if (bone._linkedTransformNode) {
- if (!bone._linkedTransformNode.rotationQuaternion) {
- bone._linkedTransformNode.rotationQuaternion = new Quaternion();
- }
- bone.getRotationQuaternionToRef(Space.LOCAL, null, bone._linkedTransformNode.rotationQuaternion);
- }
- }
- }
- BoneIKController._TmpVecs = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
- BoneIKController._TmpQuat = Quaternion.Identity();
- BoneIKController._TmpMats = [Matrix.Identity(), Matrix.Identity()];
- //# sourceMappingURL=boneIKController.js.map
|