2025-09-24 11:24:38 +05:00

282 lines
11 KiB
C#

namespace Fusion.Addons.KCC
{
using UnityEngine;
public static partial class KCCPhysicsUtility
{
// PUBLIC METHODS
public static bool ProjectOnGround(Vector3 groundNormal, Vector3 vector, out Vector3 projectedVector)
{
float dot1 = Vector3.Dot(Vector3.up, groundNormal);
float dot2 = -Vector3.Dot(vector, groundNormal);
if (dot1.IsAlmostZero(0.001f) == false)
{
projectedVector = new Vector3(vector.x, vector.y + dot2 / dot1, vector.z);
return true;
}
projectedVector = default;
return false;
}
public static void ProjectVerticalPenetration(ref Vector3 direction, ref float distance)
{
Vector3 desiredCorrection = direction * distance;
Vector3 desiredCorrectionXZ = desiredCorrection.OnlyXZ();
float correctionDistanceXZ = Vector3.Magnitude(desiredCorrectionXZ);
if (correctionDistanceXZ >= 0.000001f)
{
float reflectedDistanceXZ = desiredCorrection.y * desiredCorrection.y / correctionDistanceXZ;
direction = desiredCorrectionXZ / correctionDistanceXZ;
distance = correctionDistanceXZ + reflectedDistanceXZ;
}
}
public static void ProjectHorizontalPenetration(ref Vector3 direction, ref float distance)
{
Vector3 desiredCorrection = direction * distance;
direction = Vector3.up;
distance = 0.0f;
if (desiredCorrection.y > -0.000001f && desiredCorrection.y < 0.000001f)
return;
distance = desiredCorrection.y + (desiredCorrection.x * desiredCorrection.x + desiredCorrection.z * desiredCorrection.z) / desiredCorrection.y;
if (distance < 0.0f)
{
direction = -direction;
distance = -distance;
}
}
public static bool CheckGround(Collider collider, Vector3 position, Collider groundCollider, Vector3 groundColliderPosition, Quaternion groundColliderRotation, float radius, float height, float extent, float minGroundDot, out Vector3 groundNormal, out float groundDistance, out bool isWithinExtent)
{
return CheckGround(collider, position, groundCollider, groundColliderPosition, groundColliderRotation, radius, height, extent, minGroundDot, out Vector3 groundPosition, out groundNormal, out groundDistance, out isWithinExtent);
}
public static bool CheckGround(Collider collider, Vector3 position, Collider groundCollider, Vector3 groundColliderPosition, Quaternion groundColliderRotation, float radius, float height, float extent, float minGroundDot, out Vector3 groundPosition, out Vector3 groundNormal, out float groundDistance, out bool isWithinExtent)
{
#if KCC_DISABLE_TERRAIN
if (groundCollider is MeshCollider)
#else
if (groundCollider is MeshCollider || groundCollider is TerrainCollider)
#endif
{
Vector3 checkPosition = position - new Vector3(0.0f, extent, 0.0f);
if (Physics.ComputePenetration(collider, checkPosition, Quaternion.identity, groundCollider, groundColliderPosition, groundColliderRotation, out Vector3 direction, out float distance) == true)
{
isWithinExtent = true;
float directionUpDot = Vector3.Dot(direction, Vector3.up);
if (directionUpDot >= minGroundDot)
{
Vector3 projectedDirection = direction;
float projectedDistance = distance;
ProjectHorizontalPenetration(ref projectedDirection, ref projectedDistance);
groundNormal = direction;
groundPosition = checkPosition + Vector3.up * radius - direction * radius + projectedDirection * projectedDistance;
groundDistance = Mathf.Max(0.0f, Vector3.Distance(position, groundPosition) - radius);
return true;
}
}
}
else
{
float radiusSqr = radius * radius;
float radiusExtent = radius + extent;
float radiusExtentSqr = radiusExtent * radiusExtent;
Vector3 centerPosition = position + new Vector3(0.0f, radius, 0.0f);
Vector3 closestPoint = Physics.ClosestPoint(centerPosition, groundCollider, groundColliderPosition, groundColliderRotation);
Vector3 closestPointOffset = closestPoint - centerPosition;
Vector3 closestPointOffsetXZ = closestPointOffset.OnlyXZ();
float closestPointDistanceXZSqr = closestPointOffsetXZ.sqrMagnitude;
if (closestPointDistanceXZSqr <= radiusExtentSqr)
{
if (closestPointOffset.y < 0.0f)
{
float closestPointDistance = Vector3.Magnitude(closestPointOffset);
if (closestPointDistance <= radiusExtent)
{
isWithinExtent = true;
Vector3 closestPointDirection = closestPointOffset / closestPointDistance;
Vector3 closestGroundNormal = -closestPointDirection;
float closestGroundDot = Vector3.Dot(closestGroundNormal, Vector3.up);
if (closestGroundDot >= minGroundDot)
{
groundNormal = closestGroundNormal;
groundPosition = closestPoint;
groundDistance = Mathf.Max(0.0f, closestPointDistance - radius);
return true;
}
}
}
else if (closestPointOffset.y < height - radius * 2.0f)
{
isWithinExtent = true;
}
}
}
groundNormal = Vector3.up;
groundPosition = position;
groundDistance = default;
isWithinExtent = default;
return false;
}
public static Vector3 GetAcceleration(Vector3 velocity, Vector3 direction, Vector3 axis, float maxSpeed, bool clampSpeed, float inputAcceleration, float constantAcceleration, float relativeAcceleration, float proportionalAcceleration, float deltaTime)
{
if (inputAcceleration <= 0.0f)
return Vector3.zero;
if (constantAcceleration <= 0.0f && relativeAcceleration <= 0.0f && proportionalAcceleration <= 0.0f)
return Vector3.zero;
if (direction.IsZero() == true)
return Vector3.zero;
float baseSpeed = new Vector3(velocity.x * axis.x, velocity.y * axis.y, velocity.z * axis.z).magnitude;
Vector3 baseDirection = new Vector3(direction.x * axis.x, direction.y * axis.y, direction.z * axis.z).normalized;
float missingSpeed = Mathf.Max(0.0f, maxSpeed - baseSpeed);
if (constantAcceleration < 0.0f) { constantAcceleration = 0.0f; }
if (relativeAcceleration < 0.0f) { relativeAcceleration = 0.0f; }
if (proportionalAcceleration < 0.0f) { proportionalAcceleration = 0.0f; }
constantAcceleration *= inputAcceleration;
relativeAcceleration *= inputAcceleration;
proportionalAcceleration *= inputAcceleration;
float speedGain = (constantAcceleration + maxSpeed * relativeAcceleration + missingSpeed * proportionalAcceleration) * deltaTime;
if (speedGain <= 0.0f)
return Vector3.zero;
if (clampSpeed == true && speedGain > missingSpeed)
{
speedGain = missingSpeed;
}
return baseDirection.normalized * speedGain;
}
public static Vector3 GetAcceleration(Vector3 velocity, Vector3 direction, Vector3 axis, Vector3 normal, float targetSpeed, bool clampSpeed, float inputAcceleration, float constantAcceleration, float relativeAcceleration, float proportionalAcceleration, float deltaTime)
{
float accelerationMultiplier = 1.0f - Mathf.Clamp01(Vector3.Dot(direction.normalized, normal));
constantAcceleration *= accelerationMultiplier;
relativeAcceleration *= accelerationMultiplier;
proportionalAcceleration *= accelerationMultiplier;
return GetAcceleration(velocity, direction, axis, targetSpeed, clampSpeed, inputAcceleration, constantAcceleration, relativeAcceleration, proportionalAcceleration, deltaTime);
}
public static Vector3 GetFriction(Vector3 velocity, Vector3 direction, Vector3 axis, float maxSpeed, bool clampSpeed, float constantFriction, float relativeFriction, float proportionalFriction, float deltaTime)
{
if (constantFriction <= 0.0f && relativeFriction <= 0.0f && proportionalFriction <= 0.0f)
return Vector3.zero;
if (direction.IsZero() == true)
return Vector3.zero;
float baseSpeed = new Vector3(velocity.x * axis.x, velocity.y * axis.y, velocity.z * axis.z).magnitude;
Vector3 baseDirection = new Vector3(direction.x * axis.x, direction.y * axis.y, direction.z * axis.z).normalized;
if (constantFriction < 0.0f) { constantFriction = 0.0f; }
if (relativeFriction < 0.0f) { relativeFriction = 0.0f; }
if (proportionalFriction < 0.0f) { proportionalFriction = 0.0f; }
float speedDrop = (constantFriction + maxSpeed * relativeFriction + baseSpeed * proportionalFriction) * deltaTime;
if (speedDrop <= 0.0f)
return Vector3.zero;
if (clampSpeed == true && speedDrop > baseSpeed)
{
speedDrop = baseSpeed;
}
return -baseDirection * speedDrop;
}
public static Vector3 GetFriction(Vector3 velocity, Vector3 direction, Vector3 axis, Vector3 normal, float maxSpeed, bool clampSpeed, float constantFriction, float relativeFriction, float proportionalFriction, float deltaTime)
{
float frictionMultiplier = 1.0f - Mathf.Clamp01(Vector3.Dot(direction.normalized, normal));
constantFriction *= frictionMultiplier;
relativeFriction *= frictionMultiplier;
proportionalFriction *= frictionMultiplier;
return GetFriction(velocity, direction, axis, maxSpeed, clampSpeed, constantFriction, relativeFriction, proportionalFriction, deltaTime);
}
public static Vector3 CombineAccelerationAndFriction(Vector3 velocity, Vector3 acceleration, Vector3 friction)
{
velocity.x = CombineAxis(velocity.x, acceleration.x, friction.x);
velocity.y = CombineAxis(velocity.y, acceleration.y, friction.y);
velocity.z = CombineAxis(velocity.z, acceleration.z, friction.z);
return velocity;
static float CombineAxis(float axisVelocity, float axisAcceleration, float axisFriction)
{
float velocityDelta = axisAcceleration + axisFriction;
if (Mathf.Abs(axisAcceleration) >= Mathf.Abs(axisFriction))
{
axisVelocity += velocityDelta;
}
else
{
if (axisVelocity > 0.0)
{
axisVelocity = Mathf.Max(0.0f, axisVelocity + velocityDelta);
}
else if (axisVelocity < 0.0)
{
axisVelocity = Mathf.Min(axisVelocity + velocityDelta, 0.0f);
}
}
return axisVelocity;
}
}
public static Vector3 AccumulatePenetrationCorrection(Vector3 accumulatedCorrection, Vector3 contactCorrectionDirection, float contactCorrectionMagnitude)
{
float accumulatedCorrectionMagnitude = Vector3.Magnitude(accumulatedCorrection);
Vector3 accumulatedCorrectionDirection = accumulatedCorrectionMagnitude > 0.0000000001f ? accumulatedCorrection / accumulatedCorrectionMagnitude : Vector3.zero;
float deltaCorrectionMagnitude = default;
Vector3 deltaCorrectionDirection = Vector3.Cross(Vector3.Cross(accumulatedCorrectionDirection, contactCorrectionDirection), accumulatedCorrectionDirection).normalized;
float cos = Vector3.Dot(contactCorrectionDirection, accumulatedCorrectionDirection);
float sinSqr = 1.0f - cos * cos;
if (sinSqr > 0.001f)
{
deltaCorrectionMagnitude = (contactCorrectionMagnitude - accumulatedCorrectionMagnitude * cos) / Mathf.Sqrt(sinSqr);
}
else if (contactCorrectionMagnitude > accumulatedCorrectionMagnitude)
{
deltaCorrectionMagnitude = contactCorrectionMagnitude - accumulatedCorrectionMagnitude;
deltaCorrectionDirection = accumulatedCorrectionDirection;
}
accumulatedCorrection += deltaCorrectionDirection * deltaCorrectionMagnitude;
return accumulatedCorrection;
}
}
}