using AcDream.App.Tests.Rendering; using AcDream.App.Rendering.Walk; using DatReaderWriter; using DatReaderWriter.Options; using System.Numerics; using System.Text; namespace AcDream.App.Tests.Rendering.Walk; /// /// FW1 look-in gate microscope: for the street fixture pose, dump every /// gate input for building a9b4001a (retail punches it) vs a9b4001e /// (retail does not): raw portal flags, the BSP-emitted polygon's plane, /// the eye's signed distance and side, and the active-view clip count. /// Writes the dump to the scratch directory; always passes. /// [Trait("Lane", "InstalledDat")] public sealed class WalkPortalGateDumpTests { [Fact] public void Dump_the_gate_inputs_for_a_punching_and_a_silent_building() { string? datDir = CornerFloodReplayTests.ResolveDatDir(); if (datDir is null) { Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory."); } IReadOnlyList frames = WalkOracleTrace.Load("posed/holtburg-street-outdoor"); WalkOracleFrame frame = frames[1]; Assert.NotNull(frame.Pose); using var dats = new DatCollection(datDir!, DatAccessType.Read); WalkLandscapeDatBuilder.BuiltWorld world = WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin); var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells) { Buildings = world.Buildings, }; // Install the outdoor default view as the active clip context. var defaultView = new WalkPortalView(); WalkCopyView.AppendFullViewportQuad( defaultView, ctx.Rays, ctx.WorldViewpoint, ctx.ViewportWidth, ctx.ViewportHeight); ctx.SetActiveView(defaultView, 0); var dump = new StringBuilder(); Span clipped = stackalloc WalkScreenPoint[64]; dump.AppendLine($"pose cell={frame.Pose.CellId:x8} origin={frame.Pose.Origin}"); foreach (uint id in new[] { 0xA9B4001Au, 0xA9B4001Eu, 0xA9B40022u, 0xA9B40026u }) { WalkBuilding building = world.Buildings.Keys.Single(b => b.PositionCellId == id); float dist = ctx.ViewerDistanceTo(building); Vector3 eyeInBuilding = ctx.ViewpointInBuilding(building); dump.AppendLine( $"building {id:x8}: dist={dist:f1} eff={MathF.Max(0f, dist - 100f):f1} " + $"eyeLocal={eyeInBuilding} portals={building.Portals.Length}"); WalkBspNode? bsp = building.SelectDrawingBsp(dist); var refs = new List(); Collect(bsp, refs); dump.AppendLine($" level BSP portal refs: {refs.Count}"); foreach (WalkPortalRef portalRef in refs) { ref WalkBldPortal bp = ref building.Portals[portalRef.PortalIndex]; float d = Vector3.Dot(portalRef.Polygon.Plane.Normal, eyeInBuilding) + portalRef.Polygon.Plane.D; int side = d > WalkVisibilityMath.Epsilon ? 0 : d < -WalkVisibilityMath.Epsilon ? 1 : 2; int n = ctx.ClipBuildingPolygon(building, portalRef.Polygon, side, clipped); dump.AppendLine( $" ref idx={portalRef.PortalIndex} other={bp.OtherCellId:x8} " + $"rawSide={bp.PortalSide} exact={bp.ExactMatch} " + $"planeD@eye={d:f2} eyeSide={side} clipN={n} " + $"gate(side==rawSide)={(side == bp.PortalSide ? "PASS" : "reject")}"); } } string path = Path.Combine(Path.GetTempPath(), "fw1-portal-gate-dump.txt"); File.WriteAllText(path, dump.ToString()); Assert.True(true); } private static void Collect(WalkBspNode? node, List into) { if (node is null) return; if (node.InPortals is not null) into.AddRange(node.InPortals); Collect(node.PosNode, into); Collect(node.NegNode, into); } private sealed class NullSink : IWalkEventSink { public void Emit(in WalkEvent walkEvent) { } } /// /// Walkout-F2 microscope: retail floods buildings a9b4001e (×4) and /// a9b40026 (×1) through the cottage's exit views; the replay floods /// neither, while the SAME machinery against the root view passes the /// street fixture. Differential per portal: clip count against the /// full-viewport window vs each exit-view window, plus each window's /// vertices and signed area (winding). /// [Fact] public void Dump_the_exit_view_look_in_inputs_for_walkout_f2() { string? datDir = CornerFloodReplayTests.ResolveDatDir(); if (datDir is null) { Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory."); } var dump = new StringBuilder(); using var dats = new DatCollection(datDir!, DatAccessType.Read); // walkout F2 (retail FLOODS 001e/0026) vs doorway-still F2 (retail // floods NOTHING) — the poses differ by ~1 m and ~5° of yaw. WalkOraclePose[] poses = [ WalkOracleTrace.Load("posed/holtburg-walkout")[2].Pose!, WalkOracleTrace.Load("posed/holtburg-doorway-still")[1].Pose!, ]; foreach (WalkOraclePose pose in poses) { WalkLandscapeDatBuilder.BuiltWorld world = WalkLandscapeDatBuilder.Build(dats, pose.CellId, pose.Origin); var ctx = new WalkTraceReplayContext(pose, world.Cells) { Buildings = world.Buildings, }; WalkCell camera = world.Cells[pose.CellId]; var walk = new RetailFrameWalk(); walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, new NullSink()); WalkPortalView outside = walk.InteriorPView.OutsideView; dump.AppendLine( $"pose cell={pose.CellId:x8} origin={pose.Origin} ov={outside.ViewCount}"); for (int v = 0; v < outside.ViewCount; v++) { WalkViewPoly poly = outside.View.Polys[v]; var verts = new Vector2[poly.VertexCount]; for (int k = 0; k < poly.VertexCount; k++) verts[k] = outside.View.Vertices[poly.VertexIndex + k].Point; dump.AppendLine( $" exit view {v}: n={poly.VertexCount} area={SignedArea(verts):f1} " + $"verts={string.Join(' ', verts.Select(p => $"({p.X:f1},{p.Y:f1})"))}"); } var defaultView = new WalkPortalView(); WalkCopyView.AppendFullViewportQuad( defaultView, ctx.Rays, ctx.WorldViewpoint, ctx.ViewportWidth, ctx.ViewportHeight); { WalkViewPoly rootPoly = defaultView.View.Polys[0]; var rootVerts = new Vector2[rootPoly.VertexCount]; for (int k = 0; k < rootPoly.VertexCount; k++) rootVerts[k] = defaultView.View.Vertices[rootPoly.VertexIndex + k].Point; dump.AppendLine( $" root view: n={rootPoly.VertexCount} area={SignedArea(rootVerts):f1} " + $"verts={string.Join(' ', rootVerts.Select(p => $"({p.X:f1},{p.Y:f1})"))}"); } foreach (uint id in new[] { 0xA9B4001Eu, 0xA9B40026u, 0xA9B4002Fu }) { WalkBuilding building = world.Buildings.Keys.Single(b => b.PositionCellId == id); float dist = ctx.ViewerDistanceTo(building); Vector3 eyeInBuilding = ctx.ViewpointInBuilding(building); WalkBspNode? bsp = building.SelectDrawingBsp(dist); dump.AppendLine( $" building {id:x8}: dist={dist:f1} eff={MathF.Max(0f, dist - 100f):f1} " + $"bsp={(bsp is null ? "NULL" : "selected")} ports={CountPorts(bsp)}"); for (int li = 0; li < building.DegradeLevels.Length; li++) { WalkBuildingDegradeLevel lv = building.DegradeLevels[li]; dump.AppendLine( $" level {li}: min={lv.MinDist:f1} ideal={lv.IdealDist:f1} " + $"max={lv.MaxDist:f1} ports={CountPorts(lv.DrawingBsp)} " + $"idealArmThr={lv.IdealDist:f1} " + $"negMulThr={lv.IdealDist - (lv.IdealDist - lv.MaxDist) * -0.99f:f1}"); } if (bsp is null) continue; var refs = new List(); WalkBuildingPortals.BuildDrawPortalsOnly( bsp, 1, eyeInBuilding, (portalRef, _) => refs.Add(portalRef)); var clipped = new WalkScreenPoint[64]; foreach (WalkPortalRef portalRef in refs) { ref WalkBldPortal bp = ref building.Portals[portalRef.PortalIndex]; float d = Vector3.Dot(portalRef.Polygon.Plane.Normal, eyeInBuilding) + portalRef.Polygon.Plane.D; int side = d > WalkVisibilityMath.Epsilon ? 0 : d < -WalkVisibilityMath.Epsilon ? 1 : 2; ctx.SetActiveView(defaultView, 0); int rootN = ctx.ClipBuildingPolygon( building, portalRef.Polygon, side, clipped); string rootVerts = string.Join( ' ', clipped.Take(rootN).Select(p => $"({p.X:f1},{p.Y:f1},w={p.W:f3})")); var perView = new StringBuilder(); for (int v = 0; v < outside.ViewCount; v++) { ctx.SetActiveView(outside, v); int n = ctx.ClipBuildingPolygon( building, portalRef.Polygon, side, clipped); float area = 0f; for (int k = 0; k < n; k++) { WalkScreenPoint a = clipped[k]; WalkScreenPoint b = clipped[(k + 1) % n]; area += a.X * b.Y - b.X * a.Y; } perView.Append($" view{v}N={n}(area={0.5f * area:f1})"); } dump.AppendLine( $" ref idx={portalRef.PortalIndex} other={bp.OtherCellId:x8} " + $"rawSide={bp.PortalSide} eyeSide={side} " + $"gate={(side == bp.PortalSide ? "PASS" : "REJECT")} " + $"rootN={rootN}{perView}"); if (rootN > 0) dump.AppendLine($" root-clipped: {rootVerts}"); } } } string path = Path.Combine(Path.GetTempPath(), "fw1-walkout-f2-lookin-dump.txt"); File.WriteAllText(path, dump.ToString()); Assert.True(true); } /// /// Foundry-entry F67 microscope: retail (F67+F68, same camera P(F68)) /// orders building a9b40036's floods 116,118,11d; the replay orders /// 11d,116,118. Dump the full BSP traversal at that camera — every /// node's plane, d, arm choice, and emissions — to find the /// order-controlling node and its |d|. /// [Fact] public void Dump_the_foundry_f67_bsp_traversal_for_0036() { string? datDir = CornerFloodReplayTests.ResolveDatDir(); if (datDir is null) { Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory."); } IReadOnlyList frames = WalkOracleTrace.Load("posed/foundry-entry"); WalkOraclePose pose = frames[67].Pose!; // P(F68) = camera of F67 using var dats = new DatCollection(datDir!, DatAccessType.Read); WalkLandscapeDatBuilder.BuiltWorld world = WalkLandscapeDatBuilder.Build(dats, pose.CellId, pose.Origin); var ctx = new WalkTraceReplayContext(pose, world.Cells) { Buildings = world.Buildings, }; WalkBuilding building = world.Buildings.Keys.Single(b => b.PositionCellId == 0xA9B40036u); Vector3 eye = ctx.ViewpointInBuilding(building); WalkBspNode? bsp = building.SelectDrawingBsp(ctx.ViewerDistanceTo(building)); var dump = new StringBuilder(); dump.AppendLine($"camera cell={pose.CellId:x8} origin={pose.Origin}"); dump.AppendLine($"eyeInBuilding={eye} dist={ctx.ViewerDistanceTo(building):f2}"); DumpNode(bsp, eye, building, 0, dump); string path = Path.Combine(Path.GetTempPath(), "fw1-foundry-f67-bsp-dump.txt"); File.WriteAllText(path, dump.ToString()); Assert.True(true); } private static void DumpNode( WalkBspNode? node, Vector3 eye, WalkBuilding building, int depth, StringBuilder dump) { if (node is null) return; string pad = new(' ', depth * 2); if (node.IsFail) { dump.AppendLine($"{pad}FAIL"); return; } float d = Vector3.Dot(node.SplittingPlane.Normal, eye) + node.SplittingPlane.D; int side = d > WalkVisibilityMath.Epsilon ? 0 : d < -WalkVisibilityMath.Epsilon ? 1 : 2; string ports = node.InPortals is null ? "" : " PORT[" + string.Join(',', node.InPortals.Select( p => $"idx{p.PortalIndex}->{building.Portals[p.PortalIndex].OtherCellId:x8}")) + "]"; dump.AppendLine( $"{pad}N=({node.SplittingPlane.Normal.X:f4},{node.SplittingPlane.Normal.Y:f4}," + $"{node.SplittingPlane.Normal.Z:f4}) D={node.SplittingPlane.D:f4} " + $"d={d:f6} side={side}{ports}"); DumpNode(node.PosNode, eye, building, depth + 1, dump); DumpNode(node.NegNode, eye, building, depth + 1, dump); } private static int CountPorts(WalkBspNode? node) => node is null ? 0 : (node.IsPortal ? 1 : 0) + CountPorts(node.PosNode) + CountPorts(node.NegNode); private static float SignedArea(Vector2[] verts) { float sum = 0f; for (int i = 0; i < verts.Length; i++) { Vector2 a = verts[i]; Vector2 b = verts[(i + 1) % verts.Length]; sum += a.X * b.Y - b.X * a.Y; } return 0.5f * sum; } }