diff --git a/Core/GameEngine/Include/Common/BezierSegment.h b/Core/GameEngine/Include/Common/BezierSegment.h index 4e830be328e..a8820f159b3 100644 --- a/Core/GameEngine/Include/Common/BezierSegment.h +++ b/Core/GameEngine/Include/Common/BezierSegment.h @@ -40,7 +40,34 @@ #define USUAL_TOLERANCE 1.0f -#ifndef SAGE_USE_GLM +#ifdef SAGE_USE_GLM +// GeneralsX @bugfix Copilot 15/08/2026 Match D3DX operation ordering for deterministic Bezier trajectories. +class BezierMath +{ +public: + static glm::vec4 GLMVec4Transform(const glm::vec4& v, const glm::mat4& m) + { +#if USE_DETERMINISTIC_MATH + return glm::vec4( + ((v.x * m[0][0] + v.y * m[1][0]) + v.z * m[2][0]) + v.w * m[3][0], + ((v.x * m[0][1] + v.y * m[1][1]) + v.z * m[2][1]) + v.w * m[3][1], + ((v.x * m[0][2] + v.y * m[1][2]) + v.z * m[2][2]) + v.w * m[3][2], + ((v.x * m[0][3] + v.y * m[1][3]) + v.z * m[2][3]) + v.w * m[3][3]); +#else + return m * v; +#endif + } + + static float GLMVec4Dot(const glm::vec4& a, const glm::vec4& b) + { +#if USE_DETERMINISTIC_MATH + return ((a.x * b.x + a.y * b.y) + a.z * b.z) + a.w * b.w; +#else + return glm::dot(a, b); +#endif + } +}; +#else class BezierMath { public: diff --git a/Core/GameEngine/Source/Common/Bezier/BezFwdIterator.cpp b/Core/GameEngine/Source/Common/Bezier/BezFwdIterator.cpp index 42196401c62..6795a7de7e9 100644 --- a/Core/GameEngine/Source/Common/Bezier/BezFwdIterator.cpp +++ b/Core/GameEngine/Source/Common/Bezier/BezFwdIterator.cpp @@ -73,9 +73,9 @@ void BezFwdIterator::start() glm::vec4 pz(mBezSeg.m_controlPoints[0].z, mBezSeg.m_controlPoints[1].z, mBezSeg.m_controlPoints[2].z, mBezSeg.m_controlPoints[3].z); glm::vec4 cVec[3]; - cVec[0] = BezierSegment::s_bezBasisMatrix * px; - cVec[1] = BezierSegment::s_bezBasisMatrix * py; - cVec[2] = BezierSegment::s_bezBasisMatrix * pz; + cVec[0] = BezierMath::GLMVec4Transform(px, BezierSegment::s_bezBasisMatrix); + cVec[1] = BezierMath::GLMVec4Transform(py, BezierSegment::s_bezBasisMatrix); + cVec[2] = BezierMath::GLMVec4Transform(pz, BezierSegment::s_bezBasisMatrix); #endif mCurrPoint = mBezSeg.m_controlPoints[0]; @@ -129,4 +129,3 @@ void BezFwdIterator::next() ++mStep; } - diff --git a/Core/GameEngine/Source/Common/Bezier/BezierSegment.cpp b/Core/GameEngine/Source/Common/Bezier/BezierSegment.cpp index 34c9fd7c437..cb209126e5a 100644 --- a/Core/GameEngine/Source/Common/Bezier/BezierSegment.cpp +++ b/Core/GameEngine/Source/Common/Bezier/BezierSegment.cpp @@ -128,11 +128,11 @@ void BezierSegment::evaluateBezSegmentAtT(Real tValue, Coord3D *outResult) const glm::vec4 yCoords(m_controlPoints[0].y, m_controlPoints[1].y, m_controlPoints[2].y, m_controlPoints[3].y); glm::vec4 zCoords(m_controlPoints[0].z, m_controlPoints[1].z, m_controlPoints[2].z, m_controlPoints[3].z); - glm::vec4 tResult = BezierSegment::s_bezBasisMatrix * tVec; + glm::vec4 tResult = BezierMath::GLMVec4Transform(tVec, BezierSegment::s_bezBasisMatrix); - outResult->x = glm::dot(xCoords, tResult); - outResult->y = glm::dot(yCoords, tResult); - outResult->z = glm::dot(zCoords, tResult); + outResult->x = BezierMath::GLMVec4Dot(xCoords, tResult); + outResult->y = BezierMath::GLMVec4Dot(yCoords, tResult); + outResult->z = BezierMath::GLMVec4Dot(zCoords, tResult); #endif } diff --git a/docs/WORKLOG/2026-08-DIARY.md b/docs/WORKLOG/2026-08-DIARY.md index bc104a77df7..e611b3a7aac 100644 --- a/docs/WORKLOG/2026-08-DIARY.md +++ b/docs/WORKLOG/2026-08-DIARY.md @@ -1,5 +1,10 @@ # August 2026 +## 15/08/2026 +### Match Cross-Platform Bezier Trajectories +- Fixed deterministic projectile paths using GLM's platform-dependent matrix and dot-product evaluation order while Windows used explicitly ordered D3DX-compatible scalar operations. +- Applied the same operation order to GLM transforms and dot products so projectile positions remain bit-identical across macOS and Windows. + ## 14/08/2026 ### Restore Windows LAN Map Compatibility - Kept spaces literal when serializing map names so TheSuperHackers Windows builds can resolve official maps such as Twilight Flame.