123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- import { ArrayTools } from "../Misc/arrayTools.js";
- import { Vector3, Quaternion, Matrix } from "../Maths/math.vector.js";
- import { Space, Axis } from "../Maths/math.axis.js";
- /**
- * Class used to make a bone look toward a point in space
- * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/bonesSkeletons#bonelookcontroller
- */
- export class BoneLookController {
- /**
- * Gets or sets the minimum yaw angle that the bone can look to
- */
- get minYaw() {
- return this._minYaw;
- }
- set minYaw(value) {
- this._minYaw = value;
- this._minYawSin = Math.sin(value);
- this._minYawCos = Math.cos(value);
- if (this._maxYaw != null) {
- this._midYawConstraint = this._getAngleDiff(this._minYaw, this._maxYaw) * 0.5 + this._minYaw;
- this._yawRange = this._maxYaw - this._minYaw;
- }
- }
- /**
- * Gets or sets the maximum yaw angle that the bone can look to
- */
- get maxYaw() {
- return this._maxYaw;
- }
- set maxYaw(value) {
- this._maxYaw = value;
- this._maxYawSin = Math.sin(value);
- this._maxYawCos = Math.cos(value);
- if (this._minYaw != null) {
- this._midYawConstraint = this._getAngleDiff(this._minYaw, this._maxYaw) * 0.5 + this._minYaw;
- this._yawRange = this._maxYaw - this._minYaw;
- }
- }
- /**
- * Gets or sets the minimum pitch angle that the bone can look to
- */
- get minPitch() {
- return this._minPitch;
- }
- set minPitch(value) {
- this._minPitch = value;
- this._minPitchTan = Math.tan(value);
- }
- /**
- * Gets or sets the maximum pitch angle that the bone can look to
- */
- get maxPitch() {
- return this._maxPitch;
- }
- set maxPitch(value) {
- this._maxPitch = value;
- this._maxPitchTan = Math.tan(value);
- }
- /**
- * Create a BoneLookController
- * @param mesh the TransformNode that the bone belongs to
- * @param bone the bone that will be looking to the target
- * @param target the target Vector3 to look at
- * @param options optional settings:
- * * maxYaw: the maximum angle the bone will yaw to
- * * minYaw: the minimum angle the bone will yaw to
- * * maxPitch: the maximum angle the bone will pitch to
- * * minPitch: the minimum angle the bone will yaw to
- * * slerpAmount: set the between 0 and 1 to make the bone slerp to the target.
- * * upAxis: the up axis of the coordinate system
- * * upAxisSpace: the space that the up axis is in - Space.BONE, Space.LOCAL (default), or Space.WORLD.
- * * yawAxis: set yawAxis if the bone does not yaw on the y axis
- * * pitchAxis: set pitchAxis if the bone does not pitch on the x axis
- * * adjustYaw: used to make an adjustment to the yaw of the bone
- * * adjustPitch: used to make an adjustment to the pitch of the bone
- * * adjustRoll: used to make an adjustment to the roll of the bone
- * @param options.maxYaw
- * @param options.minYaw
- * @param options.maxPitch
- * @param options.minPitch
- * @param options.slerpAmount
- * @param options.upAxis
- * @param options.upAxisSpace
- * @param options.yawAxis
- * @param options.pitchAxis
- * @param options.adjustYaw
- * @param options.adjustPitch
- * @param options.adjustRoll
- **/
- constructor(mesh, bone, target, options) {
- /**
- * The up axis of the coordinate system that is used when the bone is rotated
- */
- this.upAxis = Vector3.Up();
- /**
- * The space that the up axis is in - Space.BONE, Space.LOCAL (default), or Space.WORLD
- */
- this.upAxisSpace = Space.LOCAL;
- /**
- * Used to make an adjustment to the yaw of the bone
- */
- this.adjustYaw = 0;
- /**
- * Used to make an adjustment to the pitch of the bone
- */
- this.adjustPitch = 0;
- /**
- * Used to make an adjustment to the roll of the bone
- */
- this.adjustRoll = 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._boneQuat = Quaternion.Identity();
- this._slerping = false;
- this._firstFrameSkipped = false;
- this._fowardAxis = Vector3.Forward();
- /**
- * Use the absolute value for yaw when checking the min/max constraints
- */
- this.useAbsoluteValueForYaw = false;
- this.mesh = mesh;
- this.bone = bone;
- this.target = target;
- if (options) {
- if (options.adjustYaw) {
- this.adjustYaw = options.adjustYaw;
- }
- if (options.adjustPitch) {
- this.adjustPitch = options.adjustPitch;
- }
- if (options.adjustRoll) {
- this.adjustRoll = options.adjustRoll;
- }
- if (options.maxYaw != null) {
- this.maxYaw = options.maxYaw;
- }
- else {
- this.maxYaw = Math.PI;
- }
- if (options.minYaw != null) {
- this.minYaw = options.minYaw;
- }
- else {
- this.minYaw = -Math.PI;
- }
- if (options.maxPitch != null) {
- this.maxPitch = options.maxPitch;
- }
- else {
- this.maxPitch = Math.PI;
- }
- if (options.minPitch != null) {
- this.minPitch = options.minPitch;
- }
- else {
- this.minPitch = -Math.PI;
- }
- if (options.slerpAmount != null) {
- this.slerpAmount = options.slerpAmount;
- }
- if (options.upAxis != null) {
- this.upAxis = options.upAxis;
- }
- if (options.upAxisSpace != null) {
- this.upAxisSpace = options.upAxisSpace;
- }
- if (options.yawAxis != null || options.pitchAxis != null) {
- let newYawAxis = Axis.Y;
- let newPitchAxis = Axis.X;
- if (options.yawAxis != null) {
- newYawAxis = options.yawAxis.clone();
- newYawAxis.normalize();
- }
- if (options.pitchAxis != null) {
- newPitchAxis = options.pitchAxis.clone();
- newPitchAxis.normalize();
- }
- const newRollAxis = Vector3.Cross(newPitchAxis, newYawAxis);
- this._transformYawPitch = Matrix.Identity();
- Matrix.FromXYZAxesToRef(newPitchAxis, newYawAxis, newRollAxis, this._transformYawPitch);
- this._transformYawPitchInv = this._transformYawPitch.clone();
- this._transformYawPitch.invert();
- }
- if (options.useAbsoluteValueForYaw !== undefined) {
- this.useAbsoluteValueForYaw = options.useAbsoluteValueForYaw;
- }
- }
- if (!bone.getParent() && this.upAxisSpace == Space.BONE) {
- this.upAxisSpace = Space.LOCAL;
- }
- }
- /**
- * Update the bone to look at the target. This should be called before the scene is rendered (use scene.registerBeforeRender())
- */
- update() {
- //skip the first frame when slerping so that the TransformNode rotation is correct
- if (this.slerpAmount < 1 && !this._firstFrameSkipped) {
- this._firstFrameSkipped = true;
- return;
- }
- const bone = this.bone;
- const bonePos = BoneLookController._TmpVecs[0];
- bone.getAbsolutePositionToRef(this.mesh, bonePos);
- let target = this.target;
- const _tmpMat1 = BoneLookController._TmpMats[0];
- const _tmpMat2 = BoneLookController._TmpMats[1];
- const mesh = this.mesh;
- const parentBone = bone.getParent();
- const upAxis = BoneLookController._TmpVecs[1];
- upAxis.copyFrom(this.upAxis);
- if (this.upAxisSpace == Space.BONE && parentBone) {
- if (this._transformYawPitch) {
- Vector3.TransformCoordinatesToRef(upAxis, this._transformYawPitchInv, upAxis);
- }
- parentBone.getDirectionToRef(upAxis, this.mesh, upAxis);
- }
- else if (this.upAxisSpace == Space.LOCAL) {
- mesh.getDirectionToRef(upAxis, upAxis);
- if (mesh.scaling.x != 1 || mesh.scaling.y != 1 || mesh.scaling.z != 1) {
- upAxis.normalize();
- }
- }
- let checkYaw = false;
- let checkPitch = false;
- if (this._maxYaw != Math.PI || this._minYaw != -Math.PI) {
- checkYaw = true;
- }
- if (this._maxPitch != Math.PI || this._minPitch != -Math.PI) {
- checkPitch = true;
- }
- if (checkYaw || checkPitch) {
- const spaceMat = BoneLookController._TmpMats[2];
- const spaceMatInv = BoneLookController._TmpMats[3];
- if (this.upAxisSpace == Space.BONE && upAxis.y == 1 && parentBone) {
- parentBone.getRotationMatrixToRef(Space.WORLD, this.mesh, spaceMat);
- }
- else if (this.upAxisSpace == Space.LOCAL && upAxis.y == 1 && !parentBone) {
- spaceMat.copyFrom(mesh.getWorldMatrix());
- }
- else {
- let forwardAxis = BoneLookController._TmpVecs[2];
- forwardAxis.copyFrom(this._fowardAxis);
- if (this._transformYawPitch) {
- Vector3.TransformCoordinatesToRef(forwardAxis, this._transformYawPitchInv, forwardAxis);
- }
- if (parentBone) {
- parentBone.getDirectionToRef(forwardAxis, this.mesh, forwardAxis);
- }
- else {
- mesh.getDirectionToRef(forwardAxis, forwardAxis);
- }
- const rightAxis = Vector3.Cross(upAxis, forwardAxis);
- rightAxis.normalize();
- forwardAxis = Vector3.Cross(rightAxis, upAxis);
- Matrix.FromXYZAxesToRef(rightAxis, upAxis, forwardAxis, spaceMat);
- }
- spaceMat.invertToRef(spaceMatInv);
- let xzlen = null;
- if (checkPitch) {
- const localTarget = BoneLookController._TmpVecs[3];
- target.subtractToRef(bonePos, localTarget);
- Vector3.TransformCoordinatesToRef(localTarget, spaceMatInv, localTarget);
- xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
- const pitch = Math.atan2(localTarget.y, xzlen);
- let newPitch = pitch;
- if (pitch > this._maxPitch) {
- localTarget.y = this._maxPitchTan * xzlen;
- newPitch = this._maxPitch;
- }
- else if (pitch < this._minPitch) {
- localTarget.y = this._minPitchTan * xzlen;
- newPitch = this._minPitch;
- }
- if (pitch != newPitch) {
- Vector3.TransformCoordinatesToRef(localTarget, spaceMat, localTarget);
- localTarget.addInPlace(bonePos);
- target = localTarget;
- }
- }
- if (checkYaw) {
- const localTarget = BoneLookController._TmpVecs[4];
- target.subtractToRef(bonePos, localTarget);
- Vector3.TransformCoordinatesToRef(localTarget, spaceMatInv, localTarget);
- const yaw = Math.atan2(localTarget.x, localTarget.z);
- const yawCheck = this.useAbsoluteValueForYaw ? Math.abs(yaw) : yaw;
- let newYaw = yaw;
- if (yawCheck > this._maxYaw || yawCheck < this._minYaw) {
- if (xzlen == null) {
- xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
- }
- if (this._yawRange > Math.PI) {
- if (this._isAngleBetween(yaw, this._maxYaw, this._midYawConstraint)) {
- localTarget.z = this._maxYawCos * xzlen;
- localTarget.x = this._maxYawSin * xzlen;
- newYaw = this._maxYaw;
- }
- else if (this._isAngleBetween(yaw, this._midYawConstraint, this._minYaw)) {
- localTarget.z = this._minYawCos * xzlen;
- localTarget.x = this._minYawSin * xzlen;
- newYaw = this._minYaw;
- }
- }
- else {
- if (yawCheck > this._maxYaw) {
- localTarget.z = this._maxYawCos * xzlen;
- localTarget.x = this._maxYawSin * xzlen;
- if (yaw < 0 && this.useAbsoluteValueForYaw) {
- localTarget.x *= -1;
- }
- newYaw = this._maxYaw;
- }
- else if (yawCheck < this._minYaw) {
- localTarget.z = this._minYawCos * xzlen;
- localTarget.x = this._minYawSin * xzlen;
- if (yaw < 0 && this.useAbsoluteValueForYaw) {
- localTarget.x *= -1;
- }
- newYaw = this._minYaw;
- }
- }
- }
- if (this._slerping && this._yawRange > Math.PI) {
- //are we going to be crossing into the min/max region?
- const boneFwd = BoneLookController._TmpVecs[8];
- boneFwd.copyFrom(Axis.Z);
- if (this._transformYawPitch) {
- Vector3.TransformCoordinatesToRef(boneFwd, this._transformYawPitchInv, boneFwd);
- }
- const boneRotMat = BoneLookController._TmpMats[4];
- this._boneQuat.toRotationMatrix(boneRotMat);
- this.mesh.getWorldMatrix().multiplyToRef(boneRotMat, boneRotMat);
- Vector3.TransformCoordinatesToRef(boneFwd, boneRotMat, boneFwd);
- Vector3.TransformCoordinatesToRef(boneFwd, spaceMatInv, boneFwd);
- const boneYaw = Math.atan2(boneFwd.x, boneFwd.z);
- const angBtwTar = this._getAngleBetween(boneYaw, yaw);
- const angBtwMidYaw = this._getAngleBetween(boneYaw, this._midYawConstraint);
- if (angBtwTar > angBtwMidYaw) {
- if (xzlen == null) {
- xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
- }
- const angBtwMax = this._getAngleBetween(boneYaw, this._maxYaw);
- const angBtwMin = this._getAngleBetween(boneYaw, this._minYaw);
- if (angBtwMin < angBtwMax) {
- newYaw = boneYaw + Math.PI * 0.75;
- localTarget.z = Math.cos(newYaw) * xzlen;
- localTarget.x = Math.sin(newYaw) * xzlen;
- }
- else {
- newYaw = boneYaw - Math.PI * 0.75;
- localTarget.z = Math.cos(newYaw) * xzlen;
- localTarget.x = Math.sin(newYaw) * xzlen;
- }
- }
- }
- if (yaw != newYaw) {
- Vector3.TransformCoordinatesToRef(localTarget, spaceMat, localTarget);
- localTarget.addInPlace(bonePos);
- target = localTarget;
- }
- }
- }
- const zaxis = BoneLookController._TmpVecs[5];
- const xaxis = BoneLookController._TmpVecs[6];
- const yaxis = BoneLookController._TmpVecs[7];
- const tmpQuat = BoneLookController._TmpQuat;
- const boneScaling = BoneLookController._TmpVecs[9];
- target.subtractToRef(bonePos, zaxis);
- zaxis.normalize();
- Vector3.CrossToRef(upAxis, zaxis, xaxis);
- xaxis.normalize();
- Vector3.CrossToRef(zaxis, xaxis, yaxis);
- yaxis.normalize();
- Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, _tmpMat1);
- if (xaxis.x === 0 && xaxis.y === 0 && xaxis.z === 0) {
- return;
- }
- if (yaxis.x === 0 && yaxis.y === 0 && yaxis.z === 0) {
- return;
- }
- if (zaxis.x === 0 && zaxis.y === 0 && zaxis.z === 0) {
- return;
- }
- if (this.adjustYaw || this.adjustPitch || this.adjustRoll) {
- Matrix.RotationYawPitchRollToRef(this.adjustYaw, this.adjustPitch, this.adjustRoll, _tmpMat2);
- _tmpMat2.multiplyToRef(_tmpMat1, _tmpMat1);
- }
- boneScaling.copyFrom(this.bone.getScale());
- if (this.slerpAmount < 1) {
- if (!this._slerping) {
- this.bone.getRotationQuaternionToRef(Space.WORLD, this.mesh, this._boneQuat);
- }
- if (this._transformYawPitch) {
- this._transformYawPitch.multiplyToRef(_tmpMat1, _tmpMat1);
- }
- Quaternion.FromRotationMatrixToRef(_tmpMat1, tmpQuat);
- Quaternion.SlerpToRef(this._boneQuat, tmpQuat, this.slerpAmount, this._boneQuat);
- this.bone.setRotationQuaternion(this._boneQuat, Space.WORLD, this.mesh);
- this._slerping = true;
- }
- else {
- if (this._transformYawPitch) {
- this._transformYawPitch.multiplyToRef(_tmpMat1, _tmpMat1);
- }
- this.bone.setRotationMatrix(_tmpMat1, Space.WORLD, this.mesh);
- this._slerping = false;
- }
- this.bone.setScale(boneScaling);
- this._updateLinkedTransformRotation();
- }
- _getAngleDiff(ang1, ang2) {
- let angDiff = ang2 - ang1;
- angDiff %= Math.PI * 2;
- if (angDiff > Math.PI) {
- angDiff -= Math.PI * 2;
- }
- else if (angDiff < -Math.PI) {
- angDiff += Math.PI * 2;
- }
- return angDiff;
- }
- _getAngleBetween(ang1, ang2) {
- ang1 %= 2 * Math.PI;
- ang1 = ang1 < 0 ? ang1 + 2 * Math.PI : ang1;
- ang2 %= 2 * Math.PI;
- ang2 = ang2 < 0 ? ang2 + 2 * Math.PI : ang2;
- let ab = 0;
- if (ang1 < ang2) {
- ab = ang2 - ang1;
- }
- else {
- ab = ang1 - ang2;
- }
- if (ab > Math.PI) {
- ab = Math.PI * 2 - ab;
- }
- return ab;
- }
- _isAngleBetween(ang, ang1, ang2) {
- ang %= 2 * Math.PI;
- ang = ang < 0 ? ang + 2 * Math.PI : ang;
- ang1 %= 2 * Math.PI;
- ang1 = ang1 < 0 ? ang1 + 2 * Math.PI : ang1;
- ang2 %= 2 * Math.PI;
- ang2 = ang2 < 0 ? ang2 + 2 * Math.PI : ang2;
- if (ang1 < ang2) {
- if (ang > ang1 && ang < ang2) {
- return true;
- }
- }
- else {
- if (ang > ang2 && ang < ang1) {
- return true;
- }
- }
- return false;
- }
- _updateLinkedTransformRotation() {
- const bone = this.bone;
- if (bone._linkedTransformNode) {
- if (!bone._linkedTransformNode.rotationQuaternion) {
- bone._linkedTransformNode.rotationQuaternion = new Quaternion();
- }
- bone.getRotationQuaternionToRef(Space.LOCAL, null, bone._linkedTransformNode.rotationQuaternion);
- }
- }
- }
- BoneLookController._TmpVecs = ArrayTools.BuildArray(10, Vector3.Zero);
- BoneLookController._TmpQuat = Quaternion.Identity();
- BoneLookController._TmpMats = ArrayTools.BuildArray(5, Matrix.Identity);
- //# sourceMappingURL=boneLookController.js.map
|