diff --git a/MUNGA_L4/L4VIDEO.cpp b/MUNGA_L4/L4VIDEO.cpp index 085972e..ae38013 100644 --- a/MUNGA_L4/L4VIDEO.cpp +++ b/MUNGA_L4/L4VIDEO.cpp @@ -1802,6 +1802,7 @@ DPLRenderer::DPLRenderer( // the next race re-reads viewangle from RPDPL.INI. mPresentationCamera = False; mPresentationFog = False; + mPresentationAspect = 0.0f; D3DXMatrixIdentity(&mPresentationView); dplMainView = NULL; dplDeathZone = NULL; @@ -2374,6 +2375,20 @@ void // tells the end-of-mission fade to stand down mPresentationFog = True; + // + // Crop to the shape the stand was composed for. 4:3 unless + // RP412PODIUMASPECT says otherwise; 0 turns it off and lets + // the podium run full width. + // + mPresentationAspect = 4.0f / 3.0f; + { + const char *aspect = getenv("RP412PODIUMASPECT"); + if (aspect != NULL) + { + mPresentationAspect = (float) atof(aspect); + } + } + // unconditional: fogUpdating is off while a vehicle drives its own // headlight fog, and the podium overrides all of that if (mDevice != NULL) @@ -6094,6 +6109,36 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte lastFrameTime = mTargetRenderTime; currentFrameTime = Now(); + // + // Pillarbox: black the whole target first, then narrow the viewport so + // everything after this - clear, scene, reticle - lands inside the crop + // and the surround stays black. Restored after Present. + // + if (mPresentationAspect > 0.0f) + { + D3DVIEWPORT9 full; + full.X = 0; + full.Y = 0; + full.Width = mPresentParams.BackBufferWidth; + full.Height = mPresentParams.BackBufferHeight; + full.MinZ = 0.0f; + full.MaxZ = 1.0f; + mDevice->SetViewport(&full); + mDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); + + DWORD cropped = (DWORD) + (mPresentParams.BackBufferHeight * mPresentationAspect); + if (cropped > mPresentParams.BackBufferWidth) + { + cropped = mPresentParams.BackBufferWidth; + } + + D3DVIEWPORT9 crop = full; + crop.Width = cropped; + crop.X = (mPresentParams.BackBufferWidth - cropped) / 2; + mDevice->SetViewport(&crop); + } + DWORD currentFog; mDevice->GetRenderState(D3DRS_FOGCOLOR, ¤tFog); hr = mDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, currentFog, 1.0f, 0); @@ -6253,6 +6298,19 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte hr = mDevice->Present(NULL, NULL, gMainPresentWindow, NULL); + // hand the whole target back + if (mPresentationAspect > 0.0f) + { + D3DVIEWPORT9 full; + full.X = 0; + full.Y = 0; + full.Width = mPresentParams.BackBufferWidth; + full.Height = mPresentParams.BackBufferHeight; + full.MinZ = 0.0f; + full.MaxZ = 1.0f; + mDevice->SetViewport(&full); + } + // // RP412RENDERDIAG=1: did the ending frame actually reach the window, and // what colour did it clear to? A black clear with a blue-violet fog set @@ -6889,11 +6947,18 @@ void DPLRenderer::SetViewAngle(Degree new_angle) viewRatio = (float) tan(view_angle / 2.0f); aspectRatio = (float) y_size / (float) x_size; + // pillarboxed shots render into a narrower viewport, so the projection + // has to use that shape or the scene comes out squashed rather than + // cropped + float projection_aspect = (mPresentationAspect > 0.0f) + ? mPresentationAspect + : ((float) x_size / (float) y_size); + D3DXMatrixIdentity(&mProjectionMatrix); D3DXMatrixPerspectiveFovLH( &mProjectionMatrix, viewAngle * (PI / 180.0f), - (float) x_size / (float) y_size, + projection_aspect, clipNear, clipFar); mProjectionMatrix(0, 0) *= -1; // handedness flip - the view is RH diff --git a/MUNGA_L4/L4VIDEO.h b/MUNGA_L4/L4VIDEO.h index 7ea6f04..4a1726c 100644 --- a/MUNGA_L4/L4VIDEO.h +++ b/MUNGA_L4/L4VIDEO.h @@ -468,6 +468,11 @@ private: D3DXMATRIX mPresentationView; Logical mPresentationCamera; Logical mPresentationFog; + // >0 pillarboxes the scene to this aspect. The Winners Circle was + // built to be looked at from a 4:3 pod monitor and its platform runs + // out at the sides of a 16:9 canvas, so the shot is cropped to the + // shape it was composed for and the surround left black. + float mPresentationAspect; void FindBestAdapterIndices(bool isWindowed); diff --git a/RP_L4/RPL4APP.cpp b/RP_L4/RPL4APP.cpp index 9e97a91..3a819b5 100644 --- a/RP_L4/RPL4APP.cpp +++ b/RP_L4/RPL4APP.cpp @@ -246,11 +246,16 @@ void // RP412PODIUMHEIGHT eye height above the group // RP412PODIUMAIM height of the aim point above the group // - // framed off the stand itself: far enough back for all eight bays and - // the numbers above them, high enough to look down the tiers - Scalar standoff = 45.0f; - Scalar height = 18.0f; - Scalar aim_lift = 2.0f; + // + // Framed off the stand itself: close enough that the platform fills the + // crop rather than trailing off into sky, high enough to look down the + // tiers, and aimed slightly below the group so the tilt buys back the + // dead sky above the grandstand. Aiming above the group tips the camera + // up and walks the winner's spot off the bottom of the frame. + // + Scalar standoff = 33.0f; + Scalar height = 20.0f; + Scalar aim_lift = -2.0f; const char *tune = getenv("RP412PODIUMSTANDOFF"); if (tune != NULL && atof(tune) != 0.0) standoff = (Scalar) atof(tune); tune = getenv("RP412PODIUMHEIGHT");