< Summary

Line coverage
100%
Covered lines: 779
Uncovered lines: 0
Coverable lines: 779
Total lines: 1568
Line coverage: 100%
Branch coverage
100%
Covered branches: 362
Total branches: 362
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: .cctor()100%11100%
File 1: .ctor(...)100%11100%
File 1: get_Context()100%11100%
File 1: get_ActivePartitionCount()100%11100%
File 1: get_InactivePartitionCount()100%11100%
File 1: get_InactivePartitionMembershipCount()100%11100%
File 1: get_RetainedPartitionCount()100%11100%
File 1: get_ActivePairCount()100%11100%
File 1: get_PooledPairCount()100%11100%
File 1: GetCandidate(...)100%11100%
File 1: Simulate()100%11100%
File 1: LateSimulate()100%11100%
File 1: ProcessMixedContacts()100%66100%
File 1: Visualize()100%11100%
File 1: Reset()100%11100%
File 2: RemovePairsFor3DCollider(...)100%44100%
File 2: RemovePairsFor2DCollider(...)100%44100%
File 2: RequireCollisionPair(...)100%2020100%
File 2: ProcessCandidate(...)100%2222100%
File 2: CleanupUntouchedPairs(...)100%1212100%
File 2: TryKeepUntouchedPair(...)100%1414100%
File 2: CreatePair(...)100%44100%
File 2: RemoveQueuedPairs(...)100%44100%
File 2: IsCurrentPair(...)100%44100%
File 2: RemoveCurrentPair(...)100%22100%
File 2: RecyclePair(...)100%44100%
File 3: Refresh3DColliderPartition(...)100%88100%
File 3: Refresh2DColliderPartition(...)100%11100%
File 3: Refresh2DColliderPartition(...)100%1010100%
File 3: ClearPartitioned3DCollider(...)100%1212100%
File 3: ClearPartitioned2DCollider(...)100%1212100%
File 3: Refresh3DPartitionAwakeState(...)100%1010100%
File 3: Refresh2DPartitionAwakeState(...)100%1010100%
File 3: ProcessPartitionCandidate(...)100%1010100%
File 3: Collect2DCandidatesInMixedBounds(...)100%1212100%
File 3: Collect3DCandidatesInMixedBounds(...)100%1212100%
File 3: ActivatePartition(...)100%11100%
File 3: DeactivatePartition(...)100%11100%
File 3: RentPartition()100%44100%
File 3: ReleasePartition(...)100%11100%
File 3: RentPartitionMembership()100%22100%
File 3: ReleasePartitionMembership(...)100%22100%
File 3: Refresh3DColliderPartitions()100%22100%
File 3: Refresh3DColliderPartitionsForQuery(...)100%66100%
File 3: Refresh2DColliderPartitions(...)100%22100%
File 3: Refresh2DColliderPartitionsForQuery(...)100%66100%
File 3: Partition3DCollider(...)100%22100%
File 3: Partition2DCollider(...)100%22100%
File 3: ScanCovered3DVoxels(...)100%22100%
File 3: ScanCovered2DMixedVoxels(...)100%22100%
File 3: CollectCoveredMixedQueryPartitions(...)100%11100%
File 3: ScanCoveredMixedQueryPartitions(...)100%11100%
File 3: VisitCoveredVoxelsForMixedQuery(...)100%88100%
File 3: TryPartition3DVoxel(...)100%22100%
File 3: TryPartition2DMixedVoxel(...)100%22100%
File 3: GetOrCreatePartition(...)100%22100%
File 3: Get3DCoverageBounds(...)100%11100%
File 3: Get2DMixedCoverageBounds(...)100%11100%
File 3: Get3DMobilityKind(...)100%44100%
File 3: Get2DMobilityKind(...)100%44100%
File 3: GetStoredMobilityKind(...)100%44100%
File 3: Add3DObject(...)100%44100%
File 3: Add2DObject(...)100%44100%
File 3: Remove3DObject(...)100%44100%
File 3: Remove2DObject(...)100%44100%
File 3: MixedBoundsOverlap(...)100%11100%
File 3: Is2DQueryCandidate(...)100%22100%
File 3: Is3DQueryCandidate(...)100%44100%
File 3: DetachRetainedPartitions()100%11100%
File 3: TrackRetainedPartition(...)100%11100%
File 3: UntrackRetainedPartition(...)100%11100%
File 3: RetireExpiredRetainedPartitions()100%11100%
File 3: TryRetireEmptyRetainedPartitionForReuse()100%11100%
File 4: ContributeReplayHash(...)100%88100%
File 5: SolveMixedResponsePairs()100%1818100%
File 5: BuildMixedIslands()100%1616100%
File 5: AddMixedIslandNodeIfMovable(...)100%22100%
File 5: AddMixedIslandNodeIfMovable(...)100%22100%
File 5: SortAndDeduplicateMixedIslandNodes()100%11100%
File 5: FindMixedIslandNode(...)100%22100%
File 5: FindMixedIslandNode(...)100%22100%
File 5: FindMixedIslandNode(...)100%11100%
File 5: UnionMixedIslandNodes(...)100%11100%
File 5: CompressMixedIslandRoots()100%11100%
File 5: ResolveMixedConstraintRootKey(...)100%11100%
File 5: WakeMixedIslandBodies(...)100%11100%
File 5: SolveMixedIslandRange(...)100%66100%
File 5: HasAwakeMovableParticipant(...)100%22100%
File 5: HasAwakeResponseParticipant(...)100%22100%
File 5: ShouldSolveSingleMixedPair(...)100%44100%
File 5: IsAwakeMovable(...)100%44100%
File 5: IsAwakeMovable(...)100%44100%
File 5: IsMovableMixedIslandBody(...)100%44100%
File 5: IsMovableMixedIslandBody(...)100%44100%
File 5: Create3DBodyKey(...)100%11100%
File 5: Create2DBodyKey(...)100%11100%
File 6: Compare(...)100%11100%
File 6: Compare(...)100%11100%
File 6: Compare(...)100%11100%
File 6: Compare(...)100%22100%
File 6: Compare(...)100%11100%
File 6: Compare(...)100%11100%
File 6: .ctor(...)100%11100%
File 6: get_IsAwakeForCollision()100%22100%
File 6: WakeFromCollision()100%22100%
File 6: .ctor(...)100%11100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Core/Mixed/GravitasMixedCollisionService.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasMixedCollisionService.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.CollisionHandling;
 9using GridForge.Grids;
 10using SwiftCollections;
 11using System;
 12
 13namespace Gravitas;
 14
 15/// <summary>
 16/// Owns mixed 2D/3D collision lifecycle and broad-phase state for one <see cref="GravitasWorldContext"/>.
 17/// </summary>
 18internal sealed partial class GravitasMixedCollisionService
 19{
 20    private const int DefaultPartitionPoolCapacity = 1024;
 121    private static readonly PhysicsMixedPartitionOrderComparer PartitionOrderComparer = new();
 122    private static readonly MixedColliderKeyComparer CandidatePairComparer = new();
 123    private static readonly MixedResponsePairComparer ResponsePairComparer = new();
 124    private static readonly IslandNodeKeyComparer<MixedIslandNode> IslandNodeComparer = new();
 125    private static readonly MixedIslandConstraintComparer IslandConstraintComparer = new();
 126    private static readonly Collider2DIdComparer Collider2DIdOrderComparer = new();
 127    private static readonly Collider3DIdComparer Collider3DIdOrderComparer = new();
 28
 29    private readonly GravitasWorldContext _context;
 508330    private readonly SwiftBucket<PhysicsMixedPartition> _activePartitions = new(DefaultPartitionPoolCapacity);
 508331    private readonly SwiftStack<PhysicsMixedPartition> _inactivePartitionPool = new(DefaultPartitionPoolCapacity);
 508332    private readonly SwiftStack<SwiftSparseSet> _inactivePartitionMembershipPool = new(DefaultPartitionPoolCapacity);
 508333    private readonly SwiftHashSet<ulong> _processedPairKeys = new();
 508334    private readonly SwiftList<Voxel> _coveredVoxels = new();
 508335    private readonly GridTraceScratch _traceScratch = new();
 508336    private readonly SwiftList<PhysicsMixedPartition> _retainedPartitions = new();
 508337    private readonly SwiftList<PhysicsMixedPartition> _distributionPartitions = new();
 508338    private readonly SwiftList<int> _distributionDynamic3DIds = new();
 508339    private readonly SwiftList<int> _distributionKinematic3DIds = new();
 508340    private readonly SwiftList<int> _distributionStatic3DIds = new();
 508341    private readonly SwiftList<int> _distributionDynamic2DIds = new();
 508342    private readonly SwiftList<int> _distributionKinematic2DIds = new();
 508343    private readonly SwiftList<int> _distributionStatic2DIds = new();
 508344    private readonly SwiftList<MixedColliderKey> _candidatePairs = new();
 508345    private readonly SwiftList<PhysicsMixedPartition> _queryPartitions = new();
 508346    private readonly SwiftList<int> _queryColliderIds = new();
 508347    private readonly SwiftHashSet<int> _queryColliderRedundancy = new();
 508348    private readonly SwiftDictionary<ulong, CollisionPairMixed> _pairs = new();
 508349    private readonly SwiftList<MixedPairLifetimeToken> _pairsToRemove = new();
 508350    private readonly SwiftStack<CollisionPairMixed> _cachedPairs = new();
 508351    private readonly SwiftList<CollisionPairMixed> _mixedResponsePairs = new();
 508352    private readonly SwiftList<MixedIslandNode> _mixedIslandNodes = new();
 508353    private readonly SwiftList<MixedIslandConstraint> _mixedIslandConstraints = new();
 54    private readonly Action<PhysicsMixedPartition> _releaseRetainedPartition;
 55
 56    private int _retainedPartitionRetirementCursor;
 508357    private int _cached3DQueryRefreshFrame = int.MinValue;
 508358    private int _cached3DQueryRefreshLateToken = int.MinValue;
 508359    private int _cached2DQueryRefreshFrame = int.MinValue;
 508360    private int _cached2DQueryRefreshLateToken = int.MinValue;
 61
 508362    internal GravitasMixedCollisionService(GravitasWorldContext context)
 63    {
 508364        SwiftThrowHelper.ThrowIfNull(context, nameof(context));
 508365        _context = context;
 508366        _releaseRetainedPartition = ReleasePartition;
 508367    }
 68
 3112869    internal GravitasWorldContext Context => _context;
 70
 71    internal uint Version { get; private set; } = 1;
 72
 7173    internal int ActivePartitionCount => _activePartitions.Count;
 74
 7575    internal int InactivePartitionCount => _inactivePartitionPool.Count;
 76
 477    internal int InactivePartitionMembershipCount => _inactivePartitionMembershipPool.Count;
 78
 7779    internal int RetainedPartitionCount => _retainedPartitions.Count;
 80
 81    internal int LastBroadPhaseCandidateCount { get; private set; }
 82
 23083    internal int ActivePairCount => _pairs.Count;
 84
 7185    internal int PooledPairCount => _cachedPairs.Count;
 86
 87    internal int SimulateCount { get; private set; }
 88
 89    internal int LateSimulateCount { get; private set; }
 90
 91    internal int VisualizeCount { get; private set; }
 92
 2093    internal MixedColliderKey GetCandidate(int index) => _candidatePairs[index];
 94
 95    internal void Simulate()
 96    {
 15397        SimulateCount++;
 15398        LastBroadPhaseCandidateCount = 0;
 15399    }
 100
 101    internal void LateSimulate()
 102    {
 345103        LateSimulateCount++;
 345104        ProcessMixedContacts();
 345105    }
 106
 107    private void ProcessMixedContacts()
 108    {
 345109        LastBroadPhaseCandidateCount = 0;
 345110        _candidatePairs.FastClear();
 345111        _mixedResponsePairs.FastClear();
 345112        _processedPairKeys.Clear();
 113
 345114        Refresh3DColliderPartitions();
 345115        Refresh2DColliderPartitions(rebuildShapes: false);
 116
 345117        Version++;
 345118        _distributionPartitions.FastClear();
 20624119        foreach (PhysicsMixedPartition partition in _activePartitions)
 9967120            _distributionPartitions.Add(partition);
 121
 345122        _distributionPartitions.SortInPlace(PartitionOrderComparer);
 20624123        for (int i = 0; i < _distributionPartitions.Count; i++)
 124        {
 9967125            _distributionPartitions[i].Distribute(
 9967126                _distributionDynamic3DIds,
 9967127                _distributionKinematic3DIds,
 9967128                _distributionStatic3DIds,
 9967129                _distributionDynamic2DIds,
 9967130                _distributionKinematic2DIds,
 9967131                _distributionStatic2DIds);
 132        }
 133
 345134        _candidatePairs.SortInPlace(CandidatePairComparer);
 345135        LastBroadPhaseCandidateCount = _candidatePairs.Count;
 345136        int frame = _context.FrameCount;
 1066137        for (int i = 0; i < _candidatePairs.Count; i++)
 188138            ProcessCandidate(_candidatePairs[i], frame);
 139
 345140        SolveMixedResponsePairs();
 345141        CleanupUntouchedPairs(frame);
 345142        RetireExpiredRetainedPartitions();
 345143    }
 144
 145    internal void Visualize()
 146    {
 1147        VisualizeCount++;
 1148    }
 149
 150    internal void Reset()
 151    {
 31152        DetachRetainedPartitions();
 31153        _activePartitions.Clear();
 31154        _inactivePartitionPool.Clear();
 31155        _inactivePartitionMembershipPool.Clear();
 31156        _processedPairKeys.Clear();
 31157        _coveredVoxels.FastClear();
 31158        _traceScratch.Clear();
 31159        _distributionPartitions.FastClear();
 31160        _distributionDynamic3DIds.FastClear();
 31161        _distributionKinematic3DIds.FastClear();
 31162        _distributionStatic3DIds.FastClear();
 31163        _distributionDynamic2DIds.FastClear();
 31164        _distributionKinematic2DIds.FastClear();
 31165        _distributionStatic2DIds.FastClear();
 31166        _candidatePairs.FastClear();
 31167        _queryPartitions.FastClear();
 31168        _queryColliderIds.FastClear();
 31169        _queryColliderRedundancy.Clear();
 31170        _pairs.Clear();
 31171        _pairsToRemove.FastClear();
 31172        _cachedPairs.Clear();
 31173        _mixedResponsePairs.FastClear();
 31174        _mixedIslandNodes.FastClear();
 31175        _mixedIslandConstraints.FastClear();
 31176        _cached3DQueryRefreshFrame = int.MinValue;
 31177        _cached3DQueryRefreshLateToken = int.MinValue;
 31178        _cached2DQueryRefreshFrame = int.MinValue;
 31179        _cached2DQueryRefreshLateToken = int.MinValue;
 31180        Version = 1;
 31181        LastBroadPhaseCandidateCount = 0;
 31182        SimulateCount = 0;
 31183        LateSimulateCount = 0;
 31184        VisualizeCount = 0;
 31185        _retainedPartitionRetirementCursor = 0;
 31186    }
 187
 188}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Core/Mixed/GravitasMixedCollisionService.Pairs.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasMixedCollisionService.Pairs.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 Gravitas.CollisionHandling;
 10using System.Runtime.CompilerServices;
 11
 12namespace Gravitas;
 13
 14internal readonly struct MixedPairLifetimeToken
 15{
 16    internal MixedPairLifetimeToken(CollisionPairMixed pair)
 17    {
 18        Pair = pair;
 19        LifetimeVersion = pair.LifetimeVersion;
 20        Collider3D = new ColliderLifetimeToken(pair.Collider3D);
 21        Collider2D = new ColliderLifetimeToken2D(pair.Collider2D);
 22    }
 23
 24    internal CollisionPairMixed Pair { get; }
 25
 26    internal long LifetimeVersion { get; }
 27
 28    private ColliderLifetimeToken Collider3D { get; }
 29
 30    private ColliderLifetimeToken2D Collider2D { get; }
 31
 32    internal bool IsCurrentLifetime => Pair.LifetimeVersion == LifetimeVersion
 33        && Collider3D.IsCurrentLifetime
 34        && Collider2D.IsCurrentLifetime;
 35}
 36
 37internal sealed partial class GravitasMixedCollisionService
 38{
 39    internal void RemovePairsFor3DCollider(LSCollider collider)
 40    {
 21941        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 21942        int removalStart = _pairsToRemove.Count;
 47443        foreach (var pairEntry in _pairs)
 44        {
 1845            CollisionPairMixed pair = pairEntry.Value;
 1846            if (!ReferenceEquals(pair.Collider3D, collider))
 47                continue;
 48
 1749            _pairsToRemove.Add(new MixedPairLifetimeToken(pair));
 50        }
 51
 21952        RemoveQueuedPairs(removalStart, _pairsToRemove.Count);
 21953    }
 54
 55    internal void RemovePairsFor2DCollider(LSCollider2D collider)
 56    {
 26457        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 26458        int removalStart = _pairsToRemove.Count;
 55259        foreach (var pairEntry in _pairs)
 60        {
 1261            CollisionPairMixed pair = pairEntry.Value;
 1262            if (!ReferenceEquals(pair.Collider2D, collider))
 63                continue;
 64
 1165            _pairsToRemove.Add(new MixedPairLifetimeToken(pair));
 66        }
 67
 26468        RemoveQueuedPairs(removalStart, _pairsToRemove.Count);
 26469    }
 70
 71    internal bool RequireCollisionPair(LSCollider collider3D, LSCollider2D collider2D)
 72    {
 524173        return collider3D.IsActive && !collider3D.IsDeactivationInProgress && collider2D.IsActive
 524174            && collider3D.Shape != ColliderType.None && collider2D.Shape != ColliderType2D.None
 524175            && (collider3D.Body != null || collider2D.Body != null)
 524176            && !ReferenceEquals(collider3D.AgentOrNull, collider2D.AgentOrNull)
 524177            && !collider3D.ExcludesMixedCollisionWith(collider2D)
 524178            && ColliderCollisionFilter.AllowsPhysicalPair(collider3D, collider2D)
 524179            && !_context.Physics.IsLayerCollisionDisabled(collider3D.Layer, collider2D.Layer);
 80    }
 81
 82    private void ProcessCandidate(MixedColliderKey candidate, int frame)
 83    {
 18884        if (!_context.Physics.TryGetColliderById(candidate.Collider3DId, out LSCollider? collider3D)
 18885            || !_context.Physics2D.TryGetColliderById(candidate.Collider2DId, out LSCollider2D? collider2D))
 86        {
 287            return;
 88        }
 89
 18690        LSCollider resolved3D = collider3D!;
 18691        LSCollider2D resolved2D = collider2D!;
 18692        bool hasPair = _pairs.TryGetValue(candidate.Key, out CollisionPairMixed pair);
 18693        bool triggerPair = resolved3D.IsTrigger || resolved2D.IsTrigger;
 18694        bool hasAwakeMovableParticipant = HasAwakeMovableParticipant(resolved3D, resolved2D);
 18695        if (!hasPair && !triggerPair && !hasAwakeMovableParticipant)
 196            return;
 97
 18598        if (!CollisionDetectionMixed.TryCollide(resolved3D, resolved2D, out MixedContact contact))
 1099            return;
 100
 175101        if (!triggerPair && !hasAwakeMovableParticipant)
 102        {
 2103            pair!.MarkResting(frame, contact);
 2104            _mixedResponsePairs.Add(pair);
 2105            return;
 106        }
 107
 173108        if (!hasPair)
 109        {
 137110            pair = CreatePair(resolved3D, resolved2D);
 137111            _pairs.Add(candidate.Key, pair);
 112        }
 113
 173114        pair!.MarkColliding(frame, contact);
 173115        if (!triggerPair)
 144116            _mixedResponsePairs.Add(pair);
 173117    }
 118
 119    private void CleanupUntouchedPairs(int frame)
 120    {
 345121        int removalStart = _pairsToRemove.Count;
 1050122        foreach (var pairEntry in _pairs)
 180123            _pairsToRemove.Add(new MixedPairLifetimeToken(pairEntry.Value));
 124
 345125        int removalEnd = _pairsToRemove.Count;
 126        try
 127        {
 1050128            for (int i = removalStart; i < removalEnd; i++)
 129            {
 180130                MixedPairLifetimeToken token = _pairsToRemove[i];
 180131                CollisionPairMixed pair = token.Pair;
 180132                if (!IsCurrentPair(token))
 133                    continue;
 134
 180135                if (pair.LastFrame == frame)
 136                    continue;
 137
 13138                if (TryKeepUntouchedPair(pair, frame))
 139                    continue;
 140
 4141                RemoveCurrentPair(token);
 142            }
 345143        }
 144        finally
 145        {
 345146            if (removalStart == 0)
 344147                _pairsToRemove.FastClear();
 345148        }
 345149    }
 150
 151    private bool TryKeepUntouchedPair(CollisionPairMixed pair, int frame)
 152    {
 13153        LSCollider collider3D = pair.Collider3D;
 13154        LSCollider2D collider2D = pair.Collider2D;
 13155        bool triggerPair = collider3D.IsTrigger || collider2D.IsTrigger;
 13156        if ((!triggerPair && HasAwakeMovableParticipant(collider3D, collider2D))
 13157            || !RequireCollisionPair(collider3D, collider2D)
 13158            || !MixedBoundsOverlap(collider3D, collider2D)
 13159            || !CollisionDetectionMixed.TryCollide(collider3D, collider2D, out MixedContact contact))
 160        {
 4161            return false;
 162        }
 163
 9164        if (triggerPair)
 165        {
 3166            pair.MarkColliding(frame, contact);
 3167            return true;
 168        }
 169
 6170        pair.MarkResting(frame);
 6171        return true;
 172    }
 173
 174    private CollisionPairMixed CreatePair(LSCollider collider3D, LSCollider2D collider2D)
 175    {
 137176        if (_context.Settings.PoolingEnabled && _cachedPairs.Count > 0)
 177        {
 2178            CollisionPairMixed pair = _cachedPairs.Pop();
 2179            pair.Initialize(collider3D, collider2D);
 2180            return pair;
 181        }
 182
 135183        return new CollisionPairMixed(collider3D, collider2D);
 184    }
 185
 186    private void RemoveQueuedPairs(int removalStart, int removalEnd)
 187    {
 188        try
 189        {
 1022190            for (int i = removalStart; i < removalEnd; i++)
 28191                RemoveCurrentPair(_pairsToRemove[i]);
 483192        }
 193        finally
 194        {
 483195            if (removalStart == 0)
 478196                _pairsToRemove.FastClear();
 483197        }
 483198    }
 199
 200    private bool IsCurrentPair(in MixedPairLifetimeToken token) =>
 212201        token.IsCurrentLifetime
 212202        && _pairs.TryGetValue(token.Pair.Key, out CollisionPairMixed currentPair)
 212203        && ReferenceEquals(currentPair, token.Pair);
 204
 205    private void RemoveCurrentPair(in MixedPairLifetimeToken token)
 206    {
 32207        if (!IsCurrentPair(token))
 3208            return;
 209
 29210        CollisionPairMixed pair = token.Pair;
 29211        _pairs.Remove(pair.Key);
 29212        pair.MarkSeparated();
 29213        RecyclePair(pair);
 29214    }
 215
 216    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 217    private void RecyclePair(CollisionPairMixed pair)
 218    {
 29219        if (_context.Settings.PoolingEnabled && !pair.IsNotificationInProgress)
 20220            _cachedPairs.Push(pair);
 29221    }
 222
 223}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Core/Mixed/GravitasMixedCollisionService.Partitioning.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasMixedCollisionService.Partitioning.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2026–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using FixedMathSharp;
 9using FixedMathSharp.Geometry;
 10using Gravitas.Colliders;
 11using Gravitas.Support;
 12using GridForge.Grids;
 13using GridForge.Spatial;
 14using GridForge.Utility;
 15using SwiftCollections;
 16using System.Runtime.CompilerServices;
 17
 18namespace Gravitas;
 19
 20internal sealed partial class GravitasMixedCollisionService
 21{
 22    internal bool Refresh3DColliderPartition(LSCollider collider)
 23    {
 157124        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 157125        SwiftThrowHelper.ThrowIfArgument(
 157126            !ReferenceEquals(collider.Context, _context),
 157127            nameof(collider),
 157128            "3D collider must belong to this mixed collision service context.");
 29
 157130        if (!collider.IsActive || collider.Shape == ColliderType.None)
 31        {
 332            ClearPartitioned3DCollider(collider, force: true);
 333            collider.MarkMixedUnpartitioned();
 334            collider.ClearMixedPartitionCoordinates();
 35
 336            return false;
 37        }
 38
 156839        Get3DCoverageBounds(collider, out Vector3d coverageMin, out Vector3d coverageMax);
 156840        MixedPartitionMobilityKind kind = Get3DMobilityKind(collider);
 156841        if (collider.MatchesMixedPartitionGridBounds(coverageMin, coverageMax, (int)kind))
 42        {
 89043            Refresh3DPartitionAwakeState(collider);
 89044            return false;
 45        }
 46
 67847        if (collider.IsMixedPartitioned)
 24848            ClearPartitioned3DCollider(collider, force: true);
 49
 67850        return Partition3DCollider(collider, coverageMin, coverageMax);
 51    }
 52
 53    internal bool Refresh2DColliderPartition(LSCollider2D collider) =>
 6454        Refresh2DColliderPartition(collider, rebuildShape: true);
 55
 56    private bool Refresh2DColliderPartition(LSCollider2D collider, bool rebuildShape)
 57    {
 126758        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 126759        SwiftThrowHelper.ThrowIfArgument(
 126760            !ReferenceEquals(collider.Context, _context),
 126761            nameof(collider),
 126762            "2D collider must belong to this mixed collision service context.");
 63
 126764        if (rebuildShape)
 70365            collider.Rebuild();
 66
 126767        if (!collider.IsActive || collider.Shape == ColliderType2D.None)
 68        {
 469            ClearPartitioned2DCollider(collider, force: true);
 470            collider.MarkMixedUnpartitioned();
 471            collider.ClearMixedPartitionCoordinates();
 72
 473            return false;
 74        }
 75
 126376        Get2DMixedCoverageBounds(collider, out Vector3d coverageMin, out Vector3d coverageMax);
 126377        MixedPartitionMobilityKind kind = Get2DMobilityKind(collider);
 126378        if (collider.MatchesMixedPartitionGridBounds(coverageMin, coverageMax, (int)kind))
 79        {
 63580            Refresh2DPartitionAwakeState(collider);
 63581            return false;
 82        }
 83
 62884        if (collider.IsMixedPartitioned)
 23785            ClearPartitioned2DCollider(collider, force: true);
 86
 62887        return Partition2DCollider(collider, coverageMin, coverageMax);
 88    }
 89
 90    internal bool ClearPartitioned3DCollider(LSCollider collider, bool force = false)
 91    {
 27892        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 27893        if (!collider.IsMixedPartitioned)
 394            return false;
 95
 27596        Get3DCoverageBounds(collider, out Vector3d coverageMin, out Vector3d coverageMax);
 27597        MixedPartitionMobilityKind currentKind = Get3DMobilityKind(collider);
 27598        if (!force && collider.MatchesMixedPartitionGridBounds(coverageMin, coverageMax, (int)currentKind))
 199            return false;
 100
 274101        SwiftList<WorldVoxelIndex> coordinates = collider.MixedPartitionCoordinates!;
 274102        GridWorld world = _context.World;
 274103        MixedPartitionMobilityKind partitionKind = GetStoredMobilityKind(collider.MixedPartitionKind);
 23002104        for (int i = 0; i < coordinates.Count; i++)
 105        {
 11227106            WorldVoxelIndex coordinate = coordinates[i];
 11227107            if (!world.TryGetVoxel(coordinate, out Voxel? voxel)
 11227108                || !voxel!.TryGetPartition(out PhysicsMixedPartition? partition))
 109            {
 110                continue;
 111            }
 112
 11198113            Remove3DObject(partition!, collider.Id, partitionKind);
 114        }
 115
 274116        collider.MarkMixedUnpartitioned();
 274117        collider.ClearMixedPartitionCoordinates();
 274118        return true;
 119    }
 120
 121    internal bool ClearPartitioned2DCollider(LSCollider2D collider, bool force = false)
 122    {
 267123        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 267124        if (!collider.IsMixedPartitioned)
 4125            return false;
 126
 263127        Get2DMixedCoverageBounds(collider, out Vector3d coverageMin, out Vector3d coverageMax);
 263128        MixedPartitionMobilityKind currentKind = Get2DMobilityKind(collider);
 263129        if (!force && collider.MatchesMixedPartitionGridBounds(coverageMin, coverageMax, (int)currentKind))
 1130            return false;
 131
 262132        SwiftList<WorldVoxelIndex> coordinates = collider.MixedPartitionCoordinates!;
 262133        GridWorld world = _context.World;
 262134        MixedPartitionMobilityKind partitionKind = GetStoredMobilityKind(collider.MixedPartitionKind);
 10672135        for (int i = 0; i < coordinates.Count; i++)
 136        {
 5074137            WorldVoxelIndex coordinate = coordinates[i];
 5074138            if (!world.TryGetVoxel(coordinate, out Voxel? voxel)
 5074139                || !voxel!.TryGetPartition(out PhysicsMixedPartition? partition))
 140            {
 141                continue;
 142            }
 143
 5045144            Remove2DObject(partition!, collider.Id, partitionKind);
 145        }
 146
 262147        collider.MarkMixedUnpartitioned();
 262148        collider.ClearMixedPartitionCoordinates();
 262149        return true;
 150    }
 151
 152    internal void Refresh3DPartitionAwakeState(LSCollider collider)
 153    {
 894154        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 894155        if (!collider.IsMixedPartitioned)
 1156            return;
 157
 893158        SwiftList<WorldVoxelIndex> coordinates = collider.MixedPartitionCoordinates!;
 893159        SolidBody? body = collider.Body;
 893160        if (collider.IsStatic)
 730161            return;
 162
 163163        bool awake = body!.IsAwakeForCollision;
 163164        GridWorld world = _context.World;
 15940165        for (int i = 0; i < coordinates.Count; i++)
 166        {
 7807167            WorldVoxelIndex coordinate = coordinates[i];
 7807168            if (!world.TryGetVoxel(coordinate, out Voxel? voxel)
 7807169                || !voxel!.TryGetPartition(out PhysicsMixedPartition? partition))
 170            {
 171                continue;
 172            }
 173
 7805174            partition!.SetDynamic3DObjectAwake(collider.Id, awake);
 175        }
 163176    }
 177
 178    internal void Refresh2DPartitionAwakeState(LSCollider2D collider)
 179    {
 639180        SwiftThrowHelper.ThrowIfNull(collider, nameof(collider));
 639181        if (!collider.IsMixedPartitioned)
 1182            return;
 183
 638184        SwiftList<WorldVoxelIndex> coordinates = collider.MixedPartitionCoordinates!;
 638185        SolidBody2D? body = collider.Body;
 638186        if (collider.IsStatic)
 581187            return;
 188
 57189        bool awake = body!.IsAwakeForCollision;
 57190        GridWorld world = _context.World;
 3194191        for (int i = 0; i < coordinates.Count; i++)
 192        {
 1540193            WorldVoxelIndex coordinate = coordinates[i];
 1540194            if (!world.TryGetVoxel(coordinate, out Voxel? voxel)
 1540195                || !voxel!.TryGetPartition(out PhysicsMixedPartition? partition))
 196            {
 197                continue;
 198            }
 199
 1538200            partition!.SetDynamic2DObjectAwake(collider.Id, awake);
 201        }
 57202    }
 203
 204    internal void ProcessPartitionCandidate(int collider3DId, int collider2DId)
 205    {
 4435206        if (!_context.Physics.TryGetColliderById(collider3DId, out LSCollider? collider3D)
 4435207            || !_context.Physics2D.TryGetColliderById(collider2DId, out LSCollider2D? collider2D)
 4435208            || !RequireCollisionPair(collider3D!, collider2D!)
 4435209            || !MixedBoundsOverlap(collider3D!, collider2D!))
 210        {
 445211            return;
 212        }
 213
 3990214        ulong key = MixedColliderKey.CreateKey(collider3DId, collider2DId);
 3990215        if (!_processedPairKeys.Add(key))
 3802216            return;
 217
 188218        _candidatePairs.Add(new MixedColliderKey(collider3DId, collider2DId));
 188219    }
 220
 221    internal void Collect2DCandidatesInMixedBounds(
 222        Vector3d min,
 223        Vector3d max,
 224        PhysicsLayerMask layerMask,
 225        SwiftList<LSCollider2D> candidates,
 226        bool staticStyleOnly = false,
 227        bool cachePartitionRefresh = false)
 228    {
 807229        SwiftThrowHelper.ThrowIfNull(candidates, nameof(candidates));
 807230        Refresh2DColliderPartitionsForQuery(cachePartitionRefresh);
 807231        candidates.FastClear();
 807232        FixedBoundBox queryBounds = FixedBoundBox.FromMinMax(min, max);
 807233        CollectCoveredMixedQueryPartitions(min, max, _queryPartitions);
 807234        _queryPartitions.SortInPlace(PartitionOrderComparer);
 807235        _queryColliderRedundancy.Clear();
 236
 56252237        for (int i = 0; i < _queryPartitions.Count; i++)
 238        {
 27319239            PhysicsMixedPartition partition = _queryPartitions[i];
 27319240            if (staticStyleOnly)
 11385241                partition.CopyStaticStyle2DColliderIds(_queryColliderIds);
 242            else
 15934243                partition.Copy2DColliderIds(_queryColliderIds);
 244
 89682245            for (int j = 0; j < _queryColliderIds.Count; j++)
 246            {
 17522247                int colliderId = _queryColliderIds[j];
 17522248                if (!_queryColliderRedundancy.Add(colliderId)
 17522249                    || !_context.Physics2D.TryGetColliderById(colliderId, out LSCollider2D? collider)
 17522250                    || !Is2DQueryCandidate(collider!, queryBounds, layerMask))
 251                {
 252                    continue;
 253                }
 254
 654255                candidates.Add(collider!);
 256            }
 257        }
 258
 807259        _queryColliderRedundancy.Clear();
 807260        candidates.SortInPlace(Collider2DIdOrderComparer);
 807261    }
 262
 263    internal void Collect3DCandidatesInMixedBounds(
 264        Vector3d min,
 265        Vector3d max,
 266        PhysicsLayerMask layerMask,
 267        SwiftList<LSCollider> candidates,
 268        bool staticStyleOnly = false,
 269        bool cachePartitionRefresh = false)
 270    {
 841271        SwiftThrowHelper.ThrowIfNull(candidates, nameof(candidates));
 841272        Refresh3DColliderPartitionsForQuery(cachePartitionRefresh);
 841273        candidates.FastClear();
 841274        FixedBoundBox queryBounds = FixedBoundBox.FromMinMax(min, max);
 841275        CollectCoveredMixedQueryPartitions(min, max, _queryPartitions);
 841276        _queryPartitions.SortInPlace(PartitionOrderComparer);
 841277        _queryColliderRedundancy.Clear();
 278
 67376279        for (int i = 0; i < _queryPartitions.Count; i++)
 280        {
 32847281            PhysicsMixedPartition partition = _queryPartitions[i];
 32847282            if (staticStyleOnly)
 7275283                partition.CopyStaticStyle3DColliderIds(_queryColliderIds);
 284            else
 25572285                partition.Copy3DColliderIds(_queryColliderIds);
 286
 110290287            for (int j = 0; j < _queryColliderIds.Count; j++)
 288            {
 22298289                int colliderId = _queryColliderIds[j];
 22298290                if (!_queryColliderRedundancy.Add(colliderId)
 22298291                    || !_context.Physics.TryGetColliderById(colliderId, out LSCollider? collider)
 22298292                    || !Is3DQueryCandidate(collider!, queryBounds, layerMask))
 293                {
 294                    continue;
 295                }
 296
 811297                candidates.Add(collider!);
 298            }
 299        }
 300
 841301        _queryColliderRedundancy.Clear();
 841302        candidates.SortInPlace(Collider3DIdOrderComparer);
 841303    }
 304
 305
 306    internal int ActivatePartition(PhysicsMixedPartition partition)
 307    {
 13132308        SwiftThrowHelper.ThrowIfNull(partition, nameof(partition));
 13132309        SwiftThrowHelper.ThrowIfArgument(
 13132310            !ReferenceEquals(partition.Owner, this),
 13132311            nameof(partition),
 13132312            "Mixed partition must belong to this collision service.");
 313
 13132314        return _activePartitions.Add(partition);
 315    }
 316
 13132317    internal void DeactivatePartition(int activationId) => _activePartitions.TryRemoveAt(activationId);
 318
 319    internal PhysicsMixedPartition RentPartition()
 320    {
 37857321        if (_inactivePartitionPool.Count == 0)
 37828322            TryRetireEmptyRetainedPartitionForReuse();
 323
 37857324        PhysicsMixedPartition partition = _inactivePartitionPool.Count > 0
 37857325            ? _inactivePartitionPool.Pop()
 37857326            : new PhysicsMixedPartition();
 37857327        partition.SetOwner(this);
 37857328        return partition;
 329    }
 330
 331    internal void ReleasePartition(PhysicsMixedPartition partition)
 332    {
 37857333        SwiftThrowHelper.ThrowIfNull(partition, nameof(partition));
 37857334        SwiftThrowHelper.ThrowIfArgument(
 37857335            !ReferenceEquals(partition.Owner, this),
 37857336            nameof(partition),
 37857337            "Mixed partition must be released through its owning collision service.");
 338
 37857339        UntrackRetainedPartition(partition);
 37857340        partition.ResetForPool();
 37857341        _inactivePartitionPool.Push(partition);
 37857342    }
 343
 344    internal SwiftSparseSet RentPartitionMembership()
 345    {
 61787346        return _inactivePartitionMembershipPool.Count > 0
 61787347            ? _inactivePartitionMembershipPool.Pop()
 61787348            : new SwiftSparseSet();
 349    }
 350
 351    internal void ReleasePartitionMembership(ref SwiftSparseSet? membership)
 352    {
 325255353        if (membership == null)
 263468354            return;
 355
 61787356        SwiftSparseSet released = membership;
 61787357        membership = null;
 61787358        released.Clear();
 61787359        _inactivePartitionMembershipPool.Push(released);
 61787360    }
 361
 362    private void Refresh3DColliderPartitions()
 363    {
 1070364        int count = _context.Physics.ColliderCount;
 4896365        for (int i = 0; i < count; i++)
 1378366            Refresh3DColliderPartition(_context.Physics.GetColliderByServiceIndex(i));
 1070367    }
 368
 369    private void Refresh3DColliderPartitionsForQuery(bool cachePartitionRefresh)
 370    {
 841371        if (!cachePartitionRefresh)
 372        {
 560373            Refresh3DColliderPartitions();
 560374            return;
 375        }
 376
 281377        int frame = _context.FrameCount;
 281378        int lateToken = _context.LateSimulateToken;
 379        // CCD batches query stable opposite-dimension targets many times in one late-sim phase.
 380        // Cache only the partition refresh; every sweep still gathers and sorts its own hits.
 281381        if (_cached3DQueryRefreshFrame == frame && _cached3DQueryRefreshLateToken == lateToken)
 116382            return;
 383
 165384        Refresh3DColliderPartitions();
 165385        _cached3DQueryRefreshFrame = frame;
 165386        _cached3DQueryRefreshLateToken = lateToken;
 165387    }
 388
 389    private void Refresh2DColliderPartitions(bool rebuildShapes = true)
 390    {
 1021391        int count = _context.Physics2D.ColliderCount;
 4448392        for (int i = 0; i < count; i++)
 1203393            Refresh2DColliderPartition(_context.Physics2D.GetColliderByServiceIndex(i), rebuildShapes);
 1021394    }
 395
 396    private void Refresh2DColliderPartitionsForQuery(bool cachePartitionRefresh)
 397    {
 807398        if (!cachePartitionRefresh)
 399        {
 525400            Refresh2DColliderPartitions();
 525401            return;
 402        }
 403
 282404        int frame = _context.FrameCount;
 282405        int lateToken = _context.LateSimulateToken;
 406        // CCD batches query stable opposite-dimension targets many times in one late-sim phase.
 407        // Cache only the partition refresh; every sweep still gathers and sorts its own hits.
 282408        if (_cached2DQueryRefreshFrame == frame && _cached2DQueryRefreshLateToken == lateToken)
 131409            return;
 410
 411        // CCD callers request a cached refresh only after the 2D frame has
 412        // prepared its authoritative target poses. Rebuilding those shapes
 413        // again here duplicates work and can churn shape-owned scratch state.
 151414        Refresh2DColliderPartitions(rebuildShapes: false);
 151415        _cached2DQueryRefreshFrame = frame;
 151416        _cached2DQueryRefreshLateToken = lateToken;
 151417    }
 418
 419    private bool Partition3DCollider(LSCollider collider, Vector3d coverageMin, Vector3d coverageMax)
 420    {
 678421        MixedPartitionMobilityKind kind = Get3DMobilityKind(collider);
 678422        SwiftList<WorldVoxelIndex> coordinates = collider.GetOrCreateMixedPartitionCoordinates();
 678423        coordinates.FastClear();
 424
 678425        ScanCovered3DVoxels(collider, coverageMin, coverageMax, coordinates, kind);
 678426        if (coordinates.Count == 0)
 16427            return false;
 428
 662429        collider.MarkMixedPartitioned(coverageMin, coverageMax, (int)kind);
 662430        return true;
 431    }
 432
 433    private bool Partition2DCollider(LSCollider2D collider, Vector3d coverageMin, Vector3d coverageMax)
 434    {
 628435        MixedPartitionMobilityKind kind = Get2DMobilityKind(collider);
 628436        SwiftList<WorldVoxelIndex> coordinates = collider.GetOrCreateMixedPartitionCoordinates();
 628437        coordinates.FastClear();
 438
 628439        ScanCovered2DMixedVoxels(collider, coverageMin, coverageMax, coordinates, kind);
 628440        if (coordinates.Count == 0)
 11441            return false;
 442
 617443        collider.MarkMixedPartitioned(coverageMin, coverageMax, (int)kind);
 617444        return true;
 445    }
 446
 447    private void ScanCovered3DVoxels(
 448        LSCollider collider,
 449        Vector3d coverageMin,
 450        Vector3d coverageMax,
 451        SwiftList<WorldVoxelIndex> coordinates,
 452        MixedPartitionMobilityKind kind)
 453    {
 678454        GridWorld world = _context.World;
 678455        GridTracer.GetCoveredVoxelsInto(
 678456            world,
 678457            coverageMin,
 678458            coverageMax,
 678459            _coveredVoxels,
 678460            _traceScratch,
 678461            Fixed64.Half);
 462
 678463        var traversal = new GridTraversalState(world, GridTraversalPaddingMode.MaxCellEdge);
 114328464        for (int i = 0; i < _coveredVoxels.Count; i++)
 56486465            TryPartition3DVoxel(collider, coordinates, _coveredVoxels[i], ref traversal, kind);
 678466    }
 467
 468    private void ScanCovered2DMixedVoxels(
 469        LSCollider2D collider,
 470        Vector3d coverageMin,
 471        Vector3d coverageMax,
 472        SwiftList<WorldVoxelIndex> coordinates,
 473        MixedPartitionMobilityKind kind)
 474    {
 628475        GridWorld world = _context.World;
 628476        GridTracer.GetCoveredVoxelsInto(
 628477            world,
 628478            coverageMin,
 628479            coverageMax,
 628480            _coveredVoxels,
 628481            _traceScratch,
 628482            Fixed64.Half);
 483
 628484        var traversal = new GridTraversalState(world, GridTraversalPaddingMode.MaxCellEdge);
 59256485        for (int i = 0; i < _coveredVoxels.Count; i++)
 29000486            TryPartition2DMixedVoxel(collider, coordinates, _coveredVoxels[i], ref traversal, kind);
 628487    }
 488
 489    private void CollectCoveredMixedQueryPartitions(
 490        Vector3d min,
 491        Vector3d max,
 492        SwiftList<PhysicsMixedPartition> partitions)
 493    {
 1648494        partitions.FastClear();
 1648495        ScanCoveredMixedQueryPartitions(min, max, partitions);
 1648496    }
 497
 498    private void ScanCoveredMixedQueryPartitions(
 499        Vector3d queryMin,
 500        Vector3d queryMax,
 501        SwiftList<PhysicsMixedPartition> partitions)
 502    {
 1648503        GridTracer.GetCoveredVoxelsInto(
 1648504            _context.World,
 1648505            queryMin,
 1648506            queryMax,
 1648507            _coveredVoxels,
 1648508            _traceScratch,
 1648509            Fixed64.Half);
 510
 1648511        VisitCoveredVoxelsForMixedQuery(queryMin, queryMax, partitions);
 1648512    }
 513
 514    private void VisitCoveredVoxelsForMixedQuery(
 515        Vector3d queryMin,
 516        Vector3d queryMax,
 517        SwiftList<PhysicsMixedPartition> partitions)
 518    {
 1648519        GridWorld world = _context.World;
 1648520        var traversal = new GridTraversalState(world, GridTraversalPaddingMode.MaxCellEdge);
 528902521        for (int i = 0; i < _coveredVoxels.Count; i++)
 522        {
 262803523            Voxel voxel = _coveredVoxels[i];
 262803524            Fixed64 cellEdge = traversal.GetCellEdge(voxel);
 525
 262803526            if (!GridTraversal.IsWorldPositionInPaddedBounds(queryMin, queryMax, cellEdge, voxel.WorldPosition)
 262803527                || !voxel.TryGetPartition(out PhysicsMixedPartition? partition)
 262803528                || partition!.IsEmpty)
 529            {
 530                continue;
 531            }
 532
 60166533            partitions.Add(partition);
 534        }
 1648535    }
 536
 537    private void TryPartition3DVoxel(
 538        LSCollider collider,
 539        SwiftList<WorldVoxelIndex> coordinates,
 540        Voxel voxel,
 541        ref GridTraversalState traversal,
 542        MixedPartitionMobilityKind kind)
 543    {
 56486544        Fixed64 cellEdge = traversal.GetCellEdge(voxel);
 56486545        if (!collider.IsPositionInBounds(cellEdge, voxel.WorldPosition))
 3969546            return;
 547
 52517548        PhysicsMixedPartition partition = GetOrCreatePartition(voxel);
 52517549        coordinates.Add(voxel.WorldIndex);
 52517550        Add3DObject(partition, collider.Id, kind);
 52517551    }
 552
 553    private void TryPartition2DMixedVoxel(
 554        LSCollider2D collider,
 555        SwiftList<WorldVoxelIndex> coordinates,
 556        Voxel voxel,
 557        ref GridTraversalState traversal,
 558        MixedPartitionMobilityKind kind)
 559    {
 29000560        Fixed64 cellEdge = traversal.GetCellEdge(voxel);
 29000561        if (!collider.IsPositionInMixedBounds(cellEdge, voxel.WorldPosition))
 13631562            return;
 563
 15369564        PhysicsMixedPartition partition = GetOrCreatePartition(voxel);
 15369565        coordinates.Add(voxel.WorldIndex);
 15369566        Add2DObject(partition, collider.Id, kind);
 15369567    }
 568
 569    private PhysicsMixedPartition GetOrCreatePartition(Voxel voxel)
 570    {
 67886571        if (voxel.TryGetPartition(out PhysicsMixedPartition? partition))
 30038572            return partition!;
 573
 37848574        partition = RentPartition();
 37848575        SwiftThrowHelper.ThrowIfTrue(
 37848576            !voxel.TryAddPartition(partition),
 37848577            nameof(GravitasMixedCollisionService),
 37848578            "Unable to attach mixed physics partition to voxel.");
 579
 37848580        TrackRetainedPartition(partition);
 37848581        return partition;
 582    }
 583
 584    private void Get3DCoverageBounds(LSCollider collider, out Vector3d coverageMin, out Vector3d coverageMax)
 585    {
 1843586        coverageMin = collider.BoundsMin;
 1843587        coverageMax = collider.BoundsMax;
 1843588    }
 589
 590    private void Get2DMixedCoverageBounds(LSCollider2D collider, out Vector3d coverageMin, out Vector3d coverageMax)
 591    {
 1526592        FixedBoundBox bounds = collider.MixedBounds3D;
 1526593        coverageMin = bounds.Min;
 1526594        coverageMax = bounds.Max;
 1526595    }
 596
 597    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 598    private static MixedPartitionMobilityKind Get3DMobilityKind(LSCollider collider)
 599    {
 2521600        if (collider.IsStatic)
 1021601            return MixedPartitionMobilityKind.Static;
 602
 1500603        SolidBody? body = collider.Body;
 1500604        return body!.IsKinematic ? MixedPartitionMobilityKind.Kinematic : MixedPartitionMobilityKind.Dynamic;
 605    }
 606
 607    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 608    private static MixedPartitionMobilityKind Get2DMobilityKind(LSCollider2D collider)
 609    {
 2154610        if (collider.IsStatic)
 911611            return MixedPartitionMobilityKind.Static;
 612
 1243613        SolidBody2D? body = collider.Body;
 1243614        return body!.IsKinematic ? MixedPartitionMobilityKind.Kinematic : MixedPartitionMobilityKind.Dynamic;
 615    }
 616
 617    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 618    private static MixedPartitionMobilityKind GetStoredMobilityKind(int partitionKind)
 619    {
 536620        return partitionKind == (int)MixedPartitionMobilityKind.Kinematic
 536621            ? MixedPartitionMobilityKind.Kinematic
 536622            : partitionKind == (int)MixedPartitionMobilityKind.Static
 536623                ? MixedPartitionMobilityKind.Static
 536624                : MixedPartitionMobilityKind.Dynamic;
 625    }
 626
 627    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 628    private static void Add3DObject(PhysicsMixedPartition partition, int id, MixedPartitionMobilityKind kind)
 629    {
 52517630        if (kind == MixedPartitionMobilityKind.Static)
 631        {
 32605632            partition.AddStatic3DObject(id);
 32605633            return;
 634        }
 635
 19912636        if (kind == MixedPartitionMobilityKind.Kinematic)
 637        {
 9666638            partition.AddKinematic3DObject(id);
 9666639            return;
 640        }
 641
 10246642        partition.AddDynamic3DObject(id);
 10246643    }
 644
 645    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 646    private static void Add2DObject(PhysicsMixedPartition partition, int id, MixedPartitionMobilityKind kind)
 647    {
 15369648        if (kind == MixedPartitionMobilityKind.Static)
 649        {
 4869650            partition.AddStatic2DObject(id);
 4869651            return;
 652        }
 653
 10500654        if (kind == MixedPartitionMobilityKind.Kinematic)
 655        {
 3837656            partition.AddKinematic2DObject(id);
 3837657            return;
 658        }
 659
 6663660        partition.AddDynamic2DObject(id);
 6663661    }
 662
 663    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 664    private static void Remove3DObject(PhysicsMixedPartition partition, int id, MixedPartitionMobilityKind kind)
 665    {
 11198666        if (kind == MixedPartitionMobilityKind.Static)
 667        {
 852668            partition.RemoveStatic3DObject(id);
 852669            return;
 670        }
 671
 10346672        if (kind == MixedPartitionMobilityKind.Kinematic)
 673        {
 6480674            partition.RemoveKinematic3DObject(id);
 6480675            return;
 676        }
 677
 3866678        partition.RemoveDynamic3DObject(id);
 3866679    }
 680
 681    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 682    private static void Remove2DObject(PhysicsMixedPartition partition, int id, MixedPartitionMobilityKind kind)
 683    {
 5045684        if (kind == MixedPartitionMobilityKind.Static)
 685        {
 288686            partition.RemoveStatic2DObject(id);
 288687            return;
 688        }
 689
 4757690        if (kind == MixedPartitionMobilityKind.Kinematic)
 691        {
 2394692            partition.RemoveKinematic2DObject(id);
 2394693            return;
 694        }
 695
 2363696        partition.RemoveDynamic2DObject(id);
 2363697    }
 698
 699    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 700    private static bool MixedBoundsOverlap(LSCollider collider3D, LSCollider2D collider2D)
 701    {
 4216702        return collider3D.Bounds.Intersects(collider2D.MixedBounds3D);
 703    }
 704
 705    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 706    private static bool Is2DQueryCandidate(LSCollider2D collider, FixedBoundBox queryBounds, PhysicsLayerMask layerMask)
 707    {
 657708        if (!layerMask.Includes(collider.Layer))
 3709            return false;
 710
 654711        return collider.MixedBounds3D.Intersects(queryBounds);
 712    }
 713
 714    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 715    private static bool Is3DQueryCandidate(LSCollider collider, FixedBoundBox queryBounds, PhysicsLayerMask layerMask)
 716    {
 831717        return collider.IsActive
 831718            && layerMask.Includes(collider.Layer)
 831719            && collider.Bounds.Intersects(queryBounds);
 720    }
 721
 31722    private void DetachRetainedPartitions() => RetainedPartitionLifecycle.DetachAll(
 31723            _retainedPartitions,
 31724            _context.World,
 31725            this,
 31726            _releaseRetainedPartition,
 31727            nameof(PhysicsMixedPartition),
 31728            "Unable to detach retained mixed physics partition from its voxel during reset.");
 729
 37848730    private void TrackRetainedPartition(PhysicsMixedPartition partition) => RetainedPartitionLifecycle.Track(
 37848731            _retainedPartitions,
 37848732            this,
 37848733            partition,
 37848734            nameof(PhysicsMixedPartition));
 735
 37857736    private void UntrackRetainedPartition(PhysicsMixedPartition partition) => RetainedPartitionLifecycle.Untrack(
 37857737            _retainedPartitions,
 37857738            this,
 37857739            partition,
 37857740            ref _retainedPartitionRetirementCursor);
 741
 345742    private void RetireExpiredRetainedPartitions() => RetainedPartitionLifecycle.RetireExpired(
 345743            _retainedPartitions,
 345744            _context.World,
 345745            this,
 345746            _context.Settings.RetainedPartitionRetirementSweepBudget,
 345747            _context.FrameCount,
 345748            _context.Settings.RetainedPartitionTimeToKillFrames,
 345749            _releaseRetainedPartition,
 345750            ref _retainedPartitionRetirementCursor);
 751
 37828752    private bool TryRetireEmptyRetainedPartitionForReuse() => RetainedPartitionLifecycle.TryRetireEmptyForReuse(
 37828753            _retainedPartitions,
 37828754            _inactivePartitionPool,
 37828755            _context.World,
 37828756            this,
 37828757            _releaseRetainedPartition,
 37828758            ref _retainedPartitionRetirementCursor);
 759
 760}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Core/Mixed/GravitasMixedCollisionService.ReplayHash.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasMixedCollisionService.ReplayHash.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 Chronicler;
 9using Gravitas.Colliders;
 10using Gravitas.CollisionHandling;
 11using SwiftCollections;
 12
 13namespace Gravitas;
 14
 15internal sealed partial class GravitasMixedCollisionService
 16{
 17    internal void ContributeReplayHash(
 18        ref ChronicleHashWriter writer,
 19        GravitasReplayHashMode mode)
 20    {
 17421        SwiftList<LSCollider> replay3DColliders = _context.Physics.PrepareReplayColliders();
 17422        SwiftList<LSCollider2D> replay2DColliders = _context.Physics2D.PrepareReplayColliders();
 23
 17424        writer.WriteSection("physics.mixed", 2);
 17425        writer.WriteUInt32(Version);
 17426        writer.WriteInt32(ActivePairCount);
 27
 71228        for (int i = 0; i < replay3DColliders.Count; i++)
 29        {
 18230            int collider3DId = replay3DColliders[i].Id;
 76031            for (int j = 0; j < replay2DColliders.Count; j++)
 32            {
 19833                int collider2DId = replay2DColliders[j].Id;
 19834                ulong key = MixedColliderKey.CreateKey(collider3DId, collider2DId);
 19835                if (_pairs.TryGetValue(key, out CollisionPairMixed? pair))
 136                    pair!.ContributeReplayHash(ref writer, mode);
 37            }
 38        }
 39
 17440        if (mode != GravitasReplayHashMode.AuthoritativeWithSolverCaches)
 11041            return;
 42
 6443        writer.WriteSection("physics.mixed.caches", 1);
 6444        writer.WriteInt32(ActivePartitionCount);
 6445        writer.WriteInt32(InactivePartitionCount);
 6446        writer.WriteInt32(RetainedPartitionCount);
 6447        writer.WriteInt32(PooledPairCount);
 6448        writer.WriteInt32(LastBroadPhaseCandidateCount);
 6449        writer.WriteInt32(SimulateCount);
 6450        writer.WriteInt32(LateSimulateCount);
 6451        writer.WriteInt32(VisualizeCount);
 6452        writer.WriteInt32(_retainedPartitionRetirementCursor);
 6453        writer.WriteInt32(_cached3DQueryRefreshFrame);
 6454        writer.WriteInt32(_cached3DQueryRefreshLateToken);
 6455        writer.WriteInt32(_cached2DQueryRefreshFrame);
 6456        writer.WriteInt32(_cached2DQueryRefreshLateToken);
 6457    }
 58}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Core/Mixed/GravitasMixedCollisionService.Response.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasMixedCollisionService.Response.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 Gravitas.Colliders;
 10using Gravitas.CollisionHandling;
 11using System.Runtime.CompilerServices;
 12
 13namespace Gravitas;
 14
 15internal sealed partial class GravitasMixedCollisionService
 16{
 17    private void SolveMixedResponsePairs()
 18    {
 34519        if (_mixedResponsePairs.Count == 0)
 22620            return;
 21
 11922        if (_mixedResponsePairs.Count == 1)
 23        {
 9524            CollisionPairMixed pair = _mixedResponsePairs[0];
 9525            bool hasPlanarResponseCoupling = CollisionResponseMixed.HasPlanarResponseCoupling(pair, pair.Contact);
 9526            if (!ShouldSolveSingleMixedPair(pair, hasPlanarResponseCoupling))
 227                return;
 28
 9329            if (hasPlanarResponseCoupling)
 4130                pair.WakeSleepingBodiesForCollision();
 9331            CollisionResponseMixed.Resolve(pair, pair.Contact);
 9332            return;
 33        }
 34
 2435        _mixedResponsePairs.SortInPlace(ResponsePairComparer);
 2436        BuildMixedIslands();
 2437        if (_mixedIslandConstraints.Count == 0)
 138            return;
 39
 2340        _mixedIslandConstraints.SortInPlace(IslandConstraintComparer);
 41
 2342        int start = 0;
 5843        while (start < _mixedIslandConstraints.Count)
 44        {
 3545            int rootKey = _mixedIslandConstraints[start].RootKey;
 3546            int end = start + 1;
 4847            while (end < _mixedIslandConstraints.Count
 4848                && _mixedIslandConstraints[end].RootKey == rootKey)
 49            {
 1350                end++;
 51            }
 52
 3553            if (WakeMixedIslandBodies(rootKey))
 3354                SolveMixedIslandRange(rootKey, start, end);
 55
 3556            start = end;
 57        }
 2358    }
 59
 60    private void BuildMixedIslands()
 61    {
 2462        _mixedIslandNodes.FastClear();
 2463        _mixedIslandConstraints.FastClear();
 64
 15065        for (int i = 0; i < _mixedResponsePairs.Count; i++)
 66        {
 5167            CollisionPairMixed pair = _mixedResponsePairs[i];
 5168            AddMixedIslandNodeIfMovable(pair.Collider3D.Body);
 5169            AddMixedIslandNodeIfMovable(pair.Collider2D.Body);
 70        }
 71
 2472        SortAndDeduplicateMixedIslandNodes();
 2473        if (_mixedIslandNodes.Count == 0)
 174            return;
 75
 14476        for (int i = 0; i < _mixedResponsePairs.Count; i++)
 77        {
 4978            CollisionPairMixed pair = _mixedResponsePairs[i];
 4979            int node3D = FindMixedIslandNode(pair.Collider3D.Body);
 4980            int node2D = FindMixedIslandNode(pair.Collider2D.Body);
 4981            if (CollisionResponseMixed.HasPlanarResponseCoupling(pair, pair.Contact)
 4982                && node3D >= 0
 4983                && node2D >= 0)
 1784                UnionMixedIslandNodes(node3D, node2D);
 85        }
 86
 2387        CompressMixedIslandRoots();
 88
 14489        for (int i = 0; i < _mixedResponsePairs.Count; i++)
 90        {
 4991            CollisionPairMixed pair = _mixedResponsePairs[i];
 4992            int node3D = FindMixedIslandNode(pair.Collider3D.Body);
 4993            int node2D = FindMixedIslandNode(pair.Collider2D.Body);
 4994            int rootKey = ResolveMixedConstraintRootKey(node3D, node2D);
 4995            if (rootKey < 0)
 96                continue;
 97
 4898            _mixedIslandConstraints.Add(new MixedIslandConstraint(pair, rootKey, pair.Key));
 99        }
 23100    }
 101
 102    private void AddMixedIslandNodeIfMovable(SolidBody? body)
 103    {
 51104        if (!IsMovableMixedIslandBody(body))
 5105            return;
 106
 46107        _mixedIslandNodes.Add(new MixedIslandNode(Create3DBodyKey(body!), body!, null));
 46108    }
 109
 110    private void AddMixedIslandNodeIfMovable(SolidBody2D? body)
 111    {
 51112        if (!IsMovableMixedIslandBody(body))
 10113            return;
 114
 41115        _mixedIslandNodes.Add(new MixedIslandNode(Create2DBodyKey(body!), null, body!));
 41116    }
 117
 118    private void SortAndDeduplicateMixedIslandNodes() =>
 24119        IslandGraphUtility.SortAndDeduplicate(_mixedIslandNodes, IslandNodeComparer);
 120
 121    private int FindMixedIslandNode(SolidBody? body)
 122    {
 98123        if (!IsMovableMixedIslandBody(body))
 6124            return -1;
 125
 92126        return FindMixedIslandNode(Create3DBodyKey(body!));
 127    }
 128
 129    private int FindMixedIslandNode(SolidBody2D? body)
 130    {
 98131        if (!IsMovableMixedIslandBody(body))
 16132            return -1;
 133
 82134        return FindMixedIslandNode(Create2DBodyKey(body!));
 135    }
 136
 137    private int FindMixedIslandNode(int key) =>
 174138        IslandGraphUtility.Find(_mixedIslandNodes, key);
 139
 140    private void UnionMixedIslandNodes(int nodeA, int nodeB) =>
 17141        IslandGraphUtility.Union(_mixedIslandNodes, nodeA, nodeB);
 142
 143    private void CompressMixedIslandRoots() =>
 23144        IslandGraphUtility.CompressRoots(_mixedIslandNodes);
 145
 146    private int ResolveMixedConstraintRootKey(int node3D, int node2D) =>
 49147        IslandGraphUtility.ResolveConstraintRootKey(_mixedIslandNodes, node3D, node2D);
 148
 149    private bool WakeMixedIslandBodies(int rootKey) =>
 35150        IslandGraphUtility.WakeBodies(_mixedIslandNodes, rootKey);
 151
 152    private void SolveMixedIslandRange(int rootKey, int start, int end)
 153    {
 33154        if (end - start == 1)
 155        {
 20156            CollisionPairMixed pair = _mixedIslandConstraints[start].Pair;
 20157            CollisionResponseMixed.Resolve(pair, pair.Contact);
 20158            return;
 159        }
 160
 13161        int iterationLimit = _context.Settings.DiscreteSolverIterations;
 13162        int iterationsUsed = 0;
 144163        for (int iteration = 0; iteration < iterationLimit; iteration++)
 164        {
 59165            bool applyPositionCorrection = iteration == 0;
 354166            for (int i = start; i < end; i++)
 167            {
 118168                CollisionPairMixed pair = _mixedIslandConstraints[i].Pair;
 118169                CollisionResponseMixed.Resolve(
 118170                    pair,
 118171                    pair.Contact,
 118172                    iteration,
 118173                    iterationLimit,
 118174                    applyPositionCorrection);
 175            }
 176
 59177            iterationsUsed = iteration + 1;
 178        }
 179
 13180        _context.Diagnostics.EmitMixedResponseIsland(
 13181            rootKey,
 13182            end - start,
 13183            iterationsUsed,
 13184            iterationsUsed >= iterationLimit);
 13185    }
 186
 187    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 188    private static bool HasAwakeMovableParticipant(LSCollider collider3D, LSCollider2D collider2D) =>
 195189        IsAwakeMovable(collider3D.Body) || IsAwakeMovable(collider2D.Body);
 190
 191    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 192    private static bool HasAwakeResponseParticipant(CollisionPairMixed pair) =>
 41193        IsAwakeMovable(pair.Collider3D.Body) || IsAwakeMovable(pair.Collider2D.Body);
 194
 195    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 196    private static bool ShouldSolveSingleMixedPair(CollisionPairMixed pair, bool hasPlanarResponseCoupling)
 197    {
 95198        if (hasPlanarResponseCoupling)
 41199            return HasAwakeResponseParticipant(pair);
 200
 54201        return IsMovableMixedIslandBody(pair.Collider3D.Body)
 54202            ? IsAwakeMovable(pair.Collider3D.Body)
 54203            : IsAwakeMovable(pair.Collider2D.Body);
 204    }
 205
 206    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 207    private static bool IsAwakeMovable(SolidBody? body) =>
 288208        body != null && body.HasSolverMobility && !body.IsSleeping;
 209
 210    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 211    private static bool IsAwakeMovable(SolidBody2D? body) =>
 57212        body != null && body.HasSolverMobility && !body.IsSleeping;
 213
 214    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 215    private static bool IsMovableMixedIslandBody(SolidBody? body) =>
 203216        body != null && body.DynamicId >= 0 && body.HasSolverMobility;
 217
 218    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 219    private static bool IsMovableMixedIslandBody(SolidBody2D? body) =>
 149220        body != null && body.DynamicId >= 0 && body.HasSolverMobility;
 221
 222    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 223    private static int Create3DBodyKey(SolidBody body) =>
 138224        body.DynamicId << 1;
 225
 226    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 227    private static int Create2DBodyKey(SolidBody2D body) =>
 123228        (body.DynamicId << 1) | 1;
 229
 230}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Core/Mixed/GravitasMixedCollisionService.SupportTypes.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasMixedCollisionService.SupportTypes.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 Gravitas.CollisionHandling;
 10using System.Collections.Generic;
 11
 12namespace Gravitas;
 13
 14internal sealed partial class GravitasMixedCollisionService
 15{
 16    private sealed class PhysicsMixedPartitionOrderComparer : IComparer<PhysicsMixedPartition>
 17    {
 18        public int Compare(PhysicsMixedPartition? left, PhysicsMixedPartition? right) =>
 21002119            WorldVoxelIndexOrdering.Compare3D(left!.WorldIndex, right!.WorldIndex);
 20    }
 21
 22    private sealed class MixedColliderKeyComparer : IComparer<MixedColliderKey>
 23    {
 24        public int Compare(MixedColliderKey left, MixedColliderKey right) =>
 8425            left.Key.CompareTo(right.Key);
 26    }
 27
 28    private sealed class MixedResponsePairComparer : IComparer<CollisionPairMixed>
 29    {
 30        public int Compare(CollisionPairMixed? left, CollisionPairMixed? right) =>
 2731            left!.Key.CompareTo(right!.Key);
 32    }
 33
 34    private sealed class MixedIslandConstraintComparer : IComparer<MixedIslandConstraint>
 35    {
 36        public int Compare(MixedIslandConstraint left, MixedIslandConstraint right)
 37        {
 2538            int compare = left.RootKey.CompareTo(right.RootKey);
 2539            if (compare != 0)
 1240                return compare;
 41
 1342            return left.PairKey.CompareTo(right.PairKey);
 43        }
 44    }
 45
 46    private sealed class Collider2DIdComparer : IComparer<LSCollider2D>
 47    {
 48        public int Compare(LSCollider2D? left, LSCollider2D? right) =>
 11449            left!.Id.CompareTo(right!.Id);
 50    }
 51
 52    private sealed class Collider3DIdComparer : IComparer<LSCollider>
 53    {
 54        public int Compare(LSCollider? left, LSCollider? right) =>
 33455            left!.Id.CompareTo(right!.Id);
 56    }
 57
 58    private struct MixedIslandNode : IIslandNodeState
 59    {
 60        public MixedIslandNode(int bodyKey, SolidBody? body3D, SolidBody2D? body2D)
 61        {
 8762            BodyKey = bodyKey;
 8763            Body3D = body3D;
 8764            Body2D = body2D;
 8765            ParentIndex = -1;
 8766            RootKey = bodyKey;
 8767        }
 68
 69        public int BodyKey { get; }
 70
 71        public SolidBody? Body3D { get; }
 72
 73        public SolidBody2D? Body2D { get; }
 74
 75        public int ParentIndex { get; set; }
 76
 77        public int RootKey { get; set; }
 78
 79        public bool IsAwakeForCollision =>
 3580            Body3D?.IsAwakeForCollision ?? Body2D!.IsAwakeForCollision;
 81
 82        public void WakeFromCollision()
 83        {
 5084            if (Body3D != null)
 85            {
 3886                Body3D.WakeFromCollision();
 3887                return;
 88            }
 89
 1290            Body2D!.WakeFromCollision();
 1291        }
 92    }
 93
 94    private readonly struct MixedIslandConstraint
 95    {
 96        public MixedIslandConstraint(CollisionPairMixed pair, int rootKey, ulong pairKey)
 97        {
 4898            Pair = pair;
 4899            RootKey = rootKey;
 48100            PairKey = pairKey;
 48101        }
 102
 103        public CollisionPairMixed Pair { get; }
 104
 105        public int RootKey { get; }
 106
 107        public ulong PairKey { get; }
 108    }
 109}

Methods/Properties

.cctor()
.ctor(Gravitas.GravitasWorldContext)
get_Context()
get_ActivePartitionCount()
get_InactivePartitionCount()
get_InactivePartitionMembershipCount()
get_RetainedPartitionCount()
get_ActivePairCount()
get_PooledPairCount()
GetCandidate(System.Int32)
Simulate()
LateSimulate()
ProcessMixedContacts()
Visualize()
Reset()
RemovePairsFor3DCollider(Gravitas.Colliders.LSCollider)
RemovePairsFor2DCollider(Gravitas.Colliders.LSCollider2D)
RequireCollisionPair(Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider2D)
ProcessCandidate(Gravitas.MixedColliderKey,System.Int32)
CleanupUntouchedPairs(System.Int32)
TryKeepUntouchedPair(Gravitas.CollisionHandling.CollisionPairMixed,System.Int32)
CreatePair(Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider2D)
RemoveQueuedPairs(System.Int32,System.Int32)
IsCurrentPair(Gravitas.MixedPairLifetimeToken&)
RemoveCurrentPair(Gravitas.MixedPairLifetimeToken&)
RecyclePair(Gravitas.CollisionHandling.CollisionPairMixed)
Refresh3DColliderPartition(Gravitas.Colliders.LSCollider)
Refresh2DColliderPartition(Gravitas.Colliders.LSCollider2D)
Refresh2DColliderPartition(Gravitas.Colliders.LSCollider2D,System.Boolean)
ClearPartitioned3DCollider(Gravitas.Colliders.LSCollider,System.Boolean)
ClearPartitioned2DCollider(Gravitas.Colliders.LSCollider2D,System.Boolean)
Refresh3DPartitionAwakeState(Gravitas.Colliders.LSCollider)
Refresh2DPartitionAwakeState(Gravitas.Colliders.LSCollider2D)
ProcessPartitionCandidate(System.Int32,System.Int32)
Collect2DCandidatesInMixedBounds(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Colliders.LSCollider2D>,System.Boolean,System.Boolean)
Collect3DCandidatesInMixedBounds(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Colliders.LSCollider>,System.Boolean,System.Boolean)
ActivatePartition(Gravitas.PhysicsMixedPartition)
DeactivatePartition(System.Int32)
RentPartition()
ReleasePartition(Gravitas.PhysicsMixedPartition)
RentPartitionMembership()
ReleasePartitionMembership(SwiftCollections.SwiftSparseSet&)
Refresh3DColliderPartitions()
Refresh3DColliderPartitionsForQuery(System.Boolean)
Refresh2DColliderPartitions(System.Boolean)
Refresh2DColliderPartitionsForQuery(System.Boolean)
Partition3DCollider(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
Partition2DCollider(Gravitas.Colliders.LSCollider2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
ScanCovered3DVoxels(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<GridForge.Spatial.WorldVoxelIndex>,Gravitas.MixedPartitionMobilityKind)
ScanCovered2DMixedVoxels(Gravitas.Colliders.LSCollider2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<GridForge.Spatial.WorldVoxelIndex>,Gravitas.MixedPartitionMobilityKind)
CollectCoveredMixedQueryPartitions(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<Gravitas.PhysicsMixedPartition>)
ScanCoveredMixedQueryPartitions(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<Gravitas.PhysicsMixedPartition>)
VisitCoveredVoxelsForMixedQuery(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<Gravitas.PhysicsMixedPartition>)
TryPartition3DVoxel(Gravitas.Colliders.LSCollider,SwiftCollections.SwiftList`1<GridForge.Spatial.WorldVoxelIndex>,GridForge.Grids.Voxel,GridForge.Utility.GridTraversalState&,Gravitas.MixedPartitionMobilityKind)
TryPartition2DMixedVoxel(Gravitas.Colliders.LSCollider2D,SwiftCollections.SwiftList`1<GridForge.Spatial.WorldVoxelIndex>,GridForge.Grids.Voxel,GridForge.Utility.GridTraversalState&,Gravitas.MixedPartitionMobilityKind)
GetOrCreatePartition(GridForge.Grids.Voxel)
Get3DCoverageBounds(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
Get2DMixedCoverageBounds(Gravitas.Colliders.LSCollider2D,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
Get3DMobilityKind(Gravitas.Colliders.LSCollider)
Get2DMobilityKind(Gravitas.Colliders.LSCollider2D)
GetStoredMobilityKind(System.Int32)
Add3DObject(Gravitas.PhysicsMixedPartition,System.Int32,Gravitas.MixedPartitionMobilityKind)
Add2DObject(Gravitas.PhysicsMixedPartition,System.Int32,Gravitas.MixedPartitionMobilityKind)
Remove3DObject(Gravitas.PhysicsMixedPartition,System.Int32,Gravitas.MixedPartitionMobilityKind)
Remove2DObject(Gravitas.PhysicsMixedPartition,System.Int32,Gravitas.MixedPartitionMobilityKind)
MixedBoundsOverlap(Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider2D)
Is2DQueryCandidate(Gravitas.Colliders.LSCollider2D,FixedMathSharp.Geometry.FixedBoundBox,Gravitas.Support.PhysicsLayerMask)
Is3DQueryCandidate(Gravitas.Colliders.LSCollider,FixedMathSharp.Geometry.FixedBoundBox,Gravitas.Support.PhysicsLayerMask)
DetachRetainedPartitions()
TrackRetainedPartition(Gravitas.PhysicsMixedPartition)
UntrackRetainedPartition(Gravitas.PhysicsMixedPartition)
RetireExpiredRetainedPartitions()
TryRetireEmptyRetainedPartitionForReuse()
ContributeReplayHash(Chronicler.ChronicleHashWriter&,Gravitas.GravitasReplayHashMode)
SolveMixedResponsePairs()
BuildMixedIslands()
AddMixedIslandNodeIfMovable(Gravitas.SolidBody)
AddMixedIslandNodeIfMovable(Gravitas.SolidBody2D)
SortAndDeduplicateMixedIslandNodes()
FindMixedIslandNode(Gravitas.SolidBody)
FindMixedIslandNode(Gravitas.SolidBody2D)
FindMixedIslandNode(System.Int32)
UnionMixedIslandNodes(System.Int32,System.Int32)
CompressMixedIslandRoots()
ResolveMixedConstraintRootKey(System.Int32,System.Int32)
WakeMixedIslandBodies(System.Int32)
SolveMixedIslandRange(System.Int32,System.Int32,System.Int32)
HasAwakeMovableParticipant(Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider2D)
HasAwakeResponseParticipant(Gravitas.CollisionHandling.CollisionPairMixed)
ShouldSolveSingleMixedPair(Gravitas.CollisionHandling.CollisionPairMixed,System.Boolean)
IsAwakeMovable(Gravitas.SolidBody)
IsAwakeMovable(Gravitas.SolidBody2D)
IsMovableMixedIslandBody(Gravitas.SolidBody)
IsMovableMixedIslandBody(Gravitas.SolidBody2D)
Create3DBodyKey(Gravitas.SolidBody)
Create2DBodyKey(Gravitas.SolidBody2D)
Compare(Gravitas.PhysicsMixedPartition,Gravitas.PhysicsMixedPartition)
Compare(Gravitas.MixedColliderKey,Gravitas.MixedColliderKey)
Compare(Gravitas.CollisionHandling.CollisionPairMixed,Gravitas.CollisionHandling.CollisionPairMixed)
Compare(Gravitas.GravitasMixedCollisionService/MixedIslandConstraint,Gravitas.GravitasMixedCollisionService/MixedIslandConstraint)
Compare(Gravitas.Colliders.LSCollider2D,Gravitas.Colliders.LSCollider2D)
Compare(Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider)
.ctor(System.Int32,Gravitas.SolidBody,Gravitas.SolidBody2D)
get_IsAwakeForCollision()
WakeFromCollision()
.ctor(Gravitas.CollisionHandling.CollisionPairMixed,System.Int32,System.UInt64)