diff --git a/Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.cpp b/Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.cpp new file mode 100644 index 00000000000..29cff123474 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.cpp @@ -0,0 +1,96 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// TheSuperHackers @refactor bobtista 10/04/2026 DX8Backend forwarding adapter. +// Every method in this file is a one-line trampoline to the existing +// DX8Wrapper static API. Keep it that way — if behavior needs to change it +// should change in DX8Wrapper, not here. + +#include "DX8Backend.h" +#include "RenderBackend.h" + +#include "WW3D2/dx8wrapper.h" +#include "WWMath/vector3.h" +#include "WW3D2/lightenvironment.h" + +DX8Backend::DX8Backend() +{ +} + +DX8Backend::~DX8Backend() +{ +} + +IRenderBackend *Create_Render_Backend() +{ + return new DX8Backend(); +} + +void DX8Backend::Set_Gamma(float gamma, float bright, float contrast, bool calibrate, bool uselimit) +{ + DX8Wrapper::Set_Gamma(gamma, bright, contrast, calibrate, uselimit); +} + +void DX8Backend::Begin_Scene() +{ + DX8Wrapper::Begin_Scene(); +} + +void DX8Backend::End_Scene(bool flip_frame) +{ + DX8Wrapper::End_Scene(flip_frame); +} + +void DX8Backend::Flip_To_Primary() +{ + DX8Wrapper::Flip_To_Primary(); +} + +void DX8Backend::Clear(bool clear_color, bool clear_z_stencil, + const Vector3 & color, + float dest_alpha, float z, unsigned int stencil) +{ + DX8Wrapper::Clear(clear_color, clear_z_stencil, color, dest_alpha, z, stencil); +} + +void DX8Backend::Set_Viewport(const RenderBackendViewport & viewport) +{ + D3DVIEWPORT8 vp; + vp.X = viewport.x; + vp.Y = viewport.y; + vp.Width = viewport.width; + vp.Height = viewport.height; + vp.MinZ = viewport.min_z; + vp.MaxZ = viewport.max_z; + DX8Wrapper::Set_Viewport(&vp); +} + +void DX8Backend::Invalidate_Cached_Render_States() +{ + DX8Wrapper::Invalidate_Cached_Render_States(); +} + +void DX8Backend::Set_Ambient(const Vector3 & color) +{ + DX8Wrapper::Set_Ambient(color); +} + +void DX8Backend::Set_Light_Environment(LightEnvironmentClass * light_env) +{ + DX8Wrapper::Set_Light_Environment(light_env); +} diff --git a/Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.h b/Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.h new file mode 100644 index 00000000000..d269194ccea --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.h @@ -0,0 +1,46 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// TheSuperHackers @refactor bobtista 10/04/2026 DX8Backend is the reference +// implementation of IRenderBackend that forwards every virtual method to the +// existing DX8Wrapper static facade. Pure adaptation, no new rendering logic. + +#pragma once + +#include "WW3D2/IRenderBackend.h" + +class DX8Backend : public IRenderBackend +{ +public: + DX8Backend(); + virtual ~DX8Backend() override; + + virtual void Set_Gamma(float gamma, float bright, float contrast, bool calibrate, bool uselimit) override; + + virtual void Begin_Scene() override; + virtual void End_Scene(bool flip_frame) override; + virtual void Flip_To_Primary() override; + virtual void Clear(bool clear_color, bool clear_z_stencil, + const Vector3 & color, + float dest_alpha, float z, unsigned int stencil) override; + virtual void Set_Viewport(const RenderBackendViewport & viewport) override; + virtual void Invalidate_Cached_Render_States() override; + + virtual void Set_Ambient(const Vector3 & color) override; + virtual void Set_Light_Environment(LightEnvironmentClass * light_env) override; +}; diff --git a/Core/Libraries/Source/WWVegas/WW3D2/Backend/RenderBackend.h b/Core/Libraries/Source/WWVegas/WW3D2/Backend/RenderBackend.h new file mode 100644 index 00000000000..94dbe6165d5 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WW3D2/Backend/RenderBackend.h @@ -0,0 +1,30 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// TheSuperHackers @refactor bobtista 10/04/2026 Backend selection seam. The +// build links exactly one backend implementation, and that implementation +// defines Create_Render_Backend. WW3D owns the instance it returns; use +// WW3D::Get_Render_Backend() to reach the active backend. + +#pragma once + +class IRenderBackend; + +// Construct the backend selected by the build. Exactly one backend +// implementation must define this function. +IRenderBackend *Create_Render_Backend(); diff --git a/Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt b/Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt index 4e10ae78d31..68f59638745 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt +++ b/Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt @@ -13,6 +13,9 @@ set(WW3D2_SRC #assetmgr.h assetstatus.cpp assetstatus.h + Backend/DX8Backend.cpp + Backend/DX8Backend.h + Backend/RenderBackend.h bitmaphandler.cpp bitmaphandler.h bmp2d.cpp @@ -90,6 +93,7 @@ set(WW3D2_SRC htree.h #htreemgr.cpp #htreemgr.h + IRenderBackend.h intersec.cpp intersec.h intersec.inl diff --git a/Core/Libraries/Source/WWVegas/WW3D2/IRenderBackend.h b/Core/Libraries/Source/WWVegas/WW3D2/IRenderBackend.h new file mode 100644 index 00000000000..802651802b7 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WW3D2/IRenderBackend.h @@ -0,0 +1,68 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// TheSuperHackers @refactor bobtista 10/04/2026 Abstract W3D-facing rendering +// interface so WW3D2 rendering can be re-targeted to other backends while the +// existing DX8 path stays as the reference implementation. + +#pragma once + +// Forward declarations keep this header includable without pulling in the full +// WW3D2 header graph. All W3D types below are passed by pointer or reference. + +class LightEnvironmentClass; +class Vector3; + +struct RenderBackendViewport +{ + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + float min_z; + float max_z; +}; + +// A method appears here once a caller routes through it, not in anticipation of +// one. The set below is what current callers route through; the rest of the +// DX8Wrapper API stays reachable through DX8Wrapper's static methods until a +// caller migrates, at which point the method it needs moves here. +// +// Method names intentionally match the existing DX8Wrapper names so migrating a +// caller is a mechanical DX8Wrapper::X(...) -> Get_Render_Backend()->X(...) +// rewrite. + +class IRenderBackend +{ +public: + virtual ~IRenderBackend() {} + + virtual void Set_Gamma(float gamma, float bright, float contrast, bool calibrate, bool uselimit) = 0; + + virtual void Begin_Scene() = 0; + virtual void End_Scene(bool flip_frame) = 0; + virtual void Flip_To_Primary() = 0; + virtual void Clear(bool clear_color, bool clear_z_stencil, + const Vector3 & color, + float dest_alpha, float z, unsigned int stencil) = 0; + virtual void Set_Viewport(const RenderBackendViewport & viewport) = 0; + virtual void Invalidate_Cached_Render_States() = 0; + + virtual void Set_Ambient(const Vector3 & color) = 0; + virtual void Set_Light_Environment(LightEnvironmentClass * light_env) = 0; +}; diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp index ba897a753a5..db1cd2707ea 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp @@ -56,6 +56,7 @@ #include "dx8vertexbuffer.h" #include "dx8indexbuffer.h" #include "dx8renderer.h" +#include "IRenderBackend.h" #include "ww3d.h" #include "camera.h" #include "WWLib/wwstring.h" diff --git a/Core/Libraries/Source/WWVegas/WW3D2/ww3d.cpp b/Core/Libraries/Source/WWVegas/WW3D2/ww3d.cpp index 226c8a63c98..31a3f37432e 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/ww3d.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/ww3d.cpp @@ -104,6 +104,8 @@ #include "dazzle.h" #include "meshmdl.h" #include "dx8renderer.h" +#include "Backend/RenderBackend.h" +#include "IRenderBackend.h" #include "render2d.h" #include "WWLib/bound.h" #include "rddesc.h" @@ -172,6 +174,8 @@ float WW3D::PixelCenterX = 0.0f; float WW3D::PixelCenterY = 0.0f; +IRenderBackend * WW3D::RenderBackend = nullptr; + bool WW3D::IsInitted = false; bool WW3D::IsRendering = false; bool WW3D::IsCapturing = false; @@ -278,6 +282,15 @@ WW3DErrorType WW3D::Init(void *hwnd, char *defaultpal, bool lite) if (!DX8Wrapper::Init(_Hwnd, lite)) { return(WW3D_ERROR_INITIALIZATION_FAILED); } + + // TheSuperHackers @refactor bobtista 07/08/2026 The backend object is created + // once here and destroyed in WW3D::Shutdown. + WWASSERT(RenderBackend == nullptr); + if (RenderBackend == nullptr) + { + RenderBackend = Create_Render_Backend(); + } + WWDEBUG_SAY(("Allocate Debug Resources")); Allocate_Debug_Resources(); @@ -364,6 +377,10 @@ WW3DErrorType WW3D::Shutdown() } DX8TextureManagerClass::Shutdown(); + + delete RenderBackend; + RenderBackend = nullptr; + if (!Lite) { DX8Wrapper::Shutdown(); } @@ -849,22 +866,22 @@ WW3DErrorType WW3D::Begin_Render(bool clear,bool clearz,const Vector3 & color, f // If we want to clear the screen, we need to set the viewport to include the entire screen: if (clear || clearz) { - D3DVIEWPORT8 vp; + RenderBackendViewport vp; int width, height, bits; bool windowed; WW3D::Get_Render_Target_Resolution(width, height, bits, windowed); - vp.X = 0; - vp.Y = 0; - vp.Width = width; - vp.Height = height; - vp.MinZ = 0.0f; - vp.MaxZ = 1.0f; - DX8Wrapper::Set_Viewport(&vp); - DX8Wrapper::Clear(clear, clearz, color, dest_alpha); + vp.x = 0; + vp.y = 0; + vp.width = width; + vp.height = height; + vp.min_z = 0.0f; + vp.max_z = 1.0f; + Get_Render_Backend()->Set_Viewport(vp); + Get_Render_Backend()->Clear(clear, clearz, color, dest_alpha, 1.0f, 0); } // Notify D3D that we are beginning to render the frame - DX8Wrapper::Begin_Scene(); + Get_Render_Backend()->Begin_Scene(); return WW3D_ERROR_OK; } @@ -961,7 +978,7 @@ WW3DErrorType WW3D::Render(SceneClass * scene,CameraClass * cam,bool clear,bool // Clear the viewport if (clear || clearz) { - DX8Wrapper::Clear(clear, clearz, color); + Get_Render_Backend()->Clear(clear, clearz, color, 0.0f, 1.0f, 0); } // set the rendering mode @@ -979,7 +996,7 @@ WW3DErrorType WW3D::Render(SceneClass * scene,CameraClass * cam,bool clear,bool // Set the global ambient light value here. If the scene is using the LightEnvironment system // this setting will get overridden. - DX8Wrapper::Set_Ambient(scene->Get_Ambient_Light()); + Get_Render_Backend()->Set_Ambient(scene->Get_Ambient_Light()); // render the scene @@ -1031,7 +1048,7 @@ WW3DErrorType WW3D::Render( // Install the lighting environment if one is supplied if (rinfo.light_environment != nullptr) { - DX8Wrapper::Set_Light_Environment(rinfo.light_environment); + Get_Render_Backend()->Set_Light_Environment(rinfo.light_environment); } // Render the object @@ -1106,8 +1123,8 @@ WW3DErrorType WW3D::End_Render(bool flip_frame) IsRendering = false; { - WWPROFILE("DX8Wrapper::End_Scene"); - DX8Wrapper::End_Scene(flip_frame); + WWPROFILE("IRenderBackend::End_Scene"); + Get_Render_Backend()->End_Scene(flip_frame); } FrameCount++; @@ -1126,7 +1143,7 @@ WW3DErrorType WW3D::End_Render(bool flip_frame) // (gth) I've found some cases where its not safe to rely on our "shadow" copy (of // matrices for example) across multiple frames. So even though this is slightly // less "optimal", lets just reset the caches each frame. - DX8Wrapper::Invalidate_Cached_Render_States(); + Get_Render_Backend()->Invalidate_Cached_Render_States(); return WW3D_ERROR_OK; } @@ -1146,7 +1163,7 @@ WW3DErrorType WW3D::End_Render(bool flip_frame) *=============================================================================================*/ void WW3D::Flip_To_Primary() { - DX8Wrapper::Flip_To_Primary(); + Get_Render_Backend()->Flip_To_Primary(); } @@ -2116,5 +2133,5 @@ void WW3D::Reset_Current_Static_Sort_Lists_To_Default() void WW3D::Set_Gamma(float gamma,float bright,float contrast,bool calibrate) { - DX8Wrapper::Set_Gamma(gamma,bright,contrast,calibrate); + Get_Render_Backend()->Set_Gamma(gamma,bright,contrast,calibrate,true); } diff --git a/Core/Libraries/Source/WWVegas/WW3D2/ww3d.h b/Core/Libraries/Source/WWVegas/WW3D2/ww3d.h index 02207fd8c53..7f993ecd078 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/ww3d.h +++ b/Core/Libraries/Source/WWVegas/WW3D2/ww3d.h @@ -46,6 +46,7 @@ class SceneClass; class CameraClass; class ShaderClass; class DX8Wrapper; +class IRenderBackend; struct RenderStatistics; class FrameGrabClass; @@ -111,6 +112,10 @@ class WW3D static WW3DErrorType Shutdown(); static bool Is_Initted() { return IsInitted; } + // TheSuperHackers @refactor bobtista 18/08/2026 The active rendering backend. + // Created in Init and destroyed in Shutdown, so it is never null in between. + static IRenderBackend * Get_Render_Backend() { return RenderBackend; } + static int Get_Render_Device_Count(); static const char * Get_Render_Device_Name(int device_index); static const RenderDeviceDescClass & Get_Render_Device_Desc(int device = -1); @@ -370,6 +375,8 @@ class WW3D static float PixelCenterX; static float PixelCenterY; + static IRenderBackend * RenderBackend; + static bool IsInitted; static bool IsRendering; static bool IsCapturing;