10 KiB
OH1 evidence — retail alpha classification and list contract
Campaign: OVERHAUL / OH1
Status: evidence complete; behavior belongs to OH7
Retail build: September 2013 End of Retail
Arbitration: named-retail pseudo-C plus Ghidra/disassembly for field offsets
This note closes the bounded alpha ambiguity left by the first OH1 synthesis. It is an executable contract for OH7, not permission to change alpha order before OH5 restores the landscape/building/object walk that owns the barriers.
1. Retail anchors
| Retail symbol | Address | Contract established here |
|---|---|---|
D3DPolyRender::ConstructMesh |
0x0059DFA0 |
initial/final subset mask |
D3DPolyRender::DrawMesh |
0x0059D4A0 |
immediate versus delayed branch table |
D3DPolyRender::AddMeshToAlphaList |
0x0059C230 |
list selection, capacity, entry state |
D3DPolyRender::FlushAlphaList |
0x0059D2E0 |
threshold and drain order |
D3DPolyRender::RenderMeshSubset |
0x0059CA10 |
delayed replay/detail behavior |
D3DPolyRender::SetSurface |
0x0059C4D0 |
clip-map override behavior |
D3DPolyRender::Begin |
0x0059EAD0 |
delay-mask registry binding |
RenderDeviceD3D::DrawEnvCell |
0x0059F170 |
environment-detail owner |
RenderDeviceD3D::DrawBuilding |
0x0059F2A0 |
building-detail owner and barrier |
RenderDeviceD3D::DrawPartCell |
0x005A07A0 |
ordinary-object detail reset |
RenderDeviceD3D::DrawBlock |
0x005A18D0 |
per-land-cell emergency barrier |
PView::DrawCells |
0x005A4840 |
post-landscape full barrier |
SmartBox::RenderNormalMode |
0x00453AA0 |
top-level full barrier |
LScape::CleanupDetailSurfaces |
0x00504AE0 |
detail-surface release/reset |
LScape::SetDetailTexturing |
0x00506B40 |
detail-surface regeneration |
LScape::ChangeRegion |
0x00506CB0 |
detail preference application |
GameSky::Draw |
0x00506FF0 |
m_currentlyDrawingSky producer |
Render::SetOverallGraphicsQuality |
0x0054B020 |
MultiPassAlpha quality defaults |
Render::Startup |
0x0054E9B0 |
preference registration/safe override |
The named-retail pseudo-C establishes the control flow. Ghidra and instruction
disassembly arbitrate the two facts for which a wrong field name changes the
branch meaning: the renderer byte at +0x7E0 is
m_currentlyDrawingSky, while material-alpha mode is at +0x794; and polygon
stippling is compared as a signed byte.
2. Exact constructed subset mask
For each source surface, ConstructMesh chooses exactly one initial mask by
this priority:
if (Surface.Type & 0x00010300) != 0: mask = 0x02
else if (Surface.Type & 0x00000004) != 0: mask = 0x08
else if (Surface.Type & 0x00000010) != 0: mask = 0x04
else: mask = 0x00
0x00010300 is the union of ALPHA (0x100), INVALPHA (0x200), and
ADDITIVE (0x10000). The order is material: a surface carrying both an
alpha-family bit and BASE1_CLIPMAP receives mask 0x02, not 0x08.
For each polygon, retail then ORs bit 0x01 into the positive surface's
mask only when (signed char)polygon.stippling > 0. Raw bytes 0x80..0xFF
therefore do not set the bit. The complete constructed mask domain is:
| Surface class | Without positive stippling | With positive stippling |
|---|---|---|
| plain | 0x00 |
0x01 |
| alpha / inverse-alpha / additive | 0x02 |
0x03 |
| translucent | 0x04 |
0x05 |
| clip map | 0x08 |
0x09 |
Bit 0x01 is absent from the registry help text, but a configured delay mask
still intersects it normally. It is a retail quirk, not a flag to normalize.
3. Delay-mask and preference state
s_AlphaDelayMask @ 0x00820D88 has static default 0x0E. Its only behavior
read is in DrawMesh; D3DPolyRender::Begin exposes it as
RenderD3D.AlphaDelayMask with help for Alpha=2, Translucent=4, and
ClipMap=8. The registry may replace the value. Unsupported higher bits can
keep the outer delay gate nonzero but never intersect a constructed mask.
m_RenderPrefs.MultiPassAlpha @ 0x0081EF96 has static default false.
SetOverallGraphicsQuality selects false for quality levels 1–3 and true for
levels 4–5. Startup registers the user preference, and the safe-render branch
forces it false. DrawMesh is its sole draw-path reader;
DetermineOverallGraphicsQuality also reads it while deriving the aggregate
quality setting.
The building and environment detail globals default null. Their only writers
are SetBuildingDetailSurface and SetEnvironmentDetailSurface; region
cleanup clears/releases them, and region detail setup regenerates both from
the EnvironmentDetailTextures preference. Generation may still yield null.
4. Exact DrawMesh branch table
After the subset passes its ordinary draw-eligibility gate, apply these rows in order:
| Priority | Condition | Result |
|---|---|---|
| 1 | m_currentlyDrawingSky or delay mask is zero or curr_detail_surface != null |
render immediately; enqueue nothing |
| 2 | MultiPassAlpha && (subsetMask & 0x08) != 0 |
append to CLIP with overrideClipmap=true, then also render immediately with overrideClipmap=false |
| 3 | (delayMask & subsetMask) != 0 |
append to CLIP iff bit 0x08 is set, otherwise ALPHA; no immediate draw |
| 4 | (delayMask & 0x04) != 0 && currentMaterial != null && currentMaterial.has_alpha != 0 |
append to ALPHA; no immediate draw |
| 5 | otherwise | render immediately |
Row 2 depends on the global delay mask being nonzero because it is inside the
outer delay gate, but it does not require delay-mask bit 0x08. It is the
clip-map base pass now plus an alpha-blended second pass later.
Under default delay mask 0x0E:
- masks
0x02/0x03and0x04/0x05enter ALPHA; - masks
0x08/0x09enter CLIP, with the additional immediate pass whenMultiPassAlphais true; - masks
0x00/0x01are immediate unless the material-alpha fallback applies.
overrideClipmap=false lets SetSurface enable the clip-map alpha-test arm.
overrideClipmap=true selects the alpha-blend arm and suppresses that clip-map
alpha-test arm. Immediate rendering passes
detailEnabled=(curr_detail_surface != null); delayed replay always passes
detailEnabled=false.
5. List entry, capacity, and replay
Retail owns two independent static arrays of 3000 AlphaListEntry records and
two uint16 counts. AddMeshToAlphaList appends strictly FIFO; it performs no
distance or material sort.
Each DrawMesh invocation owns two independent first-for-this-list flags.
The first subset appended to each list captures the current CMaterial and
object matrix and sets bNewObject=true. Later entries for that list inherit
that state during replay. The entry also stores overrideClipmap; it stores no
portal view, clip planes, cell, or part identity.
When a list is already at capacity, append returns false. A non-multipass delayed subset has no immediate fallback draw. Exact capacity behavior must be preserved and tested rather than silently expanded or recovered differently.
FlushAlphaList(threshold) returns false without changing either list only
when both counts are below threshold * 3000. Otherwise it drains the
entire CLIP list in append order, resets it, then drains the entire ALPHA list
in append order and resets it. It restores the object matrix after replay; it
does not restore the material. The DrawBlock threshold is the immutable global
0.75, so equality at 2250 in either list triggers a full two-list drain.
6. Detail owner consequences
DrawEnvCellinstalls the environment detail surface, calls the builtDrawMesh(skipNoTexture=true)path, then clears the current detail. A non-null environment detail makes every eligible subset immediate.DrawBuildinginstalls building detail, performsFlushAlphaList(0)before portal/part/shell work, then draws its forced portal-BSP pass and normal built shell. A non-null building detail makes the shell immediate.DrawBuildingLeafpreserves outer state, butDrawPartCellexplicitly forces current detail null. Ordinary leaf parts can therefore enqueue even when their containing building shell uses detail.- Ordinary object-cell GfxObj paths do not install the separate object detail surface; their current detail is normally null.
7. Exact normal-world flush barriers
DrawBuildingcallsFlushAlphaList(0)after installing building detail and before any building portal/leaf/shell draw. Delayed replay itself has detail disabled.DrawBlockvisits each admitted land cell asDrawLandCellfollowed byDrawSortCell(building, then objects), then callsFlushAlphaList(0.75). This is normally an emergency capacity drain.PView::DrawCellscallsFlushAlphaList(0)after outside landscape and before the mid-frame stamp/depth-clear/interior repaint block.SmartBox::RenderNormalModecallsFlushAlphaList(0)after the complete world path.
The fifth retail caller is the private CreatureMode::Render @ 0x004529D0
(call site 0x00452BF0). After DrawObjCellForDummies(creature_cell) and its
frame pop, that private presentation mode calls FlushAlphaList(0). It is not
part of the normal-world OH5/OH7 ordering cutover, but it must remain an exact
consumer of the shared two-list implementation.
Consequently, OH7 cannot be implemented faithfully while current acdream still stages all terrain before all building/object turns. OH5 must first restore the per-cell landscape interleave on which these barriers operate.
8. Required OH7 cutover
Current RetailAlphaQueue is a single scope-global, viewer-distance-sorted
stream with an approximate FlushFartherThan. Retail instead requires:
- separate CLIP and ALPHA FIFO lists;
- no queue-level sort;
- preservation of upstream cell order,
CShadowPartorder, and authored subset order; - raw surface type plus the exact constructed mask and positive-surface stippling ownership;
overrideClipmapand independent per-list object-boundary state;- the multipass clip-map immediate-plus-delayed duplicate;
- material
has_alphafallback; - sky/detail immediate gates and delayed
detailEnabled=false; - exact capacity and threshold behavior;
- full drains at the exact building, landscape, and top-level barriers.
Runtime state remains configurable: registry may override delay mask, the user preference may override MultiPassAlpha, and detail generation may return null. Those are input states to the exact branch table, not unresolved branch sense.