226 lines
8.2 KiB
C#
226 lines
8.2 KiB
C#
using System;
|
||
using UnityEngine;
|
||
using WaveHarmonic.Crest;
|
||
|
||
[ExecuteInEditMode]
|
||
[RequireComponent(typeof(Rigidbody))]
|
||
public class Buoyancy : MonoBehaviour
|
||
{
|
||
// public WaterSurface targetSurface;
|
||
public WaterRenderer _water;
|
||
|
||
public bool includeDeformation = true;
|
||
|
||
[Tooltip("Approxative radius of object for buoyancy.")]
|
||
public float sphereRadiusApproximation = 0.25f;
|
||
|
||
[Tooltip("Specifies the multiplier for the movement induced by other deformation (waves, swell... etc).")]
|
||
public float waveForceMultiplier = 1f;
|
||
|
||
[Tooltip("Specifies the multiplier for the movement induced by the current of the water surface.")]
|
||
public float currentSpeedMultiplier = 1f;
|
||
|
||
[Tooltip("Specifies the multiplier for the drag forces induced by the viscosity of the mediums.")]
|
||
public float dragMultiplier = 1f;
|
||
|
||
public float defaultRigidbodyDrag = 0.1f;
|
||
|
||
public float underwaterRigidbodyAngularDrag = 1f;
|
||
|
||
public float overwaterRigidbodyAngularDrag = 0.05f;
|
||
|
||
[Tooltip("Specifies the value for surface tension. A high value will stop the object bouncing faster on water.")]
|
||
public float surfaceTensionDamping = 10f;
|
||
|
||
[Tooltip("When enabled, the net force is applied with a random offset to create an angular velocity.")]
|
||
public bool applyForceWithRandomOffset;
|
||
|
||
[Tooltip(
|
||
"When enabled, a bunch of gizmos will show showing in blue, the position of the sampling for normals, in magenta the computed normal, in green the direction of the deformation force, in red the direction of the current force, and in a yellow sphere, the approximation volume the buoyancy calculations.")]
|
||
public bool drawDebug;
|
||
|
||
private Vector3 currentDirection;
|
||
|
||
|
||
private Vector3 waterPosition;
|
||
|
||
private Vector3 normal;
|
||
|
||
private Vector3 deformationDirection;
|
||
|
||
private Rigidbody rigidbodyComponent;
|
||
|
||
private float h;
|
||
|
||
private float hNormalized;
|
||
|
||
private bool _isEnabled = true;
|
||
|
||
readonly SampleFlowHelper _SampleFlowHelper = new();
|
||
|
||
private void Start()
|
||
{
|
||
rigidbodyComponent = GetComponent<Rigidbody>();
|
||
rigidbodyComponent.useGravity = false;
|
||
rigidbodyComponent.linearDamping = defaultRigidbodyDrag;
|
||
if (_water == null)
|
||
{
|
||
Debug.LogWarning(
|
||
"The variable '_water' needs a valid Water Surface to be assigned for the script to work properly.");
|
||
}
|
||
}
|
||
|
||
public void EnablePhysics(bool set)
|
||
{
|
||
_isEnabled = set;
|
||
}
|
||
|
||
private void FixedUpdate()
|
||
{
|
||
if (!_isEnabled)
|
||
{
|
||
h = 0f;
|
||
hNormalized = 0f;
|
||
rigidbodyComponent.useGravity = true;
|
||
return;
|
||
}
|
||
|
||
if (_water == null)
|
||
return;
|
||
|
||
rigidbodyComponent.useGravity = false;
|
||
|
||
// Cache frequently used values
|
||
Vector3 pos = transform.position;
|
||
Vector3 velocity = rigidbodyComponent.linearVelocity;
|
||
float radius = sphereRadiusApproximation;
|
||
float diameter = 2f * radius;
|
||
|
||
// 1) Sample water surface at current position
|
||
FetchWaterSurfaceData(pos, out waterPosition, out normal, out currentDirection);
|
||
|
||
// Horizontal “wave push” direction derived from surface normal
|
||
deformationDirection = Vector3.ProjectOnPlane(normal, Vector3.up);
|
||
|
||
// 2) Submersion depth of the approximated sphere (0..2R)
|
||
float bottomY = pos.y - radius;
|
||
h = Mathf.Clamp(waterPosition.y - bottomY, 0f, diameter);
|
||
hNormalized = (diameter > 0f) ? (h / diameter) : 0f;
|
||
|
||
// 3) Submerged volume (spherical cap) V(h) = πh²/3 * (3R - h)
|
||
float submergedVolume = MathF.PI * h * h / 3f * (3f * radius - h);
|
||
|
||
// 4) Angular damping transitions from air to water
|
||
rigidbodyComponent.angularDamping = Mathf.Lerp(
|
||
overwaterRigidbodyAngularDrag,
|
||
underwaterRigidbodyAngularDrag,
|
||
hNormalized
|
||
);
|
||
|
||
// 5) Forces (kept identical to your original math/units)
|
||
Vector3 gravityAcceleration = Physics.gravity;
|
||
|
||
// NOTE: this is kept as-is (original mixes accel & force then uses Acceleration mode)
|
||
Vector3 weightVector = rigidbodyComponent.mass * gravityAcceleration;
|
||
Vector3 gravityTerm = Vector3.Lerp(gravityAcceleration, weightVector, hNormalized);
|
||
|
||
// Physical constants (as in original)
|
||
const float rhoWater = 997f; // kg/m^3
|
||
const float rhoAir = 0.001293f; // kg/m^3
|
||
const float muAir = 0.0000181f; // Pa·s
|
||
const float muWater = 0.001f; // Pa·s
|
||
const float dragCoefficient = 0.47f;
|
||
|
||
// Buoyancy: -ρ * V * g
|
||
Vector3 buoyancyTerm = (-rhoWater) * submergedVolume * gravityAcceleration;
|
||
|
||
// Linear viscous drag (Stokes-like): 6πRμ(-v) -> blend air/water by submersion
|
||
Vector3 dragAir = MathF.PI * 6f * radius * muAir * (-velocity);
|
||
Vector3 dragWater = MathF.PI * 6f * radius * muWater * (-velocity);
|
||
Vector3 viscousDragTerm = Vector3.Lerp(dragAir, dragWater, hNormalized) * dragMultiplier;
|
||
|
||
// Net force (still applied as Acceleration)
|
||
Vector3 netAcceleration = gravityTerm + buoyancyTerm + viscousDragTerm;
|
||
|
||
// Optional random offset to induce angular velocity
|
||
Vector3 randomOffset = Vector3.zero;
|
||
if (applyForceWithRandomOffset)
|
||
{
|
||
randomOffset = new Vector3(
|
||
UnityEngine.Random.Range(-1f, 1f),
|
||
UnityEngine.Random.Range(-1f, 1f),
|
||
UnityEngine.Random.Range(-1f, 1f)
|
||
) * (radius / 5f);
|
||
}
|
||
|
||
rigidbodyComponent.AddForceAtPosition(
|
||
netAcceleration,
|
||
pos + randomOffset,
|
||
ForceMode.Acceleration
|
||
);
|
||
|
||
// 6) Extra forces only around the surface transition (0<hN<1)
|
||
if (hNormalized > 0f && hNormalized < 1f)
|
||
{
|
||
Vector3 gravityDir = gravityAcceleration.normalized;
|
||
|
||
// Surface tension damping: cancels velocity along gravity direction
|
||
Vector3 verticalVelocity = Vector3.Dot(velocity, gravityDir) * gravityDir;
|
||
Vector3 surfaceTensionAccel = -verticalVelocity * surfaceTensionDamping;
|
||
|
||
rigidbodyComponent.AddForce(surfaceTensionAccel, ForceMode.Acceleration);
|
||
rigidbodyComponent.AddForce(deformationDirection * waveForceMultiplier, ForceMode.Acceleration);
|
||
rigidbodyComponent.AddForce(currentDirection * currentSpeedMultiplier, ForceMode.Acceleration);
|
||
}
|
||
|
||
// 7) Terminal velocity clamp (same formula, just named)
|
||
float area = MathF.PI * radius * radius;
|
||
float g = -gravityAcceleration.y; // positive value
|
||
|
||
float terminalVelocityInAir = Mathf.Sqrt(2f * rigidbodyComponent.mass * g / (rhoAir * area * dragCoefficient));
|
||
float terminalVelocityInWater =
|
||
Mathf.Sqrt(2f * rigidbodyComponent.mass * g / (rhoWater * area * dragCoefficient));
|
||
float terminalVelocity = Mathf.Lerp(terminalVelocityInAir, terminalVelocityInWater, hNormalized);
|
||
|
||
float speed = rigidbodyComponent.linearVelocity.magnitude;
|
||
if (speed > terminalVelocity && speed > 1e-6f)
|
||
{
|
||
rigidbodyComponent.linearVelocity = rigidbodyComponent.linearVelocity / speed * terminalVelocity;
|
||
}
|
||
}
|
||
|
||
private Vector3 FetchWaterSurfaceData(Vector3 point, out Vector3 positionWS, out Vector3 normalWS,
|
||
out Vector3 currentDirectionWS)
|
||
{
|
||
currentDirectionWS = Vector3.zero;
|
||
positionWS = Vector3.zero;
|
||
normalWS = Vector3.up;
|
||
return point;
|
||
}
|
||
|
||
public Vector3 GetCurrentWaterPosition()
|
||
{
|
||
return waterPosition;
|
||
}
|
||
|
||
|
||
public float GetNormalizedHeightOfSphereBelowSurface()
|
||
{
|
||
return hNormalized;
|
||
}
|
||
|
||
private void OnDrawGizmosSelected()
|
||
{
|
||
if (drawDebug)
|
||
{
|
||
Gizmos.color = Color.magenta;
|
||
Gizmos.DrawLine(base.transform.position, base.transform.position + normal);
|
||
Gizmos.color = Color.green;
|
||
Gizmos.DrawLine(base.transform.position, base.transform.position + deformationDirection * 10f);
|
||
Gizmos.color = Color.red;
|
||
Gizmos.DrawLine(base.transform.position, base.transform.position + currentDirection);
|
||
Gizmos.color = Color.yellow;
|
||
Gizmos.DrawSphere(base.transform.position, sphereRadiusApproximation);
|
||
}
|
||
}
|
||
} |