< Summary

Information
Class: Gravitas.CollisionHandling.CollisionResponse2D
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Response/2D/CollisionResponse2D.cs
Line coverage
100%
Covered lines: 646
Uncovered lines: 0
Coverable lines: 646
Total lines: 948
Line coverage: 100%
Branch coverage
100%
Covered branches: 192
Total branches: 192
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Response/2D/CollisionResponse2D.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionResponse2D.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2026–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using FixedMathSharp;
 9using FixedMathSharp.Geometry;
 10using Gravitas.Materials;
 11using System.Runtime.CompilerServices;
 12
 13namespace Gravitas.CollisionHandling;
 14
 15/// <summary>
 16/// Deterministic pure 2D manifold contact response.
 17/// </summary>
 18public static class CollisionResponse2D
 19{
 20    /// <summary>Penetration tolerated before positional correction is applied.</summary>
 121    public static readonly Fixed64 PenetrationSlop = (Fixed64)0.01f;
 22
 23    /// <summary>Fraction of excess penetration corrected by the solver.</summary>
 124    public static readonly Fixed64 PenetrationCorrectionPercent = Fixed64.One;
 25
 26    internal static void Resolve(CollisionPair2D pair) =>
 45427        Resolve(pair, applyCachedImpulse: true, applyPositionCorrection: true);
 28
 29    internal static void Resolve(
 30        CollisionPair2D pair,
 31        bool applyCachedImpulse,
 32        bool applyPositionCorrection)
 33    {
 99534        if (!TryCreateBodyPair(pair, out ResponseBody2D bodyA, out ResponseBody2D bodyB))
 535            return;
 36
 99037        ContactAnchor2D responseCenterA = bodyA.Body?.GetCenterOfMassAnchor() ?? default;
 99038        ContactAnchor2D responseCenterB = bodyB.Body?.GetCenterOfMassAnchor() ?? default;
 99039        SolverContactBuffer2D contacts = BuildContactBuffer(
 99040            pair,
 99041            bodyA,
 99042            bodyB,
 99043            responseCenterA,
 99044            responseCenterB);
 99045        if (contacts.Count == 0)
 146            return;
 47
 98948        Vector2d responsePositionA = bodyA.Body?.Position ?? Vector2d.Zero;
 98949        Vector2d responsePositionB = bodyB.Body?.Position ?? Vector2d.Zero;
 98950        byte failedResponseMask = 0;
 98951        Fixed64 contactShare = Fixed64.One / (Fixed64)contacts.Count;
 98952        if (applyPositionCorrection)
 53        {
 214054            for (int i = 0; i < contacts.Count; i++)
 53955                ApplyPositionCorrection(contacts.GetContact(i), contactShare);
 56        }
 57
 98958        if (applyCachedImpulse)
 59        {
 53660            bool rebuildContacts = false;
 216261            for (int i = 0; i < contacts.Count; i++)
 62            {
 54563                SolverContact2D contact = contacts.GetContact(i);
 54564                if (TryApplyCachedImpulse(
 54565                        pair,
 54566                        contact,
 54567                        responsePositionA,
 54568                        responsePositionB))
 69                {
 70                    continue;
 71                }
 72
 273                pair.RemoveWarmStartImpulse(contact.ContactId);
 274                rebuildContacts = true;
 75            }
 76
 53677            if (rebuildContacts)
 78            {
 279                contacts = BuildContactBuffer(
 280                    pair,
 281                    bodyA,
 282                    bodyB,
 283                    responseCenterA,
 284                    responseCenterB);
 85            }
 86        }
 87
 98988        Vector2d normalLinearVelocityA =
 98989            ResolveLinearVelocity(bodyA.Body);
 98990        Fixed64 normalAngularVelocityA =
 98991            ResolveAngularVelocity(bodyA.Body);
 98992        Vector2d normalLinearVelocityB =
 98993            ResolveLinearVelocity(bodyB.Body);
 98994        Fixed64 normalAngularVelocityB =
 98995            ResolveAngularVelocity(bodyB.Body);
 98996        Fixed64 restitutionVelocityThreshold = pair.ColliderA.Context.Settings.RestitutionVelocityThreshold;
 399497        for (int i = 0; i < contacts.Count; i++)
 98        {
 100899            SolverContact2D contact = contacts.GetContact(i);
 1008100            SolidBody2D? contactBodyA = contact.A.Body;
 1008101            SolidBody2D? contactBodyB = contact.B.Body;
 102            bool normalResolved;
 1008103            ContactNormalImpulseResult2D normalResult = default;
 1008104            if (contact.RelativeA.IsExact || contact.RelativeB.IsExact)
 105            {
 19106                normalResolved = TryCalculateExactNormalResult(
 19107                    pair,
 19108                    contact,
 19109                    responsePositionA,
 19110                    responsePositionB,
 19111                    normalLinearVelocityA,
 19112                    normalAngularVelocityA,
 19113                    normalLinearVelocityB,
 19114                    normalAngularVelocityB,
 19115                    restitutionVelocityThreshold,
 19116                    contactShare,
 19117                    out normalResult);
 118            }
 119            else
 120            {
 989121                normalResolved = CanUseCompactAxisResponse(
 989122                        contact,
 989123                        contact.Normal)
 989124                    && ContactNormalImpulse2D.TryCalculateAccumulatedDelta(
 989125                        contactBodyA,
 989126                        normalLinearVelocityA,
 989127                        normalAngularVelocityA,
 989128                        contact.RelativeA.Vector,
 989129                        contactBodyB,
 989130                        normalLinearVelocityB,
 989131                        normalAngularVelocityB,
 989132                        contact.RelativeB.Vector,
 989133                        contact.Normal,
 989134                        contact.Restitution,
 989135                        restitutionVelocityThreshold,
 989136                        contact.CachedNormalImpulse,
 989137                        contactShare,
 989138                        contactShare,
 989139                        out normalResult);
 989140                if (!normalResolved)
 141                {
 17142                    normalResolved = TryCalculateExactNormalResult(
 17143                        pair,
 17144                        contact,
 17145                        responsePositionA,
 17146                        responsePositionB,
 17147                        normalLinearVelocityA,
 17148                        normalAngularVelocityA,
 17149                        normalLinearVelocityB,
 17150                        normalAngularVelocityB,
 17151                        restitutionVelocityThreshold,
 17152                        contactShare,
 17153                        out normalResult);
 154                }
 155            }
 1008156            if (!normalResolved)
 157            {
 2158                RejectResponse(pair, contact, i, ref failedResponseMask);
 2159                continue;
 160            }
 1006161            Fixed64 normalImpulse = contact.CachedNormalImpulse + normalResult.ImpulseScalar;
 1006162            contacts.SetNormalImpulse(
 1006163                i,
 1006164                normalImpulse,
 1006165                normalResult);
 166        }
 167
 3994168        for (int i = 0; i < contacts.Count; i++)
 169        {
 1008170            if (HasFailedResponse(failedResponseMask, i))
 171                continue;
 172
 1006173            if (!TryApplyNormalImpulse(
 1006174                    contacts.GetContact(i),
 1006175                    contacts.GetNormalResult(i)))
 176            {
 1177                RejectResponse(
 1178                    pair,
 1179                    contacts.GetContact(i),
 1180                    i,
 1181                    ref failedResponseMask);
 182            }
 183        }
 184
 3994185        for (int i = 0; i < contacts.Count; i++)
 186        {
 1008187            if (HasFailedResponse(failedResponseMask, i))
 188                continue;
 189
 1005190            SolverContact2D contact = contacts.GetContact(i);
 1005191            if (!TrySolveFrictionImpulse(
 1005192                    pair,
 1005193                    contact,
 1005194                    responsePositionA,
 1005195                    responsePositionB,
 1005196                    normalLinearVelocityA,
 1005197                    normalAngularVelocityA,
 1005198                    normalLinearVelocityB,
 1005199                    normalAngularVelocityB,
 1005200                    restitutionVelocityThreshold,
 1005201                    contactShare,
 1005202                    contacts.GetNormalResult(i),
 1005203                    contacts.GetNormalImpulse(i),
 1005204                    out Fixed64 tangentImpulse))
 205            {
 2206                RejectResponse(pair, contact, i, ref failedResponseMask);
 2207                continue;
 208            }
 1003209            contacts.SetTangentImpulse(
 1003210                i,
 1003211                tangentImpulse);
 212        }
 213
 3994214        for (int i = 0; i < contacts.Count; i++)
 215        {
 1008216            if (HasFailedResponse(failedResponseMask, i))
 217                continue;
 218
 1003219            SolverContact2D contact = contacts.GetContact(i);
 1003220            pair.StoreWarmStartImpulse(
 1003221                contact.ContactId,
 1003222                contacts.GetNormalImpulse(i),
 1003223                contacts.GetTangentImpulse(i));
 224        }
 989225    }
 226
 227    private static bool TryCreateBodyPair(
 228        CollisionPair2D pair,
 229        out ResponseBody2D bodyA,
 230        out ResponseBody2D bodyB)
 231    {
 995232        bodyA = default;
 995233        bodyB = default;
 234
 995235        if (pair.ColliderA.IsTrigger || pair.ColliderB.IsTrigger || !pair.Manifold.HasContact)
 3236            return false;
 237
 992238        bodyA = ResponseBody2D.Create(pair.ColliderA);
 992239        bodyB = ResponseBody2D.Create(pair.ColliderB);
 992240        return bodyA.HasSolverMobility || bodyB.HasSolverMobility;
 241    }
 242
 243    private static SolverContactBuffer2D BuildContactBuffer(
 244        CollisionPair2D pair,
 245        ResponseBody2D bodyA,
 246        ResponseBody2D bodyB,
 247        in ContactAnchor2D responseCenterA,
 248        in ContactAnchor2D responseCenterB)
 249    {
 992250        SolverContactBuffer2D contacts = default;
 4008251        for (int i = 0; i < pair.Manifold.Count; i++)
 252        {
 1012253            if (TryCreateContact(
 1012254                    pair,
 1012255                    bodyA,
 1012256                    bodyB,
 1012257                    responseCenterA,
 1012258                    responseCenterB,
 1012259                    i,
 1012260                    out SolverContact2D contact))
 1011261                contacts.Add(contact);
 262        }
 263
 992264        return contacts;
 265    }
 266
 267    private static bool TryCreateContact(
 268        CollisionPair2D pair,
 269        ResponseBody2D bodyA,
 270        ResponseBody2D bodyB,
 271        in ContactAnchor2D responseCenterA,
 272        in ContactAnchor2D responseCenterB,
 273        int contactIndex,
 274        out SolverContact2D contact)
 275    {
 1012276        contact = default;
 1012277        ManifoldContact2D manifoldContact = pair.Manifold[contactIndex];
 1012278        Vector2d normal = ResolveContactNormal(
 1012279            manifoldContact.Normal,
 1012280            pair.ColliderB.Center - pair.ColliderA.Center);
 1012281        if (normal == Vector2d.Zero)
 1282            return false;
 1011283        ContactLever2D relativeA = bodyA.Body == null
 1011284            ? ContactLever2D.Zero
 1011285            : ContactLever2D.Create(
 1011286                manifoldContact.AnchorA,
 1011287                responseCenterA);
 1011288        ContactLever2D relativeB = bodyB.Body == null
 1011289            ? ContactLever2D.Zero
 1011290            : ContactLever2D.Create(
 1011291                manifoldContact.AnchorB,
 1011292                responseCenterB);
 1011293        PhysicsMaterial materialA = manifoldContact.HasMaterialOverride
 1011294            ? manifoldContact.MaterialA
 1011295            : pair.ColliderA.Material;
 1011296        PhysicsMaterial materialB = manifoldContact.HasMaterialOverride
 1011297            ? manifoldContact.MaterialB
 1011298            : pair.ColliderB.Material;
 299
 1011300        Fixed64 cachedNormalImpulse = Fixed64.Zero;
 1011301        Fixed64 cachedTangentImpulse = Fixed64.Zero;
 1011302        if (pair.TryGetWarmStartImpulse(manifoldContact.ContactId, out ContactWarmStartImpulse cached))
 303        {
 717304            cachedNormalImpulse = cached.NormalImpulse;
 717305            cachedTangentImpulse = cached.TangentImpulse;
 306        }
 307
 1011308        contact = new SolverContact2D(
 1011309            contactIndex,
 1011310            manifoldContact.ContactId,
 1011311            bodyA,
 1011312            bodyB,
 1011313            relativeA,
 1011314            relativeB,
 1011315            manifoldContact.Depth,
 1011316            normal,
 1011317            materialA,
 1011318            materialB,
 1011319            cachedNormalImpulse,
 1011320            cachedTangentImpulse);
 1011321        return true;
 322    }
 323
 324    [MethodImpl(MethodImplOptions.NoInlining)]
 325    private static bool TryCalculateExactNormalResult(
 326        CollisionPair2D pair,
 327        SolverContact2D contact,
 328        Vector2d responsePositionA,
 329        Vector2d responsePositionB,
 330        Vector2d linearVelocityA,
 331        Fixed64 angularVelocityA,
 332        Vector2d linearVelocityB,
 333        Fixed64 angularVelocityB,
 334        Fixed64 restitutionVelocityThreshold,
 335        Fixed64 contactShare,
 336        out ContactNormalImpulseResult2D result)
 337    {
 36338        GetExactLevers(
 36339            pair,
 36340            contact,
 36341            responsePositionA,
 36342            responsePositionB,
 36343            out ExactLever3D exactA,
 36344            out ExactLever3D exactB);
 36345        return ContactNormalImpulse2D.TryCalculateAccumulatedDeltaExact(
 36346            contact.A.Body,
 36347            linearVelocityA,
 36348            angularVelocityA,
 36349            exactA,
 36350            contact.B.Body,
 36351            linearVelocityB,
 36352            angularVelocityB,
 36353            exactB,
 36354            contact.Normal,
 36355            contact.Restitution,
 36356            restitutionVelocityThreshold,
 36357            contact.CachedNormalImpulse,
 36358            contactShare,
 36359            contactShare,
 36360            out result);
 361    }
 362
 363    private static void ApplyPositionCorrection(SolverContact2D contact, Fixed64 contactShare)
 364    {
 539365        Fixed64 correctionDepth = contact.Depth - PenetrationSlop;
 539366        if (correctionDepth <= Fixed64.Zero)
 101367            return;
 368
 438369        Fixed64 inverseMassA = contact.A.GetConstrainedInverseMass(contact.Normal);
 438370        Fixed64 inverseMassB = contact.B.GetConstrainedInverseMass(contact.Normal);
 438371        Fixed64 totalInverseMass = inverseMassA + inverseMassB;
 438372        if (totalInverseMass <= Fixed64.Zero)
 1373            return;
 374
 437375        Vector2d correction = contact.Normal
 437376            * (correctionDepth * PenetrationCorrectionPercent * contactShare / totalInverseMass);
 437377        ApplyPositionCorrection(contact.A, -correction * inverseMassA);
 437378        ApplyPositionCorrection(contact.B, correction * inverseMassB);
 437379    }
 380
 381    private static bool TryApplyCachedImpulse(
 382        CollisionPair2D pair,
 383        SolverContact2D contact,
 384        Vector2d responsePositionA,
 385        Vector2d responsePositionB)
 386    {
 545387        if (contact.CachedNormalImpulse == Fixed64.Zero && contact.CachedTangentImpulse == Fixed64.Zero)
 537388            return true;
 389
 8390        return TryApplyContactImpulseCombination(
 8391            pair,
 8392            contact,
 8393            responsePositionA,
 8394            responsePositionB,
 8395            contact.Normal,
 8396            contact.CachedNormalImpulse,
 8397            contact.Tangent,
 8398            contact.CachedTangentImpulse);
 399    }
 400
 401    private static bool TryApplyNormalImpulse(
 402        SolverContact2D contact,
 403        ContactNormalImpulseResult2D result)
 404    {
 1006405        if (result.LinearVelocityDeltaA == Vector2d.Zero
 1006406            && result.AngularVelocityDeltaA == Fixed64.Zero
 1006407            && result.LinearVelocityDeltaB == Vector2d.Zero
 1006408            && result.AngularVelocityDeltaB == Fixed64.Zero)
 409        {
 926410            return true;
 411        }
 412
 80413        return TryApplyVelocityDeltas(
 80414            contact,
 80415            result.LinearVelocityDeltaA,
 80416            result.AngularVelocityDeltaA,
 80417            result.LinearVelocityDeltaB,
 80418            result.AngularVelocityDeltaB);
 419    }
 420
 421    private static void ApplyVelocityDelta(
 422        ResponseBody2D body,
 423        Vector2d linearVelocityDelta,
 424        Fixed64 angularVelocityDelta)
 425    {
 236426        if (!body.HasSolverMobility)
 64427            return;
 428
 172429        if (body.CanTranslate)
 168430            body.Body!.ApplyCollisionLinearVelocityDelta(linearVelocityDelta);
 172431        if (body.CanRotate)
 136432            body.Body!.ApplyCollisionAngularVelocityDelta(angularVelocityDelta);
 172433    }
 434
 435    private static bool TrySolveFrictionImpulse(
 436        CollisionPair2D pair,
 437        SolverContact2D contact,
 438        Vector2d responsePositionA,
 439        Vector2d responsePositionB,
 440        Vector2d normalLinearVelocityA,
 441        Fixed64 normalAngularVelocityA,
 442        Vector2d normalLinearVelocityB,
 443        Fixed64 normalAngularVelocityB,
 444        Fixed64 restitutionVelocityThreshold,
 445        Fixed64 contactShare,
 446        ContactNormalImpulseResult2D normalResult,
 447        Fixed64 normalImpulseScalar,
 448        out Fixed64 accumulated)
 449    {
 1005450        accumulated = default;
 1005451        if (contact.RelativeA.IsExact
 1005452            || contact.RelativeB.IsExact
 1005453            || !normalResult.HasRepresentableAccumulatedImpulse
 1005454            || !CanUseCompactAxisResponse(contact, contact.Tangent)
 1005455            || !Fixed64.TryMultiplyDivide(
 1005456                normalImpulseScalar,
 1005457                contact.StaticFriction,
 1005458                Fixed64.One,
 1005459                out Fixed64 staticFrictionLimit)
 1005460            || !Fixed64.TryMultiplyDivide(
 1005461                normalImpulseScalar,
 1005462                contact.DynamicFriction,
 1005463                Fixed64.One,
 1005464                out Fixed64 dynamicFrictionLimit))
 465        {
 24466            return TrySolveFrictionImpulseExact(
 24467                pair,
 24468                contact,
 24469                responsePositionA,
 24470                responsePositionB,
 24471                normalLinearVelocityA,
 24472                normalAngularVelocityA,
 24473                normalLinearVelocityB,
 24474                normalAngularVelocityB,
 24475                restitutionVelocityThreshold,
 24476                contactShare,
 24477                out accumulated);
 478        }
 479
 981480        Fixed64 impulseScalar = Fixed64.Zero;
 981481        if (staticFrictionLimit > Fixed64.Zero || dynamicFrictionLimit > Fixed64.Zero)
 482        {
 55483            Vector3d spatialTangent =
 55484                ExactContactLever2D.ToSpatial(contact.Tangent);
 55485            Fixed64 tangentVelocity = default;
 55486            bool tangentVelocityResolved =
 55487                ContactResponseArithmetic3D.TryGetRelativePointVelocity(
 55488                    ExactContactLever2D.ToSpatial(
 55489                        ResolveLinearVelocity(contact.A.Body)),
 55490                    new Vector3d(
 55491                        Fixed64.Zero,
 55492                        -ResolveAngularVelocity(contact.A.Body),
 55493                        Fixed64.Zero),
 55494                    ExactContactLever2D.ToSpatial(contact.RelativeA.Vector),
 55495                    ExactContactLever2D.ToSpatial(
 55496                        ResolveLinearVelocity(contact.B.Body)),
 55497                    new Vector3d(
 55498                        Fixed64.Zero,
 55499                        -ResolveAngularVelocity(contact.B.Body),
 55500                        Fixed64.Zero),
 55501                    ExactContactLever2D.ToSpatial(contact.RelativeB.Vector),
 55502                    spatialTangent,
 55503                    out Vector3d relativeVelocity)
 55504                && ContactResponseArithmetic3D.TryDot(
 55505                    relativeVelocity,
 55506                    spatialTangent,
 55507                    out tangentVelocity);
 55508            bool denominatorResolved =
 55509                ContactNormalImpulse2D.TryComputeDenominator(
 55510                    contact.A.Body,
 55511                    contact.RelativeA.Vector,
 55512                    contact.B.Body,
 55513                    contact.RelativeB.Vector,
 55514                    contact.Tangent,
 55515                    out Fixed64 denominator);
 55516            if (!(tangentVelocityResolved & denominatorResolved))
 517            {
 3518                return TrySolveFrictionImpulseExact(
 3519                    pair,
 3520                    contact,
 3521                    responsePositionA,
 3522                    responsePositionB,
 3523                    normalLinearVelocityA,
 3524                    normalAngularVelocityA,
 3525                    normalLinearVelocityB,
 3526                    normalAngularVelocityB,
 3527                    restitutionVelocityThreshold,
 3528                    contactShare,
 3529                    out accumulated);
 530            }
 52531            if (tangentVelocity.Abs() > Fixed64.Epsilon
 52532                && denominator > Fixed64.Zero
 52533                && !Fixed64.TryMultiplyDivide(
 52534                    -tangentVelocity,
 52535                    Fixed64.One,
 52536                    denominator,
 52537                    out impulseScalar))
 538            {
 1539                return TrySolveFrictionImpulseExact(
 1540                    pair,
 1541                    contact,
 1542                    responsePositionA,
 1543                    responsePositionB,
 1544                    normalLinearVelocityA,
 1545                    normalAngularVelocityA,
 1546                    normalLinearVelocityB,
 1547                    normalAngularVelocityB,
 1548                    restitutionVelocityThreshold,
 1549                    contactShare,
 1550                    out accumulated);
 551            }
 552        }
 553
 977554        if (!Fixed64.TryAdd(
 977555                contact.CachedTangentImpulse,
 977556                impulseScalar,
 977557                out Fixed64 desiredAccumulated))
 558        {
 2559            return TrySolveFrictionImpulseExact(
 2560                pair,
 2561                contact,
 2562                responsePositionA,
 2563                responsePositionB,
 2564                normalLinearVelocityA,
 2565                normalAngularVelocityA,
 2566                normalLinearVelocityB,
 2567                normalAngularVelocityB,
 2568                restitutionVelocityThreshold,
 2569                contactShare,
 2570                out accumulated);
 571        }
 975572        accumulated = desiredAccumulated >= -staticFrictionLimit
 975573            && desiredAccumulated <= staticFrictionLimit
 975574            ? desiredAccumulated
 975575            : FixedMath.Clamp(
 975576                desiredAccumulated,
 975577                -dynamicFrictionLimit,
 975578                dynamicFrictionLimit);
 975579        if (!Fixed64.TrySubtract(
 975580                accumulated,
 975581                contact.CachedTangentImpulse,
 975582                out impulseScalar))
 583        {
 1584            return TrySolveFrictionImpulseExact(
 1585                pair,
 1586                contact,
 1587                responsePositionA,
 1588                responsePositionB,
 1589                normalLinearVelocityA,
 1590                normalAngularVelocityA,
 1591                normalLinearVelocityB,
 1592                normalAngularVelocityB,
 1593                restitutionVelocityThreshold,
 1594                contactShare,
 1595                out accumulated);
 596        }
 974597        return impulseScalar == Fixed64.Zero
 974598            || TryApplyContactImpulseCombination(
 974599                pair,
 974600                contact,
 974601                responsePositionA,
 974602                responsePositionB,
 974603                contact.Normal,
 974604                Fixed64.Zero,
 974605                contact.Tangent,
 974606                impulseScalar);
 607    }
 608
 609    [MethodImpl(MethodImplOptions.NoInlining)]
 610    private static bool TrySolveFrictionImpulseExact(
 611        CollisionPair2D pair,
 612        SolverContact2D contact,
 613        Vector2d responsePositionA,
 614        Vector2d responsePositionB,
 615        Vector2d normalLinearVelocityA,
 616        Fixed64 normalAngularVelocityA,
 617        Vector2d normalLinearVelocityB,
 618        Fixed64 normalAngularVelocityB,
 619        Fixed64 restitutionVelocityThreshold,
 620        Fixed64 contactShare,
 621        out Fixed64 accumulated)
 622    {
 31623        accumulated = default;
 31624        GetExactLevers(
 31625            pair,
 31626            contact,
 31627            responsePositionA,
 31628            responsePositionB,
 31629            out ExactLever3D exactA,
 31630            out ExactLever3D exactB);
 31631        Vector3d spatialNormal =
 31632            ExactContactLever2D.ToSpatial(contact.Normal);
 31633        var normalConstraint = new ExactNormalConstraint3D(
 31634            ExactContactLever2D.CreateResponseOperand(
 31635                contact.A.Body,
 31636                normalLinearVelocityA,
 31637                normalAngularVelocityA,
 31638                exactA,
 31639                -spatialNormal),
 31640            ExactContactLever2D.CreateResponseOperand(
 31641                contact.B.Body,
 31642                normalLinearVelocityB,
 31643                normalAngularVelocityB,
 31644                exactB,
 31645                spatialNormal),
 31646            spatialNormal,
 31647            contact.Restitution,
 31648            restitutionVelocityThreshold,
 31649            contact.CachedNormalImpulse,
 31650            contactShare,
 31651            contactShare);
 31652        Vector3d spatialTangent =
 31653            ExactContactLever2D.ToSpatial(contact.Tangent);
 31654        if (!ExactContactResponseKernel.TryGetCoulombLineResponse(
 31655                normalConstraint,
 31656                ExactContactLever2D.CreateResponseOperand(
 31657                    contact.A.Body,
 31658                    ResolveLinearVelocity(contact.A.Body),
 31659                    ResolveAngularVelocity(contact.A.Body),
 31660                    exactA,
 31661                    -spatialTangent),
 31662                ExactContactLever2D.CreateResponseOperand(
 31663                    contact.B.Body,
 31664                    ResolveLinearVelocity(contact.B.Body),
 31665                    ResolveAngularVelocity(contact.B.Body),
 31666                    exactB,
 31667                    spatialTangent),
 31668                spatialTangent,
 31669                contact.CachedTangentImpulse,
 31670                contact.StaticFriction,
 31671                contact.DynamicFriction,
 31672                out ExactCoulombResponse3D response))
 673        {
 2674            return false;
 675        }
 676
 29677        accumulated = response.TryGetPrimaryAccumulatedImpulse(
 29678                out Fixed64 projectedAccumulated)
 29679            ? projectedAccumulated
 29680            : Fixed64.Zero;
 29681        return !response.HasAppliedImpulse
 29682            || TryApplyVelocityDeltas(
 29683                contact,
 29684                ExactContactLever2D.ToPlanar(
 29685                    response.FirstLinearVelocityDelta),
 29686                ExactContactLever2D.ToPlanarAngular(
 29687                    response.FirstAngularVelocityDelta),
 29688                ExactContactLever2D.ToPlanar(
 29689                    response.SecondLinearVelocityDelta),
 29690                ExactContactLever2D.ToPlanarAngular(
 29691                    response.SecondAngularVelocityDelta));
 692    }
 693
 694    private static bool CanUseCompactAxisResponse(
 695        SolverContact2D contact,
 696        Vector2d axis) =>
 1973697        ExactContactLever2D.CanUseCompactResponse(
 1973698            contact.A.Body,
 1973699            ResolveLinearVelocity(contact.A.Body),
 1973700            ResolveAngularVelocity(contact.A.Body),
 1973701            contact.RelativeA.Vector,
 1973702            contact.B.Body,
 1973703            ResolveLinearVelocity(contact.B.Body),
 1973704            ResolveAngularVelocity(contact.B.Body),
 1973705            contact.RelativeB.Vector,
 1973706            axis);
 707
 708    private static void ApplyPositionCorrection(ResponseBody2D body, Vector2d correction)
 709    {
 874710        if (!body.CanTranslate || correction == Vector2d.Zero)
 421711            return;
 712
 453713        body.Body!.ApplyCollisionPositionCorrection(correction);
 453714    }
 715
 716    private static bool TryApplyContactImpulseCombination(
 717        CollisionPair2D pair,
 718        SolverContact2D contact,
 719        Vector2d responsePositionA,
 720        Vector2d responsePositionB,
 721        Vector2d firstAxis,
 722        Fixed64 firstScale,
 723        Vector2d secondAxis,
 724        Fixed64 secondScale)
 725    {
 20726        if (!contact.RelativeA.IsExact
 20727            && !contact.RelativeB.IsExact
 20728            && Vector3d.TryLinearCombination(
 20729                ExactContactLever2D.ToSpatial(firstAxis),
 20730                firstScale,
 20731                ExactContactLever2D.ToSpatial(secondAxis),
 20732                secondScale,
 20733                Vector3d.Zero,
 20734                Fixed64.Zero,
 20735                out Vector3d spatialImpulse)
 20736            && TryApplyCompactImpulse(
 20737                contact,
 20738                ExactContactLever2D.ToPlanar(spatialImpulse)))
 739        {
 17740            return true;
 741        }
 742
 3743        GetExactLevers(
 3744            pair,
 3745            contact,
 3746            responsePositionA,
 3747            responsePositionB,
 3748            out ExactLever3D exactA,
 3749            out ExactLever3D exactB);
 3750        return ExactContactLever2D.TryGetImpulseVelocityDeltas(
 3751                contact.A.Body,
 3752                exactA,
 3753                contact.B.Body,
 3754                exactB,
 3755                firstAxis,
 3756                firstScale,
 3757                secondAxis,
 3758                secondScale,
 3759                out Vector2d linearA,
 3760                out Fixed64 angularA,
 3761                out Vector2d linearB,
 3762                out Fixed64 angularB)
 3763            && TryApplyVelocityDeltas(
 3764                contact,
 3765                linearA,
 3766                angularA,
 3767                linearB,
 3768                angularB);
 769    }
 770
 771    private static bool TryApplyCompactImpulse(
 772        SolverContact2D contact,
 773        Vector2d impulseB)
 774    {
 19775        Vector2d impulseA = -impulseB;
 19776        bool linearAResolved = TryGetLinearVelocityDelta(
 19777            contact.A,
 19778            impulseA,
 19779            out Vector2d linearA);
 19780        bool angularAResolved = TryGetAngularVelocityDelta(
 19781            contact.A,
 19782            contact.RelativeA.Vector,
 19783            impulseA,
 19784            out Fixed64 angularA);
 19785        bool linearBResolved = TryGetLinearVelocityDelta(
 19786            contact.B,
 19787            impulseB,
 19788            out Vector2d linearB);
 19789        bool angularBResolved = TryGetAngularVelocityDelta(
 19790            contact.B,
 19791            contact.RelativeB.Vector,
 19792            impulseB,
 19793            out Fixed64 angularB);
 19794        return linearAResolved
 19795            & angularAResolved
 19796            & linearBResolved
 19797            & angularBResolved
 19798            && TryApplyVelocityDeltas(
 19799                contact,
 19800                linearA,
 19801                angularA,
 19802                linearB,
 19803                angularB);
 804    }
 805
 806    private static bool TryGetLinearVelocityDelta(
 807        ResponseBody2D body,
 808        Vector2d impulse,
 809        out Vector2d velocityDelta)
 810    {
 38811        if (!body.CanTranslate)
 812        {
 19813            velocityDelta = Vector2d.Zero;
 19814            return true;
 815        }
 816
 19817        return ContinuousCollisionImpulsePolicy.TryResolveVelocityDelta(
 19818            body.Body!.ProjectLinearMotion(impulse),
 19819            Fixed64.One,
 19820            body.InverseMass,
 19821            Fixed64.One,
 19822            out velocityDelta);
 823    }
 824
 825    private static bool TryGetAngularVelocityDelta(
 826        ResponseBody2D body,
 827        Vector2d relativeContactPoint,
 828        Vector2d impulse,
 829        out Fixed64 velocityDelta)
 830    {
 38831        velocityDelta = Fixed64.Zero;
 38832        if (!body.CanRotate)
 25833            return true;
 834
 13835        return ContactResponseArithmetic3D.TryCross(
 13836                ExactContactLever2D.ToSpatial(relativeContactPoint),
 13837                ExactContactLever2D.ToSpatial(impulse),
 13838                out Vector3d torque)
 13839            && Fixed64.TryMultiplyDivide(
 13840                -torque.Y,
 13841                body.InverseMoment,
 13842                Fixed64.One,
 13843                out velocityDelta);
 844    }
 845
 846    private static bool TryApplyVelocityDeltas(
 847        SolverContact2D contact,
 848        Vector2d linearA,
 849        Fixed64 angularA,
 850        Vector2d linearB,
 851        Fixed64 angularB)
 852    {
 119853        bool firstFits = contact.A.Body?.CanApplyCollisionVelocityDeltas(
 119854                linearA,
 119855                angularA)
 119856            ?? true;
 119857        bool secondFits = contact.B.Body?.CanApplyCollisionVelocityDeltas(
 119858                linearB,
 119859                angularB)
 119860            ?? true;
 119861        if (!(firstFits & secondFits))
 1862            return false;
 863
 118864        ApplyVelocityDelta(contact.A, linearA, angularA);
 118865        ApplyVelocityDelta(contact.B, linearB, angularB);
 118866        return true;
 867    }
 868
 869    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 870    private static Vector2d ResolveLinearVelocity(SolidBody2D? body) =>
 6096871        body == null
 6096872            ? Vector2d.Zero
 6096873            : body.ProjectLinearMotion(
 6096874                body.IsKinematic
 6096875                    ? body.SampleContinuousCollisionLinearVelocity(Fixed64.One)
 6096876                    : body.LinearVelocity);
 877
 878    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 879    private static Fixed64 ResolveAngularVelocity(SolidBody2D? body) =>
 6096880        body == null
 6096881            ? Fixed64.Zero
 6096882            : body.IsKinematic
 6096883                ? body.SampleContinuousCollisionAngularVelocity(Fixed64.One)
 6096884                : body.AngularVelocity;
 885
 886    private static Vector2d ResolveContactNormal(Vector2d normal, Vector2d fallbackDirection)
 887    {
 1012888        Vector2d resolved = normal.MagnitudeSquared > Fixed64.Epsilon
 1012889            ? normal.Normalized
 1012890            : fallbackDirection.MagnitudeSquared > Fixed64.Epsilon
 1012891                ? fallbackDirection.Normalized
 1012892                : Vector2d.Zero;
 893
 1012894        if (resolved == Vector2d.Zero)
 1895            return resolved;
 896
 1011897        return fallbackDirection.MagnitudeSquared > Fixed64.Epsilon
 1011898            && Vector2d.Dot(resolved, fallbackDirection) < Fixed64.Zero
 1011899                ? -resolved
 1011900                : resolved;
 901    }
 902
 903    private static void GetExactLevers(
 904        CollisionPair2D pair,
 905        SolverContact2D contact,
 906        Vector2d responsePositionA,
 907        Vector2d responsePositionB,
 908        out ExactLever3D exactA,
 909        out ExactLever3D exactB)
 910    {
 70911        ManifoldContact2D manifoldContact =
 70912            pair.Manifold[contact.ManifoldIndex];
 70913        ContactAnchor2D zero =
 70914            ContactAnchor2D.FromWorldPoint(Vector2d.Zero);
 70915        exactA = contact.A.Body == null
 70916            ? zero.GetXZLeverFrom(zero)
 70917            : manifoldContact.AnchorA.GetXZLeverFrom(
 70918                new ContactAnchor2D(
 70919                    responsePositionA,
 70920                    contact.A.Body.Rotation,
 70921                    contact.A.Body.LocalCenterOfMassOffset));
 70922        exactB = contact.B.Body == null
 70923            ? zero.GetXZLeverFrom(zero)
 70924            : manifoldContact.AnchorB.GetXZLeverFrom(
 70925                new ContactAnchor2D(
 70926                    responsePositionB,
 70927                    contact.B.Body.Rotation,
 70928                    contact.B.Body.LocalCenterOfMassOffset));
 70929    }
 930
 931    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 932    private static bool HasFailedResponse(
 933        byte failedResponseMask,
 934        int contactIndex) =>
 3024935        (failedResponseMask & (1 << contactIndex)) != 0;
 936
 937    private static void RejectResponse(
 938        CollisionPair2D pair,
 939        SolverContact2D contact,
 940        int contactIndex,
 941        ref byte failedResponseMask)
 942    {
 5943        failedResponseMask |= (byte)(1 << contactIndex);
 5944        pair.RemoveWarmStartImpulse(contact.ContactId);
 5945        GravitasLogger.Channel.Error(
 5946            $"2D contact response is outside the representable velocity domain.");
 5947    }
 948}

Methods/Properties

.cctor()
Resolve(Gravitas.CollisionPair2D)
Resolve(Gravitas.CollisionPair2D,System.Boolean,System.Boolean)
TryCreateBodyPair(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.ResponseBody2D&,Gravitas.CollisionHandling.ResponseBody2D&)
BuildContactBuffer(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.ResponseBody2D,Gravitas.CollisionHandling.ResponseBody2D,Gravitas.CollisionHandling.ContactAnchor2D&,Gravitas.CollisionHandling.ContactAnchor2D&)
TryCreateContact(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.ResponseBody2D,Gravitas.CollisionHandling.ResponseBody2D,Gravitas.CollisionHandling.ContactAnchor2D&,Gravitas.CollisionHandling.ContactAnchor2D&,System.Int32,Gravitas.CollisionHandling.SolverContact2D&)
TryCalculateExactNormalResult(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactNormalImpulseResult2D&)
ApplyPositionCorrection(Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Fixed64)
TryApplyCachedImpulse(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d)
TryApplyNormalImpulse(Gravitas.CollisionHandling.SolverContact2D,Gravitas.CollisionHandling.ContactNormalImpulseResult2D)
ApplyVelocityDelta(Gravitas.CollisionHandling.ResponseBody2D,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64)
TrySolveFrictionImpulse(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactNormalImpulseResult2D,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TrySolveFrictionImpulseExact(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
CanUseCompactAxisResponse(Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d)
ApplyPositionCorrection(Gravitas.CollisionHandling.ResponseBody2D,FixedMathSharp.Vector2d)
TryApplyContactImpulseCombination(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64)
TryApplyCompactImpulse(Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d)
TryGetLinearVelocityDelta(Gravitas.CollisionHandling.ResponseBody2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d&)
TryGetAngularVelocityDelta(Gravitas.CollisionHandling.ResponseBody2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64&)
TryApplyVelocityDeltas(Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64)
ResolveLinearVelocity(Gravitas.SolidBody2D)
ResolveAngularVelocity(Gravitas.SolidBody2D)
ResolveContactNormal(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d)
GetExactLevers(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.SolverContact2D,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,Gravitas.CollisionHandling.ExactLever3D&,Gravitas.CollisionHandling.ExactLever3D&)
HasFailedResponse(System.Byte,System.Int32)
RejectResponse(Gravitas.CollisionPair2D,Gravitas.CollisionHandling.SolverContact2D,System.Int32,System.Byte&)