fix(render) Campaign FW3.2b-2: scenery buckets need the streaming recenter
The owner watched the first passing lifecycle run and reported most outdoor scenery not rendering (terrain/buildings/interiors fine, no seams). Root cause: retained-scene record positions are RENDER-ORIGIN- RELATIVE (each landblock carries (lbX - CenterX)*192 offsets), but the walk world data bucketed outdoor statics by treating them as absolute - garbage landblock bytes, so the walk's landscape-cell turns read empty buckets and trees/rocks/fences never drew. LandscapeCellId now adds the frame's RenderCenterLbX/Y back to the relative block index, producing the TRUE cell ids the walk's OnLandscapeCellTurn emits. Buildings and cell statics were unaffected (id-keyed, not position-bucketed). The same run confirmed the campaign thesis live: NO SEAMS at the doorway class under walk order + retail depth, with the lift hack still present (its retirement is FW3.3). Suites: hermetic 6,750/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1e1a0d9318
commit
1955a0d5f1
2 changed files with 41 additions and 14 deletions
|
|
@ -1167,7 +1167,11 @@ public sealed class RetailPViewRenderer
|
|||
Walk.WalkCell? cameraCell,
|
||||
Walk.WalkLandscape landscape)
|
||||
{
|
||||
_walkWorldData!.BeginFrame(_sceneFrameProduct!.SceneQuery, ctx.PlayerLandblockId ?? 0u);
|
||||
_walkWorldData!.BeginFrame(
|
||||
_sceneFrameProduct!.SceneQuery,
|
||||
ctx.PlayerLandblockId ?? 0u,
|
||||
ctx.RenderCenterLbX,
|
||||
ctx.RenderCenterLbY);
|
||||
|
||||
Action clearInteriorDepth = () =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ internal sealed class WalkProductionWorldData : IWalkFrameWorldData
|
|||
private readonly WalkBuildingRegistry _buildings;
|
||||
private RenderSceneQuery _scene;
|
||||
private uint _tupleLandblockId;
|
||||
private int _renderCenterLbX;
|
||||
private int _renderCenterLbY;
|
||||
|
||||
private readonly Dictionary<uint, WalkFrameStaticRecords> _cellCache = new();
|
||||
private readonly Dictionary<uint, List<RenderProjectionRecord>> _outdoorByCell = new();
|
||||
|
|
@ -52,11 +54,24 @@ internal sealed class WalkProductionWorldData : IWalkFrameWorldData
|
|||
}
|
||||
|
||||
/// <summary>Rebuilds the frame's outdoor/shell buckets and clears the
|
||||
/// per-cell cache. Call once per frame before the driver runs.</summary>
|
||||
internal void BeginFrame(RenderSceneQuery scene, uint tupleLandblockId)
|
||||
/// per-cell cache. Call once per frame before the driver runs.
|
||||
/// <paramref name="renderCenterLbX"/>/<paramref name="renderCenterLbY"/>
|
||||
/// are the streaming recenter origin: record positions are
|
||||
/// RENDER-ORIGIN-RELATIVE (each landblock's entities carry
|
||||
/// <c>(lbX − CenterX)·192</c> offsets), so mapping a position back to
|
||||
/// its TRUE landblock byte needs the center added back — the first
|
||||
/// connected gate of the FW3.2b-2 cutover shipped without this and most
|
||||
/// outdoor scenery landed in garbage buckets no walk turn ever reads.</summary>
|
||||
internal void BeginFrame(
|
||||
RenderSceneQuery scene,
|
||||
uint tupleLandblockId,
|
||||
int renderCenterLbX,
|
||||
int renderCenterLbY)
|
||||
{
|
||||
_scene = scene;
|
||||
_tupleLandblockId = tupleLandblockId;
|
||||
_renderCenterLbX = renderCenterLbX;
|
||||
_renderCenterLbY = renderCenterLbY;
|
||||
_cellCache.Clear();
|
||||
_outdoorMaterialized.Clear();
|
||||
_shellMaterialized.Clear();
|
||||
|
|
@ -87,26 +102,34 @@ internal sealed class WalkProductionWorldData : IWalkFrameWorldData
|
|||
shells.Add(record);
|
||||
continue;
|
||||
}
|
||||
uint cellId = LandscapeCellId(record.Transform.Position);
|
||||
uint cellId = LandscapeCellId(
|
||||
record.Transform.Position, _renderCenterLbX, _renderCenterLbY);
|
||||
if (!_outdoorByCell.TryGetValue(cellId, out List<RenderProjectionRecord>? bucket))
|
||||
_outdoorByCell[cellId] = bucket = new List<RenderProjectionRecord>();
|
||||
bucket.Add(record);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The landscape cell owning a world position — retail's
|
||||
/// 24 m cell grid inside the 192 m landblock, the same
|
||||
/// <c>(lb & 0xFFFF0000) | (cellX*8 + cellY + 1)</c> encoding the walk's
|
||||
/// landscape turn emits.</summary>
|
||||
internal static uint LandscapeCellId(Vector3 worldPosition)
|
||||
/// <summary>The landscape cell owning a RENDER-ORIGIN-RELATIVE position
|
||||
/// — retail's 24 m cell grid inside the 192 m landblock, producing the
|
||||
/// same TRUE <c>(lb & 0xFFFF0000) | (cellX*8 + cellY + 1)</c> encoding
|
||||
/// the walk's landscape turn emits: the relative block index
|
||||
/// (<c>floor(p/192)</c>) plus the streaming center recovers the true
|
||||
/// landblock byte, because entity positions carry
|
||||
/// <c>(lbX − CenterX)·192</c> world offsets
|
||||
/// (<c>LandblockBuildFactory</c>'s <c>worldOffset</c>).</summary>
|
||||
internal static uint LandscapeCellId(
|
||||
Vector3 relativePosition, int renderCenterLbX, int renderCenterLbY)
|
||||
{
|
||||
int lbX = (int)MathF.Floor(worldPosition.X / 192f);
|
||||
int lbY = (int)MathF.Floor(worldPosition.Y / 192f);
|
||||
float localX = worldPosition.X - lbX * 192f;
|
||||
float localY = worldPosition.Y - lbY * 192f;
|
||||
int relBlockX = (int)MathF.Floor(relativePosition.X / 192f);
|
||||
int relBlockY = (int)MathF.Floor(relativePosition.Y / 192f);
|
||||
float localX = relativePosition.X - relBlockX * 192f;
|
||||
float localY = relativePosition.Y - relBlockY * 192f;
|
||||
int cellX = Math.Clamp((int)(localX / 24f), 0, 7);
|
||||
int cellY = Math.Clamp((int)(localY / 24f), 0, 7);
|
||||
uint landblock = ((uint)(byte)lbX << 24) | ((uint)(byte)lbY << 16);
|
||||
uint landblock =
|
||||
((uint)(byte)(renderCenterLbX + relBlockX) << 24)
|
||||
| ((uint)(byte)(renderCenterLbY + relBlockY) << 16);
|
||||
return landblock | (uint)(cellX * 8 + cellY + 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue