Skip to content

Commit

Permalink
physics: Fix LUTs based on velocities.
Browse files Browse the repository at this point in the history
  • Loading branch information
freezy committed Jan 30, 2025
1 parent c8feddc commit c49fc79
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

using System.ComponentModel;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
using VisualPinball.Engine.Common;
Expand Down Expand Up @@ -190,6 +192,30 @@ public static float3 ToEuler(this quaternion quaternion) {
return (float3) res;
}

/// <summary>
/// Interpolates a value from a LUT.
/// </summary>
/// <param name="lut">The LUT</param>
/// <param name="min">Smallest X-value of the LUT</param>
/// <param name="max">Largest X-value of the LUT</param>
/// <param name="x">X-position</param>
/// <returns>Interpolated Y-value</returns>
public static float InterpolateLUT(this FixedList512Bytes<float> lut, float min, float max, float x)
{
var p = math.unlerp(min, max, x);
var i = p * lut.Length;
var i1 = (int) math.floor(i);
var i2 = math.clamp((int) i1 + 1, 0, lut.Length - 1);
if (i1 == i2) {
return lut[i1];
}

var v1 = lut[i1];
var v2 = lut[i2];

return math.lerp(v1, v2, i - i1);
}

public static string ToDebugString(this float4x4 m) => $"{((Matrix4x4)m).ToString()}\nt: {m.GetTranslation()}\nr: {math.degrees(m.GetRotationVector())}\ns: {m.GetScale()}";

public static string ToDebugString(this Matrix4x4 m) => ToDebugString((float4x4)m);
Expand Down
7 changes: 7 additions & 0 deletions VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,14 @@ internal struct PhysicsState
/// </remarks>
internal NativeParallelHashMap<int, float4x4> KinematicTransforms;

/// <summary>
/// The LUT of elasticity over velocity for all colliders where activated. Goes from 0 to 64 velocity units.
/// </summary>
internal NativeParallelHashMap<int, FixedList512Bytes<float>> ElasticityOverVelocityLUTs;

/// <summary>
/// The LUT of friction over velocity for all colliders where activated. Goes from 0 to 128 velocity units.
/// </summary>
internal NativeParallelHashMap<int, FixedList512Bytes<float>> FrictionOverVelocityLUTs;

/// <summary>
Expand Down
44 changes: 18 additions & 26 deletions VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallCollider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

using Unity.Mathematics;
using UnityEngine;
using VisualPinball.Engine.Common;

namespace VisualPinball.Unity
Expand Down Expand Up @@ -59,23 +60,19 @@ public static void Collide3DWall(ref BallState ball, in PhysicsMaterialData mate
// magnitude of the impulse which is just sufficient to keep the ball from
// penetrating the wall (needed for friction computations)
var reactionImpulse = ball.Mass * math.abs(dot);
float elasticity;
if (material.UseElasticityOverVelocity)
{
float elasticity = 0;
if (material.UseElasticityOverVelocity) {
// nFozzy used the xy velocity, but using the dot velocity seems more "physical".
//var velocity = math.sqrt(ball.Velocity.x * ball.Velocity.x + ball.Velocity.y * ball.Velocity.y + ball.Velocity.z * ball.Velocity.z);

var velocity = dot;
var lut = collEvent.IsKinematic
? state.ElasticityOverVelocityLUTs[state.KinematicColliders.GetItemId(collEvent.ColliderId)]
: state.ElasticityOverVelocityLUTs[state.Colliders.GetItemId(collEvent.ColliderId)];
var elasticityLow = lut[math.clamp((int)math.trunc(velocity), 0, 99)];
var elasticityHigh = lut[math.clamp((int)math.trunc(velocity+1), 0, 99)];
elasticity = elasticityLow * (velocity - math.trunc(velocity)) +
elasticityHigh * (1 - (velocity - math.trunc(velocity)));
}
else
var velocity = math.abs(dot);
var colliders = collEvent.IsKinematic ? state.KinematicColliders : state.Colliders;
var itemId = colliders.GetItemId(collEvent.ColliderId);
var lut = state.ElasticityOverVelocityLUTs[itemId];
elasticity = lut.InterpolateLUT(0, 63f, velocity);
} else {
elasticity = Math.ElasticityWithFalloff(material.Elasticity, material.ElasticityFalloff, dot);
}

dot *= -(1.0f + elasticity);
ball.Velocity += hitNormal * dot; // apply collision impulse (along normal, so no torque)

Expand All @@ -96,19 +93,14 @@ public static void Collide3DWall(ref BallState ball, in PhysicsMaterialData mate
// Cupiiis Friction over Velocity LUT-Code
// get friction based on normal Velocity
float friction;
if (material.UseFrictionOverVelocity)
{
if (material.UseFrictionOverVelocity) {
var normalVelocity = math.dot(ball.Velocity, hitNormal);
var lut = collEvent.IsKinematic
? state.FrictionOverVelocityLUTs[state.KinematicColliders.GetItemId(collEvent.ColliderId)]
: state.FrictionOverVelocityLUTs[state.Colliders.GetItemId(collEvent.ColliderId)];
var frictionLow = lut[math.clamp((int)math.trunc(normalVelocity), 0, 99)];
var frictionHigh = lut[math.clamp((int)math.trunc(normalVelocity + 1), 0, 99)];
friction = frictionLow * (normalVelocity - math.trunc(normalVelocity)) +
frictionHigh * (1 - (normalVelocity - math.trunc(normalVelocity)));
}
else
{
var colliders = collEvent.IsKinematic ? state.KinematicColliders : state.Colliders;
var itemId = colliders.GetItemId(collEvent.ColliderId);
var lut = state.FrictionOverVelocityLUTs[itemId];
friction = lut.InterpolateLUT(0, 127f, normalVelocity);

} else {
friction = material.Friction;
}
// End of Cupiiis Friction over Velocity LUT-Code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,15 @@ internal PhysicsMaterialData GetPhysicsMaterialData(
var materialData = GetPhysicsMaterialData();
if (!PhysicsOverwrite && PhysicsMaterial != null) {
if (PhysicsMaterial.UseElasticityOverVelocity) {
elasticityOverVelocityLUTs.Add(MainComponent.GetInstanceID(), PhysicsMaterial.GetElasticityOverVelocityLUT());
if (!elasticityOverVelocityLUTs.ContainsKey(gameObject.GetInstanceID())) {
elasticityOverVelocityLUTs.Add(gameObject.GetInstanceID(), PhysicsMaterial.GetElasticityOverVelocityLUT());
}
materialData.UseElasticityOverVelocity = true;
}
if (PhysicsMaterial.UseFrictionOverVelocity) {
frictionOverVelocityLUTs.Add(MainComponent.GetInstanceID(), PhysicsMaterial.GetFrictionOverVelocityLUT());
if (!frictionOverVelocityLUTs.ContainsKey(gameObject.GetInstanceID())) {
frictionOverVelocityLUTs.Add(gameObject.GetInstanceID(), PhysicsMaterial.GetFrictionOverVelocityLUT());
}
materialData.UseFrictionOverVelocity = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,21 @@ public class PhysicsMaterialAsset : ScriptableObject
// public AnimationCurve FrictionOverAngularMomentum;
public float ScatterAngle;

/// <summary>
/// Returns a lookup-table of 128 values. <br/>
///
/// The range goes from 0 to 64 velocity units, meaning an entry covers 0.5 units.
/// </summary>
/// <returns>Lookup table</returns>
/// <exception cref="InvalidOperationException"></exception>
public FixedList512Bytes<float> GetElasticityOverVelocityLUT()
{
var lut = new FixedList512Bytes<float>();
if (ElasticityOverVelocity.keys.Length == 0) {
throw new InvalidOperationException("Curve ElasticityOverVelocity is empty.");
}
for (var i = 0; i < 100; i++) {
lut.Add(ElasticityOverVelocity.Evaluate(i));
for (var i = 0; i < 127; i++) {
lut.Add(ElasticityOverVelocity.Evaluate(i / 2f));
}
return lut;
}
Expand All @@ -67,7 +74,7 @@ public FixedList512Bytes<float> GetFrictionOverVelocityLUT()
if (FrictionOverVelocity.keys.Length == 0) {
throw new InvalidOperationException("Curve ElasticityOverVelocity is empty.");
}
for (var i = 0; i < 100; i++) {
for (var i = 0; i < 127; i++) {
lut.Add(FrictionOverVelocity.Evaluate(i));
}
return lut;
Expand Down

0 comments on commit c49fc79

Please sign in to comment.