using System.Numerics; namespace AcDream.App.Rendering.Walk; /// /// Campaign FW1 — retail's CPU visibility primitives, ported from the named /// 2013 decomp (extractions: /// docs/research/2026-08-30-fw-flood-pseudocode-appendix.md, report 4; all /// x87-ambiguous branches Ghidra-arbitrated). Two families: /// /// 1. The landscape interval test — a grid corner's vertical column is /// classified against each active clip plane as a signed height /// (Render::get_pt_limit @0x0054b840), and a landblock's z-slab /// is tested against its four corner columns plane by plane /// (corner_plane_check @0x0054b930, block_plane_check /// @0x0054d060, block_check @0x0054dc50). /// 2. The sphere-vs-view test (Render::viewconeCheck @0x0054c250). /// /// Both consume the same installed state: the CY near plane (built by /// Render::update_viewpoint) plus the active view's edge planes /// (installed by Render::set_view). /// public static class WalkVisibilityMath { /// Retail F_EPSILON (raw float 0x3951B717). public const float Epsilon = 0.000199999995f; /// get_pt_limit sky cap: a clip height at or above this means /// the column holds nothing (up-normal arm) / everything (down-normal /// arm). public const float SkyHeight = 1000f; /// Sentinel: the column is wholly inside the plane's positive /// half-space. public const float InsideColumn = 0f; /// Sentinel: the column is wholly outside (static init 1000+1). public const float OutsideColumn = 1001f; /// /// Render::get_pt_limit @0x0054b840: classify the vertical column /// at (x, y) against one plane. Returns / /// , a positive h (inside only BELOW h — /// down-pointing normal), or a negative −h (inside only ABOVE h — /// up-pointing normal). Vertical planes (|N.z| ≤ ε) collapse to /// all-in/all-out via the plane side of (x, y, 0). /// public static float GetPointLimit(float x, float y, in WalkPlane plane) { Vector3 n = plane.Normal; if (n.Z > Epsilon) { float h = -((x * n.X + y * n.Y + plane.D) / n.Z); if (h >= SkyHeight) return OutsideColumn; return h > 0f ? -h : InsideColumn; } if (n.Z < -Epsilon) { float h = -((x * n.X + y * n.Y + plane.D) / n.Z); if (h <= 0f) return OutsideColumn; return h >= SkyHeight ? InsideColumn : h; } // Vertical plane: no z dependence; test point (x, y, 0), retail // Plane::which_side semantics (NEGATIVE = strictly below -epsilon). float d = x * n.X + y * n.Y + plane.D; return d < -Epsilon ? OutsideColumn : InsideColumn; } /// /// Render::get_clip_height @0x0054cff0: fill one corner's /// interval vector — bounds[0] vs the CY plane, bounds[1..edgeCount] vs /// each active edge plane (the loop is INCLUSIVE of edgeCount entries: /// edgeCount + 1 floats are written). /// public static void FillClipHeights( float x, float y, in WalkPlane cyPlane, ReadOnlySpan edgePlanes, Span bounds) { bounds[0] = GetPointLimit(x, y, cyPlane); for (int i = 0; i < edgePlanes.Length; i++) bounds[i + 1] = GetPointLimit(x, y, edgePlanes[i]); } /// /// Render::corner_plane_check @0x0054b930: one corner's encoding /// vs the block z-slab [minZ, maxZ]. Boundary semantics are retail's: /// touching the clip height on the OUT side is Outside; on the IN side /// is EntirelyInside. /// public static WalkBoundingType CornerPlaneCheck(float bound, float minZ, float maxZ) { if (bound == OutsideColumn) return WalkBoundingType.Outside; if (bound != InsideColumn) { if (bound <= 0f) { // inside is z >= h, h = -bound float h = -bound; if (h > minZ) { if (maxZ <= h) return WalkBoundingType.Outside; return WalkBoundingType.PartiallyInside; } } else if (bound < maxZ) { // inside is z <= h, h = bound; the block top pokes above h if (bound <= minZ) return WalkBoundingType.Outside; return WalkBoundingType.PartiallyInside; } } return WalkBoundingType.EntirelyInside; } /// /// Render::block_plane_check @0x0054d060: four corners vs one /// plane. Outside/EntirelyInside require unanimity; anything mixed is /// PartiallyInside. /// public static WalkBoundingType BlockPlaneCheck( float b1, float b2, float b3, float b4, float minZ, float maxZ) { WalkBoundingType c1 = CornerPlaneCheck(b1, minZ, maxZ); WalkBoundingType c2 = CornerPlaneCheck(b2, minZ, maxZ); WalkBoundingType c3 = CornerPlaneCheck(b3, minZ, maxZ); WalkBoundingType c4 = CornerPlaneCheck(b4, minZ, maxZ); if (c1 == WalkBoundingType.Outside) { if (c2 == WalkBoundingType.Outside && c3 == WalkBoundingType.Outside && c4 == WalkBoundingType.Outside) { return WalkBoundingType.Outside; } } else if (c1 == WalkBoundingType.EntirelyInside && c2 == WalkBoundingType.EntirelyInside && c3 == WalkBoundingType.EntirelyInside && c4 == WalkBoundingType.EntirelyInside) { return WalkBoundingType.EntirelyInside; } return WalkBoundingType.PartiallyInside; } /// /// Render::block_check @0x0054dc50: the block-vs-view test over /// four corner interval vectors. Retail's call site passes /// (max_zval, min_zval) and an internal double positional swap hands /// corner_plane_check (min, max) — this port takes (maxZ, minZ) to /// mirror the caller's argument order and performs the same swap, so /// call sites read like the decomp. Any plane with all four corners /// outside culls; any partial plane demotes stickily; EntirelyInside /// requires unanimity on every plane. planeCount = the active view's /// edge count (bounds[0] is the CY plane, tested first with early-out). /// public static WalkBoundingType BlockCheck( ReadOnlySpan corner00, ReadOnlySpan corner01, ReadOnlySpan corner10, ReadOnlySpan corner11, int planeCount, float maxZ, float minZ) { WalkBoundingType result = BlockPlaneCheck( corner00[0], corner01[0], corner10[0], corner11[0], minZ, maxZ); if (result == WalkBoundingType.Outside) return WalkBoundingType.Outside; for (int k = 1; k <= planeCount; k++) { WalkBoundingType r = BlockPlaneCheck( corner00[k], corner01[k], corner10[k], corner11[k], minZ, maxZ); if (r == WalkBoundingType.Outside) return WalkBoundingType.Outside; if (r == WalkBoundingType.PartiallyInside) result = WalkBoundingType.PartiallyInside; } return result; } /// /// Render::viewconeCheck @0x0054c250 (plane-test half): a sphere /// (center already in viewer-block space, radius already scaled) vs the /// CY plane plus the active view's edge planes. Cull is STRICT /// (d < −r); the partial flag is INCLUSIVE (d ≤ r) — a sphere exactly /// tangent from inside counts Partial, not EntirelyInside. The retail /// body's side effects (publishing local_object_center/radius) belong /// to the walk context, not this math. /// public static WalkBoundingType ViewconeCheck( Vector3 center, float radius, in WalkPlane cyPlane, ReadOnlySpan edgePlanes) { float d = Vector3.Dot(cyPlane.Normal, center) + cyPlane.D; if (d < -radius) return WalkBoundingType.Outside; bool partial = d <= radius; foreach (ref readonly WalkPlane plane in edgePlanes) { d = Vector3.Dot(plane.Normal, center) + plane.D; if (d < -radius) return WalkBoundingType.Outside; if (d <= radius) partial = true; } return partial ? WalkBoundingType.PartiallyInside : WalkBoundingType.EntirelyInside; } } /// Retail Plane: dot(N, p) + d, positive side = inside. public readonly record struct WalkPlane(Vector3 Normal, float D); /// Retail BoundingType, values used raw by the walk. public enum WalkBoundingType { Outside = 0, PartiallyInside = 1, EntirelyInside = 2, }