From b4db74e441c2bf21db422aef4f83d9d1e6c75a5a Mon Sep 17 00:00:00 2001 From: freezy Date: Wed, 29 Jan 2025 14:02:29 +0100 Subject: [PATCH] physics: Fix LUTs based on velocities. --- .../Extensions/MathExtensions.cs | 26 +++++++++++ .../VisualPinball.Unity/Game/PhysicsState.cs | 7 +++ .../VPT/Ball/BallCollider.cs | 44 ++++++++----------- .../VPT/ColliderComponent.cs | 8 +++- .../VPT/PhysicsMaterialAsset.cs | 13 ++++-- 5 files changed, 67 insertions(+), 31 deletions(-) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Extensions/MathExtensions.cs b/VisualPinball.Unity/VisualPinball.Unity/Extensions/MathExtensions.cs index d4b0fbdc94..181cadf424 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Extensions/MathExtensions.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Extensions/MathExtensions.cs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +using System.ComponentModel; +using Unity.Collections; using Unity.Mathematics; using UnityEngine; using VisualPinball.Engine.Common; @@ -190,6 +192,30 @@ public static float3 ToEuler(this quaternion quaternion) { return (float3) res; } + /// + /// Interpolates a value from a LUT. + /// + /// The LUT + /// Smallest X-value of the LUT + /// Largest X-value of the LUT + /// X-position + /// Interpolated Y-value + public static float InterpolateLUT(this FixedList512Bytes 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); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs index 639eb8328d..c087698d74 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsState.cs @@ -81,7 +81,14 @@ internal struct PhysicsState /// internal NativeParallelHashMap KinematicTransforms; + /// + /// The LUT of elasticity over velocity for all colliders where activated. Goes from 0 to 64 velocity units. + /// internal NativeParallelHashMap> ElasticityOverVelocityLUTs; + + /// + /// The LUT of friction over velocity for all colliders where activated. Goes from 0 to 128 velocity units. + /// internal NativeParallelHashMap> FrictionOverVelocityLUTs; /// diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallCollider.cs index a882152135..ce8bb2ca8c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Ball/BallCollider.cs @@ -15,6 +15,7 @@ // along with this program. If not, see . using Unity.Mathematics; +using UnityEngine; using VisualPinball.Engine.Common; namespace VisualPinball.Unity @@ -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) @@ -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 diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs index 7714692eee..a5df359cc8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ColliderComponent.cs @@ -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; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/PhysicsMaterialAsset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/PhysicsMaterialAsset.cs index 4ee6d518b4..8b1c7ca3d5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/PhysicsMaterialAsset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/PhysicsMaterialAsset.cs @@ -49,14 +49,21 @@ public class PhysicsMaterialAsset : ScriptableObject // public AnimationCurve FrictionOverAngularMomentum; public float ScatterAngle; + /// + /// Returns a lookup-table of 128 values.
+ /// + /// The range goes from 0 to 64 velocity units, meaning an entry covers 0.5 units. + ///
+ /// Lookup table + /// public FixedList512Bytes GetElasticityOverVelocityLUT() { var lut = new FixedList512Bytes(); 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; } @@ -67,7 +74,7 @@ public FixedList512Bytes 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;