acdream/docs/research/2026-08-28-issue178-cell-shell-culling.md
Erik f7aa8e0eb7
All checks were successful
CI / linux-portable (push) Successful in 3m41s
CI / windows-gate (push) Successful in 6m49s
CI / release (push) Successful in 3m22s
fix: complete retail parity stability pass
2026-08-28 20:01:39 +02:00

3.1 KiB

Issue #178 — retail EnvCell shell culling (2026-08-28)

Outcome

The Phase A8 CullMode.Landblock -> CullMode.None workaround is removed from both production EnvCell draw paths. Constructed cell-shell batches now use retail's clockwise cull state instead of drawing every ordinary shell face twice. No PAK rebuild is required: the package already contains the authored sides_type and the correctly expanded index geometry; this change selects the correct GPU state when that geometry is drawn.

The source and installed-DAT gates are complete. The owner visual gate passed 2026-08-28 (“Ok looks good”) at the requested interior matrix; #178 is closed.

Retail oracle

The misleading detail is that DatReaderWriter.Enums.CullMode is used for the CellStruct polygon's sides_type; its values are not direct GPU cull states.

  • D3DPolyRender::ConstructMesh @ 0x0059DFA0 expands sides_type 0 as the positive face, type 1 as that face plus a reversed copy, and type 2 as the positive and negative surfaces. Its polygon fan is [0, i-1, i]; the reversed copy is [i, i-1, 0].
  • D3DPolyRender::RenderMeshSubset @ 0x0059CA10 draws the constructed mesh with D3DCULL_CW on the ordinary path.
  • RenderDeviceD3D::DrawEnvCell @ 0x0059F170 uses that constructed-mesh route. The immediate-mode exception for a type-1 polygon does not apply to the EnvCell mesh.

MeshExtractor.PrepareCellStructMeshData already reproduces retail's fan and the required reversed geometry. The render policy therefore must be clockwise culling for every constructed shell batch; mapping DAT value 0 to None was the obsolete workaround, while mapping it to the generic Landblock render state would cull the opposite side.

Installed-DAT catalog

A8CellAudit cell-winding-catalog scanned the installed DATs:

  • 772 environments and 3,168 CellStructs
  • 38,189 polygons and 70,091 generated fan triangles
  • 37,843 Landblock(0) polygons and 346 None(1) polygons
  • no unknown or unsupported sides_type values
  • no missing polygon vertices

Vertex-normal orientation was recorded as a diagnostic, not treated as a contract: CellStruct vertex normals may be smoothed rather than geometric, and retail submits the identical authored fan.

Implementation and gates

  • EnvCellRenderer.ResolveRetailCellShellCullMode documents and enforces the constructed-mesh policy in both the main and shadow-receiver draw paths.
  • Hermetic extraction tests pin the exact type-0 fan and type-1 reversed-face expansion.
  • Renderer tests pin all four source enum values to the retail clockwise constructed-mesh state.
  • Installed-DAT audit: passed.
  • Canonical Release gate: 16,321 passed, 0 skipped, 0 failed across 14 test assemblies; Release build completed with 0 warnings and 0 errors.

Owner visual acceptance

In Holtburg buildings and the Facility Hub, rotate the camera through walls, floors, ceilings, ramps, and stairs from their ordinary playable sides. Nothing should vanish at any camera angle. Acceptance of that matrix closes #178. The owner accepted this matrix on 2026-08-28 (“Ok looks good”).