< Summary

Line coverage
100%
Covered lines: 777
Uncovered lines: 0
Coverable lines: 777
Total lines: 1346
Line coverage: 100%
Branch coverage
100%
Covered branches: 195
Total branches: 195
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: DoCapsuleSphereCheck(...)100%22100%
File 1: DoCapsulesCheck(...)100%22100%
File 1: ResolveCenteredCapsuleFallback(...)100%1212100%
File 2: DoCompoundCheck(...)100%22100%
File 2: DoCompoundOtherCheck(...)100%22100%
File 2: DoCompoundCompoundCheck(...)100%44100%
File 2: TryBuildCompoundPartContact(...)100%44100%
File 2: AddCompoundPartContactsInOwnerOrder(...)100%44100%
File 2: OrderPartPairForDetection(...)100%22100%
File 2: BoundsOverlapInclusive(...)100%1010100%
File 3: DoConeSphereCheck(...)100%66100%
File 3: DoConeConvexCheck(...)100%22100%
File 3: DoMeshConeCheck(...)100%66100%
File 3: TryFindMeshConeTriangleContact(...)100%1212100%
File 3: GetConeConvexColliders(...)100%44100%
File 3: OrientNormal(...)100%22100%
File 4: DoCollisionCheck(...)100%11100%
File 4: DoCollisionCheck(...)100%2121100%
File 5: DoCuboidSphereCheck(...)100%22100%
File 5: DoCuboidCapsuleCheck(...)100%22100%
File 5: DoCuboidsCheck(...)100%1010100%
File 5: CanBuildAxisAlignedManifold(...)100%44100%
File 5: TryBuildAxisAlignedCuboidManifold(...)100%2020100%
File 5: AddAxisAlignedCuboidContacts(...)100%1010100%
File 5: AddCuboidContact(...)100%11100%
File 6: DoCylinderSphereCheck(...)100%66100%
File 6: DoCylinderCapsuleCheck(...)100%44100%
File 6: DoCylindersCheck(...)100%44100%
File 6: DoCuboidCylinderCheck(...)100%66100%
File 6: TryAddCylinderCylinderCapContacts(...)100%44100%
File 6: TryAddCylinderCylinderCapContact(...)100%11100%
File 6: ResolvePenetrationDepth(...)100%22100%
File 6: ResolveNormal(...)100%22100%
File 6: SetContactInPairOrder(...)100%22100%
File 7: DoMeshSphereCheck(...)100%11100%
File 7: DoMeshCapsuleCheck(...)100%44100%
File 7: DoMeshCuboidCheck(...)100%66100%
File 7: DoMeshCylinderCheck(...)100%11100%
File 7: DoMeshesCheck(...)100%88100%
File 8: DoSpheresCheck(...)100%22100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.Capsule.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.Capsule.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;
 11
 12namespace Gravitas.CollisionHandling;
 13
 14public static partial class CollisionDetection
 15{
 16    #region Capsule
 17
 18    private static bool DoCapsuleSphereCheck(CollisionWorkItem pair)
 19    {
 21120        var capsule = (LSCapsuleCollider)pair.ColliderA;
 21121        var sphere = (LSSphereCollider)pair.ColliderB;
 22
 21123        if (!FixedSegment.TryGetCenteredCapsulesContact(
 21124                capsule.Center,
 21125                capsule.Rotation,
 21126                Vector3d.Up,
 21127                capsule.AxisLength,
 21128                capsule.ScaledRadius,
 21129                sphere.Center,
 21130                sphere.Rotation,
 21131                Vector3d.Up,
 21132                Fixed64.Zero,
 21133                sphere.ScaledRadius,
 21134                ResolveCenteredCapsuleFallback(capsule.Center, sphere.Center),
 21135                out FixedContactAnchors contact))
 36        {
 137            return false;
 38        }
 39
 21040        pair.Manifold.SetContact(
 21041            new ContactAnchor(contact.FirstAnchor),
 21042            new ContactAnchor(contact.SecondAnchor),
 21043            contact.Depth,
 21044            contact.Normal,
 21045            contact.DepthIsClamped);
 21046        return true;
 47    }
 48
 49    private static bool DoCapsulesCheck(CollisionWorkItem pair)
 50    {
 22151        var capsule1 = (LSCapsuleCollider)pair.ColliderA;
 22152        var capsule2 = (LSCapsuleCollider)pair.ColliderB;
 53
 22154        if (!FixedSegment.TryGetCenteredCapsulesContact(
 22155                capsule1.Center,
 22156                capsule1.Rotation,
 22157                Vector3d.Up,
 22158                capsule1.AxisLength,
 22159                capsule1.ScaledRadius,
 22160                capsule2.Center,
 22161                capsule2.Rotation,
 22162                Vector3d.Up,
 22163                capsule2.AxisLength,
 22164                capsule2.ScaledRadius,
 22165                ResolveCenteredCapsuleFallback(capsule1.Center, capsule2.Center),
 22166                out FixedContactAnchors contact))
 67        {
 168            return false;
 69        }
 70
 22071        pair.Manifold.SetContact(
 22072            new ContactAnchor(contact.FirstAnchor),
 22073            new ContactAnchor(contact.SecondAnchor),
 22074            contact.Depth,
 22075            contact.Normal,
 22076            contact.DepthIsClamped);
 22077        return true;
 78    }
 79
 80    private static Vector3d ResolveCenteredCapsuleFallback(
 81        Vector3d firstCenter,
 82        Vector3d secondCenter)
 83    {
 150484        if (secondCenter.X != firstCenter.X)
 147685            return secondCenter.X > firstCenter.X ? Vector3d.Right : Vector3d.Left;
 2886        if (secondCenter.Y != firstCenter.Y)
 387            return secondCenter.Y > firstCenter.Y ? Vector3d.Up : Vector3d.Down;
 2588        if (secondCenter.Z != firstCenter.Z)
 289            return secondCenter.Z > firstCenter.Z ? Vector3d.Forward : Vector3d.Backward;
 2390        return Vector3d.Right;
 91    }
 92
 93    #endregion
 94
 95}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.Compound.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.Compound.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 Gravitas.Colliders;
 9using System.Runtime.CompilerServices;
 10
 11namespace Gravitas.CollisionHandling;
 12
 13public static partial class CollisionDetection
 14{
 15    #region Compound
 16
 17    private static bool DoCompoundCheck(CollisionWorkItem pair)
 18    {
 1319        var compoundA = (LSCompoundCollider)pair.ColliderA;
 1320        if (pair.ColliderB is LSCompoundCollider compoundB)
 21        {
 322            return DoCompoundCompoundCheck(pair, compoundA, compoundB);
 23        }
 24
 1025        return DoCompoundOtherCheck(pair, compoundA, pair.ColliderB);
 26    }
 27
 28    private static bool DoCompoundOtherCheck(
 29        CollisionWorkItem pair,
 30        LSCompoundCollider compound,
 31        LSCollider other)
 32    {
 1033        bool collided = false;
 5034        for (int i = 0; i < compound.PartCount; i++)
 35        {
 1536            LSCollider part = compound.GetPartCollider(i);
 1537            collided |= TryBuildCompoundPartContact(
 1538                pair,
 1539                part,
 1540                featureNamespaceA: i + 1,
 1541                other,
 1542                featureNamespaceB: 0);
 43        }
 44
 1045        return collided;
 46    }
 47
 48    private static bool DoCompoundCompoundCheck(
 49        CollisionWorkItem pair,
 50        LSCompoundCollider compoundA,
 51        LSCompoundCollider compoundB)
 52    {
 353        bool collided = false;
 1654        for (int i = 0; i < compoundA.PartCount; i++)
 55        {
 556            LSCollider partA = compoundA.GetPartCollider(i);
 2857            for (int j = 0; j < compoundB.PartCount; j++)
 58            {
 959                collided |= TryBuildCompoundPartContact(
 960                    pair,
 961                    partA,
 962                    featureNamespaceA: i + 1,
 963                    compoundB.GetPartCollider(j),
 964                    featureNamespaceB: -(j + 1));
 65            }
 66        }
 67
 368        return collided;
 69    }
 70
 71    private static bool TryBuildCompoundPartContact(
 72        CollisionWorkItem ownerPair,
 73        LSCollider first,
 74        int featureNamespaceA,
 75        LSCollider second,
 76        int featureNamespaceB)
 77    {
 2478        if (!BoundsOverlapInclusive(first, second))
 1179            return false;
 80
 1381        OrderPartPairForDetection(
 1382            first,
 1383            featureNamespaceA,
 1384            second,
 1385            featureNamespaceB,
 1386            out LSCollider colliderA,
 1387            out int orderedNamespaceA,
 1388            out LSCollider colliderB,
 1389            out int orderedNamespaceB);
 1390        CollisionType collisionType = ColliderSettings.GetCollisionType(colliderA.Shape, colliderB.Shape);
 1391        ContactManifold scratch = ownerPair.Context.CollisionScratch.CompoundPartManifold;
 1392        scratch.BeginUpdate(ownerPair.Context.FrameCount);
 1393        var partPair = new CollisionWorkItem(ownerPair.Context, colliderA, colliderB, collisionType, scratch);
 1394        if (!DoCollisionCheck(partPair))
 195            return false;
 96
 1297        AddCompoundPartContactsInOwnerOrder(
 1298            ownerPair,
 1299            partPair,
 12100            orderedNamespaceA,
 12101            orderedNamespaceB);
 12102        return true;
 103    }
 104
 105    private static void AddCompoundPartContactsInOwnerOrder(
 106        CollisionWorkItem ownerPair,
 107        CollisionWorkItem partPair,
 108        int featureNamespaceA,
 109        int featureNamespaceB)
 110    {
 12111        bool addInPartOrder = ((LSCompoundCollider)ownerPair.ColliderA).ContainsPartCollider(partPair.ColliderA);
 112
 12113        ContactManifold scratch = partPair.Manifold;
 48114        for (int i = 0; i < scratch.Count; i++)
 115        {
 12116            ManifoldContact contact = scratch[i];
 12117            if (addInPartOrder)
 118            {
 11119                ownerPair.Manifold.AddContact(
 11120                    contact.AnchorA,
 11121                    contact.AnchorB,
 11122                    contact.Depth,
 11123                    contact.Normal,
 11124                    partPair.ColliderA.Material,
 11125                    partPair.ColliderB.Material,
 11126                    contact.DepthIsClamped,
 11127                    featureNamespaceA,
 11128                    featureNamespaceB);
 11129                continue;
 130            }
 131
 1132            ownerPair.Manifold.AddContact(
 1133                contact.AnchorB,
 1134                contact.AnchorA,
 1135                contact.Depth,
 1136                -contact.Normal,
 1137                partPair.ColliderB.Material,
 1138                partPair.ColliderA.Material,
 1139                contact.DepthIsClamped,
 1140                featureNamespaceB,
 1141                featureNamespaceA);
 142        }
 12143    }
 144
 145    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 146    private static void OrderPartPairForDetection(
 147        LSCollider first,
 148        int firstFeatureNamespace,
 149        LSCollider second,
 150        int secondFeatureNamespace,
 151        out LSCollider colliderA,
 152        out int featureNamespaceA,
 153        out LSCollider colliderB,
 154        out int featureNamespaceB)
 155    {
 13156        if (first.Priority >= second.Priority)
 157        {
 11158            colliderA = first;
 11159            featureNamespaceA = firstFeatureNamespace;
 11160            colliderB = second;
 11161            featureNamespaceB = secondFeatureNamespace;
 11162            return;
 163        }
 164
 2165        colliderA = second;
 2166        featureNamespaceA = secondFeatureNamespace;
 2167        colliderB = first;
 2168        featureNamespaceB = firstFeatureNamespace;
 2169    }
 170
 171    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 172    private static bool BoundsOverlapInclusive(LSCollider colliderA, LSCollider colliderB)
 173    {
 24174        return colliderA.BoundsMin.X <= colliderB.BoundsMax.X
 24175            && colliderA.BoundsMax.X >= colliderB.BoundsMin.X
 24176            && colliderA.BoundsMin.Y <= colliderB.BoundsMax.Y
 24177            && colliderA.BoundsMax.Y >= colliderB.BoundsMin.Y
 24178            && colliderA.BoundsMin.Z <= colliderB.BoundsMax.Z
 24179            && colliderA.BoundsMax.Z >= colliderB.BoundsMin.Z;
 180    }
 181
 182    #endregion
 183
 184}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.Cone.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.Cone.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 SwiftCollections.Query;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas.CollisionHandling;
 15
 16public static partial class CollisionDetection
 17{
 18    private static bool DoConeSphereCheck(CollisionWorkItem pair)
 19    {
 720        var cone = (LSConeCollider)pair.ColliderA;
 721        var sphere = (LSSphereCollider)pair.ColliderB;
 22
 723        if (!FixedSegment.TryGetClosestCenteredFiniteConeSurfaceAnchor(
 724                sphere.Center,
 725                cone.Center,
 726                cone.Rotation,
 727                Vector3d.Up,
 728                cone.Height,
 729                cone.ScaledRadius,
 730                Vector3d.Right,
 731                out FixedPointAnchor coneAnchor,
 732                out Vector3d outwardNormal,
 733                out Fixed64 signedDistance))
 34        {
 135            return false;
 36        }
 637        if (signedDistance > sphere.ScaledRadius)
 138            return false;
 39
 540        Vector3d normal = signedDistance < Fixed64.Zero
 541            ? -outwardNormal
 542            : outwardNormal;
 543        ResolvePenetrationDepth(
 544            sphere.ScaledRadius,
 545            signedDistance,
 546            out Fixed64 penetrationDepth,
 547            out bool depthIsClamped);
 548        pair.Manifold.SetContact(
 549            new ContactAnchor(coneAnchor),
 550            new ContactAnchor(
 551                FixedSegment.GetCenteredCapsuleSupportAnchor(
 552                    sphere.Center,
 553                    sphere.Rotation,
 554                    Fixed64.Zero,
 555                    sphere.ScaledRadius,
 556                    -normal)),
 557            penetrationDepth,
 558            normal,
 559            depthIsClamped);
 60
 561        return true;
 62    }
 63
 64    private static bool DoConeConvexCheck(CollisionWorkItem pair)
 65    {
 1166        GetConeConvexColliders(pair, out LSConeCollider cone, out LSCollider convex);
 67
 1168        if (!ConvexColliderSupport.Intersects(cone, convex))
 169            return false;
 70
 1071        Vector3d normalConeToConvex = ResolveNormal(convex.Center - cone.Center);
 1072        normalConeToConvex = normalConeToConvex.Normalized;
 1073        FixedPointAnchor pointOnCone = ConvexColliderSupport.GetSupportAnchor(
 1074            cone,
 1075            normalConeToConvex,
 1076            Vector3d.Zero);
 1077        FixedPointAnchor pointOnConvex = ConvexColliderSupport.GetSupportAnchor(
 1078            convex,
 1079            -normalConeToConvex,
 1080            Vector3d.Zero);
 1081        Fixed64 depth = pointOnCone.ProjectNonNegativeOffsetFrom(
 1082            pointOnConvex,
 1083            normalConeToConvex);
 84
 1085        SetContactInPairOrder(
 1086            pair,
 1087            cone,
 1088            new ContactAnchor(pointOnCone),
 1089            convex,
 1090            new ContactAnchor(pointOnConvex),
 1091            depth,
 1092            normalConeToConvex,
 1093            depthIsClamped: false);
 1094        return true;
 95    }
 96
 97    private static bool DoMeshConeCheck(CollisionWorkItem pair)
 98    {
 1399        var mesh = (LSMeshCollider)pair.ColliderA;
 13100        var cone = (LSConeCollider)pair.ColliderB;
 101
 13102        if (TryFindMeshConeTriangleContact(
 13103                mesh,
 13104                cone,
 13105                pair.Context.CollisionScratch.MeshTriangleCandidatesA,
 13106                out ContactAnchor meshAnchor,
 13107                out ContactAnchor coneAnchor,
 13108                out Vector3d normalMeshToCone,
 13109                out Fixed64 depth,
 13110                out bool depthIsClamped))
 111        {
 7112            pair.Manifold.SetContact(
 7113                meshAnchor,
 7114                coneAnchor,
 7115                depth,
 7116                normalMeshToCone,
 7117                depthIsClamped);
 7118            return true;
 119        }
 120
 6121        if (mesh.Mode == MeshColliderMode.Concave || !ConvexColliderSupport.Intersects(mesh, cone))
 4122            return false;
 123
 2124        normalMeshToCone = ResolveNormal(cone.Center - mesh.Center);
 2125        FixedPointAnchor pointOnMesh = ConvexColliderSupport.GetSupportAnchor(
 2126            mesh,
 2127            normalMeshToCone,
 2128            Vector3d.Zero);
 2129        FixedPointAnchor pointOnCone = ConvexColliderSupport.GetSupportAnchor(
 2130            cone,
 2131            -normalMeshToCone,
 2132            Vector3d.Zero);
 2133        depth = pointOnMesh.ProjectNonNegativeOffsetFrom(
 2134            pointOnCone,
 2135            normalMeshToCone);
 136
 2137        pair.Manifold.SetContact(
 2138            new ContactAnchor(pointOnMesh),
 2139            new ContactAnchor(pointOnCone),
 2140            depth,
 2141            normalMeshToCone);
 2142        return true;
 143    }
 144
 145    private static bool TryFindMeshConeTriangleContact(
 146        LSMeshCollider mesh,
 147        LSConeCollider cone,
 148        SwiftCollections.SwiftList<int> triangleBuffer,
 149        out ContactAnchor meshAnchor,
 150        out ContactAnchor coneAnchor,
 151        out Vector3d normalMeshToCone,
 152        out Fixed64 depth,
 153        out bool depthIsClamped)
 154    {
 13155        meshAnchor = default;
 13156        coneAnchor = default;
 13157        normalMeshToCone = Vector3d.Zero;
 13158        depth = Fixed64.Zero;
 13159        depthIsClamped = false;
 160
 13161        mesh.GetTrianglesInBounds(new FixedBoundVolume(cone.BoundsMin, cone.BoundsMax), triangleBuffer);
 13162        bool found = false;
 13163        Fixed64 bestDepth = Fixed64.MaxValue;
 164
 52165        for (int i = 0; i < triangleBuffer.Count; i++)
 166        {
 13167            int triangleIndex = triangleBuffer[i];
 13168            mesh.Mesh.GetLocalTriangleVertices(
 13169                triangleIndex,
 13170                out Vector3d first,
 13171                out Vector3d second,
 13172                out Vector3d third);
 13173            Vector3d windingNormal = mesh.Mesh.GetFaceNormalWorld(triangleIndex);
 13174            var triangle = new FixedTriangle(first, second, third);
 13175            var coneCenterAnchor = new FixedPointAnchor(
 13176                cone.Center,
 13177                FixedQuaternion.Identity,
 13178                Vector3d.Zero);
 13179            if (!coneCenterAnchor.TryGetLocalPointIn(
 13180                    mesh.Mesh.Origin,
 13181                    mesh.Mesh.Rotation,
 13182                    out Vector3d localConeCenter))
 183            {
 184                continue;
 185            }
 186
 12187            Vector3d candidatePointOnMesh =
 12188                triangle.ClosestPoint(localConeCenter);
 12189            FixedPointAnchor candidateMeshAnchor =
 12190                mesh.Mesh.CreatePointAnchor(candidatePointOnMesh);
 12191            Vector3d candidateNormalMeshToCone =
 12192                OrientNormal(
 12193                    candidateMeshAnchor,
 12194                    coneCenterAnchor,
 12195                    windingNormal).Normalized;
 196            FixedPointAnchor candidateConeAnchor;
 197            Fixed64 candidateDepth;
 198            bool candidateDepthIsClamped;
 199            // The closest point belongs to a triangle already admitted into
 200            // the cone's candidate bounds, so its cone-frame offset is finite.
 12201            _ = candidateMeshAnchor.TryGetLocalPointIn(
 12202                cone.Center,
 12203                cone.Rotation,
 12204                out Vector3d localPointInCone);
 12205            bool closestMeshPointInsideCone =
 12206                FixedSegment.ContainsPointInCenteredFiniteCone(
 12207                    localPointInCone,
 12208                    Vector3d.Zero,
 12209                    Vector3d.Up,
 12210                    cone.Height,
 12211                    cone.ScaledRadius);
 12212            if (closestMeshPointInsideCone)
 213            {
 214                // Exact containment proves the centered canonical surface
 215                // offset remains inside the admitted radius and height.
 6216                _ = FixedSegment.TryGetClosestCenteredFiniteConeSurfaceOffset(
 6217                    localPointInCone,
 6218                    Vector3d.Zero,
 6219                    Vector3d.Up,
 6220                    cone.Height,
 6221                    cone.ScaledRadius,
 6222                    Vector3d.Right,
 6223                    out Vector3d localConePoint,
 6224                    out _,
 6225                    out Fixed64 signedDistance);
 226
 6227                candidateDepth = -signedDistance;
 6228                candidateDepthIsClamped = false;
 6229                candidateConeAnchor = new FixedPointAnchor(
 6230                    cone.Center,
 6231                    cone.Rotation,
 6232                    localConePoint);
 233            }
 6234            else if (triangle.TryGetCenteredFiniteConeSupportContact(
 6235                         mesh.Mesh.Origin,
 6236                         mesh.Mesh.Rotation,
 6237                         cone.Center,
 6238                         cone.Rotation,
 6239                         cone.Height,
 6240                         cone.ScaledRadius,
 6241                         -candidateNormalMeshToCone,
 6242                         candidateNormalMeshToCone,
 6243                         out FixedContactAnchors contact))
 244            {
 4245                candidateMeshAnchor = contact.FirstAnchor;
 4246                candidateConeAnchor = contact.SecondAnchor;
 4247                candidateDepth = contact.Depth;
 4248                candidateDepthIsClamped = contact.DepthIsClamped;
 249            }
 250            else
 251            {
 252                continue;
 253            }
 254
 10255            if (found && candidateDepth >= bestDepth)
 256                continue;
 257
 7258            found = true;
 7259            bestDepth = candidateDepth;
 7260            meshAnchor = new ContactAnchor(candidateMeshAnchor);
 7261            coneAnchor = new ContactAnchor(candidateConeAnchor);
 7262            normalMeshToCone = candidateNormalMeshToCone;
 7263            depth = candidateDepth;
 7264            depthIsClamped = candidateDepthIsClamped;
 265        }
 266
 13267        return found;
 268    }
 269
 270    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 271    private static void GetConeConvexColliders(
 272        CollisionWorkItem pair,
 273        out LSConeCollider cone,
 274        out LSCollider convex)
 275    {
 11276        if (pair.ColliderA is LSConeCollider coneA && ConvexColliderSupport.IsSupported(pair.ColliderB))
 277        {
 5278            cone = coneA;
 5279            convex = pair.ColliderB;
 5280            return;
 281        }
 282
 6283        cone = (LSConeCollider)pair.ColliderB;
 6284        convex = pair.ColliderA;
 6285    }
 286
 287    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 288    private static Vector3d OrientNormal(
 289        in FixedPointAnchor source,
 290        in FixedPointAnchor target,
 291        Vector3d normal)
 292    {
 12293        Vector3d resolved = normal.Normalized;
 12294        return source.ProjectNonNegativeOffsetFrom(target, resolved)
 12295                > Fixed64.Zero
 12296            ? -resolved
 12297            : resolved;
 298    }
 299
 300}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.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 Gravitas.Colliders;
 9
 10namespace Gravitas.CollisionHandling;
 11
 12/// <summary>Provides deterministic 3D narrow-phase collision checks.</summary>
 13public static partial class CollisionDetection
 14{
 15    /// <summary>Rebuilds and tests the contact manifold for a collision pair.</summary>
 16    public static bool DoCollisionCheck(CollisionPair pair)
 17    {
 689918        pair.Manifold.BeginUpdate(pair.Context.FrameCount);
 689919        return DoCollisionCheck(CollisionWorkItem.Create(pair));
 20    }
 21
 22    internal static bool DoCollisionCheck(CollisionWorkItem pair)
 23    {
 1005224        return pair.CollisionType switch
 1005225        {
 107226            CollisionType.Sphere_Sphere => DoSpheresCheck(pair),
 21127            CollisionType.Capsule_Sphere => DoCapsuleSphereCheck(pair),
 22128            CollisionType.Capsule_Capsule => DoCapsulesCheck(pair),
 469029            CollisionType.Cuboid_Sphere => DoCuboidSphereCheck(pair),
 22530            CollisionType.AABox_Capsule => DoCuboidCapsuleCheck(pair),
 22031            CollisionType.OBBox_Capsule => DoCuboidCapsuleCheck(pair),
 70832            CollisionType.Cuboid_Cuboid => DoCuboidsCheck(pair),
 21733            CollisionType.Cylinder_Sphere => DoCylinderSphereCheck(pair),
 21434            CollisionType.Cylinder_Capsule => DoCylinderCapsuleCheck(pair),
 21635            CollisionType.Cylinder_Cylinder => DoCylindersCheck(pair),
 936            CollisionType.Cuboid_Cylinder => DoCuboidCylinderCheck(pair),
 737            CollisionType.Cone_Sphere => DoConeSphereCheck(pair),
 1138            CollisionType.Cone_Convex => DoConeConvexCheck(pair),
 1239            CollisionType.Mesh_Sphere => DoMeshSphereCheck(pair),
 22340            CollisionType.Mesh_Capsule => DoMeshCapsuleCheck(pair),
 68941            CollisionType.Mesh_Cuboid => DoMeshCuboidCheck(pair),
 42842            CollisionType.Mesh_Cylinder => DoMeshCylinderCheck(pair),
 1343            CollisionType.Mesh_Cone => DoMeshConeCheck(pair),
 65244            CollisionType.Mesh_Mesh => DoMeshesCheck(pair),
 1345            CollisionType.Compound => DoCompoundCheck(pair),
 146            _ => false,
 1005247        };
 48    }
 49
 50}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.Cuboid.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.Cuboid.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;
 11
 12namespace Gravitas.CollisionHandling;
 13
 14public static partial class CollisionDetection
 15{
 16    #region Cuboid
 17
 18    private static bool DoCuboidSphereCheck(CollisionWorkItem pair)
 19    {
 469020        var cuboid = (LSCuboidCollider)pair.ColliderA;
 469021        if (!cuboid.OrientedBox.TryGetSphereContact(
 469022                pair.ColliderB.Center,
 469023                pair.ColliderB.Rotation,
 469024                pair.ColliderB.ScaledRadius,
 469025                out FixedContactAnchors contact))
 26        {
 200727            return false;
 28        }
 29
 268330        pair.Manifold.SetContact(
 268331            new ContactAnchor(contact.FirstAnchor),
 268332            new ContactAnchor(contact.SecondAnchor),
 268333            contact.Depth,
 268334            contact.Normal,
 268335            contact.DepthIsClamped);
 268336        return true;
 37    }
 38
 39    private static bool DoCuboidCapsuleCheck(CollisionWorkItem pair)
 40    {
 44541        var cuboid = (LSCuboidCollider)pair.ColliderA;
 44542        var capsule = (LSCapsuleCollider)pair.ColliderB;
 43
 44544        if (!cuboid.OrientedBox.TryGetCenteredCapsuleContact(
 44545                capsule.Center,
 44546                capsule.Rotation,
 44547                Vector3d.Up,
 44548                capsule.AxisLength,
 44549                capsule.ScaledRadius,
 44550                out FixedContactAnchors contact))
 51        {
 952            return false;
 53        }
 54
 43655        pair.Manifold.SetContact(
 43656            new ContactAnchor(contact.FirstAnchor),
 43657            new ContactAnchor(contact.SecondAnchor),
 43658            contact.Depth,
 43659            contact.Normal,
 43660            contact.DepthIsClamped);
 43661        return true;
 62    }
 63
 64    /// <summary>
 65    /// Checks for collisions between two poly-poly colliders.
 66    /// </summary>
 67    /// <returns>true if a collision is detected, false otherwise.</returns>
 68    private static bool DoCuboidsCheck(CollisionWorkItem pair)
 69    {
 70870        var cuboidA = (LSCuboidCollider)pair.ColliderA;
 70871        var cuboidB = (LSCuboidCollider)pair.ColliderB;
 72
 70873        if (cuboidA.Shape == ColliderType.AABox
 70874            && cuboidB.Shape == ColliderType.AABox
 70875            && CanBuildAxisAlignedManifold(cuboidA)
 70876            && CanBuildAxisAlignedManifold(cuboidB))
 77        {
 24178            return TryBuildAxisAlignedCuboidManifold(pair, cuboidA, cuboidB);
 79        }
 80
 46781        if (!cuboidA.OrientedBox.TryGetContact(
 46782                cuboidB.OrientedBox,
 46783                out FixedContactAnchors contact))
 84        {
 585            return false;
 86        }
 87
 46288        pair.Manifold.SetContact(
 46289            new ContactAnchor(contact.FirstAnchor),
 46290            new ContactAnchor(contact.SecondAnchor),
 46291            contact.Depth,
 46292            contact.Normal,
 46293            contact.DepthIsClamped);
 46294        return true;
 95    }
 96
 97    private static bool CanBuildAxisAlignedManifold(LSCuboidCollider cuboid)
 98    {
 48399        Vector3d center = cuboid.Center;
 483100        Vector3d halfExtents = cuboid.OrientedBox.HalfExtents;
 101        // The four-point fast path materializes both bounds and subtracts their
 102        // overlap widths. Wider conceptual boxes use the exact relative path.
 483103        return Vector3d.TryAdd(center, halfExtents, out _)
 483104            && Vector3d.TrySubtract(center, halfExtents, out _)
 483105            && Vector3d.TryAdd(halfExtents, halfExtents, out _);
 106    }
 107
 108    private static bool TryBuildAxisAlignedCuboidManifold(
 109        CollisionWorkItem pair,
 110        LSCuboidCollider cuboidA,
 111        LSCuboidCollider cuboidB)
 112    {
 241113        Fixed64 overlapX = FixedMath.Min(cuboidA.BoundsMax.X, cuboidB.BoundsMax.X) - FixedMath.Max(cuboidA.BoundsMin.X, 
 241114        Fixed64 overlapY = FixedMath.Min(cuboidA.BoundsMax.Y, cuboidB.BoundsMax.Y) - FixedMath.Max(cuboidA.BoundsMin.Y, 
 241115        Fixed64 overlapZ = FixedMath.Min(cuboidA.BoundsMax.Z, cuboidB.BoundsMax.Z) - FixedMath.Max(cuboidA.BoundsMin.Z, 
 116
 241117        if (overlapX < Fixed64.Zero || overlapY < Fixed64.Zero || overlapZ < Fixed64.Zero)
 2118            return false;
 119
 239120        Vector3d centerDelta = cuboidB.Center - cuboidA.Center;
 239121        int axis = 0;
 239122        Fixed64 depth = overlapX;
 239123        if (overlapY < depth)
 124        {
 21125            axis = 1;
 21126            depth = overlapY;
 127        }
 128
 239129        if (overlapZ < depth)
 130        {
 2131            axis = 2;
 2132            depth = overlapZ;
 133        }
 134
 239135        Vector3d normal = axis switch
 239136        {
 216137            0 => new Vector3d(centerDelta.X < Fixed64.Zero ? -Fixed64.One : Fixed64.One, Fixed64.Zero, Fixed64.Zero),
 21138            1 => new Vector3d(Fixed64.Zero, centerDelta.Y < Fixed64.Zero ? -Fixed64.One : Fixed64.One, Fixed64.Zero),
 2139            _ => new Vector3d(Fixed64.Zero, Fixed64.Zero, centerDelta.Z < Fixed64.Zero ? -Fixed64.One : Fixed64.One)
 239140        };
 141
 239142        AddAxisAlignedCuboidContacts(pair.Manifold, cuboidA, cuboidB, axis, depth, normal);
 239143        return pair.Manifold.HasContact;
 144    }
 145
 146    private static void AddAxisAlignedCuboidContacts(
 147        ContactManifold manifold,
 148        LSCuboidCollider cuboidA,
 149        LSCuboidCollider cuboidB,
 150        int axis,
 151        Fixed64 depth,
 152        Vector3d normal)
 153    {
 239154        Fixed64 minX = FixedMath.Max(cuboidA.BoundsMin.X, cuboidB.BoundsMin.X);
 239155        Fixed64 maxX = FixedMath.Min(cuboidA.BoundsMax.X, cuboidB.BoundsMax.X);
 239156        Fixed64 minY = FixedMath.Max(cuboidA.BoundsMin.Y, cuboidB.BoundsMin.Y);
 239157        Fixed64 maxY = FixedMath.Min(cuboidA.BoundsMax.Y, cuboidB.BoundsMax.Y);
 239158        Fixed64 minZ = FixedMath.Max(cuboidA.BoundsMin.Z, cuboidB.BoundsMin.Z);
 239159        Fixed64 maxZ = FixedMath.Min(cuboidA.BoundsMax.Z, cuboidB.BoundsMax.Z);
 160
 161        switch (axis)
 162        {
 163            case 0:
 164                {
 216165                    Fixed64 x = normal.X > Fixed64.Zero ? cuboidA.BoundsMax.X : cuboidA.BoundsMin.X;
 216166                    AddCuboidContact(manifold, new Vector3d(x, minY, minZ), normal, depth);
 216167                    AddCuboidContact(manifold, new Vector3d(x, minY, maxZ), normal, depth);
 216168                    AddCuboidContact(manifold, new Vector3d(x, maxY, minZ), normal, depth);
 216169                    AddCuboidContact(manifold, new Vector3d(x, maxY, maxZ), normal, depth);
 216170                    break;
 171                }
 172            case 1:
 173                {
 21174                    Fixed64 y = normal.Y > Fixed64.Zero ? cuboidA.BoundsMax.Y : cuboidA.BoundsMin.Y;
 21175                    AddCuboidContact(manifold, new Vector3d(minX, y, minZ), normal, depth);
 21176                    AddCuboidContact(manifold, new Vector3d(minX, y, maxZ), normal, depth);
 21177                    AddCuboidContact(manifold, new Vector3d(maxX, y, minZ), normal, depth);
 21178                    AddCuboidContact(manifold, new Vector3d(maxX, y, maxZ), normal, depth);
 21179                    break;
 180                }
 181            default:
 182                {
 2183                    Fixed64 z = normal.Z > Fixed64.Zero ? cuboidA.BoundsMax.Z : cuboidA.BoundsMin.Z;
 2184                    AddCuboidContact(manifold, new Vector3d(minX, minY, z), normal, depth);
 2185                    AddCuboidContact(manifold, new Vector3d(minX, maxY, z), normal, depth);
 2186                    AddCuboidContact(manifold, new Vector3d(maxX, minY, z), normal, depth);
 2187                    AddCuboidContact(manifold, new Vector3d(maxX, maxY, z), normal, depth);
 188                    break;
 189                }
 190        }
 2191    }
 192
 193    private static void AddCuboidContact(ContactManifold manifold, Vector3d pointA, Vector3d normal, Fixed64 depth)
 194    {
 956195        Vector3d pointB = pointA - normal * depth;
 956196        manifold.AddContact(pointA, pointB, depth, normal);
 956197    }
 198
 199    #endregion
 200
 201}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.Cylinder.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.Cylinder.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 System;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas.CollisionHandling;
 15
 16public static partial class CollisionDetection
 17{
 18    #region Cylinder
 19
 20    private static bool DoCylinderSphereCheck(CollisionWorkItem pair)
 21    {
 21722        var cylinder = (LSCylinderCollider)pair.ColliderA;
 21723        var sphere = (LSSphereCollider)pair.ColliderB;
 24
 21725        if (!FixedSegment.TryGetClosestCenteredFiniteCylinderSurfaceAnchor(
 21726                sphere.Center,
 21727                cylinder.Center,
 21728                cylinder.Rotation,
 21729                Vector3d.Up,
 21730                cylinder.Height,
 21731                cylinder.ScaledRadius,
 21732                Vector3d.Right,
 21733                out FixedPointAnchor cylinderAnchor,
 21734                out Vector3d outwardNormal,
 21735                out Fixed64 signedDistance))
 36        {
 137            return false;
 38        }
 21639        if (signedDistance > sphere.ScaledRadius)
 140            return false;
 41
 21542        Vector3d normal = signedDistance < Fixed64.Zero
 21543            ? -outwardNormal
 21544            : outwardNormal;
 21545        ResolvePenetrationDepth(
 21546            sphere.ScaledRadius,
 21547            signedDistance,
 21548            out Fixed64 penetrationDepth,
 21549            out bool depthIsClamped);
 21550        pair.Manifold.SetContact(
 21551            new ContactAnchor(cylinderAnchor),
 21552            new ContactAnchor(
 21553                FixedSegment.GetCenteredCapsuleSupportAnchor(
 21554                    sphere.Center,
 21555                    sphere.Rotation,
 21556                    Fixed64.Zero,
 21557                    sphere.ScaledRadius,
 21558                    -normal)),
 21559            penetrationDepth,
 21560            normal,
 21561            depthIsClamped);
 62
 21563        return true;
 64    }
 65
 66    private static bool DoCylinderCapsuleCheck(CollisionWorkItem pair)
 67    {
 68        LSCylinderCollider cylinder;
 69        LSCapsuleCollider capsule;
 21470        if (pair.ColliderA is LSCylinderCollider cylinderA)
 71        {
 21372            cylinder = cylinderA;
 21373            capsule = (LSCapsuleCollider)pair.ColliderB;
 74        }
 75        else
 76        {
 177            cylinder = (LSCylinderCollider)pair.ColliderB;
 178            capsule = (LSCapsuleCollider)pair.ColliderA;
 79        }
 80
 21481        if (!FixedSegment.TryGetCenteredFiniteCylinderCapsuleContact(
 21482                cylinder.Center,
 21483                cylinder.Rotation,
 21484                Vector3d.Up,
 21485                cylinder.Height,
 21486                cylinder.ScaledRadius,
 21487                capsule.Center,
 21488                capsule.Rotation,
 21489                Vector3d.Up,
 21490                capsule.AxisLength,
 21491                capsule.ScaledRadius,
 21492                out FixedContactAnchors contact))
 93        {
 494            return false;
 95        }
 96
 21097        SetContactInPairOrder(
 21098            pair,
 21099            cylinder,
 210100            new ContactAnchor(contact.FirstAnchor),
 210101            capsule,
 210102            new ContactAnchor(contact.SecondAnchor),
 210103            contact.Depth,
 210104            contact.Normal,
 210105            contact.DepthIsClamped);
 106
 210107        return true;
 108    }
 109
 110    private static bool DoCylindersCheck(CollisionWorkItem pair)
 111    {
 216112        var cylinderA = (LSCylinderCollider)pair.ColliderA;
 216113        var cylinderB = (LSCylinderCollider)pair.ColliderB;
 114
 216115        if (!FixedSegment.TryGetCenteredFiniteCylindersContact(
 216116                cylinderA.Center,
 216117                cylinderA.Rotation,
 216118                Vector3d.Up,
 216119                cylinderA.Height,
 216120                cylinderA.ScaledRadius,
 216121                cylinderB.Center,
 216122                cylinderB.Rotation,
 216123                Vector3d.Up,
 216124                cylinderB.Height,
 216125                cylinderB.ScaledRadius,
 216126                out FixedContactAnchors contact))
 127        {
 4128            return false;
 129        }
 130
 212131        if (TryAddCylinderCylinderCapContacts(
 212132                pair,
 212133                cylinderA,
 212134                cylinderB,
 212135                contact))
 136        {
 3137            return true;
 138        }
 139
 209140        pair.Manifold.SetContact(
 209141            new ContactAnchor(contact.FirstAnchor),
 209142            new ContactAnchor(contact.SecondAnchor),
 209143            contact.Depth,
 209144            contact.Normal,
 209145            contact.DepthIsClamped);
 146
 209147        return true;
 148    }
 149
 150    private static bool DoCuboidCylinderCheck(CollisionWorkItem pair)
 151    {
 9152        var cuboid = (LSCuboidCollider)pair.ColliderA;
 9153        var cylinder = (LSCylinderCollider)pair.ColliderB;
 154
 9155        Span<FixedContactLocalPoints> capFaceContacts =
 9156            stackalloc FixedContactLocalPoints[ContactManifold.MaxContactCount];
 9157        if (!cuboid.OrientedBox.TryGetCenteredCylinderContact(
 9158                cylinder.Center,
 9159                cylinder.Rotation,
 9160                Vector3d.Up,
 9161                cylinder.Height,
 9162                cylinder.ScaledRadius,
 9163                capFaceContacts,
 9164                out FixedContactAnchors contact,
 9165                out int capFaceContactCount))
 166        {
 4167            return false;
 168        }
 169
 5170        if (capFaceContactCount > 0)
 171        {
 10172            for (int index = 0; index < capFaceContactCount; index++)
 173            {
 4174                FixedContactLocalPoints capContact = capFaceContacts[index];
 4175                pair.Manifold.AddContact(
 4176                    new ContactAnchor(
 4177                        contact.FirstAnchor.Origin,
 4178                        contact.FirstAnchor.Rotation,
 4179                        capContact.FirstLocalPoint),
 4180                    new ContactAnchor(
 4181                        contact.SecondAnchor.Origin,
 4182                        contact.SecondAnchor.Rotation,
 4183                        capContact.SecondLocalPoint),
 4184                    contact.Depth,
 4185                    contact.Normal,
 4186                    contact.DepthIsClamped);
 187            }
 1188            return true;
 189        }
 190
 4191        pair.Manifold.SetContact(
 4192            new ContactAnchor(contact.FirstAnchor),
 4193            new ContactAnchor(contact.SecondAnchor),
 4194            contact.Depth,
 4195            contact.Normal,
 4196            contact.DepthIsClamped);
 4197        return true;
 198    }
 199
 200    private static bool TryAddCylinderCylinderCapContacts(
 201        CollisionWorkItem pair,
 202        LSCylinderCollider cylinderA,
 203        LSCylinderCollider cylinderB,
 204        FixedContactAnchors contact)
 205    {
 212206        if (contact.FirstAnchor.LocalDisplacement != Vector3d.Zero
 212207            || contact.SecondAnchor.LocalDisplacement != Vector3d.Zero)
 208        {
 209209            return false;
 210        }
 211
 3212        CylinderContactGeometry.GetCapBasis(cylinderA, out Vector3d tangentA, out Vector3d tangentB);
 3213        int initialCount = pair.Manifold.Count;
 3214        TryAddCylinderCylinderCapContact(pair, cylinderA, cylinderB, tangentA, contact);
 3215        TryAddCylinderCylinderCapContact(pair, cylinderA, cylinderB, -tangentA, contact);
 3216        TryAddCylinderCylinderCapContact(pair, cylinderA, cylinderB, tangentB, contact);
 3217        TryAddCylinderCylinderCapContact(pair, cylinderA, cylinderB, -tangentB, contact);
 3218        return pair.Manifold.Count > initialCount;
 219    }
 220
 221    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 222    private static void TryAddCylinderCylinderCapContact(
 223        CollisionWorkItem pair,
 224        LSCylinderCollider cylinderA,
 225        LSCylinderCollider cylinderB,
 226        Vector3d radialDirection,
 227        FixedContactAnchors contact)
 228    {
 12229        pair.Manifold.AddContact(
 12230            new ContactAnchor(ConvexColliderSupport.GetSupportAnchor(
 12231                cylinderA,
 12232                contact.Normal + radialDirection,
 12233                Vector3d.Zero)),
 12234            new ContactAnchor(ConvexColliderSupport.GetSupportAnchor(
 12235                cylinderB,
 12236                -contact.Normal + radialDirection,
 12237                Vector3d.Zero)),
 12238            contact.Depth,
 12239            contact.Normal,
 12240            contact.DepthIsClamped);
 12241    }
 242
 243    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 244    private static void ResolvePenetrationDepth(
 245        Fixed64 radius,
 246        Fixed64 signedDistance,
 247        out Fixed64 depth,
 248        out bool depthIsClamped)
 249    {
 220250        if (Fixed64.TrySubtract(radius, signedDistance, out depth))
 251        {
 219252            depthIsClamped = false;
 219253            return;
 254        }
 255
 1256        depth = Fixed64.MaxValue;
 1257        depthIsClamped = true;
 1258    }
 259
 260    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 261    private static Vector3d ResolveNormal(Vector3d delta) =>
 12262        delta.MagnitudeSquared > Fixed64.Epsilon
 12263            ? delta.Normalized
 12264            : Vector3d.Right;
 265
 266    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 267    private static void SetContactInPairOrder(
 268        CollisionWorkItem pair,
 269        LSCollider first,
 270        ContactAnchor anchorOnFirst,
 271        LSCollider second,
 272        ContactAnchor anchorOnSecond,
 273        Fixed64 depth,
 274        Vector3d normalFirstToSecond,
 275        bool depthIsClamped)
 276    {
 220277        if (ReferenceEquals(pair.ColliderA, first))
 278        {
 214279            pair.Manifold.SetContact(
 214280                anchorOnFirst,
 214281                anchorOnSecond,
 214282                depth,
 214283                normalFirstToSecond,
 214284                depthIsClamped);
 214285            return;
 286        }
 287
 6288        pair.Manifold.SetContact(
 6289            anchorOnSecond,
 6290            anchorOnFirst,
 6291            depth,
 6292            -normalFirstToSecond,
 6293            depthIsClamped);
 6294    }
 295
 296    #endregion
 297
 298}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.Mesh.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.Mesh.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;
 11
 12namespace Gravitas.CollisionHandling;
 13
 14public static partial class CollisionDetection
 15{
 16    #region Mesh
 17
 18    private static bool DoMeshSphereCheck(CollisionWorkItem pair)
 19    {
 1220        var meshCollider = (LSMeshCollider)pair.ColliderA;
 1221        var sphere = (LSSphereCollider)pair.ColliderB;
 22
 1223        return MeshTriangleContactGenerator.TryBuildMeshSphereManifold(
 1224            pair,
 1225            meshCollider,
 1226            sphere,
 1227            pair.Context.CollisionScratch.MeshTriangleCandidatesA);
 28    }
 29
 30    private static bool DoMeshCapsuleCheck(CollisionWorkItem pair)
 31    {
 22332        var mesh = (LSMeshCollider)pair.ColliderA;
 22333        var capsule = (LSCapsuleCollider)pair.ColliderB;
 34
 22335        if (mesh.Mode == MeshColliderMode.Convex)
 36        {
 21837            PhysicsMesh physicsMesh = mesh.Mesh;
 21838            bool hasContact =
 21839                FixedConvexHullRelations
 21840                    .TryGetCenteredCapsuleContact(
 21841                        physicsMesh.Origin,
 21842                        physicsMesh.Rotation,
 21843                        physicsMesh.ScaledLocalVertices,
 21844                        physicsMesh.Triangles,
 21845                        physicsMesh.ConvexSatEdgeVertexPairs,
 21846                        capsule.Center,
 21847                        capsule.Rotation,
 21848                        Vector3d.Up,
 21849                        capsule.AxisLength,
 21850                        capsule.ScaledRadius,
 21851                        out FixedContactAnchors contact);
 21852            if (!hasContact)
 353                return false;
 21554            pair.Manifold.SetContact(
 21555                new ContactAnchor(contact.FirstAnchor),
 21556                new ContactAnchor(contact.SecondAnchor),
 21557                contact.Depth,
 21558                contact.Normal,
 21559                contact.DepthIsClamped);
 21560            return true;
 61        }
 62
 563        return MeshTriangleContactGenerator.TryBuildMeshCapsuleManifold(
 564            pair,
 565            mesh,
 566            capsule,
 567            pair.Context.CollisionScratch.MeshTriangleCandidatesA);
 68    }
 69
 70    private static bool DoMeshCuboidCheck(CollisionWorkItem pair)
 71    {
 68972        var mesh = (LSMeshCollider)pair.ColliderA;
 68973        var cuboid = (LSCuboidCollider)pair.ColliderB;
 74
 68975        if (MeshTriangleContactGenerator.TryBuildMeshCuboidManifold(
 68976            pair,
 68977            mesh,
 68978            cuboid,
 68979            pair.Context.CollisionScratch.MeshTriangleCandidatesA))
 80        {
 43481            return true;
 82        }
 83
 25584        if (mesh.Mode == MeshColliderMode.Concave)
 285            return false;
 86
 25387        PhysicsMesh physicsMesh = mesh.Mesh;
 25388        if (!cuboid.OrientedBox.TryGetConvexHullContact(
 25389                mesh.Center,
 25390                physicsMesh.Rotation,
 25391                physicsMesh.ScaledLocalVertices,
 25392                physicsMesh.Triangles,
 25393                physicsMesh.ConvexSatEdgeVertexPairs,
 25394                out FixedContactAnchors contact))
 95        {
 4296            return false;
 97        }
 98
 21199        pair.Manifold.SetContact(
 211100            new ContactAnchor(contact.SecondAnchor),
 211101            new ContactAnchor(contact.FirstAnchor),
 211102            contact.Depth,
 211103            -contact.Normal,
 211104            contact.DepthIsClamped);
 105
 211106        return true;
 107    }
 108
 109    private static bool DoMeshCylinderCheck(CollisionWorkItem pair)
 110    {
 428111        var mesh = (LSMeshCollider)pair.ColliderA;
 428112        var cylinder = (LSCylinderCollider)pair.ColliderB;
 113
 428114        return MeshTriangleContactGenerator.TryBuildMeshCylinderManifold(
 428115            pair,
 428116            mesh,
 428117            cylinder,
 428118            pair.Context.CollisionScratch.MeshTriangleCandidatesA);
 119    }
 120
 121    /// <summary>
 122    /// Checks if two mesh colliders intersect and sets the contact point if they do.
 123    /// </summary>
 124    /// <param name="pair">The pair of colliders to test for collision.</param>
 125    /// <returns>true if the colliders intersect; otherwise, false.</returns>
 126    private static bool DoMeshesCheck(CollisionWorkItem pair)
 127    {
 652128        var meshA = (LSMeshCollider)pair.ColliderA;
 652129        var meshB = (LSMeshCollider)pair.ColliderB;
 652130        if (meshA.Mode == MeshColliderMode.Concave
 652131            || meshB.Mode == MeshColliderMode.Concave)
 132        {
 645133            return MeshTriangleContactGenerator.TryBuildMeshMeshManifold(
 645134                pair,
 645135                meshA,
 645136                meshB,
 645137                pair.Context.CollisionScratch.MeshTriangleCandidatesA,
 645138                pair.Context.CollisionScratch.MeshTriangleCandidatesB);
 139        }
 140
 7141        PhysicsMesh physicsMeshA = meshA.Mesh;
 7142        PhysicsMesh physicsMeshB = meshB.Mesh;
 7143        bool hasContact = FixedConvexHullRelations.TryGetContact(
 7144            meshA.Center,
 7145            physicsMeshA.Rotation,
 7146            physicsMeshA.ScaledLocalVertices,
 7147            physicsMeshA.Triangles,
 7148            physicsMeshA.ConvexSatEdgeVertexPairs,
 7149            meshB.Center,
 7150            physicsMeshB.Rotation,
 7151            physicsMeshB.ScaledLocalVertices,
 7152            physicsMeshB.Triangles,
 7153            physicsMeshB.ConvexSatEdgeVertexPairs,
 7154            out FixedContactAnchors contact);
 7155        if (!hasContact)
 4156            return false;
 3157        if (contact.Depth <= Fixed64.Zero)
 1158            return false;
 159
 2160        pair.Manifold.SetContact(
 2161            new ContactAnchor(contact.FirstAnchor),
 2162            new ContactAnchor(contact.SecondAnchor),
 2163            contact.Depth,
 2164            contact.Normal,
 2165            contact.DepthIsClamped);
 166
 2167        return true;
 168    }
 169
 170    #endregion
 171}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Detection/3D/CollisionDetection.Sphere.cs

#LineLine coverage
 1//=======================================================================
 2// CollisionDetection.Sphere.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;
 10
 11namespace Gravitas.CollisionHandling;
 12
 13public static partial class CollisionDetection
 14{
 15    #region Sphere
 16
 17    private static bool DoSpheresCheck(CollisionWorkItem pair)
 18    {
 107219        if (!FixedSegment.TryGetCenteredCapsulesContact(
 107220                pair.ColliderA.Center,
 107221                pair.ColliderA.Rotation,
 107222                Vector3d.Up,
 107223                Fixed64.Zero,
 107224                pair.ColliderA.ScaledRadius,
 107225                pair.ColliderB.Center,
 107226                pair.ColliderB.Rotation,
 107227                Vector3d.Up,
 107228                Fixed64.Zero,
 107229                pair.ColliderB.ScaledRadius,
 107230                ResolveCenteredCapsuleFallback(pair.ColliderA.Center, pair.ColliderB.Center),
 107231                out FixedContactAnchors contact))
 32        {
 1533            return false;
 34        }
 35
 105736        pair.Manifold.SetContact(
 105737            new ContactAnchor(contact.FirstAnchor),
 105738            new ContactAnchor(contact.SecondAnchor),
 105739            contact.Depth,
 105740            contact.Normal,
 105741            contact.DepthIsClamped);
 105742        return true;
 43    }
 44
 45    #endregion
 46
 47}

Methods/Properties

DoCapsuleSphereCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCapsulesCheck(Gravitas.CollisionHandling.CollisionWorkItem)
ResolveCenteredCapsuleFallback(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
DoCompoundCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCompoundOtherCheck(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSCompoundCollider,Gravitas.Colliders.LSCollider)
DoCompoundCompoundCheck(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSCompoundCollider,Gravitas.Colliders.LSCompoundCollider)
TryBuildCompoundPartContact(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSCollider,System.Int32,Gravitas.Colliders.LSCollider,System.Int32)
AddCompoundPartContactsInOwnerOrder(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.CollisionHandling.CollisionWorkItem,System.Int32,System.Int32)
OrderPartPairForDetection(Gravitas.Colliders.LSCollider,System.Int32,Gravitas.Colliders.LSCollider,System.Int32,Gravitas.Colliders.LSCollider&,System.Int32&,Gravitas.Colliders.LSCollider&,System.Int32&)
BoundsOverlapInclusive(Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider)
DoConeSphereCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoConeConvexCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoMeshConeCheck(Gravitas.CollisionHandling.CollisionWorkItem)
TryFindMeshConeTriangleContact(Gravitas.Colliders.LSMeshCollider,Gravitas.Colliders.LSConeCollider,SwiftCollections.SwiftList`1<System.Int32>,Gravitas.CollisionHandling.ContactAnchor&,Gravitas.CollisionHandling.ContactAnchor&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
GetConeConvexColliders(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSConeCollider&,Gravitas.Colliders.LSCollider&)
OrientNormal(FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Vector3d)
DoCollisionCheck(Gravitas.CollisionHandling.CollisionPair)
DoCollisionCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCuboidSphereCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCuboidCapsuleCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCuboidsCheck(Gravitas.CollisionHandling.CollisionWorkItem)
CanBuildAxisAlignedManifold(Gravitas.Colliders.LSCuboidCollider)
TryBuildAxisAlignedCuboidManifold(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSCuboidCollider,Gravitas.Colliders.LSCuboidCollider)
AddAxisAlignedCuboidContacts(Gravitas.CollisionHandling.ContactManifold,Gravitas.Colliders.LSCuboidCollider,Gravitas.Colliders.LSCuboidCollider,System.Int32,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
AddCuboidContact(Gravitas.CollisionHandling.ContactManifold,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
DoCylinderSphereCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCylinderCapsuleCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCylindersCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoCuboidCylinderCheck(Gravitas.CollisionHandling.CollisionWorkItem)
TryAddCylinderCylinderCapContacts(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSCylinderCollider,Gravitas.Colliders.LSCylinderCollider,FixedMathSharp.Geometry.FixedContactAnchors)
TryAddCylinderCylinderCapContact(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSCylinderCollider,Gravitas.Colliders.LSCylinderCollider,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors)
ResolvePenetrationDepth(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,System.Boolean&)
ResolveNormal(FixedMathSharp.Vector3d)
SetContactInPairOrder(Gravitas.CollisionHandling.CollisionWorkItem,Gravitas.Colliders.LSCollider,Gravitas.CollisionHandling.ContactAnchor,Gravitas.Colliders.LSCollider,Gravitas.CollisionHandling.ContactAnchor,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,System.Boolean)
DoMeshSphereCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoMeshCapsuleCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoMeshCuboidCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoMeshCylinderCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoMeshesCheck(Gravitas.CollisionHandling.CollisionWorkItem)
DoSpheresCheck(Gravitas.CollisionHandling.CollisionWorkItem)