feat(render): indoor render WORKS — terminating portal flood + every-cell seal + look-in FPS
Checkpoint of the unified retail-faithful indoor render. The two-week HANG/grey is fixed and the interior seals (live-verified by the user). Commits the session render-rewrite foundation together with the fixes that made it functional. - HANG fix: PortalVisibilityBuilder.Build portal flood did not terminate (the faithful ProjectToClip near-side clip drifts per round, defeating the CellView dedup; the BFS had no bound after U.2a removed MaxReprocessPerCell). Fix = drift-tolerant snapped/canonical CellView.Add dedup (PortalView.cs) plus restored MaxReprocessPerCell=16 bounded re-enqueue (PortalVisibilityBuilder.cs). Re-enqueue is kept (load-bearing for late-slice propagation, Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit); only its count is capped. CellViewDedupTests added. - Seal (DrawCells Task 2): RetailPViewRenderer.DrawEnvCellShells draws EVERY visible cell via IndoorDrawPlan.ShellPass (was gated on the ClipFrameAssembler slot filter, leaving slot-less cells grey). - Look-in FPS: GameWindow exterior look-in candidates limited to the player landblock +-1 (was all ~81 loaded LBs iterated every outdoor frame). No behaviour change (far cells were >48m, already culled). Remaining dominant issue = the FLAP at transitions: viewer-cell metastability (render roots at the camera-eye cell, which oscillates outdoor-indoor as the 3rd-person boom drifts across the doorway, confirmed in render-sig). SEPARATE fix, NOT the DrawCells port. Full handoff + flap fix plan + tracked follow-ups (#78 terrain, look-in-from-inside, look-in FPS, L-spotlight): docs/research/2026-06-07-indoor-render-session-handoff.md. Baselines: build 0 err; App.Tests 210/210; Core.Tests 1331 pass / 4 fail (pre-existing) / 1 skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bff1955066
commit
1405dd8e90
27 changed files with 3635 additions and 814 deletions
|
|
@ -1291,21 +1291,24 @@ namespace AcDream.App.Rendering.Wb {
|
|||
ct.ThrowIfCancellationRequested();
|
||||
if (poly.VertexIds.Count < 3) continue;
|
||||
|
||||
// Handle Positive Surface
|
||||
if (!poly.Stippling.HasFlag(StipplingType.NoPos)) {
|
||||
AddSurfaceToBatch(poly, poly.PosSurface, false);
|
||||
// Retail D3DPolyRender::ConstructMesh (0x0059dfa0) treats this
|
||||
// DatReaderWriter "CullMode" as CPolygon::sides_type, not as a
|
||||
// GL cull enum: 0 = pos, 1 = pos twice with reversed winding,
|
||||
// 2 = pos + neg surface. The DAT-side NoPos/NoNeg flags still
|
||||
// suppress hidden portal/cap faces before they reach our mesh.
|
||||
bool hasPos = !poly.Stippling.HasFlag(StipplingType.NoPos);
|
||||
bool hasNeg = !poly.Stippling.HasFlag(StipplingType.NoNeg);
|
||||
|
||||
if (hasPos)
|
||||
AddSurfaceToBatch(poly, poly.PosSurface, useNegUv: false, invertNormal: false, reverseWinding: false);
|
||||
if (hasPos && poly.SidesType == CullMode.None) {
|
||||
AddSurfaceToBatch(poly, poly.PosSurface, useNegUv: false, invertNormal: true, reverseWinding: true);
|
||||
}
|
||||
else if (hasNeg && poly.SidesType == CullMode.Clockwise) {
|
||||
AddSurfaceToBatch(poly, poly.NegSurface, useNegUv: true, invertNormal: true, reverseWinding: false);
|
||||
}
|
||||
|
||||
// Handle Negative Surface
|
||||
bool hasNeg = poly.Stippling.HasFlag(StipplingType.Negative) ||
|
||||
poly.Stippling.HasFlag(StipplingType.Both) ||
|
||||
(!poly.Stippling.HasFlag(StipplingType.NoNeg) && poly.SidesType == CullMode.Clockwise);
|
||||
|
||||
if (hasNeg) {
|
||||
AddSurfaceToBatch(poly, poly.NegSurface, true);
|
||||
}
|
||||
|
||||
void AddSurfaceToBatch(Polygon poly, short surfaceIdx, bool isNeg) {
|
||||
void AddSurfaceToBatch(Polygon poly, short surfaceIdx, bool useNegUv, bool invertNormal, bool reverseWinding) {
|
||||
if (surfaceIdx < 0) return;
|
||||
|
||||
uint surfaceId;
|
||||
|
|
@ -1499,7 +1502,17 @@ namespace AcDream.App.Rendering.Wb {
|
|||
|
||||
// Helper for CellStruct vertices
|
||||
bool batchHasWrappingUVs = batch.HasWrappingUVs;
|
||||
BuildCellStructPolygonIndices(poly, cellStruct, UVLookup, vertices, batch.Indices, isNeg, transform, ref batchHasWrappingUVs);
|
||||
BuildCellStructPolygonIndices(
|
||||
poly,
|
||||
cellStruct,
|
||||
UVLookup,
|
||||
vertices,
|
||||
batch.Indices,
|
||||
useNegUv,
|
||||
invertNormal,
|
||||
reverseWinding,
|
||||
transform,
|
||||
ref batchHasWrappingUVs);
|
||||
batch.HasWrappingUVs = batchHasWrappingUVs;
|
||||
}
|
||||
}
|
||||
|
|
@ -1516,8 +1529,10 @@ namespace AcDream.App.Rendering.Wb {
|
|||
}
|
||||
|
||||
private void BuildCellStructPolygonIndices(Polygon poly, CellStruct cellStruct,
|
||||
Dictionary<(ushort vertId, ushort uvIdx, bool isNeg), ushort> UVLookup,
|
||||
List<VertexPositionNormalTexture> vertices, List<ushort> indices, bool useNegSurface, Matrix4x4 transform, ref bool hasWrappingUVs) {
|
||||
Dictionary<(ushort vertId, ushort uvIdx, bool invertNormal), ushort> UVLookup,
|
||||
List<VertexPositionNormalTexture> vertices, List<ushort> indices,
|
||||
bool useNegUv, bool invertNormal, bool reverseWinding,
|
||||
Matrix4x4 transform, ref bool hasWrappingUVs) {
|
||||
|
||||
var polyIndices = new List<ushort>();
|
||||
|
||||
|
|
@ -1525,9 +1540,9 @@ namespace AcDream.App.Rendering.Wb {
|
|||
ushort vertId = (ushort)poly.VertexIds[i];
|
||||
ushort uvIdx = 0;
|
||||
|
||||
if (useNegSurface && poly.NegUVIndices != null && i < poly.NegUVIndices.Count)
|
||||
if (useNegUv && poly.NegUVIndices != null && i < poly.NegUVIndices.Count)
|
||||
uvIdx = poly.NegUVIndices[i];
|
||||
else if (!useNegSurface && poly.PosUVIndices != null && i < poly.PosUVIndices.Count)
|
||||
else if (poly.PosUVIndices != null && i < poly.PosUVIndices.Count)
|
||||
uvIdx = poly.PosUVIndices[i];
|
||||
|
||||
if (!cellStruct.VertexArray.Vertices.TryGetValue(vertId, out var vertex)) continue;
|
||||
|
|
@ -1536,7 +1551,7 @@ namespace AcDream.App.Rendering.Wb {
|
|||
uvIdx = 0;
|
||||
}
|
||||
|
||||
var key = (vertId, uvIdx, useNegSurface);
|
||||
var key = (vertId, uvIdx, invertNormal);
|
||||
|
||||
if (!hasWrappingUVs) {
|
||||
var uvCheck = vertex.UVs.Count > 0
|
||||
|
|
@ -1553,7 +1568,7 @@ namespace AcDream.App.Rendering.Wb {
|
|||
: Vector2.Zero;
|
||||
|
||||
var normal = Vector3.Normalize(Vector3.TransformNormal(vertex.Normal, transform));
|
||||
if (useNegSurface) {
|
||||
if (invertNormal) {
|
||||
normal = -normal;
|
||||
}
|
||||
|
||||
|
|
@ -1568,18 +1583,18 @@ namespace AcDream.App.Rendering.Wb {
|
|||
polyIndices.Add(idx);
|
||||
}
|
||||
|
||||
if (useNegSurface) {
|
||||
if (reverseWinding) {
|
||||
for (int i = 2; i < polyIndices.Count; i++) {
|
||||
indices.Add(polyIndices[0]);
|
||||
indices.Add(polyIndices[i - 1]);
|
||||
indices.Add(polyIndices[i]);
|
||||
indices.Add(polyIndices[i - 1]);
|
||||
indices.Add(polyIndices[0]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 2; i < polyIndices.Count; i++) {
|
||||
indices.Add(polyIndices[i]);
|
||||
indices.Add(polyIndices[i - 1]);
|
||||
indices.Add(polyIndices[0]);
|
||||
indices.Add(polyIndices[i - 1]);
|
||||
indices.Add(polyIndices[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue