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;