< Summary

Information
Class: Gravitas.CollisionHandling.ContactNormalImpulse3D
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Response/3D/ContactNormalImpulse3D.cs
Line coverage
100%
Covered lines: 489
Uncovered lines: 0
Coverable lines: 489
Total lines: 919
Line coverage: 100%
Branch coverage
100%
Covered branches: 104
Total branches: 104
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/3D/ContactNormalImpulse3D.cs

#LineLine coverage
 1//=======================================================================
 2// ContactNormalImpulse3D.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 System.Runtime.CompilerServices;
 11
 12namespace Gravitas.CollisionHandling;
 13
 14/// <summary>
 15/// Side-effect-free 3D contact-normal impulse result for two participants.
 16/// </summary>
 17internal readonly struct ContactNormalImpulseResult3D
 18{
 19    public ContactNormalImpulseResult3D(
 20        Fixed64 normalVelocity,
 21        Fixed64 impulseScalar,
 22        Vector3d linearVelocityDeltaA,
 23        Vector3d angularVelocityDeltaA,
 24        Vector3d linearVelocityDeltaB,
 25        Vector3d angularVelocityDeltaB)
 26        : this(
 27            normalVelocity,
 28            impulseScalar,
 29            impulseScalar,
 30            linearVelocityDeltaA,
 31            angularVelocityDeltaA,
 32            linearVelocityDeltaB,
 33            angularVelocityDeltaB)
 34    {
 35    }
 36
 37    public ContactNormalImpulseResult3D(
 38        Fixed64 normalVelocity,
 39        Fixed64 impulseScalar,
 40        Fixed64 appliedImpulseScalar,
 41        Vector3d linearVelocityDeltaA,
 42        Vector3d angularVelocityDeltaA,
 43        Vector3d linearVelocityDeltaB,
 44        Vector3d angularVelocityDeltaB,
 45        bool hasRepresentableNormalVelocity = true,
 46        bool hasRepresentableAppliedImpulse = true)
 47    {
 48        NormalVelocity = normalVelocity;
 49        ImpulseScalar = impulseScalar;
 50        AppliedImpulseScalar = appliedImpulseScalar;
 51        LinearVelocityDeltaA = linearVelocityDeltaA;
 52        AngularVelocityDeltaA = angularVelocityDeltaA;
 53        LinearVelocityDeltaB = linearVelocityDeltaB;
 54        AngularVelocityDeltaB = angularVelocityDeltaB;
 55        HasRepresentableNormalVelocity = hasRepresentableNormalVelocity;
 56        HasRepresentableAppliedImpulse = hasRepresentableAppliedImpulse;
 57    }
 58
 59    public Fixed64 NormalVelocity { get; }
 60
 61    public Fixed64 ImpulseScalar { get; }
 62
 63    public Fixed64 AppliedImpulseScalar { get; }
 64
 65    public bool HasRepresentableNormalVelocity { get; }
 66
 67    public bool HasRepresentableAppliedImpulse { get; }
 68
 69    public Vector3d LinearVelocityDeltaA { get; }
 70
 71    public Vector3d AngularVelocityDeltaA { get; }
 72
 73    public Vector3d LinearVelocityDeltaB { get; }
 74
 75    public Vector3d AngularVelocityDeltaB { get; }
 76}
 77
 78/// <summary>
 79/// Side-effect-free 3D velocity deltas for a normal response whose impulse
 80/// scalar does not need to be representable.
 81/// </summary>
 82internal readonly struct ContactNormalVelocityDeltaResult3D
 83{
 84    public ContactNormalVelocityDeltaResult3D(
 85        Fixed64 normalVelocity,
 86        Vector3d linearVelocityDeltaA,
 87        Vector3d angularVelocityDeltaA,
 88        Vector3d linearVelocityDeltaB,
 89        Vector3d angularVelocityDeltaB)
 90        : this(
 91            normalVelocity,
 92            linearVelocityDeltaA,
 93            angularVelocityDeltaA,
 94            linearVelocityDeltaB,
 95            angularVelocityDeltaB,
 96            normalVelocity < Fixed64.Zero,
 97            hasRepresentableNormalVelocity: true)
 98    {
 99    }
 100
 101    public ContactNormalVelocityDeltaResult3D(
 102        Fixed64 normalVelocity,
 103        Vector3d linearVelocityDeltaA,
 104        Vector3d angularVelocityDeltaA,
 105        Vector3d linearVelocityDeltaB,
 106        Vector3d angularVelocityDeltaB,
 107        bool isClosing,
 108        bool hasRepresentableNormalVelocity)
 109    {
 110        NormalVelocity = normalVelocity;
 111        LinearVelocityDeltaA = linearVelocityDeltaA;
 112        AngularVelocityDeltaA = angularVelocityDeltaA;
 113        LinearVelocityDeltaB = linearVelocityDeltaB;
 114        AngularVelocityDeltaB = angularVelocityDeltaB;
 115        IsClosing = isClosing;
 116        HasRepresentableNormalVelocity = hasRepresentableNormalVelocity;
 117    }
 118
 119    public Fixed64 NormalVelocity { get; }
 120
 121    public bool IsClosing { get; }
 122
 123    public bool HasRepresentableNormalVelocity { get; }
 124
 125    public Vector3d LinearVelocityDeltaA { get; }
 126
 127    public Vector3d AngularVelocityDeltaA { get; }
 128
 129    public Vector3d LinearVelocityDeltaB { get; }
 130
 131    public Vector3d AngularVelocityDeltaB { get; }
 132}
 133
 134internal readonly struct ContactEffectiveMassTerms3D
 135{
 136    internal ContactEffectiveMassTerms3D(
 137        Fixed64 linearA,
 138        Fixed64 linearB,
 139        Fixed64 angularA,
 140        Fixed64 angularB)
 141    {
 142        LinearA = linearA;
 143        LinearB = linearB;
 144        AngularA = angularA;
 145        AngularB = angularB;
 146    }
 147
 148    internal Fixed64 LinearA { get; }
 149
 150    internal Fixed64 LinearB { get; }
 151
 152    internal Fixed64 AngularA { get; }
 153
 154    internal Fixed64 AngularB { get; }
 155
 156    internal Fixed64 SaturatedSum =>
 157        LinearA + LinearB + AngularA + AngularB;
 158
 159    internal bool TryGetValue(out Fixed64 value)
 160    {
 161        bool resolved =
 162            Fixed64.TryAdd(LinearA, LinearB, out Fixed64 linear);
 163        resolved &= Fixed64.TryAdd(
 164            linear,
 165            AngularA,
 166            out Fixed64 first);
 167        resolved &= Fixed64.TryAdd(first, AngularB, out value);
 168        if (!resolved)
 169            value = default;
 170        return resolved;
 171    }
 172}
 173
 174/// <summary>
 175/// Calculates allocation-free 3D contact-point normal response without mutating either body.
 176/// </summary>
 177internal static class ContactNormalImpulse3D
 178{
 179    internal static bool TryCalculateVelocityDeltas(
 180        SolidBody? bodyA,
 181        Vector3d linearVelocityA,
 182        Vector3d angularVelocityA,
 183        Vector3d relativeContactPointA,
 184        SolidBody? bodyB,
 185        Vector3d linearVelocityB,
 186        Vector3d angularVelocityB,
 187        Vector3d relativeContactPointB,
 188        Vector3d normal,
 189        Fixed64 restitution,
 190        Fixed64 restitutionVelocityThreshold,
 191        out ContactNormalVelocityDeltaResult3D result)
 192    {
 84193        result = default;
 84194        if (!TryComputeNormalVelocity(
 84195                linearVelocityA,
 84196                angularVelocityA,
 84197                relativeContactPointA,
 84198                linearVelocityB,
 84199                angularVelocityB,
 84200                relativeContactPointB,
 84201                normal,
 84202                out Fixed64 normalVelocity))
 203        {
 4204            return false;
 205        }
 206
 80207        if (normalVelocity >= Fixed64.Zero)
 208        {
 3209            result = ZeroVelocityDelta(normalVelocity);
 3210            return true;
 211        }
 212
 77213        if (!TryComputeDenominator(
 77214                bodyA,
 77215                relativeContactPointA,
 77216                bodyB,
 77217                relativeContactPointB,
 77218                normal,
 77219                out ContactEffectiveMassTerms3D denominator)
 77220            || denominator.SaturatedSum <= Fixed64.Zero)
 221        {
 49222            return false;
 223        }
 224
 28225        Fixed64 appliedRestitution = normalVelocity < -restitutionVelocityThreshold
 28226            ? restitution
 28227            : Fixed64.Zero;
 28228        Fixed64 responseFactor = -(Fixed64.One + appliedRestitution);
 28229        bool linearAResolved = TryResolveVelocityDelta(
 28230            bodyA?.ProjectLinearMotion(-normal) ?? Vector3d.Zero,
 28231            normalVelocity,
 28232            responseFactor,
 28233            bodyA?.EffectiveInverseMass ?? Fixed64.Zero,
 28234            denominator,
 28235            out Vector3d linearVelocityDeltaA);
 28236        bool angularAResolved = TryResolveAngularVelocityDelta(
 28237                bodyA,
 28238                relativeContactPointA,
 28239                -normal,
 28240                normalVelocity,
 28241                responseFactor,
 28242                denominator,
 28243                out Vector3d angularVelocityDeltaA);
 28244        bool linearBResolved = TryResolveVelocityDelta(
 28245            bodyB?.ProjectLinearMotion(normal) ?? Vector3d.Zero,
 28246            normalVelocity,
 28247            responseFactor,
 28248            bodyB?.EffectiveInverseMass ?? Fixed64.Zero,
 28249            denominator,
 28250            out Vector3d linearVelocityDeltaB);
 28251        bool angularBResolved = TryResolveAngularVelocityDelta(
 28252                bodyB,
 28253                relativeContactPointB,
 28254                normal,
 28255                normalVelocity,
 28256                responseFactor,
 28257                denominator,
 28258                out Vector3d angularVelocityDeltaB);
 28259        if (!(linearAResolved
 28260                & angularAResolved
 28261                & linearBResolved
 28262                & angularBResolved))
 263        {
 2264            return false;
 265        }
 266
 26267        result = new ContactNormalVelocityDeltaResult3D(
 26268            normalVelocity,
 26269            linearVelocityDeltaA,
 26270            angularVelocityDeltaA,
 26271            linearVelocityDeltaB,
 26272            angularVelocityDeltaB);
 26273        return true;
 274    }
 275
 276    internal static bool TryCalculateVelocityDeltasExact(
 277        SolidBody? bodyA,
 278        Vector3d linearVelocityA,
 279        Vector3d angularVelocityA,
 280        in ExactLever3D relativeContactPointA,
 281        SolidBody? bodyB,
 282        Vector3d linearVelocityB,
 283        Vector3d angularVelocityB,
 284        in ExactLever3D relativeContactPointB,
 285        Vector3d normal,
 286        Fixed64 restitution,
 287        Fixed64 restitutionVelocityThreshold,
 288        out ContactNormalVelocityDeltaResult3D result)
 289    {
 57290        result = default;
 57291        if (!ExactContactLever3D.TryGetNormalResponse(
 57292                bodyA,
 57293                linearVelocityA,
 57294                angularVelocityA,
 57295                relativeContactPointA,
 57296                bodyB,
 57297                linearVelocityB,
 57298                angularVelocityB,
 57299                relativeContactPointB,
 57300                normal,
 57301                restitution,
 57302                restitutionVelocityThreshold,
 57303                out ExactNormalResponse3D response))
 304        {
 5305            return false;
 306        }
 307
 52308        bool hasNormalVelocity =
 52309            response.TryGetNormalVelocity(out Fixed64 normalVelocity);
 52310        result = new ContactNormalVelocityDeltaResult3D(
 52311            normalVelocity,
 52312            response.FirstLinearVelocityDelta,
 52313            response.FirstAngularVelocityDelta,
 52314            response.SecondLinearVelocityDelta,
 52315            response.SecondAngularVelocityDelta,
 52316            response.IsClosing,
 52317            hasNormalVelocity);
 52318        return true;
 319    }
 320
 321    internal static ContactNormalImpulseResult3D CalculateAccumulatedDelta(
 322        SolidBody? bodyA,
 323        Vector3d linearVelocityA,
 324        Vector3d angularVelocityA,
 325        Vector3d relativeContactPointA,
 326        SolidBody? bodyB,
 327        Vector3d linearVelocityB,
 328        Vector3d angularVelocityB,
 329        Vector3d relativeContactPointB,
 330        Vector3d normal,
 331        Fixed64 restitution,
 332        Fixed64 restitutionVelocityThreshold,
 333        Fixed64 accumulatedImpulse,
 334        Fixed64 positiveImpulseScale,
 335        Fixed64 negativeImpulseScale)
 336    {
 1337        _ = TryCalculateAccumulatedDelta(
 1338            bodyA,
 1339            linearVelocityA,
 1340            angularVelocityA,
 1341            relativeContactPointA,
 1342            bodyB,
 1343            linearVelocityB,
 1344            angularVelocityB,
 1345            relativeContactPointB,
 1346            normal,
 1347            restitution,
 1348            restitutionVelocityThreshold,
 1349            accumulatedImpulse,
 1350            positiveImpulseScale,
 1351            negativeImpulseScale,
 1352            out ContactNormalImpulseResult3D result);
 1353        return result;
 354    }
 355
 356    internal static bool TryCalculateAccumulatedDelta(
 357        SolidBody? bodyA,
 358        Vector3d linearVelocityA,
 359        Vector3d angularVelocityA,
 360        Vector3d relativeContactPointA,
 361        SolidBody? bodyB,
 362        Vector3d linearVelocityB,
 363        Vector3d angularVelocityB,
 364        Vector3d relativeContactPointB,
 365        Vector3d normal,
 366        Fixed64 restitution,
 367        Fixed64 restitutionVelocityThreshold,
 368        Fixed64 accumulatedImpulse,
 369        Fixed64 positiveImpulseScale,
 370        Fixed64 negativeImpulseScale,
 371        out ContactNormalImpulseResult3D result)
 372    {
 9052373        result = default;
 9052374        if (!TryComputeNormalVelocity(
 9052375                linearVelocityA,
 9052376                angularVelocityA,
 9052377                relativeContactPointA,
 9052378                linearVelocityB,
 9052379                angularVelocityB,
 9052380                relativeContactPointB,
 9052381                normal,
 9052382                out Fixed64 normalVelocity)
 9052383            || !TryComputeDenominator(
 9052384                bodyA,
 9052385                relativeContactPointA,
 9052386                bodyB,
 9052387                relativeContactPointB,
 9052388                normal,
 9052389                out ContactEffectiveMassTerms3D denominator))
 390        {
 1353391            return false;
 392        }
 393
 7699394        if (denominator.SaturatedSum <= Fixed64.Zero)
 395        {
 2396            result = Zero(normalVelocity);
 2397            return true;
 398        }
 399
 7697400        if (!TryCalculateAccumulatedImpulseDelta(
 7697401                normalVelocity,
 7697402                denominator,
 7697403                restitution,
 7697404                restitutionVelocityThreshold,
 7697405                accumulatedImpulse,
 7697406                positiveImpulseScale,
 7697407                negativeImpulseScale,
 7697408                out Fixed64 impulseScalar))
 409        {
 5410            return false;
 411        }
 412
 7692413        if (impulseScalar == Fixed64.Zero)
 414        {
 1875415            result = Zero(normalVelocity);
 1875416            return true;
 417        }
 418
 5817419        Vector3d impulseB = normal * impulseScalar;
 5817420        Vector3d impulseA = -impulseB;
 5817421        bool linearAResolved = TryComputeLinearVelocityDelta(
 5817422            bodyA,
 5817423            impulseA,
 5817424            out Vector3d linearVelocityDeltaA);
 5817425        bool angularAResolved = TryComputeAngularVelocityDelta(
 5817426            bodyA,
 5817427            relativeContactPointA,
 5817428            impulseA,
 5817429            out Vector3d angularVelocityDeltaA);
 5817430        bool linearBResolved = TryComputeLinearVelocityDelta(
 5817431            bodyB,
 5817432            impulseB,
 5817433            out Vector3d linearVelocityDeltaB);
 5817434        bool angularBResolved = TryComputeAngularVelocityDelta(
 5817435            bodyB,
 5817436            relativeContactPointB,
 5817437            impulseB,
 5817438            out Vector3d angularVelocityDeltaB);
 5817439        if (!(linearAResolved
 5817440            & angularAResolved
 5817441            & linearBResolved
 5817442            & angularBResolved))
 443        {
 1444            return false;
 445        }
 446
 5816447        result = new ContactNormalImpulseResult3D(
 5816448            normalVelocity,
 5816449            impulseScalar,
 5816450            linearVelocityDeltaA,
 5816451            angularVelocityDeltaA,
 5816452            linearVelocityDeltaB,
 5816453            angularVelocityDeltaB);
 5816454        return true;
 455    }
 456
 457    internal static bool TryCalculateAccumulatedDeltaExact(
 458        SolidBody? bodyA,
 459        Vector3d linearVelocityA,
 460        Vector3d angularVelocityA,
 461        in ExactLever3D relativeContactPointA,
 462        SolidBody? bodyB,
 463        Vector3d linearVelocityB,
 464        Vector3d angularVelocityB,
 465        in ExactLever3D relativeContactPointB,
 466        Vector3d normal,
 467        Fixed64 restitution,
 468        Fixed64 restitutionVelocityThreshold,
 469        Fixed64 accumulatedImpulse,
 470        Fixed64 positiveImpulseScale,
 471        Fixed64 negativeImpulseScale,
 472        out ContactNormalImpulseResult3D result)
 473    {
 1394474        result = default;
 1394475        if (!ExactContactLever3D.TryGetAccumulatedNormalResponse(
 1394476                bodyA,
 1394477                linearVelocityA,
 1394478                angularVelocityA,
 1394479                relativeContactPointA,
 1394480                bodyB,
 1394481                linearVelocityB,
 1394482                angularVelocityB,
 1394483                relativeContactPointB,
 1394484                normal,
 1394485                restitution,
 1394486                restitutionVelocityThreshold,
 1394487                accumulatedImpulse,
 1394488                positiveImpulseScale,
 1394489                negativeImpulseScale,
 1394490                out ExactNormalResponse3D response))
 491        {
 5492            return false;
 493        }
 494
 1389495        bool hasNormalVelocity =
 1389496            response.TryGetNormalVelocity(out Fixed64 normalVelocity);
 1389497        bool hasAppliedImpulse =
 1389498            response.TryGetAppliedImpulse(out Fixed64 appliedImpulse);
 499        Fixed64 impulseScalar;
 1389500        if (response.TryGetAccumulatedImpulse(
 1389501                out Fixed64 newAccumulatedImpulse))
 502        {
 503            // Both values are nonnegative, so their difference is always in
 504            // [-Fixed64.MaxValue, Fixed64.MaxValue].
 1385505            impulseScalar = newAccumulatedImpulse - accumulatedImpulse;
 506        }
 507        else
 508        {
 4509            impulseScalar = -accumulatedImpulse;
 510        }
 511
 1389512        result = new ContactNormalImpulseResult3D(
 1389513            normalVelocity,
 1389514            impulseScalar,
 1389515            appliedImpulse,
 1389516            response.FirstLinearVelocityDelta,
 1389517            response.FirstAngularVelocityDelta,
 1389518            response.SecondLinearVelocityDelta,
 1389519            response.SecondAngularVelocityDelta,
 1389520            hasNormalVelocity,
 1389521            hasAppliedImpulse);
 1389522        return true;
 523    }
 524
 525    private static bool TryCalculateAccumulatedImpulseDelta(
 526        Fixed64 normalVelocity,
 527        in ContactEffectiveMassTerms3D denominator,
 528        Fixed64 restitution,
 529        Fixed64 restitutionVelocityThreshold,
 530        Fixed64 accumulatedImpulse,
 531        Fixed64 positiveImpulseScale,
 532        Fixed64 negativeImpulseScale,
 533        out Fixed64 impulseDelta)
 534    {
 7697535        Fixed64 appliedRestitution =
 7697536            normalVelocity < -restitutionVelocityThreshold
 7697537                ? restitution
 7697538                : Fixed64.Zero;
 7697539        Fixed64 responseFactor = -(Fixed64.One + appliedRestitution);
 7697540        Fixed64 impulseScale = normalVelocity < Fixed64.Zero
 7697541            ? positiveImpulseScale
 7697542            : negativeImpulseScale;
 7697543        if (!Fixed64.TryMultiplyDivideBySum(
 7697544                normalVelocity,
 7697545                responseFactor,
 7697546                impulseScale,
 7697547                Fixed64.One,
 7697548                denominator.LinearA,
 7697549                denominator.LinearB,
 7697550                denominator.AngularA,
 7697551                denominator.AngularB,
 7697552                out Fixed64 scaledImpulse))
 553        {
 3554            impulseDelta = default;
 3555            return normalVelocity >= Fixed64.Zero
 3556                && responseFactor <= Fixed64.Zero
 3557                && impulseScale >= Fixed64.Zero
 3558                && accumulatedImpulse >= Fixed64.Zero
 3559                && Fixed64.TrySubtract(
 3560                    Fixed64.Zero,
 3561                    accumulatedImpulse,
 3562                    out impulseDelta);
 563        }
 564
 7694565        if (!Fixed64.TryAdd(
 7694566                accumulatedImpulse,
 7694567                scaledImpulse,
 7694568                out Fixed64 accumulated))
 569        {
 1570            impulseDelta = default;
 1571            return false;
 572        }
 573
 7693574        return Fixed64.TrySubtract(
 7693575            FixedMath.Max(Fixed64.Zero, accumulated),
 7693576            accumulatedImpulse,
 7693577            out impulseDelta);
 578    }
 579
 580    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 581    private static Fixed64 GetConstrainedInverseMass(SolidBody? body, Vector3d axis) =>
 15458582        body?.GetConstrainedInverseMass(axis) ?? Fixed64.Zero;
 583
 584    internal static bool TryComputeNormalVelocity(
 585        Vector3d linearVelocityA,
 586        Vector3d angularVelocityA,
 587        Vector3d relativeContactPointA,
 588        Vector3d linearVelocityB,
 589        Vector3d angularVelocityB,
 590        Vector3d relativeContactPointB,
 591        Vector3d normal,
 592        out Fixed64 normalVelocity)
 593    {
 9455594        if (ContactResponseArithmetic3D.CanUseFastPointVelocity(
 9455595                linearVelocityA,
 9455596                angularVelocityA,
 9455597                relativeContactPointA,
 9455598                linearVelocityB,
 9455599                angularVelocityB,
 9455600                relativeContactPointB,
 9455601                normal))
 602        {
 9425603            Vector3d fastPointVelocityA = linearVelocityA
 9425604                + Vector3d.Cross(
 9425605                    angularVelocityA,
 9425606                    relativeContactPointA);
 9425607            Vector3d fastPointVelocityB = linearVelocityB
 9425608                + Vector3d.Cross(
 9425609                    angularVelocityB,
 9425610                    relativeContactPointB);
 9425611            normalVelocity = Vector3d.Dot(
 9425612                fastPointVelocityB - fastPointVelocityA,
 9425613                normal);
 9425614            return true;
 615        }
 616
 30617        bool resolved = ContactResponseArithmetic3D.TryCross(
 30618                angularVelocityA,
 30619                relativeContactPointA,
 30620                out Vector3d angularA)
 30621            & ContactResponseArithmetic3D.TryCross(
 30622                angularVelocityB,
 30623                relativeContactPointB,
 30624                out Vector3d angularB);
 30625        if (!resolved
 30626            || !Vector3d.TryAdd(
 30627                linearVelocityA,
 30628                angularA,
 30629                out Vector3d pointVelocityA)
 30630            || !Vector3d.TryAdd(
 30631                linearVelocityB,
 30632                angularB,
 30633                out Vector3d pointVelocityB)
 30634            || !Vector3d.TrySubtract(
 30635                pointVelocityB,
 30636                pointVelocityA,
 30637                out Vector3d relativeVelocity))
 638        {
 10639            normalVelocity = default;
 10640            return false;
 641        }
 642
 20643        return ContactResponseArithmetic3D.TryDot(
 20644            relativeVelocity,
 20645            normal,
 20646            out normalVelocity);
 647    }
 648
 649    private static bool TryComputeDenominator(
 650        SolidBody? bodyA,
 651        Vector3d relativeContactPointA,
 652        SolidBody? bodyB,
 653        Vector3d relativeContactPointB,
 654        Vector3d normal,
 655        out ContactEffectiveMassTerms3D denominator)
 656    {
 9125657        denominator = default;
 9125658        bool angularAResolved = TryComputeAngularDenominator(
 9125659            bodyA,
 9125660            relativeContactPointA,
 9125661            normal,
 9125662            out Fixed64 angularA);
 9125663        bool angularBResolved = TryComputeAngularDenominator(
 9125664            bodyB,
 9125665            relativeContactPointB,
 9125666            normal,
 9125667            out Fixed64 angularB);
 9125668        if (!(angularAResolved & angularBResolved))
 669        {
 1396670            return false;
 671        }
 672
 7729673        denominator = new ContactEffectiveMassTerms3D(
 7729674            GetConstrainedInverseMass(bodyA, normal),
 7729675            GetConstrainedInverseMass(bodyB, normal),
 7729676            angularA,
 7729677            angularB);
 7729678        return true;
 679    }
 680
 681    internal static bool TryComputeAngularDenominator(
 682        SolidBody? body,
 683        Vector3d relativeContactPoint,
 684        Vector3d axis,
 685        out Fixed64 denominator)
 686    {
 31035687        denominator = Fixed64.Zero;
 31035688        if (body?.CanRotate != true)
 864689            return true;
 690
 30171691        Fixed3x3 inverseInertia =
 30171692            body.GetConstrainedInverseInertiaTensor();
 30171693        if (ContactResponseArithmetic3D.CanUseFastAngularResponse(
 30171694                relativeContactPoint,
 30171695                axis,
 30171696                inverseInertia))
 697        {
 30115698            Vector3d fastTorqueAxis =
 30115699                Vector3d.Cross(relativeContactPoint, axis);
 30115700            Vector3d fastAngularVelocityDelta =
 30115701                Fixed3x3.TransformDirection(
 30115702                    inverseInertia,
 30115703                    fastTorqueAxis);
 30115704            Vector3d fastAngular = Vector3d.Cross(
 30115705                fastAngularVelocityDelta,
 30115706                relativeContactPoint);
 30115707            denominator = Vector3d.Dot(fastAngular, axis);
 30115708            bool responsePreserved =
 30115709                ContactResponseArithmetic3D.PreservesNonzeroCrossProduct(
 30115710                    relativeContactPoint,
 30115711                    axis,
 30115712                    fastTorqueAxis);
 30115713            responsePreserved &=
 30115714                ContactResponseArithmetic3D
 30115715                .PreservesNonzeroTransformDirection(
 30115716                    inverseInertia,
 30115717                    fastTorqueAxis,
 30115718                    fastAngularVelocityDelta);
 30115719            responsePreserved &=
 30115720                ContactResponseArithmetic3D
 30115721                .PreservesNonzeroCrossProduct(
 30115722                    fastAngularVelocityDelta,
 30115723                    relativeContactPoint,
 30115724                    fastAngular);
 30115725            responsePreserved &=
 30115726                ContactResponseArithmetic3D.PreservesNonzeroDotProduct(
 30115727                    fastAngular,
 30115728                    axis,
 30115729                    denominator);
 30115730            if (!responsePreserved)
 731            {
 1412732                denominator = default;
 1412733                return false;
 734            }
 735
 28703736            denominator = FixedMath.Max(denominator, Fixed64.Zero);
 28703737            return true;
 738        }
 739
 56740        if (!ContactResponseArithmetic3D.TryCross(
 56741                relativeContactPoint,
 56742                axis,
 56743                out Vector3d torqueAxis)
 56744            || !ContactResponseArithmetic3D.TryTransformDirection(
 56745                inverseInertia,
 56746                torqueAxis,
 56747                out Vector3d angularVelocityDelta)
 56748            || !ContactResponseArithmetic3D.TryCross(
 56749                angularVelocityDelta,
 56750                relativeContactPoint,
 56751                out Vector3d angular)
 56752            || !ContactResponseArithmetic3D.TryDot(
 56753                angular,
 56754                axis,
 56755                out denominator))
 756        {
 47757            denominator = default;
 47758            return false;
 759        }
 760
 9761        denominator = FixedMath.Max(denominator, Fixed64.Zero);
 9762        return true;
 763    }
 764
 765    internal static bool TryComputeLinearVelocityDelta(
 766        SolidBody? body,
 767        Vector3d impulse,
 768        out Vector3d velocityDelta)
 769    {
 11742770        if (body != null)
 11738771            impulse = body.ProjectLinearMotion(impulse);
 772
 11742773        if (!ContactResponseArithmetic3D.TryScale(
 11742774                impulse,
 11742775                body?.EffectiveInverseMass ?? Fixed64.Zero,
 11742776                out velocityDelta))
 777        {
 2778            return false;
 779        }
 780
 11740781        return true;
 782    }
 783
 784    internal static bool TryComputeAngularVelocityDelta(
 785        SolidBody? body,
 786        Vector3d relativeContactPoint,
 787        Vector3d impulse,
 788        out Vector3d velocityDelta)
 789    {
 11783790        velocityDelta = Vector3d.Zero;
 11783791        if (body?.CanRotate != true)
 519792            return true;
 793
 11264794        Fixed3x3 inverseInertia =
 11264795            body.GetConstrainedInverseInertiaTensor();
 11264796        if (ContactResponseArithmetic3D.CanUseFastAngularResponse(
 11264797                relativeContactPoint,
 11264798                impulse,
 11264799                inverseInertia))
 800        {
 11257801            if (!ContactResponseArithmetic3D.TryCross(
 11257802                    relativeContactPoint,
 11257803                    impulse,
 11257804                    out Vector3d torqueAxis))
 1805                return false;
 806
 11256807            velocityDelta = Fixed3x3.TransformDirection(
 11256808                inverseInertia,
 11256809                torqueAxis);
 11256810            return ContactResponseArithmetic3D
 11256811                .PreservesNonzeroTransformDirection(
 11256812                    inverseInertia,
 11256813                    torqueAxis,
 11256814                    velocityDelta);
 815        }
 816
 7817        bool torqueResolved = ContactResponseArithmetic3D.TryCross(
 7818                relativeContactPoint,
 7819                impulse,
 7820                out Vector3d checkedTorqueAxis);
 7821        bool transformResolved =
 7822            ContactResponseArithmetic3D.TryTransformDirection(
 7823                inverseInertia,
 7824                checkedTorqueAxis,
 7825                out velocityDelta);
 7826        return torqueResolved & transformResolved;
 827    }
 828
 829    private static bool TryResolveAngularVelocityDelta(
 830        SolidBody? body,
 831        Vector3d relativeContactPoint,
 832        Vector3d signedNormal,
 833        Fixed64 normalVelocity,
 834        Fixed64 responseFactor,
 835        in ContactEffectiveMassTerms3D denominator,
 836        out Vector3d velocityDelta)
 837    {
 56838        velocityDelta = Vector3d.Zero;
 56839        if (body?.CanRotate != true)
 16840            return true;
 841
 40842        bool responseResolved = TryComputeAngularVelocityDelta(
 40843            body,
 40844            relativeContactPoint,
 40845            signedNormal,
 40846            out Vector3d response);
 40847        bool deltaResolved = TryResolveVelocityDelta(
 40848            response,
 40849            normalVelocity,
 40850            responseFactor,
 40851            Fixed64.One,
 40852            denominator,
 40853            out velocityDelta);
 40854        return responseResolved & deltaResolved;
 855    }
 856
 857    private static bool TryResolveVelocityDelta(
 858        Vector3d response,
 859        Fixed64 firstMultiplier,
 860        Fixed64 secondMultiplier,
 861        Fixed64 thirdMultiplier,
 862        in ContactEffectiveMassTerms3D denominator,
 863        out Vector3d velocityDelta)
 864    {
 96865        bool xResolved = Fixed64.TryMultiplyDivideBySum(
 96866            response.X,
 96867            firstMultiplier,
 96868            secondMultiplier,
 96869            thirdMultiplier,
 96870            denominator.LinearA,
 96871            denominator.LinearB,
 96872            denominator.AngularA,
 96873            denominator.AngularB,
 96874            out Fixed64 x);
 96875        bool yResolved = Fixed64.TryMultiplyDivideBySum(
 96876            response.Y,
 96877            firstMultiplier,
 96878            secondMultiplier,
 96879            thirdMultiplier,
 96880            denominator.LinearA,
 96881            denominator.LinearB,
 96882            denominator.AngularA,
 96883            denominator.AngularB,
 96884            out Fixed64 y);
 96885        bool zResolved = Fixed64.TryMultiplyDivideBySum(
 96886            response.Z,
 96887            firstMultiplier,
 96888            secondMultiplier,
 96889            thirdMultiplier,
 96890            denominator.LinearA,
 96891            denominator.LinearB,
 96892            denominator.AngularA,
 96893            denominator.AngularB,
 96894            out Fixed64 z);
 96895        velocityDelta = xResolved & yResolved & zResolved
 96896            ? new Vector3d(x, y, z)
 96897            : default;
 96898        return xResolved & yResolved & zResolved;
 899    }
 900
 901    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 902    private static ContactNormalImpulseResult3D Zero(Fixed64 normalVelocity) =>
 1877903        new(
 1877904            normalVelocity,
 1877905            Fixed64.Zero,
 1877906            Vector3d.Zero,
 1877907            Vector3d.Zero,
 1877908            Vector3d.Zero,
 1877909            Vector3d.Zero);
 910
 911    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 912    private static ContactNormalVelocityDeltaResult3D ZeroVelocityDelta(Fixed64 normalVelocity) =>
 3913        new(
 3914            normalVelocity,
 3915            Vector3d.Zero,
 3916            Vector3d.Zero,
 3917            Vector3d.Zero,
 3918            Vector3d.Zero);
 919}

Methods/Properties

TryCalculateVelocityDeltas(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactNormalVelocityDeltaResult3D&)
TryCalculateVelocityDeltasExact(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.CollisionHandling.ExactLever3D&,Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.CollisionHandling.ExactLever3D&,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactNormalVelocityDeltaResult3D&)
CalculateAccumulatedDelta(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryCalculateAccumulatedDelta(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactNormalImpulseResult3D&)
TryCalculateAccumulatedDeltaExact(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.CollisionHandling.ExactLever3D&,Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.CollisionHandling.ExactLever3D&,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactNormalImpulseResult3D&)
TryCalculateAccumulatedImpulseDelta(FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactEffectiveMassTerms3D&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
GetConstrainedInverseMass(Gravitas.SolidBody,FixedMathSharp.Vector3d)
TryComputeNormalVelocity(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64&)
TryComputeDenominator(Gravitas.SolidBody,FixedMathSharp.Vector3d,Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.CollisionHandling.ContactEffectiveMassTerms3D&)
TryComputeAngularDenominator(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64&)
TryComputeLinearVelocityDelta(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
TryComputeAngularVelocityDelta(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
TryResolveAngularVelocityDelta(Gravitas.SolidBody,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactEffectiveMassTerms3D&,FixedMathSharp.Vector3d&)
TryResolveVelocityDelta(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.CollisionHandling.ContactEffectiveMassTerms3D&,FixedMathSharp.Vector3d&)
Zero(FixedMathSharp.Fixed64)
ZeroVelocityDelta(FixedMathSharp.Fixed64)