fix(physics): search adjacent landblocks for collision objects

GetNearbyObjects now searches the player's landblock plus all 8
neighbors. Previously only searched one landblock, missing objects
near landblock boundaries — which includes most trees/rocks since
scenery is placed across the full streaming window.

Also added diagnostic logging (will strip after verification).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-14 12:44:50 +02:00
parent f2548146b5
commit efd6a06c7d
2 changed files with 56 additions and 19 deletions

View file

@ -109,36 +109,57 @@ public sealed class ShadowObjectRegistry
}
/// <summary>
/// Get all objects in cells that a sphere at worldPos with given radius overlaps.
/// This is the main query used by the Transition system.
/// Get all objects near a world position. Searches the given landblock plus
/// all 8 adjacent landblocks to handle objects near cell/landblock boundaries.
/// Within each landblock, queries only the cells the query sphere overlaps.
/// </summary>
public void GetNearbyObjects(Vector3 worldPos, float queryRadius,
float worldOffsetX, float worldOffsetY, uint landblockId,
List<ShadowEntry> results)
{
results.Clear();
float localX = worldPos.X - worldOffsetX;
float localY = worldPos.Y - worldOffsetY;
var seen = new HashSet<uint>();
int minCx = Math.Max(0, (int)((localX - queryRadius) / 24f));
int maxCx = Math.Min(7, (int)((localX + queryRadius) / 24f));
int minCy = Math.Max(0, (int)((localY - queryRadius) / 24f));
int maxCy = Math.Min(7, (int)((localY + queryRadius) / 24f));
// Extract landblock X/Y from the ID.
int lbX = (int)((landblockId >> 24) & 0xFF);
int lbY = (int)((landblockId >> 16) & 0xFF);
uint lbPrefix = landblockId & 0xFFFF0000u;
var seen = new HashSet<uint>(); // avoid duplicates from overlapping cells
for (int cx = minCx; cx <= maxCx; cx++)
// Search the player's landblock and all 8 neighbors.
for (int dx = -1; dx <= 1; dx++)
{
for (int cy = minCy; cy <= maxCy; cy++)
for (int dy = -1; dy <= 1; dy++)
{
uint cellId = lbPrefix | (uint)(cx * 8 + cy + 1);
if (!_cells.TryGetValue(cellId, out var list)) continue;
int nx = lbX + dx;
int ny = lbY + dy;
if (nx < 0 || nx > 255 || ny < 0 || ny > 255) continue;
foreach (var entry in list)
uint neighborLb = ((uint)nx << 24) | ((uint)ny << 16) | 0xFFFFu;
uint nbPrefix = neighborLb & 0xFFFF0000u;
// Compute local position relative to this neighbor landblock.
float nbOffX = worldOffsetX + dx * 192f;
float nbOffY = worldOffsetY + dy * 192f;
float localX = worldPos.X - nbOffX;
float localY = worldPos.Y - nbOffY;
int minCx = Math.Max(0, (int)((localX - queryRadius) / 24f));
int maxCx = Math.Min(7, (int)((localX + queryRadius) / 24f));
int minCy = Math.Max(0, (int)((localY - queryRadius) / 24f));
int maxCy = Math.Min(7, (int)((localY + queryRadius) / 24f));
for (int cx = minCx; cx <= maxCx; cx++)
{
if (seen.Add(entry.EntityId))
results.Add(entry);
for (int cy = minCy; cy <= maxCy; cy++)
{
uint cellId = nbPrefix | (uint)(cx * 8 + cy + 1);
if (!_cells.TryGetValue(cellId, out var list)) continue;
foreach (var entry in list)
{
if (seen.Add(entry.EntityId))
results.Add(entry);
}
}
}
}
}