velox/Code/Utils/PhysicsExtensions.cs

108 lines
3.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Sandbox;
using System;
using System.Numerics;
namespace VeloX;
public static class PhysicsExtensions
{
public static Vector3 Transform( this Vector3 value, Quaternion rotation )
{
float x2 = rotation.X + rotation.X;
float y2 = rotation.Y + rotation.Y;
float z2 = rotation.Z + rotation.Z;
float wx2 = rotation.W * x2;
float wy2 = rotation.W * y2;
float wz2 = rotation.W * z2;
float xx2 = rotation.X * x2;
float xy2 = rotation.X * y2;
float xz2 = rotation.X * z2;
float yy2 = rotation.Y * y2;
float yz2 = rotation.Y * z2;
float zz2 = rotation.Z * z2;
return new Vector3(
value.x * (1.0f - yy2 - zz2) + value.y * (xy2 - wz2) + value.z * (xz2 + wy2),
value.x * (xy2 + wz2) + value.y * (1.0f - xx2 - zz2) + value.z * (yz2 - wx2),
value.x * (xz2 - wy2) + value.y * (yz2 + wx2) + value.z * (1.0f - xx2 - yy2)
);
}
/// <summary>
/// Calculates the linear and angular velocities on the center of mass for an offset impulse.
/// </summary>
/// <param name="physObj">The physics object</param>
/// <param name="impulse">The impulse acting on the object in kg*units/s (World frame)</param>
/// <param name="position">The location of the impulse in world coordinates</param>
/// <returns>
/// Vector1: Linear velocity from the impulse (World frame)
/// Vector2: Angular velocity from the impulse (Local frame)
/// </returns>
public static (Vector3 LinearVelocity, Vector3 AngularVelocity) CalculateVelocityOffset( this PhysicsBody physObj, Vector3 impulse, Vector3 position )
{
if ( !physObj.IsValid() || !physObj.MotionEnabled )
return (Vector3.Zero, Vector3.Zero);
Vector3 linearVelocity = impulse / physObj.Mass;
Vector3 r = position - physObj.MassCenter;
// Calculate torque impulse in world frame: τ = r × impulse
Vector3 torqueImpulseWorld = r.Cross( impulse );
Rotation worldToLocal = physObj.Rotation.Inverse;
Vector3 torqueImpulseLocal = torqueImpulseWorld.Transform( worldToLocal );
var InverseInertiaDiagLocal = physObj.Inertia.Inverse;
// Compute angular velocity change in rad/s (local frame)
Vector3 angularVelocityRadLocal = new(
InverseInertiaDiagLocal.x * torqueImpulseLocal.x,
InverseInertiaDiagLocal.y * torqueImpulseLocal.y,
InverseInertiaDiagLocal.z * torqueImpulseLocal.z
);
const float radToDeg = 180f / MathF.PI;
Vector3 angularVelocityDegLocal = angularVelocityRadLocal * radToDeg;
return (linearVelocity, angularVelocityDegLocal);
}
/// <summary>
/// Calculates the linear and angular impulses on the object's center of mass for an offset impulse.
/// </summary>
/// <param name="physObj">The physics object</param>
/// <param name="impulse">The impulse acting on the object in kg*units/s (World frame)</param>
/// <param name="position">The location of the impulse in world coordinates</param>
/// <returns>
/// Vector1: Linear impulse on center of mass (World frame)
/// Vector2: Angular impulse on center of mass (Local frame)
/// </returns>
public static (Vector3 LinearImpulse, Vector3 AngularImpulse) CalculateForceOffset(
this PhysicsBody physObj,
Vector3 impulse,
Vector3 position )
{
if ( !physObj.IsValid() || !physObj.MotionEnabled )
{
return (Vector3.Zero, Vector3.Zero);
}
// 1. Linear impulse is the same as the input impulse (conservation of momentum)
Vector3 linearImpulse = impulse;
// 2. Calculate angular impulse (torque) from the offset force
// τ = r * F (cross product of position relative to COM and force)
Vector3 centerOfMass = physObj.MassCenter;
Vector3 relativePosition = position - centerOfMass;
Vector3 worldAngularImpulse = relativePosition.Cross( impulse );
// Convert angular impulse to local space (since we'll use it with LocalInertia)
Rotation bodyRotation = physObj.Transform.Rotation;
Vector3 localAngularImpulse = bodyRotation.Inverse * worldAngularImpulse;
return (linearImpulse, localAngularImpulse);
}
}