Add friction and rolling resistance to sustained ball contacts#571
Add friction and rolling resistance to sustained ball contacts#571freezy wants to merge 6 commits into
Conversation
Exercise sustained contact through the public handler using production gravity and contact-step ordering. Cover analytic rolling transitions, incline acceleration, invariance, slip classification, and energy behavior. The legacy solver fails seven of nine cases, establishing the Phase 1 regression baseline.
Separate normal correction from steady support and express Coulomb changes as impulses with gravity consistently treated as acceleration. Select static or kinetic friction from tangential slip, validate ball mass and radius at creation boundaries, and add flipper, rubber, separation, and invalid-state regressions.
Add dimensionless Crr values to runtime materials, assets, and supported collider overrides while keeping unsupported specialized contacts inert. Expose authoring controls, preserve values through .vpe package serializers, and verify old JSON defaults plus nonzero round trips. Existing and imported tables retain a zero default.
Apply a coupled linear and angular impulse once per ball after sustained Coulomb contacts have been solved. Use steady support load and relative surface motion so no-slip rolling decelerates analytically without affecting impacts. Select a deterministic eligible representative across seam, corner, and mixed material contacts to avoid multiplying gravity support. Keep flipper and ball-to-ball rolling resistance disabled until both bodies can be coupled. Cover analytic level rolling, no-slip preservation, contact aggregation, and mixed-contact eligibility with deterministic EditMode tests.
Extend deterministic sustained-contact coverage across incline losses, near-stop clamping, monotonic energy, zero-coefficient equivalence, and relative kinematic surface motion. Prove rolling resistance remains independent of impact response, mass, radius, and substep partitioning. Isolate incline support loading with a differential zero-coefficient comparison that pins the cosine term.
Explain how Coulomb friction and rolling resistance differ and document where the new authoring value is supported. Provide fixture setup, inverse formulas, data-sheet fields, fit and validation criteria, and rollout rules. Keep all defaults at zero until measured physical evidence and an HDRP table smoke support a nonzero preset. Record runtime limitations and the sustained-contact correction in the changelog.
Greptile SummaryThis PR refactors sustained ball-contact friction into an impulse-based solve and adds an opt-in rolling-resistance coefficient (
Confidence Score: 4/5Safe to merge; rolling resistance defaults to zero so existing tables see no behavioral change, and the friction/support-impulse refactor is validated by 45 focused unit tests. The physics derivations for the coupled linear/angular rolling-resistance impulse are sound and match the solid-sphere effective-mass formula. The SurfaceAcceleration division-by-mass fix is correct and well-tested. The one logic concern is that the absolute PhysicsConstants.Precision (0.01) tolerance used to gate rolling resistance on angularRollingSpeed vs centerSpeed can disagree with the tighter Precision-squared slip-speed gate already applied, meaning rolling resistance may silently skip for a legitimately rolling ball at low speeds. ContactPhysics.cs (ball-to-ball branch dead code) and BallCollider.cs (TryGetRollingState absolute tolerance for angularRollingSpeed). Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant PC as PhysicsCycle
participant CP as ContactPhysics
participant BC as BallCollider
participant BS as BallState
PC->>CP: Update(contact, ball, state, hitTime)
CP->>BC: HandleStaticContact(ball, collEvent, material, dTime, gravity, colliderVelocity)
BC->>BC: SolveNormalContact → correctionImpulse + supportImpulse
BC->>BS: "ball.Velocity += correction"
BC->>BC: ApplyCoulombContactImpulse(supportImpulse, friction)
BC->>BS: ball.ApplySurfaceImpulse(frictionImpulse)
BC-->>CP: return supportImpulse
CP->>CP: Populate RollingContactData (IsContact, Crr, supportImpulse, normal, colliderVel)
Note over CP: repeat for each contact / ball
PC->>CP: ApplyRollingResistance(contacts, state)
CP->>CP: SelectRollingContact(contacts[first..end], ball)
loop per ball's contacts
CP->>BC: IsRollingContact(ball, candidate) — pure-rolling guard
end
CP->>BC: ApplyRollingResistance(ball, selectedContact)
BC->>BC: TryGetRollingState → tangentDir, rollingAxis, speeds, effectiveMass
BC->>BC: "deltaSpeed = min(Crr·Jn / effectiveMass, centerSpeed, angularRollingSpeed)"
BC->>BS: "ball.Velocity -= deltaSpeed · tangentDirection"
BC->>BS: "ball.AngularMomentum -= (I/R)·deltaSpeed · rollingAxis"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant PC as PhysicsCycle
participant CP as ContactPhysics
participant BC as BallCollider
participant BS as BallState
PC->>CP: Update(contact, ball, state, hitTime)
CP->>BC: HandleStaticContact(ball, collEvent, material, dTime, gravity, colliderVelocity)
BC->>BC: SolveNormalContact → correctionImpulse + supportImpulse
BC->>BS: "ball.Velocity += correction"
BC->>BC: ApplyCoulombContactImpulse(supportImpulse, friction)
BC->>BS: ball.ApplySurfaceImpulse(frictionImpulse)
BC-->>CP: return supportImpulse
CP->>CP: Populate RollingContactData (IsContact, Crr, supportImpulse, normal, colliderVel)
Note over CP: repeat for each contact / ball
PC->>CP: ApplyRollingResistance(contacts, state)
CP->>CP: SelectRollingContact(contacts[first..end], ball)
loop per ball's contacts
CP->>BC: IsRollingContact(ball, candidate) — pure-rolling guard
end
CP->>BC: ApplyRollingResistance(ball, selectedContact)
BC->>BC: TryGetRollingState → tangentDir, rollingAxis, speeds, effectiveMass
BC->>BC: "deltaSpeed = min(Crr·Jn / effectiveMass, centerSpeed, angularRollingSpeed)"
BC->>BS: "ball.Velocity -= deltaSpeed · tangentDirection"
BC->>BS: "ball.AngularMomentum -= (I/R)·deltaSpeed · rollingAxis"
Reviews (1): Last reviewed commit: "doc: document rolling resistance calibra..." | Re-trigger Greptile |
This PR refines sustained ball contact friction into a consistent impulse-based solve and adds opt-in rolling resistance for table and ball materials. Fixes #363.
Changes
Testing
BallFrictionTests,BallContactRegressionTests,PhysicsMaterialTests, andBallRollingResistanceTests.Notes
Rolling resistance defaults to zero, so existing tables retain their current behavior until explicitly tuned. Impact collisions, flipper contacts, and ball-to-ball contacts remain outside this first implementation. Physical material presets remain unchanged pending table-specific measurement and calibration.