public override void PrepareExecute() { base.PrepareExecute(); // repurpose rotation effect flag for bone rotation delta back-propagation Params.Bits.SetBit(BoingWork.ReactorFlags.EnableRotationEffect, false); float dt = Time.fixedDeltaTime; m_minScale = Mathf.Min(transform.localScale.x, transform.localScale.y, transform.localScale.z); for (int iChain = 0; iChain < BoneData.Length; ++iChain) { var chain = BoneChains[iChain]; var aBone = BoneData[iChain]; if (aBone == null || chain.Root == null || aBone.Length == 0) { continue; } Vector3 gravityDt = chain.Gravity * dt; if (UpdateMode == BoingManager.UpdateMode.FixedUpdate) { gravityDt *= BoingManager.NumFixedUpdateIterations; } // update length from root float maxLengthFromRoot = 0.0f; foreach (var bone in aBone) { if (bone.ParentIndex < 0) { bone.LengthFromRoot = 0.0f; continue; } var parentBone = aBone[bone.ParentIndex]; float distFromParent = Vector3.Distance(bone.Transform.position, parentBone.Transform.position); bone.LengthFromRoot = parentBone.LengthFromRoot + distFromParent; maxLengthFromRoot = Mathf.Max(maxLengthFromRoot, bone.LengthFromRoot); } float maxLengthFromRootInv = MathUtil.InvSafe(maxLengthFromRoot); // set up bones foreach (var bone in aBone) { // evaluate curves float tBone = bone.LengthFromRoot * maxLengthFromRootInv; bone.AnimationBlend = Chain.EvaluateCurve(chain.AnimationBlendCurveType, tBone, chain.AnimationBlendCustomCurve); bone.LengthStiffness = Chain.EvaluateCurve(chain.LengthStiffnessCurveType, tBone, chain.LengthStiffnessCustomCurve); bone.LengthStiffnessT = 1.0f - Mathf.Pow(1.0f - bone.LengthStiffness, 30.0f * dt); // a factor of 30.0f is what makes 0.5 length stiffness looks like 50% stiffness bone.FullyStiffToParentLength = bone.ParentIndex >= 0 ? Vector3.Distance(aBone[bone.ParentIndex].Transform.position, bone.Transform.position) : 0.0f; bone.PoseStiffness = Chain.EvaluateCurve(chain.PoseStiffnessCurveType, tBone, chain.PoseStiffnessCustomCurve); bone.BendAngleCap = chain.MaxBendAngleCap * MathUtil.Deg2Rad * Chain.EvaluateCurve(chain.BendAngleCapCurveType, tBone, chain.BendAngleCapCustomCurve); bone.CollisionRadius = chain.MaxCollisionRadius * Chain.EvaluateCurve(chain.CollisionRadiusCurveType, tBone, chain.CollisionRadiusCustomCurve); bone.SquashAndStretch = Chain.EvaluateCurve(chain.SquashAndStretchCurveType, tBone, chain.SquashAndStretchCustomCurve); } var rootBone = aBone[0]; Vector3 rootAnimPos = rootBone.Transform.position; for (int iBone = 0; iBone < aBone.Length; ++iBone) { var bone = aBone[iBone]; // evaluate curves { float tBone = bone.LengthFromRoot * maxLengthFromRootInv; bone.AnimationBlend = Chain.EvaluateCurve(chain.AnimationBlendCurveType, tBone, chain.AnimationBlendCustomCurve); bone.LengthStiffness = Chain.EvaluateCurve(chain.LengthStiffnessCurveType, tBone, chain.LengthStiffnessCustomCurve); bone.PoseStiffness = Chain.EvaluateCurve(chain.PoseStiffnessCurveType, tBone, chain.PoseStiffnessCustomCurve); bone.BendAngleCap = chain.MaxBendAngleCap * MathUtil.Deg2Rad * Chain.EvaluateCurve(chain.BendAngleCapCurveType, tBone, chain.BendAngleCapCustomCurve); bone.CollisionRadius = chain.MaxCollisionRadius * Chain.EvaluateCurve(chain.CollisionRadiusCurveType, tBone, chain.CollisionRadiusCustomCurve); bone.SquashAndStretch = Chain.EvaluateCurve(chain.SquashAndStretchCurveType, tBone, chain.SquashAndStretchCustomCurve); } // end: evaluate curves // gravity { // no gravity on root if (iBone > 0) { bone.Instance.PositionSpring.Velocity += gravityDt; } } // end: gravity // compute target transform { bone.RotationInverseWs = Quaternion.Inverse(bone.Transform.rotation); bone.SpringRotationWs = bone.Instance.RotationSpring.ValueQuat; bone.SpringRotationInverseWs = Quaternion.Inverse(bone.SpringRotationWs); Vector3 targetPos = bone.Transform.position; Quaternion targetRot = bone.Transform.rotation; // compute translation & rotation in parent space if (bone.ParentIndex >= 0) { // TODO: use parent spring transform to compute blended position & rotation var parentBone = aBone[bone.ParentIndex]; Vector3 parentAnimPos = parentBone.Transform.position; Vector3 parentSpringPos = parentBone.Instance.PositionSpring.Value; Vector3 springPosPs = parentBone.SpringRotationInverseWs * (bone.Instance.PositionSpring.Value - parentSpringPos); Quaternion springRotPs = parentBone.SpringRotationInverseWs * bone.Instance.RotationSpring.ValueQuat; Vector3 animPos = bone.Transform.position; Quaternion animRot = bone.Transform.rotation; Vector3 animPosPs = parentBone.RotationInverseWs * (animPos - parentAnimPos); Quaternion animRotPs = parentBone.RotationInverseWs * animRot; // apply pose stiffness float tPoseStiffness = bone.PoseStiffness; Vector3 blendedPosPs = Vector3.Lerp(springPosPs, animPosPs, tPoseStiffness); Quaternion blendedRotPs = Quaternion.Slerp(springRotPs, animRotPs, tPoseStiffness); targetPos = parentSpringPos + (parentBone.SpringRotationWs * blendedPosPs); targetRot = parentBone.SpringRotationWs * blendedRotPs; // bend angle cap if (bone.BendAngleCap < MathUtil.Pi - MathUtil.Epsilon) { Vector3 targetPosDelta = targetPos - rootAnimPos; targetPosDelta = VectorUtil.ClampBend(targetPosDelta, animPos - rootAnimPos, bone.BendAngleCap); targetPos = rootAnimPos + targetPosDelta; } } if (chain.ParamsOverride == null) { bone.Instance.PrepareExecute ( ref Params, targetPos, targetRot, MinScale, true ); } else { bone.Instance.PrepareExecute ( ref chain.ParamsOverride.Params, targetPos, targetRot, MinScale, true ); } } // end: compute target transform } } }
public void Execute(BoingBones bones, float dt) { float maxCollisionResolutionPushLen = bones.MaxCollisionResolutionSpeed * dt; for (int iChain = 0; iChain < bones.BoneData.Length; ++iChain) { var chain = bones.BoneChains[iChain]; var aBone = bones.BoneData[iChain]; if (aBone == null) { continue; } Vector3 gravityDt = chain.Gravity * dt; // execute boing work for (int iBone = 0; iBone < aBone.Length; ++iBone) // skip root { var bone = aBone[iBone]; // no gravity on root if (iBone > 0) { bone.Instance.PositionSpring.Velocity += gravityDt; } if (chain.ParamsOverride == null) { bone.Instance.Execute(ref bones.Params, dt); } else { bone.Instance.Execute(ref chain.ParamsOverride.Params, dt); } } var rootBone = aBone[0]; rootBone.ScaleWs = rootBone.BlendedScaleLs = rootBone.CachedScaleLs; rootBone.UpdateBounds(); chain.Bounds = rootBone.Bounds; Vector3 rootAnimPos = rootBone.Transform.position; // apply length stiffness & volume preservation for (int iBone = 1; iBone < aBone.Length; ++iBone) // skip root { var bone = aBone[iBone]; var parentBone = aBone[bone.ParentIndex]; float tLengthStiffness = 1.0f - Mathf.Pow(1.0f - bone.LengthStiffness, 30.0f * dt); // a factor of 30.0f is what makes 0.5 length stiffness looks like 50% stiffness Vector3 toParentVec = parentBone.Instance.PositionSpring.Value - bone.Instance.PositionSpring.Value; Vector3 toParentDir = VectorUtil.NormalizeSafe(toParentVec, Vector3.zero); float fullyStiffToParentLen = (parentBone.Transform.position - bone.Transform.position).magnitude; float toParentLen = toParentVec.magnitude; float fullyStiffLenDelta = toParentLen - fullyStiffToParentLen; float toParentAdjustLen = tLengthStiffness * fullyStiffLenDelta; // length stiffness { bone.Instance.PositionSpring.Value += toParentAdjustLen * toParentDir; Vector3 velocityInParentAdjustDir = Vector3.Project(bone.Instance.PositionSpring.Velocity, toParentDir); bone.Instance.PositionSpring.Velocity -= tLengthStiffness * velocityInParentAdjustDir; } // bend angle cap if (bone.BendAngleCap < MathUtil.Pi - MathUtil.Epsilon) { Vector3 animPos = bone.Transform.position; Vector3 posDelta = bone.Instance.PositionSpring.Value - rootAnimPos; posDelta = VectorUtil.ClampBend(posDelta, animPos - rootAnimPos, bone.BendAngleCap); bone.Instance.PositionSpring.Value = rootAnimPos + posDelta; } // volume preservation if (bone.SquashAndStretch > 0.0f) { float toParentLenRatio = toParentLen * MathUtil.InvSafe(fullyStiffToParentLen); float volumePreservationScale = Mathf.Sqrt(1.0f / toParentLenRatio); volumePreservationScale = Mathf.Clamp(volumePreservationScale, 1.0f / Mathf.Max(1.0f, chain.MaxStretch), Mathf.Max(1.0f, chain.MaxSquash)); Vector3 volumePreservationScaleVec = VectorUtil.ComponentWiseDivSafe(volumePreservationScale * Vector3.one, parentBone.ScaleWs); bone.BlendedScaleLs = Vector3.Lerp ( Vector3.Lerp ( bone.CachedScaleLs, volumePreservationScaleVec, bone.SquashAndStretch ), bone.CachedScaleLs, bone.AnimationBlend ); } else { bone.BlendedScaleLs = bone.CachedScaleLs; } bone.ScaleWs = VectorUtil.ComponentWiseMult(parentBone.ScaleWs, bone.BlendedScaleLs); bone.UpdateBounds(); chain.Bounds.Encapsulate(bone.Bounds); } chain.Bounds.Expand(0.2f * Vector3.one); // Boing Kit colliders if (chain.EnableBoingKitCollision) { foreach (var collider in bones.BoingColliders) { if (collider == null) { continue; } if (!chain.Bounds.Intersects(collider.Bounds)) { continue; } foreach (var bone in aBone) { if (!bone.Bounds.Intersects(collider.Bounds)) { continue; } Vector3 push; bool collided = collider.Collide(bone.Instance.PositionSpring.Value, bones.MinScale * bone.CollisionRadius, out push); if (!collided) { continue; } bone.Instance.PositionSpring.Value += VectorUtil.ClampLength(push, 0.0f, maxCollisionResolutionPushLen); bone.Instance.PositionSpring.Velocity -= Vector3.Project(bone.Instance.PositionSpring.Velocity, push); } } } // Unity colliders var sharedSphereCollider = BoingManager.SharedSphereCollider; if (chain.EnableUnityCollision && sharedSphereCollider != null) { sharedSphereCollider.enabled = true; foreach (var collider in bones.UnityColliders) { if (collider == null) { continue; } if (!chain.Bounds.Intersects(collider.bounds)) { continue; } foreach (var bone in aBone) { if (!bone.Bounds.Intersects(collider.bounds)) { continue; } sharedSphereCollider.center = bone.Instance.PositionSpring.Value; sharedSphereCollider.radius = bone.CollisionRadius; Vector3 pushDir; float pushDist; bool collided = Physics.ComputePenetration ( sharedSphereCollider, Vector3.zero, Quaternion.identity, collider, collider.transform.position, collider.transform.rotation, out pushDir, out pushDist ); if (!collided) { continue; } bone.Instance.PositionSpring.Value += VectorUtil.ClampLength(pushDir * pushDist, 0.0f, maxCollisionResolutionPushLen); bone.Instance.PositionSpring.Velocity -= Vector3.Project(bone.Instance.PositionSpring.Velocity, pushDir); } } sharedSphereCollider.enabled = false; } // self collision if (chain.EnableInterChainCollision) { foreach (var bone in aBone) { for (int iOtherChain = iChain + 1; iOtherChain < bones.BoneData.Length; ++iOtherChain) { var otherChain = bones.BoneChains[iOtherChain]; var aOtherBone = bones.BoneData[iOtherChain]; if (aOtherBone == null) { continue; } if (!otherChain.EnableInterChainCollision) { continue; } if (!chain.Bounds.Intersects(otherChain.Bounds)) { continue; } foreach (var otherBone in aOtherBone) { Vector3 push; bool collided = Collision.SphereSphere ( bone.Instance.PositionSpring.Value, bones.MinScale * bone.CollisionRadius, otherBone.Instance.PositionSpring.Value, bones.MinScale * otherBone.CollisionRadius, out push ); if (!collided) { continue; } push = VectorUtil.ClampLength(push, 0.0f, maxCollisionResolutionPushLen); float pushRatio = otherBone.CollisionRadius * MathUtil.InvSafe(bone.CollisionRadius + otherBone.CollisionRadius); bone.Instance.PositionSpring.Value += pushRatio * push; otherBone.Instance.PositionSpring.Value -= (1.0f - pushRatio) * push; bone.Instance.PositionSpring.Velocity -= Vector3.Project(bone.Instance.PositionSpring.Velocity, push); otherBone.Instance.PositionSpring.Velocity -= Vector3.Project(otherBone.Instance.PositionSpring.Velocity, push); } } } } } // end: foreach bone chain }
public override void PrepareExecute() { base.PrepareExecute(); // repurpose rotation effect flag for bone rotation delta back-propagation Params.Bits.SetBit(BoingWork.ReactorFlags.EnableRotationEffect, false); float dt = Time.fixedDeltaTime; m_minScale = Mathf.Min(transform.localScale.x, transform.localScale.y, transform.localScale.z); for (int iChain = 0; iChain < BoneData.Length; ++iChain) { var chain = BoneChains[iChain]; var aBone = BoneData[iChain]; if (aBone == null || chain.Root == null || aBone.Length == 0) { continue; } // update length from root float maxLengthFromRoot = 0.0f; foreach (var bone in aBone) { if (bone.ParentIndex < 0) { bone.LengthFromRoot = 0.0f; continue; } var parentBone = aBone[bone.ParentIndex]; float distFromParent = Vector3.Distance(bone.Transform.position, parentBone.Transform.position); bone.LengthFromRoot = parentBone.LengthFromRoot + distFromParent; maxLengthFromRoot = Mathf.Max(maxLengthFromRoot, bone.LengthFromRoot); } float maxLengthFromRootInv = MathUtil.InvSafe(maxLengthFromRoot); // set up bones foreach (var bone in aBone) { // evaluate curves float tBone = bone.LengthFromRoot * maxLengthFromRootInv; bone.AnimationBlend = Chain.EvaluateCurve(chain.AnimationBlendCurveType, tBone, chain.AnimationBlendCustomCurve); bone.LengthStiffness = Chain.EvaluateCurve(chain.LengthStiffnessCurveType, tBone, chain.LengthStiffnessCustomCurve); bone.PoseStiffness = Chain.EvaluateCurve(chain.PoseStiffnessCurveType, tBone, chain.PoseStiffnessCustomCurve); bone.BendAngleCap = chain.MaxBendAngleCap * MathUtil.Deg2Rad * Chain.EvaluateCurve(chain.BendAngleCapCurveType, tBone, chain.BendAngleCapCustomCurve); bone.CollisionRadius = chain.MaxCollisionRadius * Chain.EvaluateCurve(chain.CollisionRadiusCurveType, tBone, chain.CollisionRadiusCustomCurve); bone.SquashAndStretch = Chain.EvaluateCurve(chain.SquashAndStretchCurveType, tBone, chain.SquashAndStretchCustomCurve); } // compute target transform var rootBone = aBone[0]; Vector3 rootAnimPos = rootBone.Transform.position; foreach (var bone in aBone) { bone.RotationInverse = Quaternion.Inverse(bone.Transform.rotation); bone.SpringRotation = bone.Instance.RotationSpring.ValueQuat; bone.SpringRotationInverse = Quaternion.Inverse(bone.SpringRotation); Vector3 targetPos = bone.Transform.position; Quaternion targetRot = bone.Transform.rotation; // compute translation & rotation in parent space if (bone.ParentIndex >= 0) { // TODO: use parent spring transform to compute blended position & rotation var parentBone = aBone[bone.ParentIndex]; Vector3 parentAnimPos = parentBone.Transform.position; Vector3 parentSpringPos = parentBone.Instance.PositionSpring.Value; Vector3 springPosPs = parentBone.SpringRotationInverse * (bone.Instance.PositionSpring.Value - parentSpringPos); Quaternion springRotPs = parentBone.SpringRotationInverse * bone.Instance.RotationSpring.ValueQuat; Vector3 animPos = bone.Transform.position; Quaternion animRot = bone.Transform.rotation; Vector3 animPosPs = parentBone.RotationInverse * (animPos - parentAnimPos); Quaternion animRotPs = parentBone.RotationInverse * animRot; // apply pose stiffness float tPoseStiffness = bone.PoseStiffness; Vector3 blendedPosPs = Vector3.Lerp(springPosPs, animPosPs, tPoseStiffness); Quaternion blendedRotPs = Quaternion.Slerp(springRotPs, animRotPs, tPoseStiffness); targetPos = parentSpringPos + (parentBone.SpringRotation * blendedPosPs); targetRot = parentBone.SpringRotation * blendedRotPs; // bend angle cap if (bone.BendAngleCap < MathUtil.Pi - MathUtil.Epsilon) { Vector3 targetPosDelta = targetPos - rootAnimPos; targetPosDelta = VectorUtil.ClampBend(targetPosDelta, animPos - rootAnimPos, bone.BendAngleCap); targetPos = rootAnimPos + targetPosDelta; } } // do we need target-level collision? /* * // Boing Kit colliders * if (chain.EnableBoingKitCollision) * { * foreach (var collider in BoingColliders) * { * if (collider == null) * continue; * * Vector3 push; * bool collided = collider.Collide(targetPos, MinScale * bone.CollisionRadius, out push); * if (!collided) * continue; * * targetPos += push; * } * } * * // Unity colliders * if (chain.EnableUnityCollision) * { * foreach (var rigidbody in UnityRigidBodies) * { * if (rigidbody == null) * continue; * * var collider = rigidbody.GetComponent<Collider>(); * if (collider == null) * continue; * * if (!bone.Bounds.Intersects(collider.bounds)) * continue; * * SharedSphereCollider.center = targetPos; * SharedSphereCollider.radius = bone.CollisionRadius; * * Vector3 pushDir; * float pushDist; * bool collided = * Physics.ComputePenetration * ( * SharedSphereCollider, Vector3.zero, Quaternion.identity, * collider, collider.transform.position, collider.transform.rotation, * out pushDir, out pushDist * ); * if (!collided) * continue; * * targetPos += pushDir * pushDist; * } * } */ if (chain.ParamsOverride == null) { bone.Instance.PrepareExecute ( ref Params, targetPos, targetRot, MinScale, true ); } else { bone.Instance.PrepareExecute ( ref chain.ParamsOverride.Params, targetPos, targetRot, MinScale, true ); } } } }