< Summary

Information
Class: Gravitas.CollisionHandling.CollisionResponseMixed
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Response/Mixed/CollisionResponseMixed.cs
Line coverage
100%
Covered lines: 652
Uncovered lines: 0
Coverable lines: 652
Total lines: 953
Line coverage: 100%
Branch coverage
100%
Covered branches: 220
Total branches: 220
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/Mixed/CollisionResponseMixed.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionResponseMixed.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.Colliders;
 11using Gravitas.Materials;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas.CollisionHandling;
 15
 16/// <summary>
 17/// Solves deterministic mixed 3D/2D contact response with the 2D body constrained to its X/Z plane.
 18/// </summary>
 19public static class CollisionResponseMixed
 20{
 21    /// <summary>Penetration tolerated before positional correction is applied.</summary>
 122    public static readonly Fixed64 PenetrationSlop = (Fixed64)0.01f;
 23
 24    /// <summary>Fraction of excess penetration corrected by the mixed solver.</summary>
 125    public static readonly Fixed64 PenetrationCorrectionPercent = (Fixed64)0.8f;
 26
 27    internal static bool Resolve(CollisionPairMixed pair, MixedContact contact) =>
 13528        Resolve(pair, contact, iteration: 0, iterationLimit: 1, applyPositionCorrection: true);
 29
 30    internal static bool Resolve(
 31        CollisionPairMixed pair,
 32        MixedContact contact,
 33        int iteration,
 34        int iterationLimit,
 35        bool applyPositionCorrection)
 36    {
 30937        if (!contact.HasContact || pair.Collider3D.IsTrigger || pair.Collider2D.IsTrigger)
 738            return false;
 39
 30240        SolidBody? body3D = pair.Collider3D.Body;
 30241        SolidBody2D? body2D = pair.Collider2D.Body;
 30242        Vector3d normal = ResolveNormal(pair, contact);
 30243        bool hasPlanarResponseCoupling = normal.ToVector2d() != Vector2d.Zero;
 44
 30245        Fixed64 correctionInverseMass = GetConstrainedInverseMass(body3D, normal)
 30246            + GetConstrainedPlanarInverseMass(body2D, normal);
 47
 30248        Vector2d relative2D = default;
 30249        bool resolved3D = body3D != null
 30250            ? body3D.TryGetOffsetFromCenterOfMass(contact.Anchor3D, out Vector3d relative3D)
 30251            : contact.Anchor3D.TryGetOffsetFrom(pair.Collider3D.Center, out relative3D);
 30252        bool resolved2D = body2D == null
 30253            || contact.TryGetPlanarOffset2DFrom(
 30254                body2D.Position,
 30255                body2D.Rotation,
 30256                body2D.LocalCenterOfMassOffset,
 30257                out relative2D);
 30258        if (body2D == null)
 2959            relative2D = Vector2d.Zero;
 30260        Vector3d normalLinearVelocity3D =
 30261            body3D == null
 30262                ? Vector3d.Zero
 30263                : ResolveLinearVelocity(body3D);
 30264        Vector3d normalAngularVelocity3D =
 30265            body3D == null
 30266                ? Vector3d.Zero
 30267                : ResolveAngularVelocity(body3D);
 30268        Vector2d normalLinearVelocity2D =
 30269            body2D == null
 30270                ? Vector2d.Zero
 30271                : ResolveLinearVelocity(body2D);
 30272        Fixed64 normalAngularVelocity2D =
 30273            body2D == null
 30274                ? Fixed64.Zero
 30275                : ResolveAngularVelocity(body2D);
 76
 30277        bool normalResolved = TryGetNormalImpulse(
 30278            pair,
 30279            contact,
 30280            body3D,
 30281            body2D,
 30282            normal,
 30283            relative3D,
 30284            relative2D,
 30285            resolved3D,
 30286            resolved2D,
 30287            normalLinearVelocity3D,
 30288            normalAngularVelocity3D,
 30289            normalLinearVelocity2D,
 30290            normalAngularVelocity2D,
 30291            out ContactNormalImpulseResultMixed normalResult);
 30292        if (!normalResolved
 30293            || !CanApplyNormalVelocityDeltas(body3D, body2D, normalResult))
 94        {
 395            GravitasLogger.Channel.Write(
 396                DiagnosticLevel.Error,
 397                "Mixed contact response is outside the representable velocity domain.",
 398                nameof(CollisionResponseMixed));
 399            return false;
 100        }
 101
 299102        if (applyPositionCorrection && correctionInverseMass > Fixed64.Zero)
 151103            ApplyPositionCorrection(body3D, body2D, normal, contact.Depth, correctionInverseMass);
 104
 299105        bool appliedImpulse = HasVelocityDelta(normalResult);
 299106        if (appliedImpulse)
 107        {
 113108            if (normalResult.HasRepresentableAppliedImpulse
 113109                && normalResult.HasRepresentableNormalVelocity)
 110            {
 109111                pair.Context.Diagnostics.EmitMixedResponseImpulse(
 109112                    pair,
 109113                    contact,
 109114                    normal * normalResult.AppliedImpulseScalar,
 109115                    normalResult.NormalVelocity,
 109116                    iteration,
 109117                    iterationLimit);
 118            }
 113119            ApplyNormalVelocityDeltas(body3D, body2D, normalResult);
 120        }
 299121        PhysicsMaterial material3D = contact.HasMaterialOverride
 299122            ? contact.Material3D
 299123            : pair.Collider3D.Material;
 299124        PhysicsMaterial material2D = contact.HasMaterialOverride
 299125            ? contact.Material2D
 299126            : pair.Collider2D.Material;
 299127        bool frictionResolved = TryApplyFrictionImpulse(
 299128            pair,
 299129            contact,
 299130            body3D,
 299131            body2D,
 299132            normal,
 299133            relative3D,
 299134            relative2D,
 299135            material3D,
 299136            material2D,
 299137            normalResult,
 299138            normalLinearVelocity3D,
 299139            normalAngularVelocity3D,
 299140            normalLinearVelocity2D,
 299141            normalAngularVelocity2D,
 299142            pair.Context.Settings.RestitutionVelocityThreshold,
 299143            hasPlanarResponseCoupling,
 299144            resolved3D,
 299145            resolved2D,
 299146            out bool frictionApplied);
 299147        if (!frictionResolved)
 148        {
 3149            GravitasLogger.Channel.Write(
 3150                DiagnosticLevel.Error,
 3151                "Mixed contact friction is outside the representable velocity domain.",
 3152                nameof(CollisionResponseMixed));
 153        }
 154
 299155        return appliedImpulse | frictionApplied;
 156    }
 157
 158    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 159    internal static bool HasPlanarResponseCoupling(CollisionPairMixed pair, MixedContact contact) =>
 144160        ResolveNormal(pair, contact).ToVector2d() != Vector2d.Zero;
 161
 162    private static void ApplyPositionCorrection(
 163        SolidBody? body3D,
 164        SolidBody2D? body2D,
 165        Vector3d normal,
 166        Fixed64 depth,
 167        Fixed64 effectiveInverseMass)
 168    {
 151169        Fixed64 correctionDepth = depth - PenetrationSlop;
 151170        if (correctionDepth <= Fixed64.Zero)
 23171            return;
 172
 128173        Fixed64 correctionScalar = correctionDepth * PenetrationCorrectionPercent / effectiveInverseMass;
 128174        Fixed64 constrainedInverseMass3D = GetConstrainedInverseMass(body3D, normal);
 128175        if (constrainedInverseMass3D > Fixed64.Zero)
 123176            body3D!.ApplyCollisionPositionCorrection(-normal * (correctionScalar * constrainedInverseMass3D));
 177
 128178        Fixed64 constrainedInverseMass2D = GetConstrainedPlanarInverseMass(body2D, normal);
 128179        if (constrainedInverseMass2D <= Fixed64.Zero)
 82180            return;
 181
 46182        Vector2d planarNormal = normal.ToVector2d();
 46183        body2D!.ApplyCollisionPositionCorrection(planarNormal * (correctionScalar * constrainedInverseMass2D));
 46184    }
 185
 186    private static bool TryGetNormalImpulse(
 187        CollisionPairMixed pair,
 188        MixedContact contact,
 189        SolidBody? body3D,
 190        SolidBody2D? body2D,
 191        Vector3d normal,
 192        Vector3d relative3D,
 193        Vector2d relative2D,
 194        bool hasCompact3D,
 195        bool hasCompact2D,
 196        Vector3d linearVelocity3D,
 197        Vector3d angularVelocity3D,
 198        Vector2d linearVelocity2D,
 199        Fixed64 angularVelocity2D,
 200        out ContactNormalImpulseResultMixed result)
 201    {
 302202        PhysicsMaterial material3D = contact.HasMaterialOverride
 302203            ? contact.Material3D
 302204            : pair.Collider3D.Material;
 302205        PhysicsMaterial material2D = contact.HasMaterialOverride
 302206            ? contact.Material2D
 302207            : pair.Collider2D.Material;
 302208        Fixed64 restitution =
 302209            PhysicsMaterial.CombineRestitution(material3D, material2D);
 302210        Fixed64 restitutionVelocityThreshold =
 302211            pair.Context.Settings.RestitutionVelocityThreshold;
 302212        if (hasCompact3D
 302213            && hasCompact2D
 302214            && ContactNormalImpulseMixed.CanUseCompactResponse(
 302215                body3D,
 302216                linearVelocity3D,
 302217                angularVelocity3D,
 302218                relative3D,
 302219                body2D,
 302220                linearVelocity2D,
 302221                angularVelocity2D,
 302222                relative2D,
 302223                normal)
 302224            && ContactNormalImpulseMixed.TryCalculateAccumulatedDelta(
 302225                body3D,
 302226                linearVelocity3D,
 302227                angularVelocity3D,
 302228                relative3D,
 302229                body2D,
 302230                linearVelocity2D,
 302231                angularVelocity2D,
 302232                relative2D,
 302233                normal,
 302234                restitution,
 302235                restitutionVelocityThreshold,
 302236                Fixed64.Zero,
 302237                Fixed64.One,
 302238                Fixed64.One,
 302239                out result))
 240        {
 273241            return true;
 242        }
 243
 29244        GetExactLevers(
 29245            pair,
 29246            contact,
 29247            body3D,
 29248            body2D,
 29249            out ExactLever3D exact3D,
 29250            out ExactLever3D exact2D);
 29251        return ContactNormalImpulseMixed.TryCalculateAccumulatedDeltaExact(
 29252            body3D,
 29253            linearVelocity3D,
 29254            angularVelocity3D,
 29255            exact3D,
 29256            body2D,
 29257            linearVelocity2D,
 29258            angularVelocity2D,
 29259            exact2D,
 29260            normal,
 29261            restitution,
 29262            restitutionVelocityThreshold,
 29263            Fixed64.Zero,
 29264            Fixed64.One,
 29265            Fixed64.One,
 29266            out result);
 267    }
 268
 269    private static bool CanApplyNormalVelocityDeltas(
 270        SolidBody? body3D,
 271        SolidBody2D? body2D,
 272        ContactNormalImpulseResultMixed result) =>
 300273        CanApplyVelocityDeltas(
 300274            body3D,
 300275            body2D,
 300276            result.LinearVelocityDelta3D,
 300277            result.AngularVelocityDelta3D,
 300278            result.LinearVelocityDelta2D,
 300279            result.AngularVelocityDelta2D);
 280
 281    private static bool HasVelocityDelta(
 282        ContactNormalImpulseResultMixed result) =>
 299283        result.LinearVelocityDelta3D != Vector3d.Zero
 299284        || result.AngularVelocityDelta3D != Vector3d.Zero
 299285        || result.LinearVelocityDelta2D != Vector2d.Zero
 299286        || result.AngularVelocityDelta2D != Fixed64.Zero;
 287
 288    private static void ApplyNormalVelocityDeltas(
 289        SolidBody? body3D,
 290        SolidBody2D? body2D,
 291        ContactNormalImpulseResultMixed result) =>
 113292        ApplyVelocityDeltas(
 113293            body3D,
 113294            body2D,
 113295            result.LinearVelocityDelta3D,
 113296            result.AngularVelocityDelta3D,
 113297            result.LinearVelocityDelta2D,
 113298            result.AngularVelocityDelta2D);
 299
 300    private static bool CanApplyVelocityDeltas(
 301        SolidBody? body3D,
 302        SolidBody2D? body2D,
 303        Vector3d linear3D,
 304        Vector3d angular3D,
 305        Vector2d linear2D,
 306        Fixed64 angular2D)
 307    {
 343308        bool response3DFits = body3D?.CanApplyCollisionVelocityDeltas(
 343309                linear3D,
 343310                angular3D)
 343311            ?? true;
 343312        bool response2DFits = body2D?.CanApplyCollisionVelocityDeltas(
 343313                linear2D,
 343314                angular2D)
 343315            ?? true;
 343316        return response3DFits & response2DFits;
 317    }
 318
 319    private static void ApplyVelocityDeltas(
 320        SolidBody? body3D,
 321        SolidBody2D? body2D,
 322        Vector3d linear3D,
 323        Vector3d angular3D,
 324        Vector2d linear2D,
 325        Fixed64 angular2D)
 326    {
 154327        if (body3D != null)
 328        {
 150329            body3D.ApplyCollisionLinearVelocityDelta(linear3D);
 150330            body3D.ApplyCollisionAngularVelocityDelta(angular3D);
 331        }
 332
 154333        if (body2D != null)
 334        {
 146335            body2D.ApplyCollisionLinearVelocityDelta(linear2D);
 146336            body2D.ApplyCollisionAngularVelocityDelta(angular2D);
 337        }
 154338    }
 339
 340    private static bool TryApplyFrictionImpulse(
 341        CollisionPairMixed pair,
 342        MixedContact contact,
 343        SolidBody? body3D,
 344        SolidBody2D? body2D,
 345        Vector3d normal,
 346        Vector3d relative3D,
 347        Vector2d relative2D,
 348        PhysicsMaterial material3D,
 349        PhysicsMaterial material2D,
 350        ContactNormalImpulseResultMixed normalResult,
 351        Vector3d normalLinearVelocity3D,
 352        Vector3d normalAngularVelocity3D,
 353        Vector2d normalLinearVelocity2D,
 354        Fixed64 normalAngularVelocity2D,
 355        Fixed64 restitutionVelocityThreshold,
 356        bool applyTo2D,
 357        bool hasCompact3D,
 358        bool hasCompact2D,
 359        out bool applied)
 360    {
 299361        applied = false;
 299362        Fixed64 normalImpulse = normalResult.HasRepresentableAppliedImpulse
 299363            ? FixedMath.Max(
 299364                Fixed64.Zero,
 299365                normalResult.AppliedImpulseScalar)
 299366            : Fixed64.Zero;
 299367        if (normalResult.HasRepresentableAppliedImpulse
 299368            && normalImpulse <= Fixed64.Zero)
 187369            return true;
 370
 112371        PhysicsMaterial.CombineFriction(material3D, material2D, out Fixed64 staticFriction, out Fixed64 dynamicFriction)
 112372        Fixed64 restitution =
 112373            PhysicsMaterial.CombineRestitution(material3D, material2D);
 112374        if (staticFriction <= Fixed64.Zero && dynamicFriction <= Fixed64.Zero)
 8375            return true;
 376
 104377        if (normalResult.HasRepresentableAppliedImpulse
 104378            && hasCompact3D
 104379            && hasCompact2D
 104380            && TryGetCompactFrictionImpulse(
 104381                body3D,
 104382                body2D,
 104383                normal,
 104384                relative3D,
 104385                relative2D,
 104386                normalImpulse,
 104387                staticFriction,
 104388                dynamicFriction,
 104389                applyTo2D,
 104390                out Vector3d tangent,
 104391                out Fixed64 impulseScalar)
 104392            && (impulseScalar == Fixed64.Zero
 104393                || TryApplyImpulse(
 104394                    body3D,
 104395                    body2D,
 104396                    tangent,
 104397                    relative3D,
 104398                    relative2D,
 104399                    impulseScalar,
 104400                    applyTo2D)))
 401        {
 76402            applied = impulseScalar != Fixed64.Zero;
 76403            return true;
 404        }
 405
 28406        return TryApplyFrictionImpulseExact(
 28407            pair,
 28408            contact,
 28409            body3D,
 28410            body2D,
 28411            normal,
 28412            normalLinearVelocity3D,
 28413            normalAngularVelocity3D,
 28414            normalLinearVelocity2D,
 28415            normalAngularVelocity2D,
 28416            restitutionVelocityThreshold,
 28417            restitution,
 28418            staticFriction,
 28419            dynamicFriction,
 28420            applyTo2D,
 28421            out applied);
 422    }
 423
 424    private static bool TryGetCompactFrictionImpulse(
 425        SolidBody? body3D,
 426        SolidBody2D? body2D,
 427        Vector3d normal,
 428        Vector3d relative3D,
 429        Vector2d relative2D,
 430        Fixed64 normalImpulse,
 431        Fixed64 staticFriction,
 432        Fixed64 dynamicFriction,
 433        bool applyTo2D,
 434        out Vector3d tangent,
 435        out Fixed64 impulseScalar)
 436    {
 86437        tangent = default;
 86438        impulseScalar = Fixed64.Zero;
 86439        Vector3d linearVelocity3D = body3D == null
 86440            ? Vector3d.Zero
 86441            : ResolveLinearVelocity(body3D);
 86442        Vector3d angularVelocity3D = body3D == null
 86443            ? Vector3d.Zero
 86444            : ResolveAngularVelocity(body3D);
 86445        Vector3d linearVelocity2D = body2D == null
 86446            ? Vector3d.Zero
 86447            : ExactContactLever2D.ToSpatial(
 86448                ResolveLinearVelocity(body2D));
 86449        Vector3d angularVelocity2D = body2D == null
 86450            ? Vector3d.Zero
 86451            : new Vector3d(
 86452                Fixed64.Zero,
 86453                -ResolveAngularVelocity(body2D),
 86454                Fixed64.Zero);
 86455        if (!ContactResponseArithmetic3D.TryGetRelativePointVelocity(
 86456                linearVelocity3D,
 86457                angularVelocity3D,
 86458                relative3D,
 86459                linearVelocity2D,
 86460                angularVelocity2D,
 86461                ExactContactLever2D.ToSpatial(relative2D),
 86462                normal,
 86463                out Vector3d relativeVelocity)
 86464            || !TryGetCompactTangent(
 86465                relativeVelocity,
 86466                normal,
 86467                out tangent,
 86468                out bool hasTangentialMotion))
 469        {
 2470            return false;
 471        }
 84472        if (!hasTangentialMotion)
 57473            return true;
 474
 27475        bool denominatorResolved =
 27476            TryGetCompactConstrainedInverseMass(
 27477                body3D,
 27478                tangent,
 27479                out Fixed64 linear3D);
 27480        denominatorResolved &=
 27481            ContactNormalImpulse3D.TryComputeAngularDenominator(
 27482                body3D,
 27483                relative3D,
 27484                tangent,
 27485                out Fixed64 angular3D);
 27486        Fixed64 linear2D = Fixed64.Zero;
 27487        Fixed64 angular2D = Fixed64.Zero;
 27488        if (applyTo2D)
 489        {
 21490            denominatorResolved &=
 21491                TryGetCompactConstrainedPlanarInverseMass(
 21492                    body2D,
 21493                    tangent,
 21494                    out linear2D);
 21495            denominatorResolved &=
 21496                ContactNormalImpulse2D.TryComputeAngularDenominator(
 21497                    body2D,
 21498                    relative2D,
 21499                    tangent.ToVector2d(),
 21500                    out angular2D);
 501        }
 502
 27503        var denominatorTerms = new ContactEffectiveMassTerms3D(
 27504            linear3D,
 27505            linear2D,
 27506            angular3D,
 27507            angular2D);
 27508        if (!denominatorResolved
 27509            || !denominatorTerms.TryGetValue(out Fixed64 denominator))
 510        {
 3511            return false;
 512        }
 24513        if (denominator <= Fixed64.Zero)
 1514            return true;
 515
 23516        if (!ContactResponseArithmetic3D.TryDot(
 23517                relativeVelocity,
 23518                tangent,
 23519                out Fixed64 tangentVelocity)
 23520            || !Fixed64.TryMultiplyDivide(
 23521                -tangentVelocity,
 23522                Fixed64.One,
 23523                denominator,
 23524                out impulseScalar)
 23525            || !Fixed64.TryMultiplyDivide(
 23526                normalImpulse,
 23527                staticFriction,
 23528                Fixed64.One,
 23529                out Fixed64 staticLimit)
 23530            || !Fixed64.TryMultiplyDivide(
 23531                normalImpulse,
 23532                dynamicFriction,
 23533                Fixed64.One,
 23534                out Fixed64 dynamicLimit))
 535        {
 3536            return false;
 537        }
 538
 20539        if (impulseScalar < -staticLimit
 20540            || impulseScalar > staticLimit)
 541        {
 10542            impulseScalar = FixedMath.Clamp(
 10543                impulseScalar,
 10544                -dynamicLimit,
 10545                dynamicLimit);
 546        }
 20547        return true;
 548    }
 549
 550    [MethodImpl(MethodImplOptions.NoInlining)]
 551    private static bool TryApplyFrictionImpulseExact(
 552        CollisionPairMixed pair,
 553        MixedContact contact,
 554        SolidBody? body3D,
 555        SolidBody2D? body2D,
 556        Vector3d normal,
 557        Vector3d normalLinearVelocity3D,
 558        Vector3d normalAngularVelocity3D,
 559        Vector2d normalLinearVelocity2D,
 560        Fixed64 normalAngularVelocity2D,
 561        Fixed64 restitutionVelocityThreshold,
 562        Fixed64 restitution,
 563        Fixed64 staticFriction,
 564        Fixed64 dynamicFriction,
 565        bool applyTo2D,
 566        out bool applied)
 567    {
 28568        applied = false;
 28569        GetExactLevers(
 28570            pair,
 28571            contact,
 28572            body3D,
 28573            body2D,
 28574            out ExactLever3D exact3D,
 28575            out ExactLever3D exact2D);
 28576        Vector3d tangent = SolverContact.CreateTangent(normal);
 28577        Vector3d secondaryTangent =
 28578            Vector3d.Cross(normal, tangent).Normalized;
 28579        SolidBody2D? responseBody2D =
 28580            applyTo2D ? body2D : null;
 28581        Vector2d responseLinearVelocity2D =
 28582            responseBody2D == null
 28583                ? Vector2d.Zero
 28584                : normalLinearVelocity2D;
 28585        Fixed64 responseAngularVelocity2D =
 28586            responseBody2D == null
 28587                ? Fixed64.Zero
 28588                : normalAngularVelocity2D;
 28589        var normalConstraint = new ExactNormalConstraint3D(
 28590            ExactContactLever3D.CreateResponseOperand(
 28591                body3D,
 28592                normalLinearVelocity3D,
 28593                normalAngularVelocity3D,
 28594                exact3D,
 28595                -normal),
 28596            ExactContactLever2D.CreateResponseOperand(
 28597                responseBody2D,
 28598                responseLinearVelocity2D,
 28599                responseAngularVelocity2D,
 28600                exact2D,
 28601                normal),
 28602            normal,
 28603            restitution,
 28604            restitutionVelocityThreshold,
 28605            Fixed64.Zero,
 28606            Fixed64.One,
 28607            Fixed64.One);
 28608        Vector3d linearVelocity3D =
 28609            body3D == null
 28610                ? Vector3d.Zero
 28611                : ResolveLinearVelocity(body3D);
 28612        Vector3d angularVelocity3D =
 28613            body3D == null
 28614                ? Vector3d.Zero
 28615                : ResolveAngularVelocity(body3D);
 28616        Vector2d linearVelocity2D =
 28617            responseBody2D == null
 28618                ? Vector2d.Zero
 28619                : ResolveLinearVelocity(responseBody2D);
 28620        Fixed64 angularVelocity2D =
 28621            responseBody2D == null
 28622                ? Fixed64.Zero
 28623                : ResolveAngularVelocity(responseBody2D);
 28624        if (!ExactContactResponseKernel.TryGetCoulombDiskResponse(
 28625                normalConstraint,
 28626                ExactContactLever3D.CreateResponseOperand(
 28627                    body3D,
 28628                    linearVelocity3D,
 28629                    angularVelocity3D,
 28630                    exact3D,
 28631                    -tangent),
 28632                ExactContactLever2D.CreateResponseOperand(
 28633                    responseBody2D,
 28634                    linearVelocity2D,
 28635                    angularVelocity2D,
 28636                    exact2D,
 28637                    tangent),
 28638                tangent,
 28639                Fixed64.Zero,
 28640                ExactContactLever3D.CreateResponseOperand(
 28641                    body3D,
 28642                    linearVelocity3D,
 28643                    angularVelocity3D,
 28644                    exact3D,
 28645                    -secondaryTangent),
 28646                ExactContactLever2D.CreateResponseOperand(
 28647                    responseBody2D,
 28648                    linearVelocity2D,
 28649                    angularVelocity2D,
 28650                    exact2D,
 28651                    secondaryTangent),
 28652                secondaryTangent,
 28653                Fixed64.Zero,
 28654                staticFriction,
 28655                dynamicFriction,
 28656                out ExactCoulombResponse3D response))
 657        {
 2658            return false;
 659        }
 660
 26661        Vector3d linear3D =
 26662            response.FirstLinearVelocityDelta;
 26663        Vector3d angular3D =
 26664            response.FirstAngularVelocityDelta;
 26665        Vector2d linear2D = ExactContactLever2D.ToPlanar(
 26666            response.SecondLinearVelocityDelta);
 26667        Fixed64 angular2D = ExactContactLever2D.ToPlanarAngular(
 26668            response.SecondAngularVelocityDelta);
 26669        if (!CanApplyVelocityDeltas(
 26670                body3D,
 26671                body2D,
 26672                linear3D,
 26673                angular3D,
 26674                linear2D,
 26675                angular2D))
 676        {
 1677            return false;
 678        }
 679
 25680        if (!response.HasAppliedImpulse)
 1681            return true;
 682
 24683        ApplyVelocityDeltas(
 24684            body3D,
 24685            body2D,
 24686            linear3D,
 24687            angular3D,
 24688            linear2D,
 24689            angular2D);
 24690        applied = true;
 24691        return true;
 692    }
 693
 694    private static bool TryApplyImpulse(
 695        SolidBody? body3D,
 696        SolidBody2D? body2D,
 697        Vector3d axis,
 698        Vector3d relative3D,
 699        Vector2d relative2D,
 700        Fixed64 impulseScalar,
 701        bool applyTo2D)
 702    {
 19703        Vector3d linear3D = Vector3d.Zero;
 19704        Vector3d angular3D = Vector3d.Zero;
 19705        Vector2d linear2D = Vector2d.Zero;
 19706        Fixed64 angular2D = Fixed64.Zero;
 19707        Vector3d impulse3D = -axis * impulseScalar;
 19708        bool impulse3DResolved =
 19709            (impulse3D.X != Fixed64.Zero
 19710                || axis.X == Fixed64.Zero)
 19711            && (impulse3D.Y != Fixed64.Zero
 19712                || axis.Y == Fixed64.Zero)
 19713            && (impulse3D.Z != Fixed64.Zero
 19714                || axis.Z == Fixed64.Zero);
 19715        bool linear3DResolved = impulse3DResolved
 19716            && ContactNormalImpulse3D.TryComputeLinearVelocityDelta(
 19717                body3D,
 19718                impulse3D,
 19719                out linear3D);
 19720        bool angular3DResolved = impulse3DResolved
 19721            && ContactNormalImpulse3D.TryComputeAngularVelocityDelta(
 19722                body3D,
 19723                relative3D,
 19724                impulse3D,
 19725                out angular3D);
 19726        Vector2d planarAxis = axis.ToVector2d();
 19727        bool linear2DResolved = !applyTo2D
 19728            || ContactNormalImpulse2D.TryComputeLinearVelocityDelta(
 19729                body2D,
 19730                planarAxis,
 19731                impulseScalar,
 19732                out linear2D);
 19733        bool angular2DResolved = !applyTo2D
 19734            || ContactNormalImpulse2D.TryComputeAngularVelocityDelta(
 19735                body2D,
 19736                relative2D,
 19737                planarAxis,
 19738                impulseScalar,
 19739                out angular2D);
 19740        if (!(linear3DResolved
 19741            & angular3DResolved
 19742            & linear2DResolved
 19743            & angular2DResolved)
 19744            || !CanApplyVelocityDeltas(
 19745                body3D,
 19746                body2D,
 19747                linear3D,
 19748                angular3D,
 19749                linear2D,
 19750                angular2D))
 751        {
 2752            return false;
 753        }
 754
 17755        ApplyVelocityDeltas(
 17756            body3D,
 17757            body2D,
 17758            linear3D,
 17759            angular3D,
 17760            linear2D,
 17761            angular2D);
 17762        return true;
 763    }
 764
 765    private static bool TryGetCompactTangent(
 766        Vector3d relativeVelocity,
 767        Vector3d normal,
 768        out Vector3d tangent,
 769        out bool hasTangentialMotion)
 770    {
 85771        tangent = default;
 85772        hasTangentialMotion = false;
 85773        if (!ContactResponseArithmetic3D.TryDot(
 85774                relativeVelocity,
 85775                normal,
 85776                out Fixed64 normalVelocity)
 85777            || !ContactResponseArithmetic3D.TryLinearCombination(
 85778                relativeVelocity,
 85779                Fixed64.One,
 85780                normal,
 85781                -normalVelocity,
 85782                Vector3d.Zero,
 85783                Fixed64.Zero,
 85784                out Vector3d tangentVelocity))
 785        {
 1786            return false;
 787        }
 84788        if (tangentVelocity == Vector3d.Zero)
 56789            return true;
 790
 28791        Fixed64 magnitudeSquared =
 28792            tangentVelocity.MagnitudeSquared;
 28793        if (magnitudeSquared <= Fixed64.Epsilon)
 1794            return true;
 795
 27796        tangent = tangentVelocity.Normalized;
 27797        hasTangentialMotion = tangent.IsNormalized();
 27798        return hasTangentialMotion;
 799    }
 800
 801    private static bool TryGetCompactConstrainedInverseMass(
 802        SolidBody? body,
 803        Vector3d axis,
 804        out Fixed64 inverseMass)
 805    {
 27806        if (body == null)
 807        {
 1808            inverseMass = Fixed64.Zero;
 1809            return true;
 810        }
 811
 26812        inverseMass = body.GetConstrainedInverseMass(axis);
 26813        return inverseMass != Fixed64.Zero
 26814            || body.EffectiveInverseMass == Fixed64.Zero
 26815            || body.ProjectLinearMotion(axis) == Vector3d.Zero;
 816    }
 817
 818    private static bool TryGetCompactConstrainedPlanarInverseMass(
 819        SolidBody2D? body,
 820        Vector3d axis,
 821        out Fixed64 inverseMass)
 822    {
 21823        inverseMass = Fixed64.Zero;
 21824        if (body == null)
 1825            return true;
 826
 20827        Vector2d planarAxis = axis.ToVector2d();
 20828        if (!ContactNormalImpulse2D.TryGetConstrainedInverseMass(
 20829                body,
 20830                planarAxis,
 20831                out Fixed64 constrained))
 1832            return false;
 19833        if (constrained == Fixed64.Zero)
 9834            return true;
 835
 10836        Fixed64 magnitudeSquared =
 10837            planarAxis.MagnitudeSquared;
 10838        if (!Fixed64.TryMultiplyDivide(
 10839                constrained,
 10840                magnitudeSquared,
 10841                Fixed64.One,
 10842                out inverseMass))
 843        {
 1844            inverseMass = default;
 1845            return false;
 846        }
 847
 9848        return inverseMass != Fixed64.Zero;
 849    }
 850
 851    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 852    private static Vector3d ResolveLinearVelocity(SolidBody body) =>
 406853        body.ProjectLinearMotion(
 406854            body.IsKinematic
 406855                ? body.SampleContinuousCollisionLinearVelocity(Fixed64.One)
 406856                : body.LinearVelocity);
 857
 858    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 859    private static Vector2d ResolveLinearVelocity(SolidBody2D body) =>
 380860        body.ProjectLinearMotion(
 380861            body.IsKinematic
 380862                ? body.SampleContinuousCollisionLinearVelocity(Fixed64.One)
 380863                : body.LinearVelocity);
 864
 865    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 866    private static Vector3d ResolveAngularVelocity(SolidBody body) =>
 406867        body.ProjectAngularMotion(
 406868            body.IsKinematic
 406869                ? body.SampleContinuousCollisionAngularVelocity(Fixed64.One)
 406870                : body.AngularVelocity);
 871
 872    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 873    private static Fixed64 ResolveAngularVelocity(SolidBody2D body) =>
 380874        body.IsKinematic
 380875            ? body.SampleContinuousCollisionAngularVelocity(Fixed64.One)
 380876            : body.AngularVelocity;
 877
 878    private static Vector3d ResolveNormal(CollisionPairMixed pair, MixedContact contact)
 879    {
 446880        Vector3d fallback = ResolveFallbackDirection(pair, contact);
 446881        if (contact.Normal3DTo2D.MagnitudeSquared > Fixed64.Epsilon)
 882        {
 440883            Vector3d normal = contact.Normal3DTo2D.Normalized;
 440884            return fallback.MagnitudeSquared > Fixed64.Epsilon && Vector3d.Dot(normal, fallback) < Fixed64.Zero
 440885                ? -normal
 440886                : normal;
 887        }
 888
 6889        return fallback == Vector3d.Zero ? Vector3d.Up : fallback;
 890    }
 891
 892    private static Vector3d ResolveFallbackDirection(CollisionPairMixed pair, MixedContact contact)
 893    {
 446894        LSCollider2D collider2D = pair.Collider2D;
 446895        Vector3d embeddedCenter = new(
 446896            collider2D.Center.X,
 446897            collider2D.MixedSlabCenterY,
 446898            collider2D.Center.Y);
 446899        Vector3d centerDirection = embeddedCenter - pair.Collider3D.Center;
 446900        if (centerDirection.MagnitudeSquared > Fixed64.Epsilon)
 313901            return centerDirection.Normalized;
 902
 133903        if (contact.TryGetPoint2D(out Vector3d point2D)
 133904            && contact.TryGetPoint3D(out Vector3d point3D))
 905        {
 112906            Vector3d pointDirection = point2D - point3D;
 112907            if (pointDirection.MagnitudeSquared > Fixed64.Epsilon)
 3908                return pointDirection.Normalized;
 909        }
 910
 130911        return Vector3d.Zero;
 912    }
 913
 914    private static void GetExactLevers(
 915        CollisionPairMixed pair,
 916        MixedContact contact,
 917        SolidBody? body3D,
 918        SolidBody2D? body2D,
 919        out ExactLever3D exact3D,
 920        out ExactLever3D exact2D)
 921    {
 57922        ContactAnchor center3D = body3D?.GetCenterOfMassAnchor()
 57923            ?? ContactAnchor.FromWorldPoint(pair.Collider3D.Center);
 57924        exact3D = contact.Anchor3D.GetLeverFrom(center3D);
 57925        exact2D = body2D == null
 57926            ? contact.GetPlanarXZLeverFrom(
 57927                pair.Collider2D.Center,
 57928                Fixed64.Zero,
 57929                Vector2d.Zero)
 57930            : contact.GetPlanarXZLeverFrom(
 57931                body2D.Position,
 57932                body2D.Rotation,
 57933                body2D.LocalCenterOfMassOffset);
 57934    }
 935
 936    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 937    private static Fixed64 GetConstrainedInverseMass(SolidBody? body, Vector3d axis) =>
 430938        body?.GetConstrainedInverseMass(axis) ?? Fixed64.Zero;
 939
 940    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 941    private static Fixed64 GetConstrainedPlanarInverseMass(SolidBody2D? body, Vector3d axis)
 942    {
 430943        if (body == null)
 41944            return Fixed64.Zero;
 945
 389946        Vector2d planarAxis = axis.ToVector2d();
 389947        if (planarAxis == Vector2d.Zero)
 157948            return Fixed64.Zero;
 949
 232950        return body.GetConstrainedInverseMass(planarAxis) * planarAxis.MagnitudeSquared;
 951    }
 952
 953}

Methods/Properties

.cctor()
Resolve(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact)
Resolve(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact,System.Int32,System.Int32,System.Boolean)
HasPlanarResponseCoupling(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact)
ApplyPositionCorrection(Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetNormalImpulse(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact,Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,System.Boolean,System.Boolean,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactNormalImpulseResultMixed&)
CanApplyNormalVelocityDeltas(Gravitas.SolidBody,Gravitas.SolidBody2D,Gravitas.CollisionHandling.ContactNormalImpulseResultMixed)
HasVelocityDelta(Gravitas.CollisionHandling.ContactNormalImpulseResultMixed)
ApplyNormalVelocityDeltas(Gravitas.SolidBody,Gravitas.SolidBody2D,Gravitas.CollisionHandling.ContactNormalImpulseResultMixed)
CanApplyVelocityDeltas(Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64)
ApplyVelocityDeltas(Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64)
TryApplyFrictionImpulse(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact,Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,Gravitas.Materials.PhysicsMaterial,Gravitas.Materials.PhysicsMaterial,Gravitas.CollisionHandling.ContactNormalImpulseResultMixed,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean,System.Boolean,System.Boolean,System.Boolean&)
TryGetCompactFrictionImpulse(Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
TryApplyFrictionImpulseExact(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact,Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean,System.Boolean&)
TryApplyImpulse(Gravitas.SolidBody,Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,System.Boolean)
TryGetCompactTangent(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,System.Boolean&)
TryGetCompactConstrainedInverseMass(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64&)
TryGetCompactConstrainedPlanarInverseMass(Gravitas.SolidBody2D,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64&)
ResolveLinearVelocity(Gravitas.SolidBody)
ResolveLinearVelocity(Gravitas.SolidBody2D)
ResolveAngularVelocity(Gravitas.SolidBody)
ResolveAngularVelocity(Gravitas.SolidBody2D)
ResolveNormal(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact)
ResolveFallbackDirection(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact)
GetExactLevers(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.MixedContact,Gravitas.SolidBody,Gravitas.SolidBody2D,Gravitas.CollisionHandling.ExactLever3D&,Gravitas.CollisionHandling.ExactLever3D&)
GetConstrainedInverseMass(Gravitas.SolidBody,FixedMathSharp.Vector3d)
GetConstrainedPlanarInverseMass(Gravitas.SolidBody2D,FixedMathSharp.Vector3d)