private void Iterate(int iteration) { if (!base.enabled) { return; } if (!this.ik.enabled) { return; } if (!base.gameObject.activeInHierarchy) { return; } if (this.ik.solver.iterations == 0) { return; } this.leftShoulderPos = base.transform.position + (this.leftShoulderPos - base.transform.position).normalized * this.leftShoulderDist; this.rightShoulderPos = base.transform.position + (this.rightShoulderPos - base.transform.position).normalized * this.rightShoulderDist; this.Solve(ref this.leftShoulderPos, ref this.rightShoulderPos, this.shoulderDist); this.LerpSolverPosition(this.ik.solver.leftShoulderEffector, this.leftShoulderPos, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.leftShoulderEffector.positionOffset); this.LerpSolverPosition(this.ik.solver.rightShoulderEffector, this.rightShoulderPos, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.rightShoulderEffector.positionOffset); Quaternion to = Quaternion.LookRotation(base.transform.position - this.leftShoulderPos, this.rightShoulderPos - this.leftShoulderPos); Quaternion quaternion = QuaTools.FromToRotation(this.chestRotation, to); Vector3 b = quaternion * this.headToBody; this.LerpSolverPosition(this.ik.solver.bodyEffector, base.transform.position + b, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.bodyEffector.positionOffset - this.ik.solver.pullBodyOffset); Quaternion rotation = Quaternion.Lerp(Quaternion.identity, quaternion, this.thighWeight); Vector3 b2 = rotation * this.headToLeftThigh; Vector3 b3 = rotation * this.headToRightThigh; this.LerpSolverPosition(this.ik.solver.leftThighEffector, base.transform.position + b2, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.bodyEffector.positionOffset - this.ik.solver.pullBodyOffset + this.ik.solver.leftThighEffector.positionOffset); this.LerpSolverPosition(this.ik.solver.rightThighEffector, base.transform.position + b3, this.positionWeight * this.ik.solver.IKPositionWeight, this.ik.solver.bodyEffector.positionOffset - this.ik.solver.pullBodyOffset + this.ik.solver.rightThighEffector.positionOffset); }
// Bending the spine to the head effector private void SpineBend() { float w = bendWeight * ik.solver.IKPositionWeight; if (w <= 0f) { return; } if (bendBones.Length == 0) { return; } Quaternion rotation = transform.rotation * Quaternion.Inverse(ik.references.root.rotation * headRotationRelativeToRoot); rotation = QuaTools.ClampRotation(rotation, bodyClampWeight, 2); float step = 1f / bendBones.Length; for (int i = 0; i < bendBones.Length; i++) { if (bendBones[i].transform != null) { bendBones[i].transform.rotation = Quaternion.Lerp(Quaternion.identity, rotation, step * bendBones[i].weight * w) * bendBones[i].transform.rotation; } } }
// Move and rotate the pelvis private void TranslatePelvis(Leg[] legs, Vector3 deltaPosition, Quaternion deltaRotation, float scale) { // Rotation Vector3 p = head.solverPosition; deltaRotation = QuaTools.ClampRotation(deltaRotation, chestClampWeight, 2); Quaternion r = Quaternion.Slerp(Quaternion.identity, deltaRotation, bodyRotStiffness * rotationWeight); r = Quaternion.Slerp(r, QuaTools.FromToRotation(pelvis.solverRotation, IKRotationPelvis), pelvisRotationWeight); VirtualBone.RotateAroundPoint(bones, 0, pelvis.solverPosition, pelvisRotationOffset * r); deltaPosition -= head.solverPosition - p; // Position // Move the body back when head is moving down Vector3 m = rootRotation * Vector3.forward; float deltaY = V3Tools.ExtractVertical(deltaPosition, rootRotation * Vector3.up, 1f).magnitude; if (scale > 0f) { deltaY /= scale; } float backOffset = deltaY * -moveBodyBackWhenCrouching * headHeight; deltaPosition += m * backOffset; MovePosition(LimitPelvisPosition(legs, pelvis.solverPosition + deltaPosition * bodyPosStiffness * positionWeight, false)); }
// Called by the FBBIK each time it is finished updating private void OnPostUpdate() { if (!enabled) { return; } if (!ik.enabled) { return; } if (!gameObject.activeInHierarchy) { return; } // Stretching the spine and neck PostStretching(); // Rotate the head bone Quaternion headRotation = QuaTools.FromToRotation(ik.references.head.rotation, transform.rotation); headRotation = QuaTools.ClampRotation(headRotation, headClampWeight, 2); ik.references.head.rotation = Quaternion.Lerp(Quaternion.identity, headRotation, rotationWeight * ik.solver.IKPositionWeight) * ik.references.head.rotation; }
// Bending the spine to the head effector private void Bend(VirtualBone[] bones, int firstIndex, int lastIndex, Quaternion targetRotation, float clampWeight, bool uniformWeight, float w) { if (w <= 0f) { return; } if (bones.Length == 0) { return; } int bonesCount = (lastIndex + 1) - firstIndex; if (bonesCount < 1) { return; } Quaternion r = QuaTools.FromToRotation(bones[lastIndex].solverRotation, targetRotation); r = QuaTools.ClampRotation(r, clampWeight, 2); float step = uniformWeight? 1f / bonesCount: 0f; for (int i = firstIndex; i < lastIndex + 1; i++) { if (!uniformWeight) { step = Mathf.Clamp(((i - firstIndex) + 1) / bonesCount, 0, 1f); } VirtualBone.RotateAroundPoint(bones, i, bones[i].solverPosition, Quaternion.Slerp(Quaternion.identity, r, step * w)); } }
/* * Stage 2 of FABRIK algorithm with limited rotations * */ private void BackwardReachLimited(Vector3 position) { // Move first bone to position bones[0].solverPosition = position; // Applying rotation limits bone by bone for (int i = 0; i < bones.Length - 1; i++) { // Rotating bone to look at the solved joint position Vector3 nextPosition = SolveJoint(bones[i + 1].solverPosition, bones[i].solverPosition, bones[i].length); Quaternion swing = Quaternion.FromToRotation(bones[i].solverRotation * bones[i].axis, nextPosition - bones[i].solverPosition); Quaternion targetRotation = swing * bones[i].solverRotation; // Rotation Constraints if (bones[i].rotationLimit != null) { bool changed = false; targetRotation = GetLimitedRotation(i, targetRotation, out changed); } Quaternion fromTo = QuaTools.FromToRotation(bones[i].solverRotation, targetRotation); bones[i].solverRotation = targetRotation; SolverRotateChildren(i, fromTo); // Positioning the next bone to its default local position bones[i + 1].solverPosition = bones[i].solverPosition + bones[i].solverRotation * solverLocalPositions[i + 1]; } // Reconstruct solver rotations to protect from invalid Quaternions for (int i = 0; i < bones.Length; i++) { bones[i].solverRotation = Quaternion.LookRotation(bones[i].solverRotation * Vector3.forward, bones[i].solverRotation * Vector3.up); } }
private void SpineBend() { float num = this.bendWeight * this.ik.solver.IKPositionWeight; if (num <= 0f) { return; } if (this.bendBones.Length == 0) { return; } Quaternion quaternion = base.transform.rotation * Quaternion.Inverse(this.ik.references.root.rotation * this.headRotationRelativeToRoot); quaternion = QuaTools.ClampRotation(quaternion, this.bodyClampWeight, 2); float num2 = 1f / (float)this.bendBones.Length; for (int i = 0; i < this.bendBones.Length; i++) { if (this.bendBones[i].transform != null) { this.bendBones[i].transform.rotation = Quaternion.Lerp(Quaternion.identity, quaternion, num2 * this.bendBones[i].weight * num) * this.bendBones[i].transform.rotation; } } }
// Move and rotate the pelvis private void TranslatePelvis(Leg[] legs, Vector3 deltaPosition, Quaternion deltaRotation, float w) { // Rotation Vector3 p = head.solverPosition; deltaRotation = QuaTools.ClampRotation(deltaRotation, chestClampWeight, 2); Quaternion f = w >= 1f? pelvisRotationOffset: Quaternion.Slerp(Quaternion.identity, pelvisRotationOffset, w); VirtualBone.RotateAroundPoint(bones, 0, pelvis.solverPosition, f * Quaternion.Slerp(Quaternion.identity, deltaRotation, w * bodyRotStiffness)); deltaPosition -= head.solverPosition - p; // Position // Move the body back when head is moving down Vector3 m = anchorRotation * Vector3.forward; m.y = 0f; float backOffset = deltaPosition.y * 0.35f * headHeight; deltaPosition += m * backOffset; /* * if (backOffset < 0f) { * foreach (Leg leg in legs) leg.heelPositionOffset += Vector3.up * backOffset * backOffset; // TODO Ignoring root rotation * } */ MovePosition(LimitPelvisPosition(legs, pelvis.solverPosition + deltaPosition * w * bodyPosStiffness, false)); }
/* * Updates the 3 plane points * */ public void UpdatePlane() { Quaternion l = lastAnimatedTargetRotation; defaultLocalTargetRotation = QuaTools.GetAxisConvert(transform.rotation, l); planePosition = Quaternion.Inverse(l) * (transform.position - planeNode1.transform.position); }
public void RotateTo(Vector3 position) { if (this.pivot == null) { return; } if (this.pivot != this.lastPivot) { this.defaultLocalRotation = this.pivot.localRotation; this.lastPivot = this.pivot; } this.pivot.localRotation = this.defaultLocalRotation; if (this.twistWeight > 0f) { Vector3 fromDirection = base.transform.position - this.pivot.position; Vector3 vector = this.pivot.rotation * this.twistAxis; Vector3 vector2 = vector; Vector3.OrthoNormalize(ref vector2, ref fromDirection); vector2 = vector; Vector3 toDirection = position - this.pivot.position; Vector3.OrthoNormalize(ref vector2, ref toDirection); Quaternion b = QuaTools.FromToAroundAxis(fromDirection, toDirection, vector); this.pivot.rotation = Quaternion.Lerp(Quaternion.identity, b, this.twistWeight) * this.pivot.rotation; } if (this.swingWeight > 0f) { Quaternion b2 = Quaternion.FromToRotation(base.transform.position - this.pivot.position, position - this.pivot.position); this.pivot.rotation = Quaternion.Lerp(Quaternion.identity, b2, this.swingWeight) * this.pivot.rotation; } }
public void Initiate(Vector3 childPosition, Vector3 bendNormal) { Quaternion rotation = Quaternion.LookRotation(childPosition - this.transform.position, bendNormal); this.targetToLocalSpace = QuaTools.RotationToLocalSpace(this.transform.rotation, rotation); this.defaultLocalBendNormal = Quaternion.Inverse(this.transform.rotation) * bendNormal; }
// Called by the FBBIK before each solver iteration private void Iterate(int iteration) { if (ik.solver.iterations == 0) { return; } // Shoulders leftShoulderPos = transform.position + (leftShoulderPos - transform.position).normalized * leftShoulderDist; rightShoulderPos = transform.position + (rightShoulderPos - transform.position).normalized * rightShoulderDist; Solve(ref leftShoulderPos, ref rightShoulderPos, shoulderDist); LerpSolverPosition(ik.solver.leftShoulderEffector, leftShoulderPos, positionWeight); LerpSolverPosition(ik.solver.rightShoulderEffector, rightShoulderPos, positionWeight); // Body Quaternion chestRotationSolved = Quaternion.LookRotation(transform.position - leftShoulderPos, rightShoulderPos - leftShoulderPos); Quaternion rBody = QuaTools.FromToRotation(chestRotation, chestRotationSolved); Vector3 headToBodySolved = rBody * headToBody; LerpSolverPosition(ik.solver.bodyEffector, transform.position + headToBodySolved, positionWeight); // Thighs Quaternion rThighs = Quaternion.Lerp(Quaternion.identity, rBody, thighWeight); Vector3 headToLeftThighSolved = rThighs * headToLeftThigh; Vector3 headToRightThighSolved = rThighs * headToRightThigh; LerpSolverPosition(ik.solver.leftThighEffector, transform.position + headToLeftThighSolved, positionWeight); LerpSolverPosition(ik.solver.rightThighEffector, transform.position + headToRightThighSolved, positionWeight); }
private void WriteTransforms() { for (int i = 0; i < solverTransforms.Length; i++) { if (solverTransforms[i] != null) { bool isRootOrPelvis = i < 2; bool isArm = i > 5 && i < 14; bool isLeg = i >= 14; if (isRootOrPelvis) { solverTransforms[i].position = V3Tools.Lerp(solverTransforms[i].position, GetPosition(i), IKPositionWeight); } if (isArm || isLeg) { solverTransforms[i].position = V3Tools.Lerp(solverTransforms[i].position, GetPosition(i), IKPositionWeight); } solverTransforms[i].rotation = QuaTools.Lerp(solverTransforms[i].rotation, GetRotation(i), IKPositionWeight); } } }
// Called by the InteractionSystem when an interaction starts private void OnStart(FullBodyBipedEffector effectorType, InteractionObject interactionObject) { if (effectorType != FullBodyBipedEffector.LeftHand) { return; } // Rotate the pivot of the hand targets by 90 degrees so we could grab the box from any direction // Get the flat direction towards the character Vector3 characterDirection = (interactionSystem.transform.position - pivot.position).normalized; characterDirection.y = 0f; // Convert the direction to local space of the box Vector3 characterDirectionLocal = box.transform.InverseTransformDirection(characterDirection); // QuaTools.GetAxis returns a 90 degree ortographic axis for any direction Vector3 axis = QuaTools.GetAxis(characterDirectionLocal); Vector3 upAxis = QuaTools.GetAxis(box.transform.InverseTransformDirection(interactionSystem.transform.up)); // Rotate towards axis and upAxis pivot.localRotation = Quaternion.LookRotation(axis, upAxis); // Rotate the hold point so it matches the current rotation of the box boxHoldPoint.rotation = box.transform.rotation; }
public void RecordVelocity() { this.deltaPosition = this.t.position - this.lastPosition; this.lastPosition = this.t.position; this.deltaRotation = QuaTools.FromToRotation(this.lastRotation, this.t.rotation); this.lastRotation = this.t.rotation; this.deltaTime = Time.deltaTime; }
/* * Applying rotation limit to a bone in stage 1 in a more stable way * */ private void LimitForward(int rotateBone, int limitBone) { if (!useRotationLimits) { return; } if (bones[limitBone].rotationLimit == null) { return; } // Storing last bone's position before applying the limit Vector3 lastBoneBeforeLimit = bones[bones.Length - 1].solverPosition; // Moving and rotating this bone and all its children to their solver positions for (int i = rotateBone; i < bones.Length - 1; i++) { if (limitedBones[i]) { break; } Quaternion fromTo = Quaternion.FromToRotation(bones[i].solverRotation * bones[i].axis, bones[i + 1].solverPosition - bones[i].solverPosition); SolverRotate(i, fromTo, false); } // Limit the bone's rotation bool changed = false; Quaternion afterLimit = GetLimitedRotation(limitBone, bones[limitBone].solverRotation, out changed); if (changed) { // Rotating and positioning the hierarchy so that the last bone's position is maintained if (limitBone < bones.Length - 1) { Quaternion change = QuaTools.FromToRotation(bones[limitBone].solverRotation, afterLimit); bones[limitBone].solverRotation = afterLimit; SolverRotateChildren(limitBone, change); SolverMoveChildrenAroundPoint(limitBone, change); // Rotating to compensate for the limit Quaternion fromTo = Quaternion.FromToRotation(bones[bones.Length - 1].solverPosition - bones[rotateBone].solverPosition, lastBoneBeforeLimit - bones[rotateBone].solverPosition); SolverRotate(rotateBone, fromTo, true); SolverMoveChildrenAroundPoint(rotateBone, fromTo); // Moving the bone so that last bone maintains it's initial position SolverMove(rotateBone, lastBoneBeforeLimit - bones[bones.Length - 1].solverPosition); } else { // last bone bones[limitBone].solverRotation = afterLimit; } } limitedBones[limitBone] = true; }
/* * Initiates the bone, precalculates values. * */ public void Initiate(Vector3 childPosition, Vector3 bendNormal) { // Get default target rotation that looks at child position with bendNormal as up Quaternion defaultTargetRotation = Quaternion.LookRotation(childPosition - transform.position, bendNormal); // Covert default target rotation to local space targetToLocalSpace = QuaTools.RotationToLocalSpace(transform.rotation, defaultTargetRotation); defaultLocalBendNormal = Quaternion.Inverse(transform.rotation) * bendNormal; }
protected override void RotatePivot() { Vector3 normalized = (this.pivot.position - this.interactionSystem.transform.position).normalized; normalized.y = 0f; Vector3 v = this.obj.transform.InverseTransformDirection(normalized); Vector3 axis = QuaTools.GetAxis(v); Vector3 axis2 = QuaTools.GetAxis(this.obj.transform.InverseTransformDirection(this.interactionSystem.transform.up)); this.pivot.localRotation = Quaternion.LookRotation(axis, axis2); }
/* * Rotates the bone relative to it's 3 plane nodes * */ public void RotateToPlane(float weight) { Quaternion r = QuaTools.ConvertAxis(targetRotation, defaultLocalTargetRotation); if (weight >= 1f) { transform.rotation = r; return; } transform.rotation = Quaternion.Lerp(transform.rotation, r, weight); }
public override void ApplyOffsets() { headPosition += headPositionOffset; headPosition.y = Math.Max(rootPosition.y + 0.8f, headPosition.y); headRotation = headRotationOffset * headRotation; headDeltaPosition = headPosition - head.solverPosition; pelvisDeltaRotation = QuaTools.FromToRotation(pelvis.solverRotation, headRotation * pelvisRelativeRotation); anchorRotation = headRotation * anchorRelativeToHead; }
public void UpdatePlane(bool rotation, bool position) { Quaternion lastAnimatedTargetRotation = this.lastAnimatedTargetRotation; if (rotation) { this.defaultLocalTargetRotation = QuaTools.RotationToLocalSpace(this.transform.rotation, lastAnimatedTargetRotation); } if (position) { this.planePosition = Quaternion.Inverse(lastAnimatedTargetRotation) * (this.transform.position - this.planeBone1.position); } }
/* * Updates the 3 plane points * */ public void UpdatePlane(bool rotation, bool position) { Quaternion t = lastAnimatedTargetRotation; if (rotation) { defaultLocalTargetRotation = QuaTools.RotationToLocalSpace(transform.rotation, t); } if (position) { planePosition = Quaternion.Inverse(t) * (transform.position - planeBone1.position); } }
public override void ApplyOffsets(float scale) { headPosition += headPositionOffset; float mHH = minHeadHeight * scale; Vector3 rootUp = rootRotation * Vector3.up; if (rootUp == Vector3.up) { headPosition.y = Math.Max(rootPosition.y + mHH, headPosition.y); } else { Vector3 toHead = headPosition - rootPosition; Vector3 hor = V3Tools.ExtractHorizontal(toHead, rootUp, 1f); Vector3 ver = toHead - hor; float dot = Vector3.Dot(ver, rootUp); if (dot > 0f) { if (ver.magnitude < mHH) { ver = ver.normalized * mHH; } } else { ver = -ver.normalized * mHH; } headPosition = rootPosition + hor + ver; } headRotation = headRotationOffset * headRotation; headDeltaPosition = headPosition - head.solverPosition; pelvisDeltaRotation = QuaTools.FromToRotation(pelvis.solverRotation, headRotation * pelvisRelativeRotation); if (pelvisRotationWeight <= 0f) { anchorRotation = headRotation * anchorRelativeToHead; } else if (pelvisRotationWeight > 0f && pelvisRotationWeight < 1f) { anchorRotation = Quaternion.Lerp(headRotation * anchorRelativeToHead, pelvisRotation * anchorRelativeToPelvis, pelvisRotationWeight); } else if (pelvisRotationWeight >= 1f) { anchorRotation = pelvisRotation * anchorRelativeToPelvis; } }
private void WriteTransforms() { for (int i = 0; i < solverTransforms.Length; i++) { if (solverTransforms[i] != null) { if (i < 2) { solverTransforms[i].position = V3Tools.Lerp(solverTransforms[i].position, GetPosition(i), IKPositionWeight); } solverTransforms[i].rotation = QuaTools.Lerp(solverTransforms[i].rotation, GetRotation(i), IKPositionWeight); } } }
public override void PreSolve() { if (target != null) { IKPosition = target.position; IKRotation = target.rotation; } position = V3Tools.Lerp(hand.solverPosition, IKPosition, positionWeight); rotation = QuaTools.Lerp(hand.solverRotation, IKRotation, rotationWeight); shoulder.axis = shoulder.axis.normalized; forearmRelToUpperArm = Quaternion.Inverse(upperArm.solverRotation) * forearm.solverRotation; }
public void TranslateRoot(Vector3 newRootPos, Quaternion newRootRot) { Vector3 deltaPosition = newRootPos - rootPosition; rootPosition = newRootPos; foreach (VirtualBone bone in bones) { bone.solverPosition += deltaPosition; } Quaternion deltaRotation = QuaTools.FromToRotation(rootRotation, newRootRot); rootRotation = newRootRot; VirtualBone.RotateAroundPoint(bones, 0, newRootPos, deltaRotation); }
// How fast the mapped target is moving? Will be used to set rigidbody velocities when puppet is killed. // Rigidbody velocities otherwise might be close to 0 when FixedUpdate called more than once per frame or velocity wrongully changing when mapping weights not 1. public void CalculateMappedVelocity() { float writeDeltaTime = Time.time - lastWriteTime; if (writeDeltaTime > 0f) { mappedVelocity = (target.position - lastMappedPosition) / writeDeltaTime; mappedAngularVelocity = QuaTools.FromToRotation(lastMappedRotation, target.rotation).eulerAngles / writeDeltaTime; lastWriteTime = Time.time; } lastMappedPosition = target.position; lastMappedRotation = target.rotation; }
/* * Apply the bend constraint * */ public void Solve() { weight = Mathf.Clamp(weight, 0f, 1f); // Get the direction to node2 ortho-normalized to the chain direction Vector3 directionTangent = OrthoToLimb(rotationOffset * OrthoToLimb(GetDir())); Vector3 node2Tangent = OrthoToLimb(node2.solverPosition - node1.solverPosition); // Rotation from the current position to the desired position Quaternion fromTo = QuaTools.FromToAroundAxis(node2Tangent, directionTangent, (node3.solverPosition - node1.solverPosition).normalized); // Repositioning node2 Vector3 to2 = node2.solverPosition - node1.solverPosition; node2.solverPosition = node1.solverPosition + fromTo * to2; }
public override void PreSolve() { if (headTarget != null) { IKPositionHead = headTarget.position; IKRotationHead = headTarget.rotation; } if (pelvisTarget != null) { IKPositionPelvis = pelvisTarget.position; } headPosition = V3Tools.Lerp(head.solverPosition, IKPositionHead, positionWeight); headRotation = QuaTools.Lerp(head.solverRotation, IKRotationHead, rotationWeight); }
// Rotate the pivot of the hand targets by 90 degrees so we could grab the object from any direction protected override void RotatePivot() { // Get the flat direction towards the character Vector3 characterDirection = (pivot.position - interactionSystem.transform.position).normalized; characterDirection.y = 0f; // Convert the direction to local space of the object Vector3 characterDirectionLocal = obj.transform.InverseTransformDirection(characterDirection); // QuaTools.GetAxis returns a 90 degree ortographic axis for any direction Vector3 axis = QuaTools.GetAxis(characterDirectionLocal); Vector3 upAxis = QuaTools.GetAxis(obj.transform.InverseTransformDirection(interactionSystem.transform.up)); // Rotate towards axis and upAxis pivot.localRotation = Quaternion.LookRotation(axis, upAxis); }