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;