One checkbox drives building + EnvCell detail; base texture always drawn; detail is a region-authored ImgTex with its own tiling, modulate-blended over (single-pass multitexture or two-pass fallback). Port sketch included. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.8 KiB
Retail "Environment Detail Textures" — mechanism (decomp-verified 2026-07-10)
Question: does the option apply a texture OVER the original, or a new texture altogether?
Answer: OVERLAY — the base texture is always drawn unchanged; detail is an extra blended layer.
All references are docs/research/named-retail/acclient_2013_pseudo_c.txt line:address or acclient.h:line.
Chain
- Checkbox:
RenderPrefs.EnvironmentDetailTextures(acclient.h:41674-41678) →Render::UpdateFromPreferences(344244:0054d850) →SmartBox::SetDetailTexturing(90961:00451df0) →LScape::SetDetailTexturing(268389:00506b40). - One checkbox drives TWO categories: building (cat 1) AND EnvCell/dungeon (cat 2).
Landscape detail is force-zeroed at the options layer in this Sept-2013 build
(344384:0054d9c8) — vestigial but fully wired at draw time. Object/creature detail is
never enabled;
DrawPartCell(429201:005a07a6) explicitly nullscurr_detail_surface. - Asset: per-terrain-type
detail_tex_gid+detail_tex_tilingauthored in the region dat (acclient.h:52986-52987; read viaTexMerge::GetDetailTex264927:005039e0). Ordinary ImgTex (QDID type 0xb).LScape::GenerateDetailSurface(267853:00506230) wraps it in a custom CSurface (makeCustomSurface(SH_CUSTOMDB)+UseTextureMap,type |= 0x20000=SurfaceType::DETAIL) and publishes toRender::environment/building_detail_surface(setters 342321-342349:0054b7c0+) with its tiling factor.
Draw + blend (base ALWAYS stays)
Per frame RenderDeviceD3D::DrawEnvCell (427885:0059f170) / DrawBuilding (427938:0059f2a0)
stash the category surface into Render::curr_detail_surface with blend
src=9 D3DBLEND_DESTCOLOR / dst=6 INVSRCALPHA (427897-427900 / 427950-427953); landscape path
(DrawBlock 430027:005a17c0, full-LOD side_cell_count==8 only) uses 5/6 SRCALPHA/INVSRCALPHA.
Detail UVs = baseUV × tiling, written as a second texcoord set (landPolyDraw 702299:006b6525,
FVF 0x242/0x252; D3DPolyRender::SetDetailTiling 424834:0059c150 caches in
MeshBuffer.detailTilingFactorSet acclient.h:31954).
Two hardware paths, both compositing over the base:
- Single-pass multitexture (when
bCanDoSinglePassDetailing, caps 428180:0059f6cf):D3DPolyRender::SetSurface(425083:0059c4d0) — stage 0 = BASE tex MODULATE diffuse + ALPHAOP PREMODULATE; stage 1 = DETAIL texTEXOP_BLENDCURRENTALPHAon texcoord set 1 (425109-425115:0059c51c-0059c574);ACRender::SetDetailSurfaceInternal(1)(702183:006b6280) binds detail in stage 1, WRAP+LINEAR. - Two-pass fallback: draw base normally, then rebind detail into stage 0 with
SetBlendFunction(src,dst,ADD)+ AlphaBlendEnable + DEPTHTEST_LESSEQUAL (702199-702204:006b62db) and redraw the same geometry with detail-tiled UVs (landPolyDraw 2nd pass 702352:006b6664;RenderMeshSubset2nd pass 425604:0059cdb3).
Blend math: buildings/EnvCells → final = base × (detail.rgb + (1 − detail.a)) — modulate-style
tint fading to identity where detail alpha→0. Landscape → classic alpha-lerp. Detail suppressed on
clip-mapped (color-keyed) base surfaces (BASE1_CLIPMAP=4 gate, 702215:006b6333).
Open ends
- A 10→50-unit distance fade exists (
get_alpha_for_z) but itsnoFadeDetailglobal inits to 1 (fade DISABLED, alpha forced 0xff) at 1105973:00820e38 with no writer found — as-shipped the detail likely renders full-strength at all distances. A cdb poke on live retail would settle it.
Port sketch for acdream (when we add the option)
Draw the base exactly as today; add detail layer = detailTex sampled at (baseUV × detail_tex_tiling);
combine per category — buildings/EnvCells: base × (detail.rgb + (1−detail.a)); terrain:
lerp(base, detail, detail.a) (vestigial in 2013). One option gates buildings + EnvCells together.