fix(physics): make collision activation starvation-free
This commit is contained in:
parent
d94145e6b8
commit
6b28ff999c
14 changed files with 4637 additions and 474 deletions
|
|
@ -18,8 +18,24 @@ namespace AcDream.Core.World.Cells;
|
|||
/// </summary>
|
||||
public sealed class CellGraph
|
||||
{
|
||||
private readonly ConcurrentDictionary<uint, EnvCell> _envCells = new();
|
||||
private readonly ConcurrentDictionary<uint, CellGraphTerrain> _terrain = new();
|
||||
private readonly CollisionWorldStateSlot _collisionWorld;
|
||||
private ConcurrentDictionary<uint, EnvCell> _envCells =>
|
||||
_collisionWorld.Current.EnvCells;
|
||||
private ConcurrentDictionary<uint, CellGraphTerrain> _terrain =>
|
||||
_collisionWorld.Current.Terrain;
|
||||
private ConcurrentDictionary<uint, ObjCell> _outdoorCells =>
|
||||
_collisionWorld.Current.OutdoorCells;
|
||||
|
||||
public CellGraph()
|
||||
: this(new CollisionWorldStateSlot())
|
||||
{
|
||||
}
|
||||
|
||||
internal CellGraph(CollisionWorldStateSlot collisionWorld)
|
||||
{
|
||||
_collisionWorld = collisionWorld
|
||||
?? throw new ArgumentNullException(nameof(collisionWorld));
|
||||
}
|
||||
|
||||
/// <summary>The player's current cell — the render/lighting root. Written ONLY at the
|
||||
/// player chokepoint <see cref="AcDream.Core.Physics.PhysicsEngine.UpdatePlayerCurrCell"/>
|
||||
|
|
@ -34,8 +50,21 @@ public sealed class CellGraph
|
|||
|
||||
/// <param name="landblockPrefix">Any id in the cell's landblock; masked to (id & 0xFFFF0000).</param>
|
||||
public void RegisterTerrain(uint landblockPrefix, TerrainSurface terrain, Vector3 worldOrigin)
|
||||
=> _terrain[landblockPrefix & 0xFFFF0000u] =
|
||||
new CellGraphTerrain(terrain, worldOrigin);
|
||||
{
|
||||
uint prefix = landblockPrefix & 0xFFFF0000u;
|
||||
_terrain[prefix] = new CellGraphTerrain(terrain, worldOrigin);
|
||||
for (uint low = 1u; low <= 0x40u; low++)
|
||||
{
|
||||
uint id = prefix | low;
|
||||
int index = (int)(low - 1u);
|
||||
_outdoorCells[id] = LandCell.Synthesize(
|
||||
id,
|
||||
terrain,
|
||||
worldOrigin,
|
||||
index / 8,
|
||||
index % 8);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// World origin (SW corner) of the landblock containing <paramref name="id"/>,
|
||||
|
|
@ -65,6 +94,8 @@ public sealed class CellGraph
|
|||
CurrCell = null;
|
||||
}
|
||||
_terrain.TryRemove(lb, out _);
|
||||
for (uint low = 1u; low <= 0x40u; low++)
|
||||
_outdoorCells.TryRemove(lb | low, out _);
|
||||
foreach (var id in new List<uint>(_envCells.Keys))
|
||||
if ((id & 0xFFFF0000u) == lb) _envCells.TryRemove(id, out _);
|
||||
}
|
||||
|
|
@ -95,9 +126,9 @@ public sealed class CellGraph
|
|||
|
||||
uint low = id & 0xFFFFu;
|
||||
if (low < 1u || low > 0x40u) return null;
|
||||
if (!_terrain.TryGetValue(id & 0xFFFF0000u, out var t)) return null;
|
||||
int idx = (int)(low - 1u);
|
||||
return LandCell.Synthesize(id, t.Terrain, t.Origin, idx / 8, idx % 8);
|
||||
return _outdoorCells.TryGetValue(id, out ObjCell? cell)
|
||||
? cell
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -127,63 +158,10 @@ public sealed class CellGraph
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an immutable-reference snapshot for collision-generation
|
||||
/// preparation. EnvCell and TerrainSurface records are immutable after
|
||||
/// publication, so copying the registries is sufficient; the active graph
|
||||
/// remains untouched while the staging graph is rebuilt.
|
||||
/// </summary>
|
||||
internal CellGraph CreateCollisionStagingCopy()
|
||||
{
|
||||
var copy = new CellGraph { CurrCell = CurrCell };
|
||||
foreach ((uint id, EnvCell cell) in _envCells)
|
||||
copy._envCells.TryAdd(id, cell);
|
||||
foreach ((uint id, CellGraphTerrain terrain) in _terrain)
|
||||
{
|
||||
copy._terrain.TryAdd(id, terrain);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
internal LandblockReplacementBuilder CreateLandblockReplacementBuilder(
|
||||
CellGraph staging,
|
||||
uint landblockId) => new(this, staging, landblockId);
|
||||
|
||||
internal void CommitLandblockReplacement(
|
||||
PreparedCellGraphLandblock replacement)
|
||||
{
|
||||
uint currentCellId = CurrCell?.Id ?? 0u;
|
||||
for (int index = 0; index < replacement.EnvCellIdsToRemove.Count; index++)
|
||||
_envCells.TryRemove(replacement.EnvCellIdsToRemove[index], out _);
|
||||
if (replacement.HasTerrain)
|
||||
{
|
||||
_terrain[replacement.LandblockPrefix] = replacement.Terrain!;
|
||||
}
|
||||
else
|
||||
{
|
||||
_terrain.TryRemove(replacement.LandblockPrefix, out _);
|
||||
}
|
||||
for (int index = 0; index < replacement.EnvCells.Count; index++)
|
||||
{
|
||||
(uint id, EnvCell cell) = replacement.EnvCells[index];
|
||||
_envCells[id] = cell;
|
||||
}
|
||||
|
||||
uint desiredCurrentCellId =
|
||||
(currentCellId & 0xFFFF0000u) == replacement.LandblockPrefix
|
||||
? currentCellId
|
||||
: currentCellId == 0u
|
||||
&& (replacement.CurrentCellId & 0xFFFF0000u)
|
||||
== replacement.LandblockPrefix
|
||||
? replacement.CurrentCellId
|
||||
: 0u;
|
||||
if (desiredCurrentCellId != 0u)
|
||||
CurrCell = GetVisible(desiredCurrentCellId);
|
||||
else if ((currentCellId & 0xFFFF0000u)
|
||||
== replacement.LandblockPrefix)
|
||||
CurrCell = null;
|
||||
}
|
||||
|
||||
internal sealed class LandblockReplacementBuilder : IDisposable
|
||||
{
|
||||
private readonly CellGraph _active;
|
||||
|
|
@ -249,8 +227,7 @@ public sealed class CellGraph
|
|||
_removeIds,
|
||||
_envCells,
|
||||
hasTerrain,
|
||||
terrain,
|
||||
_staging.CurrCell?.Id ?? 0u);
|
||||
terrain);
|
||||
_phase = 2;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -271,8 +248,7 @@ internal sealed record PreparedCellGraphLandblock(
|
|||
IReadOnlyList<uint> EnvCellIdsToRemove,
|
||||
IReadOnlyList<KeyValuePair<uint, EnvCell>> EnvCells,
|
||||
bool HasTerrain,
|
||||
CellGraphTerrain? Terrain,
|
||||
uint CurrentCellId);
|
||||
CellGraphTerrain? Terrain);
|
||||
|
||||
internal sealed record CellGraphTerrain(
|
||||
TerrainSurface Terrain,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue