diff --git a/context/cockpit-view.md b/context/cockpit-view.md index 915c844..4718caa 100644 --- a/context/cockpit-view.md +++ b/context/cockpit-view.md @@ -147,6 +147,19 @@ Raptor / Firestarter / Blackjack have `_COP` meshes, SKLs and gauge configs but names: `avatar bhk1 loki madcat owens sunder thor vulture` (+ variants ava1/lok1/lok2/mad1/mad2/ own1/snd1/thr1/vul1/blkhawk). +## The authentic view field (FOV) — viewangle is HORIZONTAL [T1] + +`BTDPL.INI [dpl_defaults] viewangle=60.0` is the **horizontal** field of view: the original Tesla +call (preserved in the DPLRenderer::SetView comment, L4VIDEO.cpp) is +`dpl_SetViewProjection(view, -1, -aspect, +1, +aspect, 1/tan(viewangle/2))` — horizontal +half-extent 1.0, vertical scaled by aspect. Authentic frustum at 4:3 = **60° × 46.8°**. The +port had fed the 60 to D3DX as the VERTICAL fov (75° × 60° at 4:3, worse on widescreen) — the +world read ~25% farther away ("camera too far back" reports) and the canopy frames were +massively over-revealed. Fixed (`BTFovYFromHorizontal`, all 5 projection sites incl. sky + +resize): the horizontal field stays 60° at any window shape. The aim/pick feed reads the live +projection (`gBTAimP22`) so targeting adapts automatically (combat re-verified). Credit: a +user-community report ("frames were never this revealed") + the resolution/FOV hypothesis. + ## Diagnostics / env gates `BT_FORCE_MODEL=` (force the player mech: madcat/owens/sunder/…; btl4mssn.cpp), diff --git a/engine/MUNGA_L4/L4VIDEO.cpp b/engine/MUNGA_L4/L4VIDEO.cpp index 8eff79e..e9f5361 100644 --- a/engine/MUNGA_L4/L4VIDEO.cpp +++ b/engine/MUNGA_L4/L4VIDEO.cpp @@ -111,6 +111,23 @@ static float gBTAimCamY[3] = {0, 1, 0}; // camera up (LookAtRH yaxis) static float gBTAimCamZ[3] = {0, 0, 1}; // camera BACK (LookAtRH zaxis; view dir = -Z) static float gBTAimP11 = 0.0f; // proj._11 (x scale; carries the aspect) static float gBTAimP22 = 0.0f; // proj._22 (y scale = 1/tan(fovY/2)) + +// AUTHENTIC VIEW FIELD (task #55 FOV fix): the INI 'viewangle' (BTDPL.INI +// [dpl_defaults] viewangle=60.0) is the HORIZONTAL field of view -- the +// original Tesla call (kept in the comment at DPLRenderer::SetView below) is +// dpl_SetViewProjection(view, -1, -aspect, +1, +aspect, 1/tan(viewangle/2)) +// i.e. horizontal half-extent 1.0 at plane distance 1/tan(viewangle/2), with +// the VERTICAL extent scaled by aspect. Our port fed the 60 to D3DX as the +// VERTICAL fov -> ~75x60 deg instead of the pod's 60x46.8 (4:3): the world +// read ~25%% farther away ("camera too far back") and the cockpit canopy was +// massively over-revealed. Derive fovY from the horizontal angle at the live +// aspect so the horizontal field stays the authentic 60 at any window shape. +static float BTFovYFromHorizontal(float viewAngleDeg, float aspect) +{ + if (aspect <= 0.0f) + aspect = 4.0f / 3.0f; + return 2.0f * atanf(tanf(viewAngleDeg * 0.5f * (PI / 180.0f)) / aspect); +} static float gBTAimVpW = 0.0f; // D3D viewport (backbuffer) size -- static float gBTAimVpH = 0.0f; // the dpl2d frame's pixel space static int gBTAimCamValid = 0; @@ -4132,8 +4149,11 @@ void // Use the matching RH projection so the mech becomes visible. // BT (task #20): honor the live window aspect if the window has been resized // (gWindowAspect, top of file; set by L4NotifyWindowResized on WM_SIZE). - D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, viewAngle * (PI/180.0f), - gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size, clipNear, clipFar); + { + const float projAspect = gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size; + D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, + BTFovYFromHorizontal(viewAngle, projAspect), projAspect, clipNear, clipFar); + } //mProjectionMatrix(0, 0) *= -1; mDecalEpsilon = 0.0000005f; @@ -7371,7 +7391,7 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte { appliedAspect = gWindowAspect; D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, - viewAngle * (PI / 180.0f), appliedAspect, clipNear, clipFar); + BTFovYFromHorizontal(viewAngle, appliedAspect), appliedAspect, clipNear, clipFar); mDecalProjectionMatrix = mProjectionMatrix; mDecalProjectionMatrix._33 -= mDecalEpsilon; mDevice->SetTransform(D3DTS_PROJECTION, &mProjectionMatrix); @@ -7669,9 +7689,11 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte mDevice->SetRenderState(D3DRS_FOGSTART, *((DWORD*)(¤tFogNear))); mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(¤tFogFar))); D3DXMATRIX skyProj; - D3DXMatrixPerspectiveFovRH(&skyProj, viewAngle * (PI / 180.0f), - gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size, - clipNear, 9000.0f); + { + const float skyAspect = gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size; + D3DXMatrixPerspectiveFovRH(&skyProj, + BTFovYFromHorizontal(viewAngle, skyAspect), skyAspect, clipNear, 9000.0f); + } mDevice->SetTransform(D3DTS_PROJECTION, &skyProj); } else @@ -8564,8 +8586,8 @@ void << viewAngle << ")\n" << std::flush; return; // projection not built yet; the builder below picks it up } - D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, viewAngle * (PI / 180.0f), - gWindowAspect, clipNear, clipFar); + D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, + BTFovYFromHorizontal(viewAngle, gWindowAspect), gWindowAspect, clipNear, clipFar); mDecalProjectionMatrix = mProjectionMatrix; mDecalProjectionMatrix._33 -= mDecalEpsilon; if (mDevice != NULL) @@ -8611,9 +8633,11 @@ void viewRatio = (float)tan(viewAngle * 0.5f * (PI / 180.0f)); D3DXMatrixIdentity(&mProjectionMatrix); - D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, viewAngle * (PI / 180.0f), - gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size, - clipNear, clipFar); + { + const float fbAspect = gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size; + D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, + BTFovYFromHorizontal(viewAngle, fbAspect), fbAspect, clipNear, clipFar); + } mDecalEpsilon = 0.0000005f; mDecalProjectionMatrix = mProjectionMatrix;