From 9560888868d925f654dccde900a788169f8e9657 Mon Sep 17 00:00:00 2001 From: Felipe Keller Braz Date: Sun, 27 Sep 2026 00:15:06 -0300 Subject: [PATCH 1/2] fix(graphics): correct D3DX row-major matrix operations to restore animated cloud shadows Direct3D 8 and DirectX expect row-vector conventions (v' = v * M) where translation is stored in row 4 (_41, _42, _43). The previous GLM-delegated implementations mapped translation to column 4 (_14, _24, _34) and multiplied matrices in reversed order. This caused terrain cloud animation offsets to be completely dropped when generating texture projection matrices in DXVK, leaving cloud shadows static or invisible. Replaced GLM matrix delegations with direct row-major operations and synchronized d3dx8math headers across Zero Hour and base game. --- Generals/Code/CompatLib/Include/d3dx8math.h | 1 + GeneralsMD/Code/CompatLib/Include/d3dx8math.h | 1 + .../Code/CompatLib/Source/d3dx8math.cpp | 320 ++++++++++-------- docs/WORKLOG/2026-09-DIARY.md | 24 ++ 4 files changed, 199 insertions(+), 147 deletions(-) diff --git a/Generals/Code/CompatLib/Include/d3dx8math.h b/Generals/Code/CompatLib/Include/d3dx8math.h index b3d1b431c46..d66440fbf72 100644 --- a/Generals/Code/CompatLib/Include/d3dx8math.h +++ b/Generals/Code/CompatLib/Include/d3dx8math.h @@ -52,6 +52,7 @@ typedef struct D3DXVECTOR4 #define D3DX_PI 3.141592654f +D3DXMATRIX *WINAPI D3DXMatrixIdentity(D3DXMATRIX *pOut); D3DXMATRIX *WINAPI D3DXMatrixInverse(D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM); D3DXMATRIX *WINAPI D3DXMatrixScaling(D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz); D3DXMATRIX *WINAPI D3DXMatrixTranslation(D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z); diff --git a/GeneralsMD/Code/CompatLib/Include/d3dx8math.h b/GeneralsMD/Code/CompatLib/Include/d3dx8math.h index b3d1b431c46..d66440fbf72 100644 --- a/GeneralsMD/Code/CompatLib/Include/d3dx8math.h +++ b/GeneralsMD/Code/CompatLib/Include/d3dx8math.h @@ -52,6 +52,7 @@ typedef struct D3DXVECTOR4 #define D3DX_PI 3.141592654f +D3DXMATRIX *WINAPI D3DXMatrixIdentity(D3DXMATRIX *pOut); D3DXMATRIX *WINAPI D3DXMatrixInverse(D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM); D3DXMATRIX *WINAPI D3DXMatrixScaling(D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz); D3DXMATRIX *WINAPI D3DXMatrixTranslation(D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z); diff --git a/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp b/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp index baf4c99f9e9..cce5293f669 100644 --- a/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp +++ b/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp @@ -1,147 +1,173 @@ -// GLM_ENABLE_EXPERIMENTAL is passed via target_compile_definitions in CMakeLists.txt. -// Do NOT redefine it here -- that causes -Wmacro-redefined with Clang. - -#ifdef _WIN32 -#include -#else -#include "windows_compat.h" -#endif -#include "d3dx8core.h" - -#include "d3dx8math.h" - -#include -#include - - -static void ConvertGLMToD3DX (const glm::mat4x4 &glm, D3DXMATRIX &d3dx) -{ - d3dx._11 = glm[0][0]; - d3dx._12 = glm[1][0]; - d3dx._13 = glm[2][0]; - d3dx._14 = glm[3][0]; - - d3dx._21 = glm[0][1]; - d3dx._22 = glm[1][1]; - d3dx._23 = glm[2][1]; - d3dx._24 = glm[3][1]; - - d3dx._31 = glm[0][2]; - d3dx._32 = glm[1][2]; - d3dx._33 = glm[2][2]; - d3dx._34 = glm[3][2]; - - d3dx._41 = glm[0][3]; - d3dx._42 = glm[1][3]; - d3dx._43 = glm[2][3]; - d3dx._44 = glm[3][3]; -} - -static void ConvertD3DXToGLM (const D3DXMATRIX &d3dx, glm::mat4x4 &glm) -{ - glm[0][0] = d3dx._11; - glm[1][0] = d3dx._12; - glm[2][0] = d3dx._13; - glm[3][0] = d3dx._14; - - glm[0][1] = d3dx._21; - glm[1][1] = d3dx._22; - glm[2][1] = d3dx._23; - glm[3][1] = d3dx._24; - - glm[0][2] = d3dx._31; - glm[1][2] = d3dx._32; - glm[2][2] = d3dx._33; - glm[3][2] = d3dx._34; - - glm[0][3] = d3dx._41; - glm[1][3] = d3dx._42; - glm[2][3] = d3dx._43; - glm[3][3] = d3dx._44; -} - -D3DXMATRIX *WINAPI D3DXMatrixInverse(D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM) -{ - glm::mat4x4 m; - ConvertD3DXToGLM(*pM, m); - - if (pDeterminant) - *pDeterminant = glm::determinant(m); - - glm::mat4x4 inv = glm::inverse(m); - ConvertGLMToD3DX(inv, *pOut); - - return pOut; -} - -D3DXMATRIX *WINAPI D3DXMatrixScaling(D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz) -{ - glm::mat4x4 m = glm::scale(glm::mat4x4(1.0f), glm::vec3(sx, sy, sz)); - ConvertGLMToD3DX(m, *pOut); - return pOut; -} - -D3DXMATRIX *WINAPI D3DXMatrixTranslation(D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z) -{ - glm::mat4x4 m = glm::translate(glm::mat4x4(1.0f), glm::vec3(x, y, z)); - ConvertGLMToD3DX(m, *pOut); - return pOut; -} - -D3DXMATRIX *WINAPI D3DXMatrixMultiply(D3DXMATRIX *pOut, CONST D3DXMATRIX *pM1, CONST D3DXMATRIX *pM2) -{ - glm::mat4x4 m1, m2; - ConvertD3DXToGLM(*pM1, m1); - ConvertD3DXToGLM(*pM2, m2); - - glm::mat4x4 m = m1 * m2; - ConvertGLMToD3DX(m, *pOut); - return pOut; -} - -D3DXVECTOR4 *WINAPI D3DXVec3Transform(D3DXVECTOR4 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM) -{ - glm::vec4 v(pV->x, pV->y, pV->z, 1.0f); - glm::mat4x4 m; - ConvertD3DXToGLM(*pM, m); - - glm::vec4 result = m * v; - pOut->x = result.x; - pOut->y = result.y; - pOut->z = result.z; - pOut->w = result.w; - return pOut; -} - -D3DXMATRIX *WINAPI D3DXMatrixTranspose(D3DXMATRIX *pOut, CONST D3DXMATRIX *pM) -{ - glm::mat4x4 m; - ConvertD3DXToGLM(*pM, m); - - glm::mat4x4 mTransposed; - mTransposed = glm::transpose(m); - - ConvertGLMToD3DX(mTransposed, *pOut); - return pOut; -} - -D3DXMATRIX *WINAPI D3DXMatrixRotationZ(D3DXMATRIX *pOut, FLOAT Angle) -{ - glm::mat4x4 m = glm::rotate(glm::mat4x4(1.0f), Angle, glm::vec3(0.0f, 0.0f, 1.0f)); - ConvertGLMToD3DX(m, *pOut); - return pOut; -} - -D3DXVECTOR4 *WINAPI D3DXVec4Transform(D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV, CONST D3DXMATRIX *pM) -{ - glm::vec4 v(pV->x, pV->y, pV->z, pV->w); - glm::mat4x4 m; - ConvertD3DXToGLM(*pM, m); - - glm::vec4 result = m * v; - pOut->x = result.x; - pOut->y = result.y; - pOut->z = result.z; - pOut->w = result.w; - return pOut; -} \ No newline at end of file +// GLM_ENABLE_EXPERIMENTAL is passed via target_compile_definitions in CMakeLists.txt. +// Do NOT redefine it here -- that causes -Wmacro-redefined with Clang. + +#ifdef _WIN32 +#include +#else +#include "windows_compat.h" +#endif +#include "d3dx8core.h" + +#include "d3dx8math.h" + +#include +#include + +// GeneralsX @bugfix fbraz 27/09/2026 Direct D3DX row-major matrix operations to fix cloud shadows, UV animation, and transformations + +static void ConvertGLMToD3DX (const glm::mat4x4 &glm, D3DXMATRIX &d3dx) +{ + d3dx._11 = glm[0][0]; + d3dx._12 = glm[1][0]; + d3dx._13 = glm[2][0]; + d3dx._14 = glm[3][0]; + + d3dx._21 = glm[0][1]; + d3dx._22 = glm[1][1]; + d3dx._23 = glm[2][1]; + d3dx._24 = glm[3][1]; + + d3dx._31 = glm[0][2]; + d3dx._32 = glm[1][2]; + d3dx._33 = glm[2][2]; + d3dx._34 = glm[3][2]; + + d3dx._41 = glm[0][3]; + d3dx._42 = glm[1][3]; + d3dx._43 = glm[2][3]; + d3dx._44 = glm[3][3]; +} + +static void ConvertD3DXToGLM (const D3DXMATRIX &d3dx, glm::mat4x4 &glm) +{ + glm[0][0] = d3dx._11; + glm[1][0] = d3dx._12; + glm[2][0] = d3dx._13; + glm[3][0] = d3dx._14; + + glm[0][1] = d3dx._21; + glm[1][1] = d3dx._22; + glm[2][1] = d3dx._23; + glm[3][1] = d3dx._24; + + glm[0][2] = d3dx._31; + glm[1][2] = d3dx._32; + glm[2][2] = d3dx._33; + glm[3][2] = d3dx._34; + + glm[0][3] = d3dx._41; + glm[1][3] = d3dx._42; + glm[2][3] = d3dx._43; + glm[3][3] = d3dx._44; +} + +D3DXMATRIX *WINAPI D3DXMatrixIdentity(D3DXMATRIX *pOut) +{ + if (!pOut) return nullptr; + pOut->m[0][0] = 1.0f; pOut->m[0][1] = 0.0f; pOut->m[0][2] = 0.0f; pOut->m[0][3] = 0.0f; + pOut->m[1][0] = 0.0f; pOut->m[1][1] = 1.0f; pOut->m[1][2] = 0.0f; pOut->m[1][3] = 0.0f; + pOut->m[2][0] = 0.0f; pOut->m[2][1] = 0.0f; pOut->m[2][2] = 1.0f; pOut->m[2][3] = 0.0f; + pOut->m[3][0] = 0.0f; pOut->m[3][1] = 0.0f; pOut->m[3][2] = 0.0f; pOut->m[3][3] = 1.0f; + return pOut; +} + +D3DXMATRIX *WINAPI D3DXMatrixInverse(D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM) +{ + if (!pOut || !pM) return nullptr; + glm::mat4x4 m; + ConvertD3DXToGLM(*pM, m); + + if (pDeterminant) + *pDeterminant = glm::determinant(m); + + glm::mat4x4 inv = glm::inverse(m); + ConvertGLMToD3DX(inv, *pOut); + + return pOut; +} + +D3DXMATRIX *WINAPI D3DXMatrixScaling(D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz) +{ + if (!pOut) return nullptr; + pOut->m[0][0] = sx; pOut->m[0][1] = 0.0f; pOut->m[0][2] = 0.0f; pOut->m[0][3] = 0.0f; + pOut->m[1][0] = 0.0f; pOut->m[1][1] = sy; pOut->m[1][2] = 0.0f; pOut->m[1][3] = 0.0f; + pOut->m[2][0] = 0.0f; pOut->m[2][1] = 0.0f; pOut->m[2][2] = sz; pOut->m[2][3] = 0.0f; + pOut->m[3][0] = 0.0f; pOut->m[3][1] = 0.0f; pOut->m[3][2] = 0.0f; pOut->m[3][3] = 1.0f; + return pOut; +} + +D3DXMATRIX *WINAPI D3DXMatrixTranslation(D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z) +{ + if (!pOut) return nullptr; + pOut->m[0][0] = 1.0f; pOut->m[0][1] = 0.0f; pOut->m[0][2] = 0.0f; pOut->m[0][3] = 0.0f; + pOut->m[1][0] = 0.0f; pOut->m[1][1] = 1.0f; pOut->m[1][2] = 0.0f; pOut->m[1][3] = 0.0f; + pOut->m[2][0] = 0.0f; pOut->m[2][1] = 0.0f; pOut->m[2][2] = 1.0f; pOut->m[2][3] = 0.0f; + pOut->m[3][0] = x; pOut->m[3][1] = y; pOut->m[3][2] = z; pOut->m[3][3] = 1.0f; + return pOut; +} + +D3DXMATRIX *WINAPI D3DXMatrixMultiply(D3DXMATRIX *pOut, CONST D3DXMATRIX *pM1, CONST D3DXMATRIX *pM2) +{ + if (!pOut || !pM1 || !pM2) return nullptr; + D3DXMATRIX temp; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + temp.m[i][j] = pM1->m[i][0] * pM2->m[0][j] + + pM1->m[i][1] * pM2->m[1][j] + + pM1->m[i][2] * pM2->m[2][j] + + pM1->m[i][3] * pM2->m[3][j]; + } + } + *pOut = temp; + return pOut; +} + +D3DXVECTOR4 *WINAPI D3DXVec3Transform(D3DXVECTOR4 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM) +{ + if (!pOut || !pV || !pM) return nullptr; + D3DXVECTOR4 temp; + temp.x = pV->x * pM->m[0][0] + pV->y * pM->m[1][0] + pV->z * pM->m[2][0] + pM->m[3][0]; + temp.y = pV->x * pM->m[0][1] + pV->y * pM->m[1][1] + pV->z * pM->m[2][1] + pM->m[3][1]; + temp.z = pV->x * pM->m[0][2] + pV->y * pM->m[1][2] + pV->z * pM->m[2][2] + pM->m[3][2]; + temp.w = pV->x * pM->m[0][3] + pV->y * pM->m[1][3] + pV->z * pM->m[2][3] + pM->m[3][3]; + *pOut = temp; + return pOut; +} + +D3DXMATRIX *WINAPI D3DXMatrixTranspose(D3DXMATRIX *pOut, CONST D3DXMATRIX *pM) +{ + if (!pOut || !pM) return nullptr; + D3DXMATRIX temp; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + temp.m[i][j] = pM->m[j][i]; + } + } + *pOut = temp; + return pOut; +} + +D3DXMATRIX *WINAPI D3DXMatrixRotationZ(D3DXMATRIX *pOut, FLOAT Angle) +{ + if (!pOut) return nullptr; + FLOAT fSin = sinf(Angle); + FLOAT fCos = cosf(Angle); + + pOut->m[0][0] = fCos; pOut->m[0][1] = fSin; pOut->m[0][2] = 0.0f; pOut->m[0][3] = 0.0f; + pOut->m[1][0] = -fSin; pOut->m[1][1] = fCos; pOut->m[1][2] = 0.0f; pOut->m[1][3] = 0.0f; + pOut->m[2][0] = 0.0f; pOut->m[2][1] = 0.0f; pOut->m[2][2] = 1.0f; pOut->m[2][3] = 0.0f; + pOut->m[3][0] = 0.0f; pOut->m[3][1] = 0.0f; pOut->m[3][2] = 0.0f; pOut->m[3][3] = 1.0f; + return pOut; +} + +D3DXVECTOR4 *WINAPI D3DXVec4Transform(D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV, CONST D3DXMATRIX *pM) +{ + if (!pOut || !pV || !pM) return nullptr; + D3DXVECTOR4 temp; + temp.x = pV->x * pM->m[0][0] + pV->y * pM->m[1][0] + pV->z * pM->m[2][0] + pV->w * pM->m[3][0]; + temp.y = pV->x * pM->m[0][1] + pV->y * pM->m[1][1] + pV->z * pM->m[2][1] + pV->w * pM->m[3][1]; + temp.z = pV->x * pM->m[0][2] + pV->y * pM->m[1][2] + pV->z * pM->m[2][2] + pV->w * pM->m[3][2]; + temp.w = pV->x * pM->m[0][3] + pV->y * pM->m[1][3] + pV->z * pM->m[2][3] + pV->w * pM->m[3][3]; + *pOut = temp; + return pOut; +} \ No newline at end of file diff --git a/docs/WORKLOG/2026-09-DIARY.md b/docs/WORKLOG/2026-09-DIARY.md index 7a72c962fbf..445e31cc31f 100644 --- a/docs/WORKLOG/2026-09-DIARY.md +++ b/docs/WORKLOG/2026-09-DIARY.md @@ -3,6 +3,30 @@ > [!NOTE] > **AI-Generated Content Disclosure**: This worklog is automatically generated and maintained by AI coding agents to document daily progress, debugging sessions, and technical decisions. +## 27/09/2026 +### Fix Cloud Shadows Animation & D3DX Row-Major Matrix Operations +- **Context**: In Command & Conquer: Generals and Zero Hour on Windows, animated cloud shadows scroll across the terrain when the cloud shadow setting (`UseCloudMap`) is enabled. On macOS and Linux, cloud shadows were completely static or invisible despite the option being enabled in `Options.ini` and Game LOD settings. +- **Root Cause**: + - In `GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp`, `D3DXMatrixTranslation`, `D3DXMatrixMultiply`, `D3DXMatrixRotationZ`, `D3DXVec3Transform`, and `D3DXVec4Transform` were implemented via GLM matrix functions assuming OpenGL column-vector conventions ($v' = M \cdot v$). + - In GLM, `glm::translate` places translation offsets $(x, y, z)$ into column 3 (`m[3][0]=x, m[3][1]=y, m[3][2]=z`). When converted to `D3DXMATRIX`, translation was mapped to column 4 (`_14, _24, _34`) instead of row 4 (`_41, _42, _43`) required by DirectX row-vector conventions ($v' = v \cdot M$). + - In addition, `ConvertD3DXToGLM` and `ConvertGLMToD3DX` caused `m1 * m2` in GLM to evaluate $(M_1^T \cdot M_2^T)^T = M_2 \cdot M_1$, reversing matrix multiplication order. + - When `TerrainShader2Stage::updateNoise1` calculated `*destMatrix = *curViewInverse * scale; *destMatrix *= offset;`, the combined matrix had 0 offset in `_41` and `_42`. In DXVK's fixed function vertex shader generator (`d3d9_fixed_function.cpp`), `D3DTS_TEXTURE2` transforms camera-space position into texture coordinates $(u, v)$ using row-major multiplication. Because `_41` and `_42` were zero, the animated sliding offset $(m\_xOffset, m\_yOffset)$ was completely lost. + - `D3DXMatrixRotationZ` also inverted rotation direction in `pointgr.cpp` particle rendering due to column-major vs row-major sine sign mapping. +- **Changes**: + - **Direct Row-Major D3DX Implementations (`GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp`)**: + - Replaced GLM helper delegation in `D3DXMatrixTranslation`, `D3DXMatrixScaling`, `D3DXMatrixMultiply`, `D3DXMatrixTranspose`, `D3DXMatrixRotationZ`, `D3DXVec3Transform`, and `D3DXVec4Transform` with direct, row-major DirectX implementations. + - Implemented `D3DXMatrixIdentity` directly. + - `D3DXMatrixTranslation` explicitly sets `m[3][0] = x`, `m[3][1] = y`, `m[3][2] = z`, `m[3][3] = 1.0f`. + - `D3DXMatrixMultiply` explicitly computes standard row-major multiplication into a temporary matrix to safely handle in-place multiplication (`*destMatrix *= offset`). + - Retained `D3DXMatrixInverse` via GLM since matrix inversion and determinant are algebraic invariants under transpose mapping. + - **Header Synchronization**: + - Added `D3DXMatrixIdentity` declaration to `GeneralsMD/Code/CompatLib/Include/d3dx8math.h` and `Generals/Code/CompatLib/Include/d3dx8math.h` for full parity with base game and `W3DWater.cpp`. +- **Validation**: + - Built `d3dx8` static library cleanly with Clang. + - Built `GeneralsXZH` (Zero Hour) and `GeneralsX` (base game) targets cleanly with exit code 0. + - Deployed binaries and dylibs to `~/GeneralsX/GeneralsZH/` and `~/GeneralsX/Generals/`. + - Executed matrix transform verification suite confirming that `destMatrix` translation row `_41` and `_42` correctly receive scaled world offsets and animated sliding values (`m_xOffset`, `m_yOffset`), whereas old GLM code lost offsets into column 4. + ## 26/09/2026 ### Universal Font Resolution & Bundled Liberation Fonts - **Context**: The game engine previously relied exclusively on system `Fontconfig` on non-Win32 platforms (Linux and macOS) to locate fonts. When system fonts (such as Microsoft's proprietary `Arial`) were missing or Fontconfig configuration was absent (e.g. clean macOS installations, stripped Linux/Flatpak environments, or iOS), font resolution failed and resulted in missing in-game text or startup crashes. From e9d06ba8dd46fba2655d67df4451d78178e8704f Mon Sep 17 00:00:00 2001 From: Felipe Keller Braz Date: Sun, 27 Sep 2026 01:21:16 -0300 Subject: [PATCH 2/2] fix(graphics): use deterministic gamemath trig in D3DXMatrixRotationZ --- GeneralsMD/Code/CompatLib/CMakeLists.txt | 4 ++++ GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp | 17 +++++++++++++++++ docs/WORKLOG/2026-09-DIARY.md | 1 + 3 files changed, 22 insertions(+) diff --git a/GeneralsMD/Code/CompatLib/CMakeLists.txt b/GeneralsMD/Code/CompatLib/CMakeLists.txt index 0466a3d8859..33223963616 100644 --- a/GeneralsMD/Code/CompatLib/CMakeLists.txt +++ b/GeneralsMD/Code/CompatLib/CMakeLists.txt @@ -39,6 +39,10 @@ target_include_directories(d3dx8 PUBLIC ${CMAKE_SOURCE_DIR}/Core/Libraries/Source/WWVegas/WWLib ) +if(SAGE_USE_DETERMINISTIC_MATH) + target_link_libraries(d3dx8 PUBLIC gamemath) +endif() + # Link d3dx8 to d3d8lib to get DirectX8 headers (min-dx8-sdk or DXVK) target_link_libraries(d3dx8 PUBLIC d3d8lib) diff --git a/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp b/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp index cce5293f669..5d6acc3524e 100644 --- a/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp +++ b/GeneralsMD/Code/CompatLib/Source/d3dx8math.cpp @@ -10,6 +10,18 @@ #include "d3dx8math.h" +#if defined(__has_include) && __has_include("gmath.h") +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmacro-redefined" +#endif +#include "gmath.h" +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#define USE_DETERMINISTIC_MATH (1) +#endif + #include #include @@ -150,8 +162,13 @@ D3DXMATRIX *WINAPI D3DXMatrixTranspose(D3DXMATRIX *pOut, CONST D3DXMATRIX *pM) D3DXMATRIX *WINAPI D3DXMatrixRotationZ(D3DXMATRIX *pOut, FLOAT Angle) { if (!pOut) return nullptr; +#if defined(USE_DETERMINISTIC_MATH) + FLOAT fSin = gm_sinf(Angle); + FLOAT fCos = gm_cosf(Angle); +#else FLOAT fSin = sinf(Angle); FLOAT fCos = cosf(Angle); +#endif pOut->m[0][0] = fCos; pOut->m[0][1] = fSin; pOut->m[0][2] = 0.0f; pOut->m[0][3] = 0.0f; pOut->m[1][0] = -fSin; pOut->m[1][1] = fCos; pOut->m[1][2] = 0.0f; pOut->m[1][3] = 0.0f; diff --git a/docs/WORKLOG/2026-09-DIARY.md b/docs/WORKLOG/2026-09-DIARY.md index 445e31cc31f..8f8d440e913 100644 --- a/docs/WORKLOG/2026-09-DIARY.md +++ b/docs/WORKLOG/2026-09-DIARY.md @@ -19,6 +19,7 @@ - `D3DXMatrixTranslation` explicitly sets `m[3][0] = x`, `m[3][1] = y`, `m[3][2] = z`, `m[3][3] = 1.0f`. - `D3DXMatrixMultiply` explicitly computes standard row-major multiplication into a temporary matrix to safely handle in-place multiplication (`*destMatrix *= offset`). - Retained `D3DXMatrixInverse` via GLM since matrix inversion and determinant are algebraic invariants under transpose mapping. + - Updated `D3DXMatrixRotationZ` to utilize deterministic GameMath (`gm_sinf`, `gm_cosf`) when `USE_DETERMINISTIC_MATH` is enabled, linking `gamemath` in `GeneralsMD/Code/CompatLib/CMakeLists.txt` to enforce cross-platform replay determinism. - **Header Synchronization**: - Added `D3DXMatrixIdentity` declaration to `GeneralsMD/Code/CompatLib/Include/d3dx8math.h` and `Generals/Code/CompatLib/Include/d3dx8math.h` for full parity with base game and `W3DWater.cpp`. - **Validation**: