< Summary

Information
Class: GridForge.Grids.GridWorld
Assembly: GridForge
File(s): /home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridWorld.cs
Line coverage
100%
Covered lines: 512
Uncovered lines: 0
Coverable lines: 512
Total lines: 1464
Line coverage: 100%
Branch coverage
99%
Covered branches: 307
Total branches: 308
Branch coverage: 99.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
add_OnActiveGridAdded(...)100%11100%
remove_OnActiveGridAdded(...)100%11100%
add_OnActiveGridRemoved(...)100%11100%
remove_OnActiveGridRemoved(...)100%11100%
add_OnActiveGridChange(...)100%11100%
remove_OnActiveGridChange(...)100%11100%
add_OnReset(...)100%11100%
remove_OnReset(...)100%11100%
Reset(...)100%66100%
NotifyResetHandlers()100%66100%
ReleaseActiveGrids()100%22100%
Dispose()100%11100%
TryAddGrid(...)100%11100%
TryAddGrid(...)100%11100%
TryAddGrid(...)100%11100%
TryAddGridCore(...)100%1010100%
TryRemoveGrid(...)100%66100%
CanAddGrid()100%88100%
TryPrepareConfiguredVoxels(...)100%44100%
TryValidateGridDimensions(...)100%66100%
TryPrepareConfiguredVoxelMask(...)100%2626100%
TryPrepareConfiguredVoxelIndices(...)100%1212100%
IsConfiguredVoxelInBounds(...)100%44100%
CompactPreparedVoxels(...)100%88100%
UpdateMaxTopologyCellEdge(...)100%22100%
RecalculateMaxTopologyCellEdgeIfNeeded(...)100%66100%
TryFindExistingGrid(...)100%44100%
RegisterGridSpatialCells(...)100%44100%
LinkGridWithCellNeighbors(...)100%88100%
UnregisterGridSpatialCells(...)100%88100%
UnlinkGridCellNeighbors(...)100%88100%
TryGetGrid(...)100%22100%
TryGetGrid(...)100%66100%
TryGetGrid(...)100%11100%
TryGetGrid(...)100%11100%
TryGetClosestGrid(...)100%1212100%
TryGetClosestGrid(...)100%11100%
TryGetClosestGrid(...)100%11100%
TryGetGrid(...)100%66100%
TryGetGridAndVoxel(...)100%22100%
TryGetGridAndVoxel(...)100%11100%
TryGetGridAndVoxel(...)100%11100%
TryGetClosestGridAndVoxel(...)96.15%2626100%
TryGetClosestGridAndVoxel(...)100%11100%
TryGetClosestGridAndVoxel(...)100%11100%
TryGetGridAndVoxel(...)100%22100%
TryGetVoxel(...)100%22100%
TryGetVoxel(...)100%11100%
TryGetVoxel(...)100%11100%
TryGetClosestVoxel(...)100%22100%
TryGetClosestVoxel(...)100%11100%
TryGetClosestVoxel(...)100%11100%
TryGetVoxel(...)100%22100%
TryNormalizeConfiguration(...)100%22100%
IncrementGridVersion(...)100%88100%
GetSpatialGridCells()100%66100%
GetSpatialGridCellBounds(...)100%66100%
FindOverlappingGrids(...)100%66100%
MatchesTopologyKind(...)100%22100%
IsBetterClosestVoxel(...)100%66100%
GetDistanceSquaredToBounds(...)100%11100%
GetAxisDistanceToBounds(...)100%44100%
CanResolveGrid(...)100%66100%
CanResolveActiveGrid()100%44100%
IsGridIndexInActiveRange(...)100%22100%
IsGridIndexAllocated(...)100%44100%
TryGetSpatialGridCandidates(...)100%44100%
TryGetContainingGrid(...)100%66100%
TryGetActiveGridCandidate(...)100%22100%
AddOverlappingGridsFromCell(...)100%44100%
TryAddOverlappingGrid(...)100%66100%
GetSpatialGridKey(...)100%11100%
NotifyActiveGridChange(...)100%44100%
NotifyActiveGridChange(...)100%44100%
ResolveSpatialGridCellSize(...)100%44100%
CreateGridEventInfo(...)100%11100%
CreateGridEventInfo(...)100%11100%
NotifyActiveGridAdded(...)100%66100%
NotifyActiveGridRemoved(...)100%66100%
NotifyActiveGridChange(...)100%66100%
SnapToSpatialGrid(...)100%11100%

File(s)

/home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridWorld.cs

#LineLine coverage
 1//=======================================================================
 2// GridWorld.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using FixedMathSharp;
 9using GridForge.Configuration;
 10using GridForge.Grids.Storage;
 11using GridForge.Grids.Topology;
 12using GridForge.Spatial;
 13using SwiftCollections;
 14using SwiftCollections.Utility;
 15using System;
 16using System.Collections.Generic;
 17using System.Runtime.CompilerServices;
 18using System.Threading;
 19
 20namespace GridForge.Grids;
 21
 22/// <summary>
 23/// Owns the mutable runtime state for one GridForge world.
 24/// </summary>
 25public sealed class GridWorld : IDisposable
 26{
 27    #region Constants
 28
 29    /// <summary>
 30    /// Maximum number of grids that can be managed within a world.
 31    /// </summary>
 32    public const ushort MaxGrids = ushort.MaxValue - 1;
 33
 34    /// <summary>
 35    /// The default rectangular cell edge in world units.
 36    /// </summary>
 137    public static readonly Fixed64 DefaultRectangularCellSize = Fixed64.One;
 38
 39    /// <summary>
 40    /// The default size of a spatial hash cell used for grid lookup.
 41    /// </summary>
 42    public const int DefaultSpatialGridCellSize = 50;
 43
 44    #endregion
 45
 46    #region Properties
 47
 148    private static readonly Comparison<VoxelIndex> CompareVoxelIndices =
 149        static (left, right) => left.CompareTo(right);
 50
 51    /// <summary>
 52    /// The size of a spatial hash cell used for grid lookup in this world.
 53    /// </summary>
 54    public int SpatialGridCellSize { get; }
 55
 56    /// <summary>
 57    /// Collection of all active grids owned by this world.
 58    /// </summary>
 59    public SwiftBucket<VoxelGrid> ActiveGrids { get; }
 60
 61    /// <summary>
 62    /// Dictionary mapping exact grid configuration keys to grid indices to prevent duplicate grids.
 63    /// </summary>
 64    public SwiftDictionary<GridConfigurationKey, ushort> BoundsTracker { get; }
 65
 66    /// <summary>
 67    /// Dictionary mapping spatial hash keys to grid indices for fast lookups.
 68    /// </summary>
 69    public SwiftDictionary<int, SwiftHashSet<ushort>> SpatialGridHash { get; }
 70
 71    /// <summary>
 72    /// Runtime token identifying this specific world instance.
 73    /// </summary>
 74    public int SpawnToken { get; private set; }
 75
 76    /// <summary>
 77    /// The current version of the world, incremented on major changes.
 78    /// </summary>
 79    public uint Version { get; private set; }
 80
 81    /// <summary>
 82    /// Indicates whether this world is currently active.
 83    /// </summary>
 84    public bool IsActive { get; private set; }
 85
 86    internal Fixed64 MaxTopologyCellEdge { get; private set; }
 87
 42788    private readonly ReaderWriterLockSlim _gridLock = new();
 89
 90    #endregion
 91
 92    #region Events
 93
 94    private Action<GridEventInfo>? _onActiveGridAdded;
 95    private Action<GridEventInfo>? _onActiveGridRemoved;
 96    private Action<GridEventInfo>? _onActiveGridChange;
 97    private Action? _onReset;
 98
 99    /// <summary>
 100    /// Event triggered when a new grid is added to this world.
 101    /// </summary>
 102    public event Action<GridEventInfo> OnActiveGridAdded
 103    {
 156104        add => _onActiveGridAdded += value;
 154105        remove => _onActiveGridAdded -= value;
 106    }
 107
 108    /// <summary>
 109    /// Event triggered when a grid is removed from this world.
 110    /// </summary>
 111    public event Action<GridEventInfo> OnActiveGridRemoved
 112    {
 156113        add => _onActiveGridRemoved += value;
 154114        remove => _onActiveGridRemoved -= value;
 115    }
 116
 117    /// <summary>
 118    /// Event triggered when a grid in this world undergoes a significant change.
 119    /// </summary>
 120    public event Action<GridEventInfo> OnActiveGridChange
 121    {
 161122        add => _onActiveGridChange += value;
 155123        remove => _onActiveGridChange -= value;
 124    }
 125
 126    /// <summary>
 127    /// Event triggered when this world is reset.
 128    /// </summary>
 129    public event Action OnReset
 130    {
 156131        add => _onReset += value;
 154132        remove => _onReset -= value;
 133    }
 134
 135    #endregion
 136
 137    /// <summary>
 138    /// Initializes a new world with the supplied spatial-hash settings.
 139    /// </summary>
 140    /// <param name="spatialGridCellSize">Optional spatial hash cell size for this world.</param>
 427141    public GridWorld(int spatialGridCellSize = DefaultSpatialGridCellSize)
 142    {
 427143        ActiveGrids = new SwiftBucket<VoxelGrid>();
 427144        BoundsTracker = new SwiftDictionary<GridConfigurationKey, ushort>();
 427145        SpatialGridHash = new SwiftDictionary<int, SwiftHashSet<ushort>>();
 146
 427147        SpatialGridCellSize = ResolveSpatialGridCellSize(spatialGridCellSize);
 427148        SpawnToken = GetHashCode();
 427149        Version = 1;
 427150        IsActive = true;
 427151    }
 152
 153    #region Lifecycle
 154
 155    /// <summary>
 156    /// Clears all grids and spatial data owned by this world.
 157    /// </summary>
 158    /// <param name="deactivate">If true, marks the world inactive and releases its event handlers.</param>
 159    public void Reset(bool deactivate = false)
 160    {
 431161        if (!IsActive)
 162        {
 4163            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot reset an inactive world.");
 4164            return;
 165        }
 166
 427167        NotifyResetHandlers();
 427168        ReleaseActiveGrids();
 427169        GridOccupantManager.ClearTrackedOccupancies(this);
 170
 427171        if (!deactivate)
 4172            return;
 173
 423174        GridOccupantManager.ReleaseTrackedOccupancies(this);
 423175        IsActive = false;
 423176        SpawnToken = 0;
 423177        _onActiveGridAdded = null;
 423178        _onActiveGridRemoved = null;
 423179        _onActiveGridChange = null;
 423180        _onReset = null;
 423181    }
 182
 183    private void NotifyResetHandlers()
 184    {
 427185        Action? resetHandlers = _onReset;
 427186        if (resetHandlers == null)
 406187            return;
 188
 21189        var handlerDelegates = resetHandlers.GetInvocationList();
 306190        for (int i = 0; i < handlerDelegates.Length; i++)
 191        {
 192            try
 193            {
 132194                ((Action)handlerDelegates[i])();
 130195            }
 2196            catch (Exception ex)
 197            {
 2198                GridForgeLogger.Channel.Error($"World reset notification error: {ex.Message}");
 2199            }
 200        }
 21201    }
 202
 203    private void ReleaseActiveGrids()
 204    {
 1610205        foreach (VoxelGrid grid in ActiveGrids)
 378206            Pools.GridPool.Release(grid);
 207
 427208        ActiveGrids.Clear();
 427209        BoundsTracker.Clear();
 427210        SpatialGridHash.Clear();
 427211        MaxTopologyCellEdge = Fixed64.Zero;
 427212    }
 213
 214    /// <inheritdoc />
 215    public void Dispose()
 216    {
 425217        Reset(deactivate: true);
 425218        _gridLock.Dispose();
 425219        GC.SuppressFinalize(this);
 425220    }
 221
 222    #endregion
 223
 224    #region Grid Management
 225
 226    /// <summary>
 227    /// Adds a new grid to this world and registers it in the spatial hash.
 228    /// </summary>
 229    /// <param name="configuration">The grid configuration to normalize and register.</param>
 230    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 231    /// <returns>True if the grid was added; otherwise false.</returns>
 232    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 233    public bool TryAddGrid(GridConfiguration configuration, out ushort allocatedIndex) =>
 363234        TryAddGridCore(configuration, null, null, out allocatedIndex);
 235
 236    /// <summary>
 237    /// Adds a new grid to this world and materializes the supplied sparse voxel indices when sparse storage is configur
 238    /// Dense grids ignore the configured voxel input and materialize every in-bounds voxel.
 239    /// </summary>
 240    /// <param name="configuration">The grid configuration to normalize and register.</param>
 241    /// <param name="configuredVoxels">Grid-local voxel indices to materialize for sparse storage.</param>
 242    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 243    /// <returns>True if the grid was added; otherwise false.</returns>
 244    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 245    public bool TryAddGrid(
 246        GridConfiguration configuration,
 247        IEnumerable<VoxelIndex>? configuredVoxels,
 248        out ushort allocatedIndex) =>
 59249        TryAddGridCore(configuration, configuredVoxels, null, out allocatedIndex);
 250
 251    /// <summary>
 252    /// Adds a new grid to this world and materializes true cells from the supplied sparse voxel mask when sparse storag
 253    /// Dense grids ignore the configured voxel input and materialize every in-bounds voxel.
 254    /// </summary>
 255    /// <param name="configuration">The grid configuration to normalize and register.</param>
 256    /// <param name="configuredVoxels">A [x, y, z] mask whose true values identify sparse voxels to materialize. Sparse 
 257    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 258    /// <returns>True if the grid was added; otherwise false.</returns>
 259    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 260    public bool TryAddGrid(
 261        GridConfiguration configuration,
 262        bool[,,]? configuredVoxels,
 263        out ushort allocatedIndex) =>
 7264        TryAddGridCore(configuration, null, configuredVoxels, out allocatedIndex);
 265
 266    private bool TryAddGridCore(
 267        GridConfiguration configuration,
 268        IEnumerable<VoxelIndex>? configuredVoxels,
 269        bool[,,]? configuredVoxelMask,
 270        out ushort allocatedIndex)
 271    {
 429272        allocatedIndex = ushort.MaxValue;
 273
 429274        if (!CanAddGrid())
 5275            return false;
 276
 424277        if (!TryNormalizeConfiguration(
 424278                configuration,
 424279                out GridConfiguration normalizedConfiguration,
 424280                out IGridTopology topology,
 424281                out GridDimensions dimensions)
 424282            || !TryValidateGridDimensions(dimensions))
 283        {
 6284            return false;
 285        }
 286
 418287        if (!TryPrepareConfiguredVoxels(
 418288            normalizedConfiguration,
 418289            dimensions,
 418290            configuredVoxels,
 418291            configuredVoxelMask,
 418292            out VoxelIndex[] preparedVoxels))
 293        {
 12294            return false;
 295        }
 296
 406297        GridConfigurationKey boundsKey = normalizedConfiguration.ToGridKey();
 298
 406299        if (TryFindExistingGrid(boundsKey, out allocatedIndex))
 3300            return false;
 301
 403302        VoxelGrid newGrid = Pools.GridPool.Rent();
 403303        GridEventInfo addedGridInfo = default;
 304
 403305        _gridLock.EnterWriteLock();
 306        try
 307        {
 403308            allocatedIndex = (ushort)ActiveGrids.Add(newGrid);
 403309            BoundsTracker.Add(boundsKey, allocatedIndex);
 310
 403311            newGrid.Initialize(this, allocatedIndex, normalizedConfiguration, topology, preparedVoxels);
 403312            UpdateMaxTopologyCellEdge(newGrid.Topology.MaxCellEdge);
 403313            RegisterGridSpatialCells(newGrid, allocatedIndex);
 314
 403315            Version++;
 403316            addedGridInfo = CreateGridEventInfo(newGrid, GridEventKind.GridAdded);
 403317        }
 318        finally
 319        {
 403320            _gridLock.ExitWriteLock();
 403321        }
 322
 403323        NotifyActiveGridAdded(addedGridInfo);
 403324        return true;
 325    }
 326
 327    /// <summary>
 328    /// Removes a grid from this world and updates all references to ensure integrity.
 329    /// </summary>
 330    /// <param name="removeIndex">The world-local grid slot to remove.</param>
 331    /// <returns>True if the grid was removed; otherwise false.</returns>
 332    public bool TryRemoveGrid(ushort removeIndex)
 333    {
 27334        if (!IsActive || !ActiveGrids.IsAllocated(removeIndex))
 2335            return false;
 336
 337        VoxelGrid gridToRemove;
 25338        GridEventInfo removedGridInfo = default;
 339
 25340        _gridLock.EnterWriteLock();
 341        try
 342        {
 25343            gridToRemove = ActiveGrids[removeIndex];
 25344            Fixed64 removedMaxCellEdge = gridToRemove.Topology.MaxCellEdge;
 25345            UnregisterGridSpatialCells(gridToRemove, removeIndex);
 25346            BoundsTracker.Remove(gridToRemove.Configuration.ToGridKey());
 25347            ActiveGrids.RemoveAt(removeIndex);
 25348            RecalculateMaxTopologyCellEdgeIfNeeded(removedMaxCellEdge);
 349
 25350            Version++;
 25351            removedGridInfo = CreateGridEventInfo(gridToRemove, GridEventKind.GridRemoved);
 25352        }
 353        finally
 354        {
 25355            _gridLock.ExitWriteLock();
 25356        }
 357
 25358        Pools.GridPool.Release(gridToRemove);
 25359        NotifyActiveGridRemoved(removedGridInfo);
 360
 25361        if (ActiveGrids.Count == 0)
 15362            ActiveGrids.TrimExcessCapacity();
 363
 25364        return true;
 365    }
 366
 367    #endregion
 368
 369    private bool CanAddGrid()
 370    {
 429371        if (!IsActive)
 372        {
 3373            GridForgeLogger.Channel.Error($"Grid world not active. Cannot add grids to an inactive world.");
 3374            return false;
 375        }
 376
 426377        if ((uint)ActiveGrids.Count >= MaxGrids)
 378        {
 2379            GridForgeLogger.Channel.Warn($"No more grids can be added at this time.");
 2380            return false;
 381        }
 382
 424383        return true;
 384    }
 385
 386    private static bool TryPrepareConfiguredVoxels(
 387        GridConfiguration configuration,
 388        GridDimensions dimensions,
 389        IEnumerable<VoxelIndex>? configuredVoxels,
 390        bool[,,]? configuredVoxelMask,
 391        out VoxelIndex[] preparedVoxels)
 392    {
 418393        preparedVoxels = Array.Empty<VoxelIndex>();
 418394        if (configuration.StorageKind != GridStorageKind.Sparse)
 343395            return true;
 396
 75397        if (configuredVoxelMask != null)
 7398            return TryPrepareConfiguredVoxelMask(configuredVoxelMask, dimensions, out preparedVoxels);
 399
 68400        return TryPrepareConfiguredVoxelIndices(configuredVoxels, dimensions, out preparedVoxels);
 401    }
 402
 403    private static bool TryValidateGridDimensions(GridDimensions dimensions)
 404    {
 423405        long layerSize = (long)dimensions.Width * dimensions.Height;
 423406        if (layerSize > int.MaxValue || layerSize * dimensions.Length > int.MaxValue)
 407        {
 4408            GridForgeLogger.Channel.Warn($"Grid dimensions exceed the supported int voxel address space.");
 4409            return false;
 410        }
 411
 419412        return true;
 413    }
 414
 415    private static bool TryPrepareConfiguredVoxelMask(
 416        bool[,,] configuredVoxelMask,
 417        GridDimensions dimensions,
 418        out VoxelIndex[] preparedVoxels)
 419    {
 7420        preparedVoxels = Array.Empty<VoxelIndex>();
 421
 7422        if (configuredVoxelMask.GetLength(0) != dimensions.Width
 7423            || configuredVoxelMask.GetLength(1) != dimensions.Height
 7424            || configuredVoxelMask.GetLength(2) != dimensions.Length)
 425        {
 5426            GridForgeLogger.Channel.Warn($"Sparse voxel mask dimensions must match normalized grid dimensions.");
 5427            return false;
 428        }
 429
 2430        int configuredCount = 0;
 12431        for (int x = 0; x < dimensions.Width; x++)
 432        {
 16433            for (int y = 0; y < dimensions.Height; y++)
 434            {
 24435                for (int z = 0; z < dimensions.Length; z++)
 436                {
 8437                    if (configuredVoxelMask[x, y, z])
 2438                        configuredCount++;
 439                }
 440            }
 441        }
 442
 2443        if (configuredCount == 0)
 1444            return true;
 445
 1446        preparedVoxels = new VoxelIndex[configuredCount];
 1447        int index = 0;
 6448        for (int x = 0; x < dimensions.Width; x++)
 449        {
 8450            for (int y = 0; y < dimensions.Height; y++)
 451            {
 12452                for (int z = 0; z < dimensions.Length; z++)
 453                {
 4454                    if (configuredVoxelMask[x, y, z])
 2455                        preparedVoxels[index++] = new VoxelIndex(x, y, z);
 456                }
 457            }
 458        }
 459
 1460        return true;
 461    }
 462
 463    private static bool TryPrepareConfiguredVoxelIndices(
 464        IEnumerable<VoxelIndex>? configuredVoxels,
 465        GridDimensions dimensions,
 466        out VoxelIndex[] preparedVoxels)
 467    {
 68468        preparedVoxels = Array.Empty<VoxelIndex>();
 68469        if (configuredVoxels == null)
 9470            return true;
 471
 59472        SwiftList<VoxelIndex> indices = configuredVoxels is ICollection<VoxelIndex> collection
 59473            ? new SwiftList<VoxelIndex>(collection.Count)
 59474            : new SwiftList<VoxelIndex>();
 475
 291476        foreach (VoxelIndex configuredVoxel in configuredVoxels)
 477        {
 90478            if (!IsConfiguredVoxelInBounds(configuredVoxel, dimensions))
 479            {
 7480                GridForgeLogger.Channel.Warn($"Sparse voxel index {configuredVoxel} is outside normalized grid dimension
 7481                return false;
 482            }
 483
 83484            indices.Add(configuredVoxel);
 485        }
 486
 52487        if (indices.Count == 0)
 1488            return true;
 489
 51490        preparedVoxels = indices.ToArray();
 51491        Array.Sort(preparedVoxels, CompareVoxelIndices);
 51492        CompactPreparedVoxels(ref preparedVoxels);
 51493        return true;
 7494    }
 495
 496    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 497    private static bool IsConfiguredVoxelInBounds(VoxelIndex voxelIndex, GridDimensions dimensions) =>
 90498        (uint)voxelIndex.x < (uint)dimensions.Width
 90499        && (uint)voxelIndex.y < (uint)dimensions.Height
 90500        && (uint)voxelIndex.z < (uint)dimensions.Length;
 501
 502    private static void CompactPreparedVoxels(ref VoxelIndex[] preparedVoxels)
 503    {
 51504        if (preparedVoxels.Length < 2)
 30505            return;
 506
 21507        int writeIndex = 1;
 21508        VoxelIndex previous = preparedVoxels[0];
 106509        for (int readIndex = 1; readIndex < preparedVoxels.Length; readIndex++)
 510        {
 32511            VoxelIndex current = preparedVoxels[readIndex];
 32512            if (current == previous)
 513                continue;
 514
 30515            preparedVoxels[writeIndex++] = current;
 30516            previous = current;
 517        }
 518
 21519        if (writeIndex != preparedVoxels.Length)
 2520            Array.Resize(ref preparedVoxels, writeIndex);
 21521    }
 522
 523    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 524    private void UpdateMaxTopologyCellEdge(Fixed64 candidate)
 525    {
 403526        if (candidate > MaxTopologyCellEdge)
 353527            MaxTopologyCellEdge = candidate;
 403528    }
 529
 530    private void RecalculateMaxTopologyCellEdgeIfNeeded(Fixed64 removedMaxCellEdge)
 531    {
 25532        if (removedMaxCellEdge < MaxTopologyCellEdge)
 1533            return;
 534
 24535        Fixed64 maxCellEdge = Fixed64.Zero;
 70536        foreach (VoxelGrid grid in ActiveGrids)
 537        {
 11538            if (grid.Topology.MaxCellEdge > maxCellEdge)
 9539                maxCellEdge = grid.Topology.MaxCellEdge;
 540        }
 541
 24542        MaxTopologyCellEdge = maxCellEdge;
 24543    }
 544
 545    private bool TryFindExistingGrid(GridConfigurationKey boundsKey, out ushort allocatedIndex)
 546    {
 406547        _gridLock.EnterReadLock();
 548        try
 549        {
 406550            if (BoundsTracker.TryGetValue(boundsKey, out allocatedIndex))
 551            {
 3552                GridForgeLogger.Channel.Warn($"A grid with these bounds has already been allocated.");
 3553                return true;
 554            }
 403555        }
 556        finally
 557        {
 406558            _gridLock.ExitReadLock();
 406559        }
 560
 403561        allocatedIndex = ushort.MaxValue;
 403562        return false;
 3563    }
 564
 565    private void RegisterGridSpatialCells(VoxelGrid newGrid, ushort allocatedIndex)
 566    {
 1782567        foreach (int cellIndex in GetSpatialGridCells(newGrid.BoundsMin, newGrid.BoundsMax))
 568        {
 488569            if (!SpatialGridHash.TryGetValue(cellIndex, out SwiftHashSet<ushort> gridList))
 570            {
 423571                gridList = new SwiftHashSet<ushort>();
 423572                SpatialGridHash.Add(cellIndex, gridList);
 573            }
 574
 488575            LinkGridWithCellNeighbors(newGrid, allocatedIndex, gridList);
 488576            gridList.Add(allocatedIndex);
 577        }
 403578    }
 579
 580    private void LinkGridWithCellNeighbors(
 581        VoxelGrid newGrid,
 582        ushort allocatedIndex,
 583        SwiftHashSet<ushort> gridList)
 584    {
 1128585        foreach (ushort neighborIndex in gridList)
 586        {
 76587            if (!ActiveGrids.IsAllocated(neighborIndex) || neighborIndex == allocatedIndex)
 588                continue;
 589
 75590            VoxelGrid neighborGrid = ActiveGrids[neighborIndex];
 75591            if (!VoxelGrid.IsGridOverlapValid(newGrid, neighborGrid))
 592                continue;
 593
 41594            newGrid.TryAddGridNeighbor(neighborGrid);
 41595            neighborGrid.TryAddGridNeighbor(newGrid);
 596        }
 488597    }
 598
 599    private void UnregisterGridSpatialCells(VoxelGrid gridToRemove, ushort removeIndex)
 600    {
 116601        foreach (int cellIndex in GetSpatialGridCells(gridToRemove.BoundsMin, gridToRemove.BoundsMax))
 602        {
 33603            if (!SpatialGridHash.TryGetValue(cellIndex, out SwiftHashSet<ushort> gridList))
 604                continue;
 605
 32606            gridList.Remove(gridToRemove.GridIndex);
 607
 32608            if (gridToRemove.IsConjoined)
 6609                UnlinkGridCellNeighbors(gridToRemove, removeIndex, gridList);
 610
 32611            if (gridList.Count == 0)
 22612                SpatialGridHash.Remove(cellIndex);
 613        }
 25614    }
 615
 616    private void UnlinkGridCellNeighbors(
 617        VoxelGrid gridToRemove,
 618        ushort removeIndex,
 619        SwiftHashSet<ushort> gridList)
 620    {
 30621        foreach (ushort neighborIndex in gridList)
 622        {
 9623            if (!ActiveGrids.IsAllocated(neighborIndex) || neighborIndex == removeIndex)
 624                continue;
 625
 8626            VoxelGrid neighborGrid = ActiveGrids[neighborIndex];
 8627            if (VoxelGrid.IsGridOverlapValid(gridToRemove, neighborGrid))
 8628                neighborGrid.TryRemoveGridNeighbor(gridToRemove);
 629        }
 6630    }
 631
 632    #region Lookup
 633
 634    /// <summary>
 635    /// Retrieves a grid by its world-local index.
 636    /// </summary>
 637    /// <param name="index">The world-local grid slot to resolve.</param>
 638    /// <param name="outGrid">The resolved grid, if found.</param>
 639    /// <returns>True if the grid was resolved; otherwise false.</returns>
 640    public bool TryGetGrid(int index, out VoxelGrid? outGrid)
 641    {
 348642        outGrid = null;
 348643        if (!CanResolveGrid(index))
 13644            return false;
 645
 335646        outGrid = ActiveGrids[index];
 335647        return true;
 648    }
 649
 650    /// <summary>
 651    /// Retrieves the grid containing a given world position.
 652    /// </summary>
 653    /// <param name="position">The world position to resolve.</param>
 654    /// <param name="outGrid">The resolved grid, if found.</param>
 655    /// <returns>True if a containing grid was found; otherwise false.</returns>
 656    public bool TryGetGrid(Vector3d position, out VoxelGrid? outGrid)
 657    {
 155658        outGrid = null;
 155659        if (!TryGetSpatialGridCandidates(position, out SwiftHashSet<ushort>? gridList))
 3660            return false;
 661
 152662        if (TryGetContainingGrid(position, gridList!, out outGrid))
 67663            return true;
 664
 85665        GridForgeLogger.Channel.Info($"No grid contains position {position}.");
 85666        return false;
 667    }
 668
 669    /// <summary>
 670    /// Retrieves the grid containing a 2D XZ-plane world position on the default world Y layer.
 671    /// </summary>
 672    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 673    /// <param name="outGrid">The resolved grid, if found.</param>
 674    /// <returns>True if a containing grid was found; otherwise false.</returns>
 675    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 676    public bool TryGetGrid(Vector2d position, out VoxelGrid? outGrid) =>
 1677         TryGetGrid(position, default, out outGrid);
 678
 679    /// <summary>
 680    /// Retrieves the grid containing a 2D XZ-plane world position on the supplied world Y layer.
 681    /// </summary>
 682    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 683    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 684    /// <param name="outGrid">The resolved grid, if found.</param>
 685    /// <returns>True if a containing grid was found; otherwise false.</returns>
 686    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 687    public bool TryGetGrid(Vector2d position, Fixed64 layerY, out VoxelGrid? outGrid) =>
 3688        TryGetGrid(GridPlane2d.ToWorld(position, layerY), out outGrid);
 689
 690    /// <summary>
 691    /// Retrieves the active grid whose bounds are nearest to the supplied world position.
 692    /// </summary>
 693    /// <param name="position">The world position to resolve.</param>
 694    /// <param name="outGrid">The closest grid, if found.</param>
 695    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 696    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 697    public bool TryGetClosestGrid(
 698        Vector3d position,
 699        out VoxelGrid? outGrid,
 700        GridTopologyKind? topologyKind = null)
 701    {
 26702        outGrid = null;
 26703        if (!CanResolveActiveGrid())
 1704            return false;
 705
 25706        Fixed64 closestDistanceSquared = Fixed64.MaxValue;
 118707        foreach (VoxelGrid candidateGrid in ActiveGrids)
 708        {
 34709            if (!candidateGrid.IsActive
 34710                || !MatchesTopologyKind(candidateGrid, topologyKind))
 711            {
 712                continue;
 713            }
 714
 30715            Fixed64 distanceSquared = GetDistanceSquaredToBounds(position, candidateGrid.BoundsMin, candidateGrid.Bounds
 30716            if (outGrid == null || distanceSquared < closestDistanceSquared)
 717            {
 23718                outGrid = candidateGrid;
 23719                closestDistanceSquared = distanceSquared;
 720            }
 721        }
 722
 25723        return outGrid != null;
 724    }
 725
 726    /// <summary>
 727    /// Retrieves the active grid whose bounds are nearest to a 2D XZ-plane world position on the default world Y layer.
 728    /// </summary>
 729    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 730    /// <param name="outGrid">The closest grid, if found.</param>
 731    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 732    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 733    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 734    public bool TryGetClosestGrid(
 735        Vector2d position,
 736        out VoxelGrid? outGrid,
 737        GridTopologyKind? topologyKind = null) =>
 1738        TryGetClosestGrid(position, default, out outGrid, topologyKind);
 739
 740    /// <summary>
 741    /// Retrieves the active grid whose bounds are nearest to a 2D XZ-plane world position on the supplied world Y layer
 742    /// </summary>
 743    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 744    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 745    /// <param name="outGrid">The closest grid, if found.</param>
 746    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 747    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 748    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 749    public bool TryGetClosestGrid(
 750        Vector2d position,
 751        Fixed64 layerY,
 752        out VoxelGrid? outGrid,
 753        GridTopologyKind? topologyKind = null) =>
 3754        TryGetClosestGrid(GridPlane2d.ToWorld(position, layerY), out outGrid, topologyKind);
 755
 756    /// <summary>
 757    /// Retrieves a grid by a world-scoped voxel identity.
 758    /// </summary>
 759    /// <param name="worldVoxelIndex">The voxel identity whose grid should be resolved.</param>
 760    /// <param name="result">The resolved grid, if found.</param>
 761    /// <returns>True if the grid was resolved; otherwise false.</returns>
 762    public bool TryGetGrid(WorldVoxelIndex worldVoxelIndex, out VoxelGrid? result)
 763    {
 72764        result = null;
 72765        if (worldVoxelIndex.WorldSpawnToken != SpawnToken
 72766            || !TryGetGrid(worldVoxelIndex.GridIndex, out VoxelGrid? resolvedGrid)
 72767            || worldVoxelIndex.GridSpawnToken != resolvedGrid!.SpawnToken)
 768        {
 13769            return false;
 770        }
 771
 59772        result = resolvedGrid;
 59773        return true;
 774    }
 775
 776    /// <summary>
 777    /// Retrieves the grid and voxel containing a given world position.
 778    /// </summary>
 779    /// <param name="position">The world position to resolve.</param>
 780    /// <param name="outGrid">The resolved grid, if found.</param>
 781    /// <param name="outVoxel">The resolved voxel, if found.</param>
 782    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 783    public bool TryGetGridAndVoxel(
 784        Vector3d position,
 785        out VoxelGrid? outGrid,
 786        out Voxel? outVoxel)
 787    {
 45788        outVoxel = null;
 45789        return TryGetGrid(position, out outGrid)
 45790            && outGrid!.TryGetVoxel(position, out outVoxel);
 791    }
 792
 793    /// <summary>
 794    /// Retrieves the grid and voxel containing a 2D XZ-plane world position on the default world Y layer.
 795    /// </summary>
 796    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 797    /// <param name="outGrid">The resolved grid, if found.</param>
 798    /// <param name="outVoxel">The resolved voxel, if found.</param>
 799    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 800    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 801    public bool TryGetGridAndVoxel(
 802        Vector2d position,
 803        out VoxelGrid? outGrid,
 804        out Voxel? outVoxel) =>
 1805         TryGetGridAndVoxel(position, default, out outGrid, out outVoxel);
 806
 807    /// <summary>
 808    /// Retrieves the grid and voxel containing a 2D XZ-plane world position on the supplied world Y layer.
 809    /// </summary>
 810    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 811    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 812    /// <param name="outGrid">The resolved grid, if found.</param>
 813    /// <param name="outVoxel">The resolved voxel, if found.</param>
 814    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 815    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 816    public bool TryGetGridAndVoxel(
 817        Vector2d position,
 818        Fixed64 layerY,
 819        out VoxelGrid? outGrid,
 820        out Voxel? outVoxel) =>
 3821         TryGetGridAndVoxel(GridPlane2d.ToWorld(position, layerY), out outGrid, out outVoxel);
 822
 823    /// <summary>
 824    /// Retrieves the physical voxel whose center is nearest to the supplied world position and the grid that owns it.
 825    /// Sparse grids only consider configured physical voxels.
 826    /// </summary>
 827    /// <param name="position">The world position to resolve.</param>
 828    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 829    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 830    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 831    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 832    public bool TryGetClosestGridAndVoxel(
 833        Vector3d position,
 834        out VoxelGrid? outGrid,
 835        out Voxel? outVoxel,
 836        GridTopologyKind? topologyKind = null)
 837    {
 15838        outGrid = null;
 15839        outVoxel = null;
 15840        if (!CanResolveActiveGrid())
 2841            return false;
 842
 13843        Fixed64 closestDistanceSquared = Fixed64.MaxValue;
 13844        if (TryGetClosestGrid(position, out VoxelGrid? closestBoundsGrid, topologyKind)
 13845            && closestBoundsGrid!.ConfiguredVoxelCount != 0
 13846            && closestBoundsGrid.TryGetClosestVoxel(position, out outVoxel, out closestDistanceSquared))
 847        {
 10848            outGrid = closestBoundsGrid;
 849        }
 850
 60851        foreach (VoxelGrid candidateGrid in ActiveGrids)
 852        {
 17853            if (candidateGrid == null
 17854                || !candidateGrid.IsActive
 17855                || candidateGrid.ConfiguredVoxelCount == 0
 17856                || !MatchesTopologyKind(candidateGrid, topologyKind))
 857            {
 858                continue;
 859            }
 14860            if (ReferenceEquals(candidateGrid, outGrid))
 861                continue;
 862
 5863            Fixed64 boundsDistanceSquared = GetDistanceSquaredToBounds(position, candidateGrid.BoundsMin, candidateGrid.
 5864            if (outVoxel != null && boundsDistanceSquared > closestDistanceSquared)
 865                continue;
 866
 4867            candidateGrid.TryGetClosestVoxel(
 4868                position,
 4869                out Voxel? candidateVoxel,
 4870                out Fixed64 candidateDistanceSquared);
 871
 4872            if (IsBetterClosestVoxel(
 4873                candidateDistanceSquared,
 4874                candidateGrid,
 4875                candidateVoxel!,
 4876                closestDistanceSquared,
 4877                outGrid,
 4878                outVoxel))
 879            {
 3880                outGrid = candidateGrid;
 3881                outVoxel = candidateVoxel;
 3882                closestDistanceSquared = candidateDistanceSquared;
 883            }
 884        }
 885
 13886        return outVoxel != null;
 887    }
 888
 889    /// <summary>
 890    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the default world Y laye
 891    /// Sparse grids only consider configured physical voxels.
 892    /// </summary>
 893    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 894    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 895    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 896    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 897    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 898    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 899    public bool TryGetClosestGridAndVoxel(
 900        Vector2d position,
 901        out VoxelGrid? outGrid,
 902        out Voxel? outVoxel,
 903        GridTopologyKind? topologyKind = null) =>
 1904        TryGetClosestGridAndVoxel(position, default, out outGrid, out outVoxel, topologyKind);
 905
 906    /// <summary>
 907    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the supplied world Y lay
 908    /// Sparse grids only consider configured physical voxels.
 909    /// </summary>
 910    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 911    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 912    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 913    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 914    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 915    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 916    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 917    public bool TryGetClosestGridAndVoxel(
 918        Vector2d position,
 919        Fixed64 layerY,
 920        out VoxelGrid? outGrid,
 921        out Voxel? outVoxel,
 922        GridTopologyKind? topologyKind = null) =>
 3923        TryGetClosestGridAndVoxel(GridPlane2d.ToWorld(position, layerY), out outGrid, out outVoxel, topologyKind);
 924
 925    /// <summary>
 926    /// Retrieves the grid and voxel for a given voxel identity.
 927    /// </summary>
 928    /// <param name="worldVoxelIndex">The voxel identity to resolve.</param>
 929    /// <param name="outGrid">The resolved grid, if found.</param>
 930    /// <param name="result">The resolved voxel, if found.</param>
 931    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 932    public bool TryGetGridAndVoxel(
 933        WorldVoxelIndex worldVoxelIndex,
 934        out VoxelGrid? outGrid,
 935        out Voxel? result)
 936    {
 63937        result = null;
 63938        return TryGetGrid(worldVoxelIndex, out outGrid)
 63939            && outGrid!.TryGetVoxel(worldVoxelIndex.VoxelIndex, out result);
 940    }
 941
 942    /// <summary>
 943    /// Retrieves a voxel from a world position.
 944    /// </summary>
 945    /// <param name="position">The world position to resolve.</param>
 946    /// <param name="result">The resolved voxel, if found.</param>
 947    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 948    public bool TryGetVoxel(
 949        Vector3d position,
 950        out Voxel? result)
 951    {
 97952        result = null;
 97953        return TryGetGrid(position, out VoxelGrid? grid)
 97954            && grid!.TryGetVoxel(position, out result);
 955    }
 956
 957    /// <summary>
 958    /// Retrieves a voxel from a 2D XZ-plane world position on the default world Y layer.
 959    /// </summary>
 960    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 961    /// <param name="result">The resolved voxel, if found.</param>
 962    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 963    public bool TryGetVoxel(
 964        Vector2d position,
 965        out Voxel? result)
 966    {
 1967        return TryGetVoxel(position, default, out result);
 968    }
 969
 970    /// <summary>
 971    /// Retrieves a voxel from a 2D XZ-plane world position on the supplied world Y layer.
 972    /// </summary>
 973    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 974    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 975    /// <param name="result">The resolved voxel, if found.</param>
 976    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 977    public bool TryGetVoxel(
 978        Vector2d position,
 979        Fixed64 layerY,
 980        out Voxel? result)
 981    {
 3982        return TryGetVoxel(GridPlane2d.ToWorld(position, layerY), out result);
 983    }
 984
 985    /// <summary>
 986    /// Retrieves the physical voxel whose center is nearest to the supplied world position.
 987    /// Sparse grids only consider configured physical voxels.
 988    /// </summary>
 989    /// <param name="position">The world position to resolve.</param>
 990    /// <param name="result">The closest physical voxel, if found.</param>
 991    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 992    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 993    public bool TryGetClosestVoxel(
 994        Vector3d position,
 995        out Voxel? result,
 996        GridTopologyKind? topologyKind = null)
 997    {
 6998        result = null;
 6999        if (!TryGetClosestGridAndVoxel(position, out _, out Voxel? closestVoxel, topologyKind))
 21000            return false;
 1001
 41002        result = closestVoxel;
 41003        return true;
 1004    }
 1005
 1006    /// <summary>
 1007    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the default world Y laye
 1008    /// Sparse grids only consider configured physical voxels.
 1009    /// </summary>
 1010    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1011    /// <param name="result">The closest physical voxel, if found.</param>
 1012    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1013    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 1014    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1015    public bool TryGetClosestVoxel(
 1016        Vector2d position,
 1017        out Voxel? result,
 1018        GridTopologyKind? topologyKind = null) =>
 11019        TryGetClosestVoxel(position, default, out result, topologyKind);
 1020
 1021    /// <summary>
 1022    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the supplied world Y lay
 1023    /// Sparse grids only consider configured physical voxels.
 1024    /// </summary>
 1025    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1026    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 1027    /// <param name="result">The closest physical voxel, if found.</param>
 1028    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1029    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 1030    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1031    public bool TryGetClosestVoxel(
 1032        Vector2d position,
 1033        Fixed64 layerY,
 1034        out Voxel? result,
 1035        GridTopologyKind? topologyKind = null) =>
 31036        TryGetClosestVoxel(GridPlane2d.ToWorld(position, layerY), out result, topologyKind);
 1037
 1038    /// <summary>
 1039    /// Retrieves a voxel from a world-scoped voxel identity.
 1040    /// </summary>
 1041    /// <param name="worldVoxelIndex">The voxel identity to resolve.</param>
 1042    /// <param name="result">The resolved voxel, if found.</param>
 1043    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 1044    public bool TryGetVoxel(
 1045        WorldVoxelIndex worldVoxelIndex,
 1046        out Voxel? result)
 1047    {
 21048        result = null;
 21049        return TryGetGrid(worldVoxelIndex, out VoxelGrid? grid)
 21050            && grid!.TryGetVoxel(worldVoxelIndex.VoxelIndex, out result);
 1051    }
 1052
 1053    #endregion
 1054
 1055    #region Internal Helpers
 1056
 1057    internal static bool TryNormalizeConfiguration(
 1058        GridConfiguration configuration,
 1059        out GridConfiguration normalizedConfiguration,
 1060        out IGridTopology topology,
 1061        out GridDimensions dimensions)
 1062    {
 4341063        normalizedConfiguration = default;
 4341064        topology = null!;
 4341065        dimensions = default;
 4341066        if (!GridTopologyFactory.TryCreate(configuration, out IGridTopology? createdTopology))
 111067            return false;
 1068
 4231069        (Vector3d boundsMin, Vector3d boundsMax) =
 4231070            createdTopology!.NormalizeBounds(configuration.BoundsMin, configuration.BoundsMax);
 1071
 4231072        normalizedConfiguration = new GridConfiguration(
 4231073            boundsMin,
 4231074            boundsMax,
 4231075            configuration.ScanCellSize,
 4231076            configuration.TopologyKind,
 4231077            configuration.TopologyMetrics,
 4231078            configuration.StorageKind);
 4231079        topology = createdTopology;
 4231080        dimensions = topology.CalculateDimensions(boundsMin, boundsMax);
 4231081        return true;
 1082    }
 1083
 1084    /// <summary>
 1085    /// Increments the version of the specified grid and optionally the world version.
 1086    /// </summary>
 1087    public void IncrementGridVersion(int index, bool significant = false)
 1088    {
 51089        if (!IsActive)
 1090        {
 31091            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot increment grid versions.");
 31092            return;
 1093        }
 1094
 21095        _gridLock.EnterWriteLock();
 1096        try
 1097        {
 21098            if (significant)
 11099                Version++;
 1100
 21101            if (ActiveGrids.IsAllocated(index))
 11102                ActiveGrids[index].IncrementVersion();
 21103        }
 1104        finally
 1105        {
 21106            _gridLock.ExitWriteLock();
 21107        }
 21108    }
 1109
 1110    /// <summary>
 1111    /// Enumerates the spatial-hash cells that intersect the supplied bounds.
 1112    /// </summary>
 1113    public IEnumerable<int> GetSpatialGridCells(Vector3d min, Vector3d max)
 1114    {
 4321115        (int xMin, int yMin, int zMin, int xMax, int yMax, int zMax) = GetSpatialGridCellBounds(min, max);
 1116
 17921117        for (int z = zMin; z <= zMax; z++)
 1118        {
 18561119            for (int y = yMin; y <= yMax; y++)
 1120            {
 20861121                for (int x = xMin; x <= xMax; x++)
 5791122                    yield return SwiftHashTools.CombineHashCodes(x, y, z);
 1123            }
 1124        }
 4321125    }
 1126
 1127    /// <summary>
 1128    /// Computes normalized spatial-hash cell bounds for the supplied world-space bounds.
 1129    /// </summary>
 1130    internal (int xMin, int yMin, int zMin, int xMax, int yMax, int zMax) GetSpatialGridCellBounds(
 1131        Vector3d min,
 1132        Vector3d max)
 1133    {
 12241134        (int xMin, int yMin, int zMin) = SnapToSpatialGrid(min);
 12241135        (int xMax, int yMax, int zMax) = SnapToSpatialGrid(max);
 1136
 12241137        (xMin, xMax) = xMin > xMax ? (xMax, xMin) : (xMin, xMax);
 12241138        (yMin, yMax) = yMin > yMax ? (yMax, yMin) : (yMin, yMax);
 12241139        (zMin, zMax) = zMin > zMax ? (zMax, zMin) : (zMin, zMax);
 1140
 12241141        return (xMin, yMin, zMin, xMax, yMax, zMax);
 1142    }
 1143
 1144    /// <summary>
 1145    /// Finds active grids in this world that overlap the supplied target grid.
 1146    /// </summary>
 1147    public IEnumerable<VoxelGrid> FindOverlappingGrids(VoxelGrid targetGrid)
 1148    {
 51149        SwiftHashSet<VoxelGrid> overlappingGrids = new();
 1150
 51151        if (!IsActive)
 1152        {
 31153            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve overlaps.");
 31154            return overlappingGrids;
 1155        }
 1156
 241157        foreach (int cellIndex in GetSpatialGridCells(targetGrid.BoundsMin, targetGrid.BoundsMax))
 101158            AddOverlappingGridsFromCell(targetGrid, cellIndex, overlappingGrids);
 1159
 21160        return overlappingGrids;
 1161    }
 1162
 1163    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1164    private static bool MatchesTopologyKind(VoxelGrid grid, GridTopologyKind? topologyKind) =>
 491165        !topologyKind.HasValue || grid.TopologyKind == topologyKind.Value;
 1166
 1167    private static bool IsBetterClosestVoxel(
 1168        Fixed64 candidateDistanceSquared,
 1169        VoxelGrid candidateGrid,
 1170        Voxel candidateVoxel,
 1171        Fixed64 closestDistanceSquared,
 1172        VoxelGrid? closestGrid,
 1173        Voxel? closestVoxel)
 1174    {
 41175        if (closestVoxel == null || closestGrid == null)
 11176            return true;
 1177
 31178        if (candidateDistanceSquared != closestDistanceSquared)
 11179            return candidateDistanceSquared < closestDistanceSquared;
 1180
 21181        return candidateGrid.GridIndex < closestGrid.GridIndex;
 1182    }
 1183
 1184    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1185    private static Fixed64 GetDistanceSquaredToBounds(Vector3d position, Vector3d boundsMin, Vector3d boundsMax)
 1186    {
 351187        Fixed64 x = GetAxisDistanceToBounds(position.X, boundsMin.X, boundsMax.X);
 351188        Fixed64 y = GetAxisDistanceToBounds(position.Y, boundsMin.Y, boundsMax.Y);
 351189        Fixed64 z = GetAxisDistanceToBounds(position.Z, boundsMin.Z, boundsMax.Z);
 351190        return x * x + y * y + z * z;
 1191    }
 1192
 1193    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1194    private static Fixed64 GetAxisDistanceToBounds(Fixed64 coordinate, Fixed64 min, Fixed64 max)
 1195    {
 1051196        if (coordinate < min)
 171197            return min - coordinate;
 1198
 881199        return coordinate > max ? coordinate - max : Fixed64.Zero;
 1200    }
 1201
 1202    private bool CanResolveGrid(int index)
 1203    {
 3481204        if (!CanResolveActiveGrid())
 21205            return false;
 1206
 3461207        if (!IsGridIndexInActiveRange(index))
 1208        {
 71209            GridForgeLogger.Channel.Error($"GridIndex '{index}' is out-of-bounds for ActiveGrids.");
 71210            return false;
 1211        }
 1212
 3391213        return IsGridIndexAllocated(index);
 1214    }
 1215
 1216    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1217    private bool CanResolveActiveGrid()
 1218    {
 3891219        if (IsActive)
 3841220            return true;
 1221
 51222        GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve grids.");
 51223        return false;
 1224    }
 1225
 1226    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1227    private bool IsGridIndexInActiveRange(int index) =>
 3461228         (uint)index < MaxGrids && (uint)index <= ActiveGrids.Count;
 1229
 1230    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1231    private bool IsGridIndexAllocated(int index)
 1232    {
 3391233        if (ActiveGrids.IsAllocated(index))
 3351234            return true;
 1235
 41236        GridForgeLogger.Channel.Error($"GridIndex '{index}' has not been allocated to ActiveGrids.");
 41237        return false;
 1238    }
 1239
 1240    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1241    private bool TryGetSpatialGridCandidates(
 1242        Vector3d position,
 1243        out SwiftHashSet<ushort>? gridList)
 1244    {
 1551245        gridList = null;
 1551246        if (!IsActive)
 1247        {
 21248            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve positions.");
 21249            return false;
 1250        }
 1251
 1531252        return SpatialGridHash.TryGetValue(GetSpatialGridKey(position), out gridList);
 1253    }
 1254
 1255    private bool TryGetContainingGrid(
 1256        Vector3d position,
 1257        SwiftHashSet<ushort> gridList,
 1258        out VoxelGrid? outGrid)
 1259    {
 1521260        outGrid = null;
 1261
 5731262        foreach (ushort candidateIndex in gridList)
 1263        {
 1681264            if (TryGetActiveGridCandidate(candidateIndex, out VoxelGrid? candidateGrid)
 1681265                && candidateGrid!.IsInBounds(position))
 1266            {
 671267                outGrid = candidateGrid;
 671268                return true;
 1269            }
 1270        }
 1271
 851272        return false;
 671273    }
 1274
 1275    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1276    private bool TryGetActiveGridCandidate(ushort gridIndex, out VoxelGrid? grid)
 1277    {
 1751278        grid = null;
 1751279        if (!ActiveGrids.IsAllocated(gridIndex))
 21280            return false;
 1281
 1731282        grid = ActiveGrids[gridIndex];
 1731283        return grid.IsActive;
 1284    }
 1285
 1286    private void AddOverlappingGridsFromCell(
 1287        VoxelGrid targetGrid,
 1288        int cellIndex,
 1289        SwiftHashSet<VoxelGrid> overlappingGrids)
 1290    {
 101291        if (!SpatialGridHash.TryGetValue(cellIndex, out SwiftHashSet<ushort> gridList))
 11292            return;
 1293
 501294        foreach (ushort neighborIndex in gridList)
 161295            TryAddOverlappingGrid(targetGrid, neighborIndex, overlappingGrids);
 91296    }
 1297
 1298    private void TryAddOverlappingGrid(
 1299        VoxelGrid targetGrid,
 1300        ushort neighborIndex,
 1301        SwiftHashSet<VoxelGrid> overlappingGrids)
 1302    {
 161303        if (neighborIndex == targetGrid.GridIndex
 161304            || !TryGetActiveGridCandidate(neighborIndex, out VoxelGrid? neighborGrid))
 1305        {
 101306            return;
 1307        }
 1308
 61309        if (VoxelGrid.IsGridOverlapValid(targetGrid, neighborGrid!))
 41310            overlappingGrids.Add(neighborGrid!);
 61311    }
 1312
 1313    /// <summary>
 1314    /// Computes the spatial-hash key for the supplied world-space position.
 1315    /// </summary>
 1316    public int GetSpatialGridKey(Vector3d position)
 1317    {
 1631318        (int x, int y, int z) = (
 1631319            position.X.FloorToInt() / SpatialGridCellSize,
 1631320            position.Y.FloorToInt() / SpatialGridCellSize,
 1631321            position.Z.FloorToInt() / SpatialGridCellSize
 1631322        );
 1323
 1631324        return SwiftHashTools.CombineHashCodes(x, y, z);
 1325    }
 1326
 1327    internal void NotifyActiveGridChange(VoxelGrid? grid)
 1328    {
 13811329        if (grid == null || !grid.IsActive)
 41330            return;
 1331
 13771332        NotifyActiveGridChange(CreateGridEventInfo(grid, GridEventKind.GridChanged));
 13771333    }
 1334
 1335    internal void NotifyActiveGridChange(
 1336        VoxelGrid? grid,
 1337        GridEventKind changeKind,
 1338        VoxelIndex voxelIndex,
 1339        Vector3d affectedPosition)
 1340    {
 461341        if (grid == null || !grid.IsActive)
 21342            return;
 1343
 441344        NotifyActiveGridChange(CreateGridEventInfo(grid, changeKind, voxelIndex, affectedPosition, affectedPosition));
 441345    }
 1346
 1347    #endregion
 1348
 1349    #region Private Helpers
 1350
 1351    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1352    private static int ResolveSpatialGridCellSize(int spatialGridCellSize)
 1353    {
 4271354        if (spatialGridCellSize <= 0)
 1355        {
 31356            GridForgeLogger.Channel.Warn($"Spatial grid cell size must be greater than zero. Falling back to default siz
 31357            return DefaultSpatialGridCellSize;
 1358        }
 1359
 4241360        return spatialGridCellSize;
 1361    }
 1362
 1363    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1364    private GridEventInfo CreateGridEventInfo(VoxelGrid grid, GridEventKind changeKind) =>
 18051365        new(
 18051366            SpawnToken,
 18051367            grid.GridIndex,
 18051368            grid.SpawnToken,
 18051369            grid.Configuration,
 18051370            grid.Version,
 18051371            changeKind,
 18051372            default,
 18051373            grid.BoundsMin,
 18051374            grid.BoundsMax);
 1375
 1376    private GridEventInfo CreateGridEventInfo(
 1377        VoxelGrid grid,
 1378        GridEventKind changeKind,
 1379        VoxelIndex voxelIndex,
 1380        Vector3d affectedBoundsMin,
 1381        Vector3d affectedBoundsMax) =>
 441382        new(
 441383            SpawnToken,
 441384            grid.GridIndex,
 441385            grid.SpawnToken,
 441386            grid.Configuration,
 441387            grid.Version,
 441388            changeKind,
 441389            voxelIndex,
 441390            affectedBoundsMin,
 441391            affectedBoundsMax);
 1392
 1393    private void NotifyActiveGridAdded(GridEventInfo eventInfo)
 1394    {
 4031395        Action<GridEventInfo>? handlers = _onActiveGridAdded;
 4031396        if (handlers == null)
 3941397            return;
 1398
 91399        var handlerDelegates = handlers.GetInvocationList();
 381400        for (int i = 0; i < handlerDelegates.Length; i++)
 1401        {
 1402            try
 1403            {
 101404                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 91405            }
 11406            catch (Exception ex)
 1407            {
 11408                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] added notification error: {ex.Message}");
 11409            }
 1410        }
 91411    }
 1412
 1413    private void NotifyActiveGridRemoved(GridEventInfo eventInfo)
 1414    {
 251415        Action<GridEventInfo>? handlers = _onActiveGridRemoved;
 251416        if (handlers == null)
 201417            return;
 1418
 51419        var handlerDelegates = handlers.GetInvocationList();
 221420        for (int i = 0; i < handlerDelegates.Length; i++)
 1421        {
 1422            try
 1423            {
 61424                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 51425            }
 11426            catch (Exception ex)
 1427            {
 11428                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] removed notification error: {ex.Message}");
 11429            }
 1430        }
 51431    }
 1432
 1433    private void NotifyActiveGridChange(GridEventInfo eventInfo)
 1434    {
 14211435        Action<GridEventInfo>? handlers = _onActiveGridChange;
 14211436        if (handlers == null)
 6261437            return;
 1438
 7951439        var handlerDelegates = handlers.GetInvocationList();
 441881440        for (int i = 0; i < handlerDelegates.Length; i++)
 1441        {
 1442            try
 1443            {
 212991444                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 212971445            }
 21446            catch (Exception ex)
 1447            {
 21448                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] change notification error: {ex.Message}");
 21449            }
 1450        }
 7951451    }
 1452
 1453    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1454    private (int xMin, int yMin, int zMin) SnapToSpatialGrid(Vector3d position)
 1455    {
 24481456        return (
 24481457            (position.X.Abs() / SpatialGridCellSize).FloorToInt() * position.X.Sign(),
 24481458            (position.Y.Abs() / SpatialGridCellSize).FloorToInt() * position.Y.Sign(),
 24481459            (position.Z.Abs() / SpatialGridCellSize).FloorToInt() * position.Z.Sign()
 24481460        );
 1461    }
 1462
 1463    #endregion
 1464}

Methods/Properties

.cctor()
.ctor(System.Int32)
add_OnActiveGridAdded(System.Action`1<GridForge.Grids.GridEventInfo>)
remove_OnActiveGridAdded(System.Action`1<GridForge.Grids.GridEventInfo>)
add_OnActiveGridRemoved(System.Action`1<GridForge.Grids.GridEventInfo>)
remove_OnActiveGridRemoved(System.Action`1<GridForge.Grids.GridEventInfo>)
add_OnActiveGridChange(System.Action`1<GridForge.Grids.GridEventInfo>)
remove_OnActiveGridChange(System.Action`1<GridForge.Grids.GridEventInfo>)
add_OnReset(System.Action)
remove_OnReset(System.Action)
Reset(System.Boolean)
NotifyResetHandlers()
ReleaseActiveGrids()
Dispose()
TryAddGrid(GridForge.Configuration.GridConfiguration,System.UInt16&)
TryAddGrid(GridForge.Configuration.GridConfiguration,System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,System.UInt16&)
TryAddGrid(GridForge.Configuration.GridConfiguration,System.Boolean[0...,0...,0...],System.UInt16&)
TryAddGridCore(GridForge.Configuration.GridConfiguration,System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,System.Boolean[0...,0...,0...],System.UInt16&)
TryRemoveGrid(System.UInt16)
CanAddGrid()
TryPrepareConfiguredVoxels(GridForge.Configuration.GridConfiguration,GridForge.Grids.Topology.GridDimensions,System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,System.Boolean[0...,0...,0...],GridForge.Spatial.VoxelIndex[]&)
TryValidateGridDimensions(GridForge.Grids.Topology.GridDimensions)
TryPrepareConfiguredVoxelMask(System.Boolean[0...,0...,0...],GridForge.Grids.Topology.GridDimensions,GridForge.Spatial.VoxelIndex[]&)
TryPrepareConfiguredVoxelIndices(System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,GridForge.Grids.Topology.GridDimensions,GridForge.Spatial.VoxelIndex[]&)
IsConfiguredVoxelInBounds(GridForge.Spatial.VoxelIndex,GridForge.Grids.Topology.GridDimensions)
CompactPreparedVoxels(GridForge.Spatial.VoxelIndex[]&)
UpdateMaxTopologyCellEdge(FixedMathSharp.Fixed64)
RecalculateMaxTopologyCellEdgeIfNeeded(FixedMathSharp.Fixed64)
TryFindExistingGrid(GridForge.Configuration.GridConfigurationKey,System.UInt16&)
RegisterGridSpatialCells(GridForge.Grids.VoxelGrid,System.UInt16)
LinkGridWithCellNeighbors(GridForge.Grids.VoxelGrid,System.UInt16,SwiftCollections.SwiftHashSet`1<System.UInt16>)
UnregisterGridSpatialCells(GridForge.Grids.VoxelGrid,System.UInt16)
UnlinkGridCellNeighbors(GridForge.Grids.VoxelGrid,System.UInt16,SwiftCollections.SwiftHashSet`1<System.UInt16>)
TryGetGrid(System.Int32,GridForge.Grids.VoxelGrid&)
TryGetGrid(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&)
TryGetGrid(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&)
TryGetGrid(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&)
TryGetClosestGrid(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGrid(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGrid(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetGrid(GridForge.Spatial.WorldVoxelIndex,GridForge.Grids.VoxelGrid&)
TryGetGridAndVoxel(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetGridAndVoxel(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetGridAndVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetClosestGridAndVoxel(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGridAndVoxel(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGridAndVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetGridAndVoxel(GridForge.Spatial.WorldVoxelIndex,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetVoxel(FixedMathSharp.Vector3d,GridForge.Grids.Voxel&)
TryGetVoxel(FixedMathSharp.Vector2d,GridForge.Grids.Voxel&)
TryGetVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.Voxel&)
TryGetClosestVoxel(FixedMathSharp.Vector3d,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestVoxel(FixedMathSharp.Vector2d,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetVoxel(GridForge.Spatial.WorldVoxelIndex,GridForge.Grids.Voxel&)
TryNormalizeConfiguration(GridForge.Configuration.GridConfiguration,GridForge.Configuration.GridConfiguration&,GridForge.Grids.Topology.IGridTopology&,GridForge.Grids.Topology.GridDimensions&)
IncrementGridVersion(System.Int32,System.Boolean)
GetSpatialGridCells()
GetSpatialGridCellBounds(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
FindOverlappingGrids(GridForge.Grids.VoxelGrid)
MatchesTopologyKind(GridForge.Grids.VoxelGrid,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
IsBetterClosestVoxel(FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel)
GetDistanceSquaredToBounds(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetAxisDistanceToBounds(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CanResolveGrid(System.Int32)
CanResolveActiveGrid()
IsGridIndexInActiveRange(System.Int32)
IsGridIndexAllocated(System.Int32)
TryGetSpatialGridCandidates(FixedMathSharp.Vector3d,SwiftCollections.SwiftHashSet`1<System.UInt16>&)
TryGetContainingGrid(FixedMathSharp.Vector3d,SwiftCollections.SwiftHashSet`1<System.UInt16>,GridForge.Grids.VoxelGrid&)
TryGetActiveGridCandidate(System.UInt16,GridForge.Grids.VoxelGrid&)
AddOverlappingGridsFromCell(GridForge.Grids.VoxelGrid,System.Int32,SwiftCollections.SwiftHashSet`1<GridForge.Grids.VoxelGrid>)
TryAddOverlappingGrid(GridForge.Grids.VoxelGrid,System.UInt16,SwiftCollections.SwiftHashSet`1<GridForge.Grids.VoxelGrid>)
GetSpatialGridKey(FixedMathSharp.Vector3d)
NotifyActiveGridChange(GridForge.Grids.VoxelGrid)
NotifyActiveGridChange(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind,GridForge.Spatial.VoxelIndex,FixedMathSharp.Vector3d)
ResolveSpatialGridCellSize(System.Int32)
CreateGridEventInfo(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind)
CreateGridEventInfo(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind,GridForge.Spatial.VoxelIndex,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
NotifyActiveGridAdded(GridForge.Grids.GridEventInfo)
NotifyActiveGridRemoved(GridForge.Grids.GridEventInfo)
NotifyActiveGridChange(GridForge.Grids.GridEventInfo)
SnapToSpatialGrid(FixedMathSharp.Vector3d)