From 21acf53d3f67f345235e97fccee4e19454d39edc Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 18 Sep 2019 15:22:02 -0700 Subject: [PATCH] improve vector operations when using implicit conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It used to be that operations e.g. like: float3{} + double{} would be computed as float3{} + float3{double{}} instead of float3{} + double3{double{}} I other words, when an implicit conversion was involved on the right it would be converted to the left side’s type, possibly losing precision. Another problem was that swiping the operands could produce different Results, e.g.: float3{1} * 5.0 -> float3{5.0f} 5.0 * float3{1} -> double3{5.0} This is no longer the case, now both expressions would return a double3. Note: float3 r{}; r *= 5; Is now equivalent to: r[0] *= 5; r[1] *= 5; r[2] *= 5; Instead of before: r[0] *= 5.0f; r[1] *= 5.0f; r[2] *= 5.0f; --- libs/math/include/math/TVecHelpers.h | 128 ++++++++++----------------- libs/math/tests/test_vec.cpp | 5 +- 2 files changed, 51 insertions(+), 82 deletions(-) diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h index 03867d8b2b..405d87dfb5 100644 --- a/libs/math/include/math/TVecHelpers.h +++ b/libs/math/include/math/TVecHelpers.h @@ -79,8 +79,8 @@ public: /* compound assignment from a another vector of the same size but different * element type. */ - template - constexpr VECTOR& operator+=(const VECTOR& v) { + template + constexpr VECTOR& operator+=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] += v[i]; @@ -88,8 +88,13 @@ public: return lhs; } - template - constexpr VECTOR& operator-=(const VECTOR& v) { + template> + constexpr VECTOR& operator+=(U v) { + return operator+=(VECTOR(v)); + } + + template + constexpr VECTOR& operator-=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] -= v[i]; @@ -97,25 +102,9 @@ public: return lhs; } - /* compound assignment from a another vector of the same type. - * These operators can be used for implicit conversion and handle operations - * like "vector *= scalar" by letting the compiler implicitly convert a scalar - * to a vector (assuming the BASE allows it). - */ - constexpr VECTOR& operator+=(const VECTOR& v) { - VECTOR& lhs = static_cast&>(*this); - for (size_t i = 0; i < lhs.size(); i++) { - lhs[i] += v[i]; - } - return lhs; - } - - constexpr VECTOR& operator-=(const VECTOR& v) { - VECTOR& lhs = static_cast&>(*this); - for (size_t i = 0; i < lhs.size(); i++) { - lhs[i] -= v[i]; - } - return lhs; + template> + constexpr VECTOR& operator-=(U v) { + return operator-=(VECTOR(v)); } /* @@ -126,9 +115,6 @@ public: * (the first one, BASE being known). */ - /* The operators below handle operation between vectors of the same size - * but of a different element type. - */ template friend inline constexpr VECTOR> MATH_PURE operator+(const VECTOR& lv, const VECTOR& rv) { @@ -137,6 +123,12 @@ public: return res; } + template> + friend inline constexpr + VECTOR> MATH_PURE operator+(const VECTOR& lv, U rv) { + return lv + VECTOR(rv); + } + template friend inline constexpr VECTOR> MATH_PURE operator-(const VECTOR& lv, const VECTOR& rv) { @@ -145,20 +137,10 @@ public: return res; } - /* The operators below (which are not templates once this class is instanced, - * i.e.: BASE is known) can be used for implicit conversion on both sides. - * These handle operations like "vector + scalar" and "scalar + vector" by - * letting the compiler implicitly convert a scalar to a vector (assuming - * the BASE allows it). - */ - friend inline constexpr VECTOR MATH_PURE operator+(VECTOR lv, const VECTOR& rv) { - // don't pass lv by reference because we need a copy anyways - return lv += rv; - } - - friend inline constexpr VECTOR MATH_PURE operator-(VECTOR lv, const VECTOR& rv) { - // don't pass lv by reference because we need a copy anyways - return lv -= rv; + template> + friend inline constexpr + VECTOR> MATH_PURE operator-(const VECTOR& lv, U rv) { + return lv - VECTOR(rv); } }; @@ -168,8 +150,8 @@ public: /* compound assignment from a another vector of the same size but different * element type. */ - template - constexpr VECTOR& operator*=(const VECTOR& v) { + template + constexpr VECTOR& operator*=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] *= v[i]; @@ -177,8 +159,13 @@ public: return lhs; } - template - constexpr VECTOR& operator/=(const VECTOR& v) { + template> + constexpr VECTOR& operator*=(U v) { + return operator*=(VECTOR(v)); + } + + template + constexpr VECTOR& operator/=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] /= v[i]; @@ -186,25 +173,9 @@ public: return lhs; } - /* compound assignment from a another vector of the same type. - * These operators can be used for implicit conversion and handle operations - * like "vector *= scalar" by letting the compiler implicitly convert a scalar - * to a vector (assuming the BASE allows it). - */ - constexpr VECTOR& operator*=(const VECTOR& v) { - VECTOR& lhs = static_cast&>(*this); - for (size_t i = 0; i < lhs.size(); i++) { - lhs[i] *= v[i]; - } - return lhs; - } - - constexpr VECTOR& operator/=(const VECTOR& v) { - VECTOR& lhs = static_cast&>(*this); - for (size_t i = 0; i < lhs.size(); i++) { - lhs[i] /= v[i]; - } - return lhs; + template> + constexpr VECTOR& operator/=(U v) { + return operator/=(VECTOR(v)); } /* @@ -215,9 +186,6 @@ public: * (the first one, BASE being known). */ - /* The operators below handle operation between vectors of the same size - * but of a different element type. - */ template friend inline constexpr VECTOR> MATH_PURE operator*(const VECTOR& lv, const VECTOR& rv) { @@ -226,28 +194,26 @@ public: return res; } + template> + friend inline constexpr + VECTOR> MATH_PURE operator*(const VECTOR& lv, U rv) { + VECTOR> res(lv); + return res * VECTOR(rv); + } + template friend inline constexpr VECTOR> MATH_PURE operator/(const VECTOR& lv, const VECTOR& rv) { - arithmetic_result_t res(lv); + VECTOR> res(lv); res /= rv; return res; } - /* The operators below (which are not templates once this class is instanced, - * i.e.: BASE is known) can be used for implicit conversion on both sides. - * These handle operations like "vector * scalar" and "scalar * vector" by - * letting the compiler implicitly convert a scalar to a vector (assuming - * the BASE allows it). - */ - friend inline constexpr VECTOR MATH_PURE operator*(VECTOR lv, const VECTOR& rv) { - // don't pass lv by reference because we need a copy anyways - return lv *= rv; - } - - friend inline constexpr VECTOR MATH_PURE operator/(VECTOR lv, const VECTOR& rv) { - // don't pass lv by reference because we need a copy anyways - return lv /= rv; + template> + friend inline constexpr + VECTOR> MATH_PURE operator/(const VECTOR& lv, U rv) { + VECTOR> res(lv); + return res / VECTOR(rv); } }; diff --git a/libs/math/tests/test_vec.cpp b/libs/math/tests/test_vec.cpp index afd3e93182..99fb6a0d60 100644 --- a/libs/math/tests/test_vec.cpp +++ b/libs/math/tests/test_vec.cpp @@ -31,6 +31,8 @@ TEST_F(VecTest, Constexpr) { constexpr float2 A2 = a; constexpr float2 B2 = { a, a }; constexpr float2 C2 = A2; + constexpr float2 E2 = A2 + 0.5f; + constexpr float2 F2 = A2 + 0.5 - 1.0 + (1 + A2); constexpr float3 D2 = cross(A2, C2); constexpr float3 A3 = a; @@ -64,7 +66,8 @@ TEST_F(VecTest, Constexpr) { constexpr float4 S0 = A4 + B4; constexpr float4 S1 = C4 - D4; - constexpr float4 S2 = A4 * a; + constexpr float4 S2 = (a * A4) + (A4 * a); + constexpr float4 S7 = (a / A4) + (A4 / a); constexpr float4 S3 = A4 * A4; constexpr float4 S4 = A4 / a; constexpr float4 S5 = A4 / A4;