Render: FOG restored -- authentic per-map/time/weather fog now renders (task #63)
User: "aren't some levels supposed to have fog?" -- they were, and none showed. BTDPL.INI (the DPL env INI, L4DPLCFG) authors fog=near far r g b (+ often nosearchlightfog=) on EVERY map/time/WEATHER leaf; weather is a fixed egg field (clear/fog/soup). Shipped eggs pin cavern/night/clear -> page dsnitclear (near 90, far 1100, dark blue). The env pipeline resolved + pushed it to D3D, but it rendered INVISIBLE: D3DRS_FOGTABLEMODE=D3DFOG_LINEAR (table fog) derives its factor from the perspective-NONLINEAR z-buffer, so without WFOG the 90..1100 range collapsed to fog-factor ~1 (no fog everywhere). Fix (L4VIDEO.cpp world pass): auto-detect D3DPRASTERCAPS_WFOG -> per-pixel W-fog (smooth, == the arcade dpl_fog_type_pixel_lin); else VERTEX fog (eye-space but per-vertex -> splotchy on coarse terrain tris, so only a fallback). FOGENABLE + the fog mode are re-asserted each world-pass frame (the old one-time set got clobbered per-pass, which is why plain table fog showed nothing). Verified on this GPU: WFOG present -> per-pixel table fog, user-confirmed smooth. Also corrected task #20's false "shipped maps define no fog" comment (it checked .MAP/.RES, not BTDPL.INI). Env hooks: BT_FOGMODE=table|vertex|off, BT_WEATHER= clear|fog|soup, BT_FOG="near far r g b" (now honored on the authored branch too); [fog] resolved/model diagnostics. KB: rendering.md fog section added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a9c0d2c854
commit
083b148bb0
@@ -52,6 +52,21 @@ DECLOUDS must stay in the sky pass). Authored TEXTURE **SCROLL** (BMF TEXTURE ta
|
||||
`L4DPLCFG`): resolves map+time → clip/fog/ambient + a directional sun. Bring-up stubs
|
||||
(EnsureValidProjection / per-frame ambient / uninit cloud colours) had clobbered it — now they
|
||||
RE-ASSERT the env values. Debug: copy BTDPL.INI → *DBG.INI with `debug=True`. [T2]
|
||||
- **Fog — per map/time/WEATHER (task #63).** `BTDPL.INI` authors `fog=<near> <far> <r> <g> <b>` (+ often
|
||||
`nosearchlightfog=`) on EVERY map/time/weather leaf. WEATHER is a fixed authored egg field
|
||||
(`clear`/`fog`/`soup`, `mission->GetMissionWeather()`; missing = fatal in the arcade); the env pipeline
|
||||
branches location→time→weather to the leaf. Shipped eggs pin `cavern/night/clear` → page `dsnitclear`
|
||||
(near 90, far 1100, dark blue 0.1,0.1,0.12); `fog`→`dsnitfog` (500), `soup`→`dsnitsoup` (300). The
|
||||
arcade fog model is per-PIXEL linear (`dpl_fog_type_pixel_lin`, dpl.h:265). **It rendered INVISIBLE in
|
||||
the port:** `D3DRS_FOGTABLEMODE=D3DFOG_LINEAR` (table fog) reads the perspective-NONLINEAR z-buffer, so
|
||||
without WFOG the authored 90..1100 range collapsed to fog-factor≈1 (no fog). FIX (L4VIDEO.cpp world
|
||||
pass ~8194): auto-detect `D3DPRASTERCAPS_WFOG` → per-pixel W-fog (smooth, == the arcade); else VERTEX
|
||||
fog (eye-space but per-vertex → splotchy on coarse terrain tris); FOGENABLE + mode re-asserted each
|
||||
world-pass frame (the old one-time set got clobbered per-pass). **Task #20's "shipped maps define no
|
||||
fog" was WRONG** — it checked `.MAP`/`.RES`, but fog lives in BTDPL.INI (the DPL env INI), as in the
|
||||
arcade. Night fog is dark BY DESIGN (distance→murk, not light haze); day pages author light haze. Env:
|
||||
`BT_FOGMODE=table|vertex|off`, `BT_WEATHER=clear|fog|soup`, `BT_FOG="near far r g b"` (all branches);
|
||||
`[fog] resolved`/`[fog] model` log lines. [T1 pipeline / T0 arcade model / T2 fix]
|
||||
- **Projection / stencil (task #55):** the BTDPL.INI `viewangle=60` is the **HORIZONTAL** FOV —
|
||||
authentic 60×47 frustum at 4:3; fovY derived via `BTFovYFromHorizontal` (L4VIDEO.cpp), applied at
|
||||
all 5 projection sites (it was mis-applied as vertical). The device depth-stencil is now
|
||||
|
||||
@@ -4697,6 +4697,11 @@ void
|
||||
if(master_notation_file->GetEntry(starting_page_name, "fog" ,&TempStringPtr))
|
||||
{
|
||||
sscanf(TempStringPtr, "%f %f %f %f %f", &fogNear, &fogFar, &fogRed, &fogGreen, &fogBlue);
|
||||
// BT (task #63): BT_FOG="near far r g b" overrides the AUTHORED fog for
|
||||
// tuning/demo (works on the main branch now, not just the no-fog fallback).
|
||||
if (const char *bf = getenv("BT_FOG"))
|
||||
if (!(bf[0]=='0' && bf[1]==0))
|
||||
sscanf(bf, "%f %f %f %f %f", &fogNear, &fogFar, &fogRed, &fogGreen, &fogBlue);
|
||||
searchLightFogRed = fogRed;
|
||||
searchLightFogGreen = fogGreen;
|
||||
searchLightFogBlue = fogBlue;
|
||||
@@ -4709,6 +4714,9 @@ void
|
||||
noSearchLightFogFar = fogFar;
|
||||
currentFogNear = fogNear;
|
||||
currentFogFar = fogFar;
|
||||
DEBUG_STREAM << "[fog] resolved page='" << starting_page_name << "' near=" << fogNear
|
||||
<< " far=" << fogFar << " rgb=(" << fogRed << "," << fogGreen << ","
|
||||
<< fogBlue << ")\n" << std::flush;
|
||||
// Force a 0-0 black fog on startup
|
||||
// dpl_SetViewFog(dplMainView, dpl_fog_type_pixel_lin, 0.0, 0.0, 0.0, 0.01, 0.05);
|
||||
//TODO: for fog testing, just set the values here
|
||||
@@ -4722,13 +4730,15 @@ void
|
||||
else
|
||||
{
|
||||
//
|
||||
// BT port (task #20): the SHIPPED maps define no fog entry at all (verified:
|
||||
// no 'fog' key in any .MAP source nor in BTL4.RES strings) -- so the world
|
||||
// edge and the void beyond are fully visible, which no pod player ever saw
|
||||
// (cockpit FOV + mission design kept views inward). Provide a period-
|
||||
// plausible default curtain: color matched to the horizon so geometry fades
|
||||
// into the void seamlessly. env BT_FOG="near far r g b" overrides;
|
||||
// BT_FOG=0 disables.
|
||||
// CORRECTION (task #63): task #20's claim "the SHIPPED maps define no fog"
|
||||
// was WRONG -- it looked in .MAP/.RES, but the arcade sourced fog from the
|
||||
// DPL env INI (L4DPLCFG = content/BTDPL.INI), which authors a fog= (usually
|
||||
// nosearchlightfog= too) key on EVERY map/time/weather leaf page. The main
|
||||
// (if) branch above handles the real authored fog; this else branch is only
|
||||
// reached by a page that genuinely lacks a fog= key (e.g. a *_default page
|
||||
// whose weather compare is commented out AND has no own fog). Keep a period-
|
||||
// plausible fallback curtain there so such a page still fades to the horizon;
|
||||
// BT_FOG="near far r g b" overrides, BT_FOG=0 disables.
|
||||
//
|
||||
float fN = 150.0f, fF = 520.0f, fR = 0.44f, fG = 0.44f, fB = 0.62f;
|
||||
int fogOn = 1;
|
||||
@@ -5167,7 +5177,11 @@ void
|
||||
}
|
||||
else if(strcmp(target_string,"weather") == 0)
|
||||
{
|
||||
compare_strings[token_count] = mission->GetMissionWeather();
|
||||
// BT (task #63): BT_WEATHER overrides the egg's authored weather so
|
||||
// clear/fog/soup fog curtains can be A/B'd without re-authoring eggs
|
||||
// (the shipped eggs pin weather=clear). Authentic default = the egg.
|
||||
const char *wov = getenv("BT_WEATHER");
|
||||
compare_strings[token_count] = (wov && *wov) ? wov : mission->GetMissionWeather();
|
||||
// std::cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
|
||||
}
|
||||
else if(strcmp(target_string,"scenario") == 0)
|
||||
@@ -8191,6 +8205,47 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
|
||||
}
|
||||
mDevice->SetRenderState(D3DRS_FOGSTART, *((DWORD*)(¤tFogNear)));
|
||||
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(¤tFogFar)));
|
||||
// BT (task #63): the arcade fog was EYE-SPACE linear (dpl_fog_type_pixel_lin).
|
||||
// The port left D3DRS_FOGTABLEMODE = D3DFOG_LINEAR (table/PIXEL fog), whose fog
|
||||
// factor comes from the perspective-NONLINEAR z-buffer -- and without a WFOG cap
|
||||
// FOGSTART/END are z-buffer [0,1] units, so the authored 90..1100 range gave a
|
||||
// fog factor of ~1 (NO fog) across the entire scene: fog was wired but invisible.
|
||||
// Use VERTEX fog: it is computed per-vertex in EYE SPACE, so FOGSTART/END are the
|
||||
// authored world near/far and the haze curtain renders across the real range.
|
||||
// This is the world-geometry pass (SHOULD be fogged; sky/cockpit passes manage
|
||||
// their own). BT_FOGMODE=table = the old z-based mode (A/B); BT_FOGMODE=off.
|
||||
{
|
||||
static const char *s_fm = getenv("BT_FOGMODE");
|
||||
static const int s_fOff = (s_fm && strcmp(s_fm, "off") == 0) ? 1 : 0;
|
||||
// Fog MODEL. The arcade used per-PIXEL linear fog (dpl_fog_type_pixel_lin) --
|
||||
// smooth. D3D fixed-function per-pixel (TABLE) fog is only eye-space (correct
|
||||
// over the authored near/far) when the GPU exposes D3DPRASTERCAPS_WFOG; without
|
||||
// it, table fog reads the NONLINEAR z-buffer and the 90..1100 range collapses
|
||||
// to no fog. Auto-detect: WFOG -> table (smooth, faithful); else VERTEX fog
|
||||
// (eye-space but per-vertex -> splotchy on coarse terrain tris). s_mode:
|
||||
// 1=table, 0=vertex. BT_FOGMODE=table|vertex|off forces.
|
||||
static int s_mode = -1;
|
||||
if (s_mode < 0)
|
||||
{
|
||||
if (s_fm && strcmp(s_fm, "table") == 0) s_mode = 1;
|
||||
else if (s_fm && strcmp(s_fm, "vertex") == 0) s_mode = 0;
|
||||
else
|
||||
{
|
||||
D3DCAPS9 caps; memset(&caps, 0, sizeof(caps));
|
||||
s_mode = (SUCCEEDED(mDevice->GetDeviceCaps(&caps))
|
||||
&& (caps.RasterCaps & D3DPRASTERCAPS_WFOG)) ? 1 : 0;
|
||||
}
|
||||
DEBUG_STREAM << "[fog] model=" << (s_mode ? "table/WFOG (per-pixel, smooth)"
|
||||
: "vertex (per-vertex; splotchy on coarse tris)")
|
||||
<< " (BT_FOGMODE=table|vertex|off overrides)\n" << std::flush;
|
||||
}
|
||||
mDevice->SetRenderState(D3DRS_FOGENABLE, s_fOff ? FALSE : TRUE);
|
||||
if (!s_fOff)
|
||||
{
|
||||
mDevice->SetRenderState(D3DRS_FOGVERTEXMODE, s_mode ? D3DFOG_NONE : D3DFOG_LINEAR);
|
||||
mDevice->SetRenderState(D3DRS_FOGTABLEMODE, s_mode ? D3DFOG_LINEAR : D3DFOG_NONE);
|
||||
}
|
||||
}
|
||||
mDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);
|
||||
mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
|
||||
|
||||
Reference in New Issue
Block a user