protected virtual void OnDestroy() { if (this.ik != null) { IKSolverVR solver = this.ik.solver; solver.OnPreUpdate = (IKSolver.UpdateDelegate)Delegate.Remove(solver.OnPreUpdate, new IKSolver.UpdateDelegate(this.ModifyOffset)); } }
Transform GetRootTransform(ref RootMotion.FinalIK.IKSolverVR solver) { Transform l_result = null; l_result = solver.spine?.headTarget?.transform?.parent; if (l_result == null) { l_result = VRCPlayer.field_Internal_Static_VRCPlayer_0.transform; } return(l_result); }
private IEnumerator Initiate() { while (this.ik == null) { yield return(null); } IKSolverVR solver = this.ik.solver; solver.OnPreUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(solver.OnPreUpdate, new IKSolver.UpdateDelegate(this.ModifyOffset)); this.lastTime = Time.time; yield break; }
/// <summary> /// Reinitializes the IK and Solver. /// </summary> public void ReInitialize() { Debug.Log($"Reinit CustomVRIK"); solver.initiated = false; componentInitiated = false; FindAnimatorRecursive(transform, true); //We only call the base solver because on reinit we will want to assume //that the avatar has been replaced, and we'll need to find the data component that can be used to retreieve it. IAvatarIKReferenceContainer <CustomVRIKReferences> referenceContainer = this.GetComponentInChildren <IAvatarIKReferenceContainer <CustomVRIKReferences> >(); //If we contain no reference data //then we should just autodetect and wish for the best //though this could cost performance issues on failure maybe? if (referenceContainer == null) { Debug.LogWarning($"Failed to find IK reference container."); AutoDetectReferences(); } else { //Set the references in the IK controller this.references = referenceContainer.references; //We should do nothing if the references are empty. if (references.isEmpty) { return; } var oldSolver = solver; solver = new IKSolverVR { spine = { headTarget = oldSolver.spine.headTarget }, leftArm = { target = oldSolver.leftArm.target }, rightArm = { target = oldSolver.rightArm.target } }; //First let's set the trackers to the appropriate local rotation //These compute from precomputed EULER angles from the SDK. solver.leftArm.target.localEulerAngles = references.LocalLeftHandRotation; solver.rightArm.target.localEulerAngles = references.LocalRightHandRotation; solver.spine.headTarget.localEulerAngles = references.LocalHeadRotation; solver.SetToReferences(references); } base.InitiateSolver(); componentInitiated = true; }
public void Solve_Animated(IKSolverVR solver, float scale, float deltaTime) { if (animator == null) { Debug.LogError("VRIK cannot find Animator on the VRIK root gameobject.", solver.root); return; } if (deltaTime <= 0f) { return; } if (!animator.enabled) { Debug.LogWarning("Trying to use VRIK animated locomotion with a disabled animator!", solver.root); return; } // Root up vector Vector3 rootUp = solver.rootBone.solverRotation * Vector3.up; // Substract any motion from parent transforms Vector3 externalDelta = solver.rootBone.solverPosition - lastEndRootPos; externalDelta -= animator.deltaPosition; // Head target position Vector3 headTargetPos = solver.spine.headPosition; Vector3 standOffsetWorld = solver.rootBone.solverRotation * new Vector3(standOffset.x, 0f, standOffset.y) * scale; headTargetPos += standOffsetWorld; if (firstFrame) { lastHeadTargetPos = headTargetPos; firstFrame = false; } // Head target velocity Vector3 headTargetVelocity = (headTargetPos - lastHeadTargetPos) / deltaTime; lastHeadTargetPos = headTargetPos; headTargetVelocity = V3Tools.Flatten(headTargetVelocity, rootUp); // Head target offset Vector3 offset = headTargetPos - solver.rootBone.solverPosition; offset -= externalDelta; offset -= lastCorrection; offset = V3Tools.Flatten(offset, rootUp); // Turning Vector3 headForward = (solver.spine.IKRotationHead * solver.spine.anchorRelativeToHead) * Vector3.forward; headForward.y = 0f; Vector3 headForwardLocal = Quaternion.Inverse(solver.rootBone.solverRotation) * headForward; float angle = Mathf.Atan2(headForwardLocal.x, headForwardLocal.z) * Mathf.Rad2Deg; angle += solver.spine.rootHeadingOffset; float turnTarget = angle / 90f; bool isTurning = true; if (Mathf.Abs(turnTarget) < 0.2f) { turnTarget = 0f; isTurning = false; } turn = Mathf.Lerp(turn, turnTarget, Time.deltaTime * 3f); animator.SetFloat(VRIK_Turn, turn * 2f); // Local Velocity, animation smoothing Vector3 velocityLocalTarget = Quaternion.Inverse(solver.readRotations[0]) * (headTargetVelocity + offset); velocityLocalTarget *= weight * stepLengthMlp; float animationSmoothTimeTarget = isTurning && !isMoving ? 0.2f : animationSmoothTime; currentAnimationSmoothTime = Mathf.Lerp(currentAnimationSmoothTime, animationSmoothTimeTarget, deltaTime * 20f); velocityLocal = Vector3.SmoothDamp(velocityLocal, velocityLocalTarget, ref velocityLocalV, currentAnimationSmoothTime, Mathf.Infinity, deltaTime); float velLocalMag = velocityLocal.magnitude / stepLengthMlp; //animator.SetBool("VRIK_StartWithRightFoot", velocityLocal.x >= 0f); animator.SetFloat(VRIK_Horizontal, velocityLocal.x / scale); animator.SetFloat(VRIK_Vertical, velocityLocal.z / scale); // Is Moving float m = moveThreshold * scale; if (isMoving) { m *= 0.9f; } bool isMovingRaw = velocityLocal.sqrMagnitude > m * m; if (isMovingRaw) { stopMoveTimer = 0f; } else { stopMoveTimer += deltaTime; } isMoving = stopMoveTimer < 0.05f; // Max root angle float maxRootAngleTarget = isMoving ? maxRootAngleMoving : maxRootAngleStanding; solver.spine.maxRootAngle = Mathf.SmoothDamp(solver.spine.maxRootAngle, maxRootAngleTarget, ref maxRootAngleV, 0.2f, Mathf.Infinity, deltaTime); animator.SetBool(VRIK_IsMoving, isMoving); // Animation speed Vector3 currentRootPos = solver.rootBone.solverPosition; currentRootPos -= externalDelta; currentRootPos -= lastCorrection; Vector3 rootVelocity = (currentRootPos - lastSpeedRootPos) / deltaTime; lastSpeedRootPos = solver.rootBone.solverPosition; float rootVelocityMag = rootVelocity.magnitude; float animSpeedTarget = minAnimationSpeed; if (rootVelocityMag > 0f && isMovingRaw) { animSpeedTarget = animSpeed * (velLocalMag / rootVelocityMag); } animSpeedTarget = Mathf.Clamp(animSpeedTarget, minAnimationSpeed, maxAnimationSpeed); animSpeed = Mathf.SmoothDamp(animSpeed, animSpeedTarget, ref animSpeedV, 0.05f, Mathf.Infinity, deltaTime); animSpeed = Mathf.Lerp(1f, animSpeed, weight); animator.SetFloat(VRIK_Speed, animSpeed); // Is Stopping AnimatorTransitionInfo transInfo = animator.GetAnimatorTransitionInfo(0); bool isStopping = transInfo.IsUserName("VRIK_Stop"); // Root lerp speed float rootLerpSpeedTarget = 0; if (isMoving) { rootLerpSpeedTarget = rootLerpSpeedWhileMoving; } if (isStopping) { rootLerpSpeedTarget = rootLerpSpeedWhileStopping; } if (isTurning) { rootLerpSpeedTarget = rootLerpSpeedWhileTurning; } rootLerpSpeedTarget *= Mathf.Max(headTargetVelocity.magnitude, 0.2f); rootLerpSpeed = Mathf.Lerp(rootLerpSpeed, rootLerpSpeedTarget, deltaTime * 20f); // Root lerp and limits headTargetPos += V3Tools.ExtractVertical(solver.rootBone.solverPosition - headTargetPos, rootUp, 1f); if (maxRootOffset > 0f) { // Lerp towards head target position Vector3 p = solver.rootBone.solverPosition; if (rootLerpSpeed > 0f) { solver.rootBone.solverPosition = Vector3.Lerp(solver.rootBone.solverPosition, headTargetPos, rootLerpSpeed * deltaTime * weight); } lastCorrection = solver.rootBone.solverPosition - p; // Max offset offset = headTargetPos - solver.rootBone.solverPosition; offset = V3Tools.Flatten(offset, rootUp); float offsetMag = offset.magnitude; if (offsetMag > maxRootOffset) { lastCorrection += (offset - (offset / offsetMag) * maxRootOffset) * weight; solver.rootBone.solverPosition += lastCorrection; } } else { // Snap to head target position lastCorrection = (headTargetPos - solver.rootBone.solverPosition) * weight; solver.rootBone.solverPosition += lastCorrection; } lastEndRootPos = solver.rootBone.solverPosition; }