research(render): Phase U.4c-1 — characterize the flap on real dat evidence
A8CellAudit portals dump extended to print per-portal plane + centroid-derived InsideSide vs the dat's authored PortalSide. Real Holtburg cottage cells show: the flap is a DIRECT 0xA9B40171->0xA9B40170 portal side-test flip (0170 is a direct neighbour, not multi-hop), and our centroid-derived InsideSide is anti-correlated with the dat PortalSide that retail InitCell (432896) uses. Evidence selects H2 (port the side test) over H1 (PVS set-grounding). Camera cell 0171 seenOutside=Y. Full reading + fix direction + open sign question in the note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
639f20fa8a
commit
13d58cae6a
2 changed files with 127 additions and 0 deletions
|
|
@ -189,9 +189,46 @@ static void DumpCellPortals(DatCollection dats, uint envCellId)
|
|||
(missing.Count > 0 ? $" MISSING=[{string.Join(",", missing)}]" : "");
|
||||
}
|
||||
|
||||
// U.4c characterization: compute the plane + centroid-derived InsideSide
|
||||
// EXACTLY as GameWindow.BuildLoadedCell does, and compare its traversal
|
||||
// SENSE against the dat's authored PortalSide flag (retail portal_side).
|
||||
// If they disagree for a portal the camera should see through, that is the
|
||||
// flap's root: our centroid guess culls where retail's authored bit traverses.
|
||||
string sideText = "no-cellStruct";
|
||||
if (cellStruct is not null && cellStruct.Polygons.TryGetValue(portal.PolygonId, out var sp)
|
||||
&& sp.VertexIds.Count >= 3
|
||||
&& TryGetOrigin(cellStruct, (ushort)sp.VertexIds[0], out var s0)
|
||||
&& TryGetOrigin(cellStruct, (ushort)sp.VertexIds[1], out var s1)
|
||||
&& TryGetOrigin(cellStruct, (ushort)sp.VertexIds[2], out var s2))
|
||||
{
|
||||
var n = Vector3.Cross(s1 - s0, s2 - s0);
|
||||
n = n.LengthSquared() > 0f ? Vector3.Normalize(n) : Vector3.Zero;
|
||||
float d = -Vector3.Dot(n, s0);
|
||||
// Cell centroid = AABB center over all cellStruct verts (matches BuildLoadedCell).
|
||||
var mn = new Vector3(float.MaxValue); var mx = new Vector3(float.MinValue);
|
||||
foreach (var v in cellStruct.VertexArray.Vertices.Values)
|
||||
{ mn = Vector3.Min(mn, v.Origin); mx = Vector3.Max(mx, v.Origin); }
|
||||
var centroid = (mn + mx) * 0.5f;
|
||||
float centroidDot = Vector3.Dot(n, centroid) + d;
|
||||
int ourInsideSide = centroidDot >= 0 ? 0 : 1; // GameWindow.cs:5648
|
||||
int datPortalSide = portal.Flags.HasFlag(PortalFlags.PortalSide) ? 1 : 0;
|
||||
// Our test traverses when the camera is on the centroid's side of the plane.
|
||||
// Retail (InitCell 432962) traverses when computed-sidedness != portal_side,
|
||||
// where sidedness==1 means "in front" (dot>eps). At the centroid our test
|
||||
// always traverses; the meaningful question is whether the centroid side
|
||||
// (front if centroidDot>0) matches retail's traverse-sense for this portal.
|
||||
int centroidSidedness = centroidDot > 0 ? 1 : 0; // 1=front
|
||||
bool retailTraversesAtCentroid = centroidSidedness != datPortalSide;
|
||||
sideText =
|
||||
$"N=({n.X:F2},{n.Y:F2},{n.Z:F2}) d={d:F2} centroidDot={centroidDot:F3} " +
|
||||
$"ourInsideSide={ourInsideSide} datPortalSide={datPortalSide} " +
|
||||
$"retailTraversesAtCentroid={(retailTraversesAtCentroid ? "Y" : "n")}";
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
$" portal[{i}] other=0x{portal.OtherCellId:X4} -> {dest} " +
|
||||
$"flags={portal.Flags} polyId={portal.PolygonId} | {resolveText}");
|
||||
Console.WriteLine($" SIDES: {sideText}");
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue