/* * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include "Vector.h" namespace Math { FORCE_INLINE Vector Vector::Cross2(const Vector& v0, const Vector& v1) { return v0.Cross2(v1); } FORCE_INLINE Vector Vector::Cross3(const Vector& v0, const Vector& v1) { return v0.Cross3(v1); } FORCE_INLINE Vector Vector::Dot2(const Vector& v0, const Vector& v1) { return v0.Dot2(v1); } FORCE_INLINE Vector Vector::Dot3(const Vector& v0, const Vector& v1) { return v0.Dot3(v1); } FORCE_INLINE Vector Vector::Dot4(const Vector& v0, const Vector& v1) { return v0.Dot4(v1); } FORCE_INLINE Vector Vector::Average2(const Vector& v0, const Vector& v1) { auto avg4 = Average4(v0, v1); return Vector::Select(avg4, Vector::Zero, Vector(0, 0, 1, 1)); } FORCE_INLINE Vector Vector::Average3(const Vector& v0, const Vector& v1) { auto avg4 = Average4(v0, v1); return Vector::Select(avg4, Vector::Zero, Vector(0, 0, 0, 1)); } FORCE_INLINE Vector Vector::Average4(const Vector& v0, const Vector& v1) { return (v0 + v1) * Vector::Half; } FORCE_INLINE Vector Vector::Min(const Vector& v0, const Vector& v1) { Vector result; result = _mm_min_ps(v0, v1); return result; } FORCE_INLINE Vector Vector::Max(const Vector& v0, const Vector& v1) { Vector result; result = _mm_max_ps(v0, v1); return result; } FORCE_INLINE float Vector::Min(const Vector& v) { __m128 shufReg, sumsReg; shufReg = _mm_movehdup_ps(v); sumsReg = _mm_min_ps(v, shufReg); shufReg = _mm_movehl_ps(shufReg, sumsReg); sumsReg = _mm_min_ss(sumsReg, shufReg); return _mm_cvtss_f32(sumsReg); } FORCE_INLINE float Vector::Max(const Vector& v) { __m128 shufReg, sumsReg; shufReg = _mm_movehdup_ps(v); sumsReg = _mm_max_ps(v, shufReg); shufReg = _mm_movehl_ps(shufReg, sumsReg); sumsReg = _mm_max_ss(sumsReg, shufReg); return _mm_cvtss_f32(sumsReg); } FORCE_INLINE Vector Vector::Clamp(const Vector& v, const Vector& min, const Vector& max) { Vector result; result = _mm_max_ps(min, v); result = _mm_min_ps(result, max); return result; } FORCE_INLINE Vector Vector::Xor(const Vector& v0, const Vector& v1) { __m128i V = _mm_xor_si128(_mm_castps_si128(v0), _mm_castps_si128(v1)); Vector result; result = _mm_castsi128_ps(V); return result; } FORCE_INLINE Vector Vector::MultiplyAdd(const Vector& v, const Vector& multiplier, const Vector& addend) { // result = addend + ( vec * multiplier ) Vector result; result = _mm_mul_ps(v, multiplier); result = _mm_add_ps(result, addend); return result; } FORCE_INLINE Vector Vector::MultiplySubtract(const Vector& vec, const Vector& multiplier, const Vector& subtrahend) { // result = ( vec * multiplier ) - subtrahend auto r = _mm_mul_ps(vec, multiplier); return _mm_sub_ps(r, subtrahend); } FORCE_INLINE Vector Vector::NegativeMultiplySubtract(const Vector& vec, const Vector& multiplier, const Vector& minuend) { // result = minuend - ( vec * multiplier ) auto r = _mm_mul_ps(vec, multiplier); return _mm_sub_ps(minuend, r); } FORCE_INLINE Vector Vector::LinearCombination(const Vector& v0, const Vector& v1, float scale0, float scale1) { return (v0 * scale0) + (v1 * scale1); } FORCE_INLINE Vector Vector::Lerp(const Vector& from, const Vector& to, float t) { ASSERT(t >= 0.0f && t <= 1.0f); Vector L = _mm_sub_ps(to, from); Vector S = _mm_set_ps1(t); Vector result; result = _mm_mul_ps(L, S); result = _mm_add_ps(result, from); return result; } FORCE_INLINE Vector Vector::NLerp(const Vector& from, const Vector& to, float t) { ASSERT(t >= 0.0f && t <= 1.0f); // Calculate the final length auto const fromLength = from.Length3(); auto const toLength = to.Length3(); auto const finalLength = Vector::Lerp(fromLength, toLength, t); // Normalize vectors Vector const normalizedFrom = from / fromLength; Vector const normalizedTo = to / toLength; // LERP auto const finalDirection = Lerp(normalizedFrom, normalizedTo, t); auto result = finalDirection.GetNormalized3() * finalLength; return result; } FORCE_INLINE Vector Vector::Select(const Vector& v0, const Vector& v1, const Vector& control) { auto const ctrl = _mm_cmpneq_ps(control, Vector::Zero); Vector result; auto vTemp1 = _mm_andnot_ps(ctrl, v0); auto vTemp2 = _mm_and_ps(v1, ctrl); result = _mm_or_ps(vTemp1, vTemp2); return result; } template FORCE_INLINE Vector Vector::Permute(const Vector& v0, const Vector& v1) { static_assert(PermuteX <= 7, "Element index parameter out of range"); static_assert(PermuteY <= 7, "Element index parameter out of range"); static_assert(PermuteZ <= 7, "Element index parameter out of range"); static_assert(PermuteW <= 7, "Element index parameter out of range"); uint32_t const shuffle = _MM_SHUFFLE(PermuteW & 3, PermuteZ & 3, PermuteY & 3, PermuteX & 3); bool const whichX = PermuteX > 3; bool const whichY = PermuteY > 3; bool const whichZ = PermuteZ > 3; bool const whichW = PermuteW > 3; static SIMD::UIntMask const selectMask = { whichX ? 0xFFFFFFFF : 0, whichY ? 0xFFFFFFFF : 0, whichZ ? 0xFFFFFFFF : 0, whichW ? 0xFFFFFFFF : 0 }; __m128 shuffled1 = _mm_shuffle_ps(v0, v0, shuffle); __m128 shuffled2 = _mm_shuffle_ps(v1, v1, shuffle); __m128 masked1 = _mm_andnot_ps(selectMask, shuffled1); __m128 masked2 = _mm_and_ps(selectMask, shuffled2); return _mm_or_ps(masked1, masked2); } FORCE_INLINE Vector Vector::Sin(const Vector& vec) { // Force the value within the bounds of pi auto m_x = Vector::AngleMod2Pi(vec); // Map in [-pi/2,pi/2] with sin(m_y) = sin(m_x). __m128 sign = _mm_and_ps(m_x, SIMD::g_signMask); __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0 __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x| __m128 rflx = _mm_sub_ps(c, m_x); __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo); __m128 select0 = _mm_and_ps(comp, m_x); __m128 select1 = _mm_andnot_ps(comp, rflx); m_x = _mm_or_ps(select0, select1); __m128 x2 = _mm_mul_ps(m_x, m_x); // Compute polynomial approximation const auto SC1 = SIMD::g_sinCoefficients1; auto vConstants = _mm_shuffle_ps(SC1, SC1, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Result = _mm_mul_ps(vConstants, x2); const auto SC0 = SIMD::g_sinCoefficients0; vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(3, 3, 3, 3)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Result = _mm_add_ps(Result, Vector::One); Result = _mm_mul_ps(Result, m_x); return Result; } FORCE_INLINE Vector Vector::Cos(const Vector& vec) { // Map V to m_x in [-pi,pi]. auto m_x = Vector::AngleMod2Pi(vec); // Map in [-pi/2,pi/2] with cos(m_y) = sign*cos(m_x). auto sign = _mm_and_ps(m_x, SIMD::g_signMask); __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0 __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x| __m128 rflx = _mm_sub_ps(c, m_x); __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo); __m128 select0 = _mm_and_ps(comp, m_x); __m128 select1 = _mm_andnot_ps(comp, rflx); m_x = _mm_or_ps(select0, select1); select0 = _mm_and_ps(comp, Vector::One); select1 = _mm_andnot_ps(comp, Vector::NegativeOne); sign = _mm_or_ps(select0, select1); __m128 x2 = _mm_mul_ps(m_x, m_x); // Compute polynomial approximation const auto CC1 = SIMD::g_cosCoefficients1; auto vConstants = _mm_shuffle_ps(CC1, CC1, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Result = _mm_mul_ps(vConstants, x2); const auto CC0 = SIMD::g_cosCoefficients0; vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(3, 3, 3, 3)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Result = _mm_add_ps(Result, Vector::One); Result = _mm_mul_ps(Result, sign); return Result; } FORCE_INLINE Vector Vector::Tan(const Vector& vec) { static const Vector tanCoefficients0 = { 1.0f, -4.667168334e-1f, 2.566383229e-2f, -3.118153191e-4f }; static const Vector tanCoefficients1 = { 4.981943399e-7f, -1.333835001e-1f, 3.424887824e-3f, -1.786170734e-5f }; static const Vector tanConstants = { 1.570796371f, 6.077100628e-11f, 0.000244140625f, 0.63661977228f /*2 / Pi*/ }; static const SIMD::UIntMask mask = { 0x1, 0x1, 0x1, 0x1 }; Vector TwoDivPi = tanConstants.GetSplatW(); Vector C0 = tanConstants.GetSplatX(); Vector C1 = tanConstants.GetSplatY(); Vector vEpsilon = tanConstants.GetSplatZ(); Vector VA = (vec * TwoDivPi).Round(); Vector VC = Vector::NegativeMultiplySubtract(VA, C0, vec); Vector VB = VA.GetAbs(); VC = Vector::NegativeMultiplySubtract(VA, C1, VC); reinterpret_cast<__m128i*>(&VB)[0] = _mm_cvttps_epi32(VB); Vector VC2 = VC * VC; Vector T7 = tanCoefficients1.GetSplatW(); Vector T6 = tanCoefficients1.GetSplatZ(); Vector T4 = tanCoefficients1.GetSplatX(); Vector T3 = tanCoefficients0.GetSplatW(); Vector T5 = tanCoefficients1.GetSplatY(); Vector T2 = tanCoefficients0.GetSplatZ(); Vector T1 = tanCoefficients0.GetSplatY(); Vector T0 = tanCoefficients0.GetSplatX(); Vector VBIsEven = _mm_and_ps(VB, mask); VBIsEven = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(VBIsEven), _mm_castps_si128(Vector::Zero))); Vector N = Vector::MultiplyAdd(VC2, T7, T6); Vector D = Vector::MultiplyAdd(VC2, T4, T3); N = Vector::MultiplyAdd(VC2, N, T5); D = Vector::MultiplyAdd(VC2, D, T2); N = VC2 * N; D = Vector::MultiplyAdd(VC2, D, T1); N = Vector::MultiplyAdd(VC, N, VC); Vector VCNearZero = VC.InBounds(vEpsilon); D = Vector::MultiplyAdd(VC2, D, T0); N = Vector::Select(N, VC, VCNearZero); D = Vector::Select(D, Vector::One, VCNearZero); Vector R0 = N.GetNegated(); Vector R1 = N / D; R0 = D / R0; Vector VIsZero = vec.EqualsZero(); Vector Result = Vector::Select(R0, R1, VBIsEven); Result = Vector::Select(Result, Zero, VIsZero); return Result; } FORCE_INLINE Vector Vector::ASin(const Vector& vec) { __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero); __m128 mvalue = _mm_sub_ps(Vector::Zero, vec); __m128 m_x = _mm_max_ps(vec, mvalue); // |vec| // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number. __m128 oneMValue = _mm_sub_ps(Vector::One, m_x); __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue); __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|) // Compute polynomial approximation const auto AC1 = SIMD::g_arcCoefficients1; auto vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(3, 3, 3, 3)); __m128 t0 = _mm_mul_ps(vConstants, m_x); vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(2, 2, 2, 2)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(1, 1, 1, 1)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(0, 0, 0, 0)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); const auto AC0 = SIMD::g_arcCoefficients0; vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(3, 3, 3, 3)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(2, 2, 2, 2)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(1, 1, 1, 1)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(0, 0, 0, 0)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, root); __m128 t1 = _mm_sub_ps(Vector::Pi, t0); t0 = _mm_and_ps(nonnegative, t0); t1 = _mm_andnot_ps(nonnegative, t1); t0 = _mm_or_ps(t0, t1); t0 = _mm_sub_ps(Vector::PiDivTwo, t0); return t0; } FORCE_INLINE Vector Vector::ACos(const Vector& vec) { __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero); __m128 mvalue = _mm_sub_ps(Vector::Zero, vec); __m128 m_x = _mm_max_ps(vec, mvalue); // |vec| // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number. __m128 oneMValue = _mm_sub_ps(Vector::One, m_x); __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue); __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|) // Compute polynomial approximation const auto AC1 = SIMD::g_arcCoefficients1; auto vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(3, 3, 3, 3)); __m128 t0 = _mm_mul_ps(vConstants, m_x); vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(2, 2, 2, 2)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(1, 1, 1, 1)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC1, AC1, _MM_SHUFFLE(0, 0, 0, 0)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); const auto AC0 = SIMD::g_arcCoefficients0; vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(3, 3, 3, 3)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(2, 2, 2, 2)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(1, 1, 1, 1)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AC0, AC0, _MM_SHUFFLE(0, 0, 0, 0)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, root); __m128 t1 = _mm_sub_ps(Vector::Pi, t0); t0 = _mm_and_ps(nonnegative, t0); t1 = _mm_andnot_ps(nonnegative, t1); t0 = _mm_or_ps(t0, t1); return t0; } FORCE_INLINE Vector Vector::ATan(const Vector& vec) { __m128 absV = vec.GetAbs(); __m128 invV = _mm_div_ps(Vector::One, vec); __m128 comp = _mm_cmpgt_ps(vec, Vector::One); __m128 select0 = _mm_and_ps(comp, Vector::One); __m128 select1 = _mm_andnot_ps(comp, Vector::NegativeOne); __m128 sign = _mm_or_ps(select0, select1); comp = _mm_cmple_ps(absV, Vector::One); select0 = _mm_and_ps(comp, Vector::Zero); select1 = _mm_andnot_ps(comp, sign); sign = _mm_or_ps(select0, select1); select0 = _mm_and_ps(comp, vec); select1 = _mm_andnot_ps(comp, invV); __m128 m_x = _mm_or_ps(select0, select1); __m128 x2 = _mm_mul_ps(m_x, m_x); // Compute polynomial approximation Vector const TC1 = SIMD::g_aTanCoefficients1; Vector vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Result = _mm_mul_ps(vConstants, x2); vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(TC1, TC1, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Vector const TC0 = SIMD::g_aTanCoefficients0; vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(3, 3, 3, 3)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(TC0, TC0, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Result = _mm_add_ps(Result, Vector::One); Result = _mm_mul_ps(Result, m_x); __m128 result1 = _mm_mul_ps(sign, Vector::PiDivTwo); result1 = _mm_sub_ps(result1, Result); comp = _mm_cmpeq_ps(sign, Vector::Zero); select0 = _mm_and_ps(comp, Result); select1 = _mm_andnot_ps(comp, result1); Result = _mm_or_ps(select0, select1); return Result; } FORCE_INLINE Vector Vector::ATan2(const Vector& Y, const Vector& X) { Vector ATanResultValid = Vector(SIMD::g_trueMask); Vector vPi = Vector(SIMD::g_aTan2Constants).GetSplatX(); Vector vPiOverTwo = Vector(SIMD::g_aTan2Constants).GetSplatY(); Vector vPiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatZ(); Vector vThreePiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatW(); Vector YEqualsZero = Y.EqualsZero(); Vector XEqualsZero = X.EqualsZero(); Vector XIsPositive = _mm_and_ps(X, SIMD::g_signMask); XIsPositive = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(XIsPositive), _mm_castps_si128(Vector::Zero))); Vector YEqualsInfinity = Y.EqualsInfinity(); Vector XEqualsInfinity = X.EqualsInfinity(); Vector YSign = _mm_and_ps(Y, SIMD::g_signMask); vPi = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPi), _mm_castps_si128(YSign))); vPiOverTwo = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverTwo), _mm_castps_si128(YSign))); vPiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverFour), _mm_castps_si128(YSign))); vThreePiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vThreePiOverFour), _mm_castps_si128(YSign))); Vector R1 = Vector::Select(vPi, YSign, XIsPositive); Vector R2 = Vector::Select(ATanResultValid, vPiOverTwo, XEqualsZero); Vector R3 = Vector::Select(R2, R1, YEqualsZero); Vector R4 = Vector::Select(vThreePiOverFour, vPiOverFour, XIsPositive); Vector R5 = Vector::Select(vPiOverTwo, R4, XEqualsInfinity); Vector Result = Vector::Select(R3, R5, YEqualsInfinity); ATanResultValid = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(Result), _mm_castps_si128(ATanResultValid))); Vector V = Y / X; Vector R0 = Vector::ATan(V); R1 = Vector::Select(vPi, Vector(SIMD::g_signMask), XIsPositive); R2 = R0 + R1; return Vector::Select(Result, R2, ATanResultValid); } FORCE_INLINE Vector Vector::SinEst(const Vector& vec) { // Force the value within the bounds of pi auto m_x = Vector::AngleMod2Pi(vec); // Map in [-pi/2,pi/2] with sin(m_y) = sin(m_x). __m128 sign = _mm_and_ps(m_x, SIMD::g_signMask); __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0 __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x| __m128 rflx = _mm_sub_ps(c, m_x); __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo); __m128 select0 = _mm_and_ps(comp, m_x); __m128 select1 = _mm_andnot_ps(comp, rflx); m_x = _mm_or_ps(select0, select1); __m128 x2 = _mm_mul_ps(m_x, m_x); // Compute polynomial approximation const auto SEC = SIMD::g_sinCoefficients1; auto vConstants = _mm_shuffle_ps(SEC, SEC, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Result = _mm_mul_ps(vConstants, x2); vConstants = _mm_shuffle_ps(SEC, SEC, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(SEC, SEC, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Result = _mm_add_ps(Result, Vector::One); Result = _mm_mul_ps(Result, m_x); return Result; } FORCE_INLINE Vector Vector::CosEst(const Vector& vec) { // Map V to m_x in [-pi,pi]. auto m_x = Vector::AngleMod2Pi(vec); // Map in [-pi/2,pi/2] with cos(m_y) = sign*cos(m_x). auto sign = _mm_and_ps(m_x, SIMD::g_signMask); __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0 __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x| __m128 rflx = _mm_sub_ps(c, m_x); __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo); __m128 select0 = _mm_and_ps(comp, m_x); __m128 select1 = _mm_andnot_ps(comp, rflx); m_x = _mm_or_ps(select0, select1); select0 = _mm_and_ps(comp, Vector::One); select1 = _mm_andnot_ps(comp, Vector::NegativeOne); sign = _mm_or_ps(select0, select1); __m128 x2 = _mm_mul_ps(m_x, m_x); // Compute polynomial approximation const auto CEC = SIMD::g_cosCoefficients1; auto vConstants = _mm_shuffle_ps(CEC, CEC, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Result = _mm_mul_ps(vConstants, x2); vConstants = _mm_shuffle_ps(CEC, CEC, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(CEC, CEC, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Result = _mm_add_ps(Result, Vector::One); Result = _mm_mul_ps(Result, sign); return Result; } FORCE_INLINE Vector Vector::TanEst(const Vector& vec) { Vector W = Vector(SIMD::g_tanEstCoefficients).GetSplatW(); Vector V1 = (vec * W).Round(); V1 = Vector::NegativeMultiplySubtract(Vector::Pi, V1, vec); Vector const T0 = Vector(SIMD::g_tanEstCoefficients).GetSplatX(); Vector const T1 = Vector(SIMD::g_tanEstCoefficients).GetSplatY(); Vector const T2 = Vector(SIMD::g_tanEstCoefficients).GetSplatZ(); auto V2T2 = Vector::NegativeMultiplySubtract(V1, V1, T2); auto V2 = V1 * V1; auto V1T0 = V1 * T0; auto V1T1 = V1 * T1; auto N = Vector::MultiplyAdd(V2, V1T1, V1T0); auto D = V2T2.GetInverseEst(); return N * D; } FORCE_INLINE Vector Vector::ASinEst(const Vector& vec) { __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero); __m128 mvalue = _mm_sub_ps(Vector::Zero, vec); __m128 m_x = _mm_max_ps(vec, mvalue); // |vec| // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number. __m128 oneMValue = _mm_sub_ps(Vector::One, m_x); __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue); __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|) // Compute polynomial approximation const auto AEC = SIMD::g_arcEstCoefficients; auto vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(3, 3, 3, 3)); __m128 t0 = _mm_mul_ps(vConstants, m_x); vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(2, 2, 2, 2)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(1, 1, 1, 1)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(0, 0, 0, 0)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, root); __m128 t1 = _mm_sub_ps(Vector::Pi, t0); t0 = _mm_and_ps(nonnegative, t0); t1 = _mm_andnot_ps(nonnegative, t1); t0 = _mm_or_ps(t0, t1); t0 = _mm_sub_ps(Vector::PiDivTwo, t0); return t0; } FORCE_INLINE Vector Vector::ACosEst(const Vector& vec) { __m128 nonnegative = _mm_cmpge_ps(vec, Vector::Zero); __m128 mvalue = _mm_sub_ps(Vector::Zero, vec); __m128 m_x = _mm_max_ps(vec, mvalue); // |vec| // Compute (1-|vec|), clamp to zero to avoid sqrt of negative number. __m128 oneMValue = _mm_sub_ps(Vector::One, m_x); __m128 clampOneMValue = _mm_max_ps(Vector::Zero, oneMValue); __m128 root = _mm_sqrt_ps(clampOneMValue); // sqrt(1-|vec|) // Compute polynomial approximation auto vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(3, 3, 3, 3)); __m128 t0 = _mm_mul_ps(vConstants, m_x); vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(2, 2, 2, 2)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(1, 1, 1, 1)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, m_x); vConstants = _mm_shuffle_ps(SIMD::g_arcEstCoefficients, SIMD::g_arcEstCoefficients, _MM_SHUFFLE(0, 0, 0, 0)); t0 = _mm_add_ps(t0, vConstants); t0 = _mm_mul_ps(t0, root); __m128 t1 = _mm_sub_ps(Vector::Pi, t0); t0 = _mm_and_ps(nonnegative, t0); t1 = _mm_andnot_ps(nonnegative, t1); t0 = _mm_or_ps(t0, t1); return t0; } FORCE_INLINE Vector Vector::ATanEst(const Vector& vec) { __m128 absV = vec.GetAbs(); __m128 invV = _mm_div_ps(Vector::One, vec); __m128 comp = _mm_cmpgt_ps(vec, Vector::One); __m128 select0 = _mm_and_ps(comp, Vector::One); __m128 select1 = _mm_andnot_ps(comp, Vector::NegativeOne); __m128 sign = _mm_or_ps(select0, select1); comp = _mm_cmple_ps(absV, Vector::One); select0 = _mm_and_ps(comp, Vector::Zero); select1 = _mm_andnot_ps(comp, sign); sign = _mm_or_ps(select0, select1); select0 = _mm_and_ps(comp, vec); select1 = _mm_andnot_ps(comp, invV); __m128 m_x = _mm_or_ps(select0, select1); __m128 x2 = _mm_mul_ps(m_x, m_x); // Compute polynomial approximation Vector const AEC = SIMD::g_aTanEstCoefficients1; Vector vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Result = _mm_mul_ps(vConstants, x2); vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(AEC, AEC, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); // ATanEstCoefficients0 is already splatted Result = _mm_add_ps(Result, SIMD::g_aTanEstCoefficients0); Result = _mm_mul_ps(Result, m_x); __m128 result1 = _mm_mul_ps(sign, Vector::PiDivTwo); result1 = _mm_sub_ps(result1, Result); comp = _mm_cmpeq_ps(sign, Vector::Zero); select0 = _mm_and_ps(comp, Result); select1 = _mm_andnot_ps(comp, result1); Result = _mm_or_ps(select0, select1); return Result; } FORCE_INLINE Vector Vector::ATan2Est(const Vector& X, const Vector& Y) { Vector ATanResultValid = Vector(SIMD::g_trueMask); Vector vPi = Vector(SIMD::g_aTan2Constants).GetSplatX(); Vector vPiOverTwo = Vector(SIMD::g_aTan2Constants).GetSplatY(); Vector vPiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatZ(); Vector vThreePiOverFour = Vector(SIMD::g_aTan2Constants).GetSplatW(); Vector YEqualsZero = Y.EqualsZero(); Vector XEqualsZero = X.EqualsZero(); Vector XIsPositive = _mm_and_ps(X, SIMD::g_signMask); XIsPositive = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(XIsPositive), _mm_castps_si128(Vector::Zero))); Vector YEqualsInfinity = Y.EqualsInfinity(); Vector XEqualsInfinity = X.EqualsInfinity(); Vector YSign = _mm_and_ps(Y, SIMD::g_signMask); vPi = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPi), _mm_castps_si128(YSign))); vPiOverTwo = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverTwo), _mm_castps_si128(YSign))); vPiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vPiOverFour), _mm_castps_si128(YSign))); vThreePiOverFour = _mm_castsi128_ps(_mm_or_si128(_mm_castps_si128(vThreePiOverFour), _mm_castps_si128(YSign))); Vector R1 = Vector::Select(vPi, YSign, XIsPositive); Vector R2 = Vector::Select(ATanResultValid, vPiOverTwo, XEqualsZero); Vector R3 = Vector::Select(R2, R1, YEqualsZero); Vector R4 = Vector::Select(vThreePiOverFour, vPiOverFour, XIsPositive); Vector R5 = Vector::Select(vPiOverTwo, R4, XEqualsInfinity); Vector Result = Vector::Select(R3, R5, YEqualsInfinity); ATanResultValid = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_castps_si128(Result), _mm_castps_si128(ATanResultValid))); Vector Reciprocal = X.GetInverseEst(); Vector V = Y * Reciprocal; Vector R0 = Vector::ATanEst(V); R1 = Vector::Select(vPi, Vector(SIMD::g_signMask), XIsPositive); R2 = R0 + R1; Result = Vector::Select(Result, R2, ATanResultValid); return Result; } FORCE_INLINE void Vector::SinCos(Vector& sin, Vector& cos, float angle) { return SinCos(sin, cos, Vector(angle)); } FORCE_INLINE void Vector::SinCos(Vector& sin, Vector& cos, const Vector& angle) { // Force the value within the bounds of pi auto m_x = Vector::AngleMod2Pi(angle); // Map in [-pi/2,pi/2] with sin(m_y) = sin(m_x), cos(m_y) = sign*cos(m_x). auto sign = _mm_and_ps(m_x, SIMD::g_signMask); __m128 c = _mm_or_ps(Vector::Pi, sign); // pi when m_x >= 0, -pi when m_x < 0 __m128 absx = _mm_andnot_ps(sign, m_x); // |m_x| __m128 rflx = _mm_sub_ps(c, m_x); __m128 comp = _mm_cmple_ps(absx, Vector::PiDivTwo); __m128 select0 = _mm_and_ps(comp, m_x); __m128 select1 = _mm_andnot_ps(comp, rflx); m_x = _mm_or_ps(select0, select1); select0 = _mm_and_ps(comp, Vector::One); select1 = _mm_andnot_ps(comp, Vector::NegativeOne); sign = _mm_or_ps(select0, select1); __m128 x2 = _mm_mul_ps(m_x, m_x); // Compute polynomial approximation of sine const auto SC1 = SIMD::g_sinCoefficients1; auto vConstants = _mm_shuffle_ps(SC1, SC1, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Result = _mm_mul_ps(vConstants, x2); const auto SC0 = SIMD::g_sinCoefficients0; vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(3, 3, 3, 3)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(SC0, SC0, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Result = _mm_add_ps(Result, Vector::One); Result = _mm_mul_ps(Result, m_x); sin = Result; // Compute polynomial approximation of cosine const auto CC1 = SIMD::g_cosCoefficients1; vConstants = _mm_shuffle_ps(CC1, CC1, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_mul_ps(vConstants, x2); const auto CC0 = SIMD::g_cosCoefficients0; vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(3, 3, 3, 3)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(2, 2, 2, 2)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(1, 1, 1, 1)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); vConstants = _mm_shuffle_ps(CC0, CC0, _MM_SHUFFLE(0, 0, 0, 0)); Result = _mm_add_ps(Result, vConstants); Result = _mm_mul_ps(Result, x2); Result = _mm_add_ps(Result, Vector::One); Result = _mm_mul_ps(Result, sign); cos = Result; } FORCE_INLINE Vector Vector::AngleMod2Pi(const Vector& angles) { // Modulo the range of the given angles such that -Pi <= Angles < Pi Vector result = _mm_mul_ps(angles, Vector::OneDivTwoPi); result.Round(); result = _mm_mul_ps(result, Vector::TwoPi); result = _mm_sub_ps(angles, result); return result; } FORCE_INLINE Vector::operator __m128& () { return m_data; } FORCE_INLINE Vector::operator const __m128& () const { return m_data; } FORCE_INLINE Vector::Vector() { } FORCE_INLINE Vector::Vector(Axis axis) { switch (axis) { case Axis::X: *this = Vector::UnitX; break; case Axis::Y: *this = Vector::UnitY; break; case Axis::Z: *this = Vector::UnitZ; break; default: HALT(); break; } } FORCE_INLINE Vector::Vector(ZeroInit_t) { memset(this, 0, sizeof(Vector)); } FORCE_INLINE Vector::Vector(float v) { m_data = _mm_set1_ps(v); } FORCE_INLINE Vector::Vector(__m128 v) : m_data(v) { } FORCE_INLINE Vector::Vector(float ix, float iy, float iz, float iw) { m_data = _mm_set_ps(iw, iz, iy, ix); } FORCE_INLINE Vector::Vector(const Float2& v, float iz, float iw) { m_data = _mm_set_ps(iw, iz, v.m_y, v.m_x); } FORCE_INLINE Vector::Vector(const Float3& v, float iw) { m_data = _mm_set_ps(iw, v.m_z, v.m_y, v.m_x); } FORCE_INLINE Vector::Vector(const Float4& v) { m_data = _mm_loadu_ps(&v.m_x); } FORCE_INLINE Vector::Vector(const float* pValues) { m_data = _mm_loadu_ps(pValues); } FORCE_INLINE bool Vector::IsValid() const { return !IsNaN4() && !IsInfinite4(); } FORCE_INLINE void Vector::Store(float* pValues) const { _mm_storeu_ps(pValues, m_data); } FORCE_INLINE void Vector::StoreFloat(float& value) const { _mm_store_ss(&value, m_data); } FORCE_INLINE void Vector::StoreFloat2(Float2& value) const { auto yVec = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(1, 1, 1, 1)); _mm_store_ss(&value.m_x, m_data); _mm_store_ss(&value.m_y, yVec); } FORCE_INLINE void Vector::StoreFloat3(Float3& value) const { auto yVec = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(1, 1, 1, 1)); auto zVec = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(2, 2, 2, 2)); _mm_store_ss(&value.m_x, m_data); _mm_store_ss(&value.m_y, yVec); _mm_store_ss(&value.m_z, zVec); } FORCE_INLINE void Vector::StoreFloat4(Float4& value) const { _mm_storeu_ps(&value.m_x, m_data); } FORCE_INLINE float Vector::ToFloat() const { float v; StoreFloat(v); return v; } FORCE_INLINE Float2 Vector::ToFloat2() const { Float2 v; StoreFloat2(v); return v; } FORCE_INLINE Float3 Vector::ToFloat3() const { Float3 v; StoreFloat3(v); return v; } FORCE_INLINE Float4 Vector::ToFloat4() const { Float4 v; StoreFloat4(v); return v; } FORCE_INLINE Vector::operator Float2() const { return ToFloat2(); } FORCE_INLINE Vector::operator Float3() const { return ToFloat3(); } FORCE_INLINE Vector::operator Float4() const { return ToFloat4(); } FORCE_INLINE float Vector::GetX() const { return _mm_cvtss_f32(m_data); } FORCE_INLINE float Vector::GetY() const { auto vTemp = GetSplatY(); return _mm_cvtss_f32(vTemp); } FORCE_INLINE float Vector::GetZ() const { auto vTemp = GetSplatZ(); return _mm_cvtss_f32(vTemp); } FORCE_INLINE float Vector::GetW() const { auto vTemp = GetSplatW(); return _mm_cvtss_f32(vTemp); } FORCE_INLINE void Vector::SetX(float x) { m_data = _mm_move_ss(m_data, _mm_set_ss(x)); } FORCE_INLINE void Vector::SetY(float y) { m_data = _mm_insert_ps(m_data, _mm_set_ss(y), 0x10); } FORCE_INLINE void Vector::SetZ(float z) { m_data = _mm_insert_ps(m_data, _mm_set_ss(z), 0x20); } FORCE_INLINE void Vector::SetW(float w) { m_data = _mm_insert_ps(m_data, _mm_set_ss(w), 0x30); } FORCE_INLINE float Vector::operator[](uint32_t i) const { ASSERT(i < 4); switch (i) { case 0: return GetX(); break; case 1: return GetY(); break; case 2: return GetZ(); break; case 3: return GetW(); break; } UNREACHABLE_CODE(); return 0.0f; } FORCE_INLINE bool Vector::IsW1() const { return GetSplatW().IsEqual4(Vector::One); } FORCE_INLINE bool Vector::IsW0() const { return GetSplatW().IsZero4(); } FORCE_INLINE Vector& Vector::SetW0() { SetW(0.0f); return *this; } FORCE_INLINE Vector& Vector::SetW1() { SetW(1.0f); return *this; } FORCE_INLINE Vector Vector::GetWithW0() const { Vector v = *this; v.SetW0(); return v; } FORCE_INLINE Vector Vector::GetWithW1() const { Vector v = *this; v.SetW1(); return v; } FORCE_INLINE Vector Vector::Get2D() const { return Vector::Select(*this, Vector::Zero, Vector::Select0011); } FORCE_INLINE Vector Vector::Get3D() const { return Vector::Select(*this, Vector::Zero, Vector::Select0001); } FORCE_INLINE Vector Vector::operator+(const Vector& v) const { return _mm_add_ps(m_data, v); } FORCE_INLINE Vector& Vector::operator+=(const Vector& v) { m_data = _mm_add_ps(m_data, v); return *this; } FORCE_INLINE Vector Vector::operator-(const Vector& v) const { return _mm_sub_ps(m_data, v); } FORCE_INLINE Vector& Vector::operator-=(const Vector& v) { m_data = _mm_sub_ps(m_data, v); return *this; } FORCE_INLINE Vector Vector::operator*(const Vector& v) const { return _mm_mul_ps(m_data, v); } FORCE_INLINE Vector& Vector::operator*=(const Vector& v) { m_data = _mm_mul_ps(m_data, v); return *this; } FORCE_INLINE Vector Vector::operator/(const Vector& v) const { return _mm_div_ps(m_data, v); } FORCE_INLINE Vector& Vector::operator/=(const Vector& v) { m_data = _mm_div_ps(m_data, v); return *this; } FORCE_INLINE Vector Vector::operator*(float const f) const { return operator*(Vector(f)); } FORCE_INLINE Vector& Vector::operator*=(float const f) { return operator*=(Vector(f)); } FORCE_INLINE Vector Vector::operator/(float const f) const { return operator/(Vector(f)); } FORCE_INLINE Vector& Vector::operator/=(float const f) { return operator/=(Vector(f)); } FORCE_INLINE Vector Vector::operator-() const { return GetNegated(); } FORCE_INLINE Vector Vector::Orthogonal2D() const { static Vector const negX(-1.0f, 1.0f, 1.0f, 1.0f); Vector result; result = _mm_shuffle_ps(*this, *this, _MM_SHUFFLE(3, 2, 0, 1)); result = _mm_mul_ps(result, negX); return result; } FORCE_INLINE Vector Vector::Cross2(const Vector& other) const { Vector vResult = _mm_shuffle_ps(other.m_data, other.m_data, _MM_SHUFFLE(0, 1, 0, 1)); vResult = _mm_mul_ps(vResult, m_data); Vector vTemp = vResult.GetSplatY(); vResult = _mm_sub_ss(vResult, vTemp); vResult = vResult.GetSplatX(); return vResult; } FORCE_INLINE Vector Vector::Cross3(const Vector& other) const { auto vTemp1 = _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(3, 0, 2, 1)); auto vTemp2 = _mm_shuffle_ps(other, other, _MM_SHUFFLE(3, 1, 0, 2)); Vector result = _mm_mul_ps(vTemp1, vTemp2); vTemp1 = _mm_shuffle_ps(vTemp1, vTemp1, _MM_SHUFFLE(3, 0, 2, 1)); vTemp2 = _mm_shuffle_ps(vTemp2, vTemp2, _MM_SHUFFLE(3, 1, 0, 2)); vTemp1 = _mm_mul_ps(vTemp1, vTemp2); result = _mm_sub_ps(result, vTemp1); result = _mm_and_ps(result, SIMD::g_maskXYZ0); return result; } FORCE_INLINE Vector Vector::Dot2(const Vector& other) const { // Perform the dot product on m_x and m_y Vector result = _mm_mul_ps(m_data, other); // vTemp has m_y splatted auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 1, 1, 1)); // m_x+m_y result = _mm_add_ss(result, vTemp); result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(0, 0, 0, 0)); return result; } FORCE_INLINE Vector Vector::Dot3(const Vector& vOther) const { // Perform the dot product auto vDot = _mm_mul_ps(m_data, vOther); // m_x=Dot.vector4_f32[1], m_y=Dot.vector4_f32[2] auto vTemp = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(2, 1, 2, 1)); // Result.vector4_f32[0] = m_x+m_y vDot = _mm_add_ss(vDot, vTemp); // m_x=Dot.vector4_f32[2] vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1)); // Result.vector4_f32[0] = (m_x+m_y)+m_z vDot = _mm_add_ss(vDot, vTemp); // Splat m_x Vector result = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(0, 0, 0, 0)); return result; } FORCE_INLINE Vector Vector::Dot4(const Vector& other) const { auto vTemp2 = other; auto vTemp = _mm_mul_ps(m_data, vTemp2); vTemp2 = _mm_shuffle_ps(vTemp2, vTemp, _MM_SHUFFLE(1, 0, 0, 0)); // Copy X to the Z position and Y to the W position vTemp2 = _mm_add_ps(vTemp2, vTemp); // Add Z = X+Z; W = Y+W; vTemp = _mm_shuffle_ps(vTemp, vTemp2, _MM_SHUFFLE(0, 3, 0, 0)); // Copy W to the Z position vTemp = _mm_add_ps(vTemp, vTemp2); // Add Z and W together return _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(2, 2, 2, 2)); // Splat Z and return } FORCE_INLINE float Vector::GetDot2(const Vector& other) const { return Dot2(other).ToFloat(); } FORCE_INLINE float Vector::GetDot3(const Vector& other) const { return Dot3(other).ToFloat(); } FORCE_INLINE float Vector::GetDot4(const Vector& other) const { return Dot4(other).ToFloat(); } FORCE_INLINE Vector Vector::ScalarProjection(const Vector& other) const { Vector const normalizedThis = GetNormalized3(); Vector const projection = other.Dot3(normalizedThis); return projection; } FORCE_INLINE float Vector::GetScalarProjection(const Vector& other) const { return ScalarProjection(other).ToFloat(); } FORCE_INLINE Vector Vector::VectorProjection(const Vector& other) const { Vector const normalizedThis = GetNormalized3(); Vector const dotOther = other.Dot3(normalizedThis); Vector const projection = normalizedThis * dotOther; return projection; } FORCE_INLINE Vector& Vector::Invert() { m_data = _mm_div_ps(Vector::One, m_data); return *this; } FORCE_INLINE Vector Vector::GetInverse() const { return _mm_div_ps(Vector::One, m_data); } FORCE_INLINE Vector Vector::GetReciprocal() const { return GetInverse(); } FORCE_INLINE Vector& Vector::InvertEst() { m_data = _mm_rcp_ps(m_data); return *this; } FORCE_INLINE Vector Vector::GetInverseEst() const { return _mm_rcp_ps(m_data); } FORCE_INLINE Vector& Vector::Negate() { m_data = _mm_sub_ps(Vector::Zero, m_data); return *this; } FORCE_INLINE Vector Vector::GetNegated() const { return _mm_sub_ps(Vector::Zero, m_data); } FORCE_INLINE Vector& Vector::Abs() { m_data = _mm_max_ps(_mm_sub_ps(Vector::Zero, m_data), m_data); return *this; } FORCE_INLINE Vector Vector::GetAbs() const { return _mm_max_ps(_mm_sub_ps(Vector::Zero, m_data), m_data); } FORCE_INLINE Vector& Vector::Sqrt() { m_data = _mm_sqrt_ps(m_data); return *this; } FORCE_INLINE Vector Vector::GetSqrt() { return _mm_sqrt_ps(m_data); } FORCE_INLINE Vector& Vector::ReciprocalSqrt() { m_data = _mm_div_ps(Vector::One, _mm_sqrt_ps(m_data)); return *this; } FORCE_INLINE Vector Vector::GetReciprocalSqrt() { return _mm_div_ps(Vector::One, _mm_sqrt_ps(m_data)); } FORCE_INLINE Vector& Vector::EstimatedReciprocalSqrt() { m_data = _mm_rsqrt_ps(m_data); return *this; } FORCE_INLINE Vector Vector::GetEstimatedReciprocalSqrt() { return _mm_rsqrt_ps(m_data); } FORCE_INLINE Vector& Vector::Normalize2() { // Perform the dot product on m_x and m_y only auto vLengthSq = _mm_mul_ps(m_data, m_data); auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); vLengthSq = _mm_add_ss(vLengthSq, vTemp); vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); // Prepare for the division auto vResult = _mm_sqrt_ps(vLengthSq); // Create zero with a single instruction auto vZeroMask = _mm_setzero_ps(); // Test for a divide by zero (Must be FP to detect -0.0) vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); // Failsafe on zero (Or epsilon) length planes // If the length is infinity, set the elements to zero vLengthSq = _mm_cmpneq_ps(vLengthSq, Vector::Infinity); // Divide to perform the normalization vResult = _mm_div_ps(m_data, vResult); // Any that are infinity, set to zero vResult = _mm_and_ps(vResult, vZeroMask); // Select qnan or result based on infinite length auto vTemp1 = _mm_andnot_ps(vLengthSq, Vector::QNaN); auto vTemp2 = _mm_and_ps(vResult, vLengthSq); m_data = _mm_or_ps(vTemp1, vTemp2); *this = Select(*this, Vector::Zero, Select0011); return *this; } FORCE_INLINE Vector& Vector::Normalize3() { // Perform the dot product on m_x,m_y and m_z only auto vLengthSq = _mm_mul_ps(m_data, m_data); auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(2, 1, 2, 1)); vLengthSq = _mm_add_ss(vLengthSq, vTemp); vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1)); vLengthSq = _mm_add_ss(vLengthSq, vTemp); vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); // Prepare for the division auto vResult = _mm_sqrt_ps(vLengthSq); // Create zero with a single instruction auto vZeroMask = _mm_setzero_ps(); // Test for a divide by zero (Must be FP to detect -0.0) vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); // Failsafe on zero (Or epsilon) length planes // If the length is infinity, set the elements to zero vLengthSq = _mm_cmpneq_ps(vLengthSq, Vector::Infinity); // Divide to perform the normalization vResult = _mm_div_ps(m_data, vResult); // Any that are infinity, set to zero vResult = _mm_and_ps(vResult, vZeroMask); // Select qnan or result based on infinite length auto vTemp1 = _mm_andnot_ps(vLengthSq, Vector::QNaN); auto vTemp2 = _mm_and_ps(vResult, vLengthSq); m_data = _mm_or_ps(vTemp1, vTemp2); *this = Select(*this, Vector::Zero, Select0001); return *this; } FORCE_INLINE Vector& Vector::Normalize4() { // Perform the dot product on m_x,m_y,m_z and m_w auto vLengthSq = _mm_mul_ps(m_data, m_data); // vTemp has m_z and m_w auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); // m_x+m_z, m_y+m_w vLengthSq = _mm_add_ps(vLengthSq, vTemp); // m_x+m_z,m_x+m_z,m_x+m_z,m_y+m_w vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); // ??,??,m_y+m_w,m_y+m_w vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); // ??,??,m_x+m_z+m_y+m_w,?? vLengthSq = _mm_add_ps(vLengthSq, vTemp); // Splat the length vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); // Prepare for the division auto vResult = _mm_sqrt_ps(vLengthSq); // Create zero with a single instruction auto vZeroMask = _mm_setzero_ps(); // Test for a divide by zero (Must be FP to detect -0.0) vZeroMask = _mm_cmpneq_ps(vZeroMask, vResult); // Failsafe on zero (Or epsilon) length planes // If the length is infinity, set the elements to zero vLengthSq = _mm_cmpneq_ps(vLengthSq, Vector::Infinity); // Divide to perform the normalization vResult = _mm_div_ps(m_data, vResult); // Any that are infinity, set to zero vResult = _mm_and_ps(vResult, vZeroMask); // Select qnan or result based on infinite length auto vTemp1 = _mm_andnot_ps(vLengthSq, Vector::QNaN); auto vTemp2 = _mm_and_ps(vResult, vLengthSq); m_data = _mm_or_ps(vTemp1, vTemp2); return *this; } FORCE_INLINE Vector Vector::GetNormalized2() const { Vector v = *this; v.Normalize2(); return v; } FORCE_INLINE Vector Vector::GetNormalized3() const { Vector v = *this; v.Normalize3(); return v; } FORCE_INLINE Vector Vector::GetNormalized4() const { Vector v = *this; v.Normalize4(); return v; } FORCE_INLINE Vector& Vector::Floor() { Vector result; // To handle NAN, INF and numbers greater than 8388608, use masking __m128i vTest = _mm_and_si128(_mm_castps_si128(m_data), SIMD::g_absMask); vTest = _mm_cmplt_epi32(vTest, SIMD::g_noFraction); // Truncate __m128i vInt = _mm_cvttps_epi32(m_data); result = _mm_cvtepi32_ps(vInt); __m128 vLarger = _mm_cmpgt_ps(result, m_data); // 0 -> 0, 0xffffffff -> -1.0f vLarger = _mm_cvtepi32_ps(_mm_castps_si128(vLarger)); result = _mm_add_ps(result, vLarger); // All numbers less than 8388608 will use the round to int result = _mm_and_ps(result, _mm_castsi128_ps(vTest)); // All others, use the ORIGINAL value vTest = _mm_andnot_si128(vTest, _mm_castps_si128(m_data)); result = _mm_or_ps(result, _mm_castsi128_ps(vTest)); m_data = result; return *this; } FORCE_INLINE Vector Vector::GetFloor() const { Vector v = *this; v.Floor(); return v; } FORCE_INLINE Vector& Vector::Ceil() { Vector result; // To handle NAN, INF and numbers greater than 8388608, use masking __m128i vTest = _mm_and_si128(_mm_castps_si128(m_data), SIMD::g_absMask); vTest = _mm_cmplt_epi32(vTest, SIMD::g_noFraction); // Truncate __m128i vInt = _mm_cvttps_epi32(m_data); result = _mm_cvtepi32_ps(vInt); __m128 vSmaller = _mm_cmplt_ps(result, m_data); // 0 -> 0, 0xffffffff -> -1.0f vSmaller = _mm_cvtepi32_ps(_mm_castps_si128(vSmaller)); result = _mm_sub_ps(result, vSmaller); // All numbers less than 8388608 will use the round to int result = _mm_and_ps(result, _mm_castsi128_ps(vTest)); // All others, use the ORIGINAL value vTest = _mm_andnot_si128(vTest, _mm_castps_si128(m_data)); result = _mm_or_ps(result, _mm_castsi128_ps(vTest)); m_data = result; return *this; } FORCE_INLINE Vector Vector::GetCeil() const { Vector v = *this; v.Ceil(); return v; } FORCE_INLINE Vector& Vector::Round() { __m128 sign = _mm_and_ps(m_data, SIMD::g_signMask); __m128 sMagic = _mm_or_ps(SIMD::g_noFraction, sign); __m128 R1 = _mm_add_ps(m_data, sMagic); R1 = _mm_sub_ps(R1, sMagic); __m128 R2 = _mm_and_ps(m_data, SIMD::g_absMask); __m128 mask = _mm_cmple_ps(R2, SIMD::g_noFraction); R2 = _mm_andnot_ps(mask, m_data); R1 = _mm_and_ps(R1, mask); m_data = _mm_xor_ps(R1, R2); return *this; } FORCE_INLINE Vector Vector::GetRound() const { Vector v = *this; v.Round(); return v; } FORCE_INLINE Vector Vector::GetSign() const { Vector const selectMask = GreaterThanEqual(Vector::Zero); return Vector::Select(Vector::NegativeOne, Vector::One, selectMask); } FORCE_INLINE Vector Vector::GetSplatX() const { return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(0, 0, 0, 0)); } FORCE_INLINE Vector Vector::GetSplatY() const { return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(1, 1, 1, 1)); } FORCE_INLINE Vector Vector::GetSplatZ() const { return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(2, 2, 2, 2)); } FORCE_INLINE Vector Vector::GetSplatW() const { return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(3, 3, 3, 3)); } template FORCE_INLINE Vector Vector::Swizzle() const { static_assert(xIdx < 4, "Element index parameter out of range"); static_assert(yIdx < 4, "Element index parameter out of range"); static_assert(zIdx < 4, "Element index parameter out of range"); static_assert(wIdx < 4, "Element index parameter out of range"); return _mm_shuffle_ps(m_data, m_data, _MM_SHUFFLE(wIdx, zIdx, yIdx, xIdx)); } FORCE_INLINE Vector Vector::Swizzle(uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx) const { ASSERT(xIdx < 4 && yIdx < 4 && zIdx < 4 && wIdx < 4); uint32_t const elem[4] = { xIdx, yIdx, zIdx, wIdx }; __m128i vControl = _mm_loadu_si128(reinterpret_cast(&elem[0])); return _mm_permutevar_ps(m_data, vControl); } FORCE_INLINE Vector Vector::Shuffle(uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx) const { return Swizzle(xIdx, yIdx, zIdx, wIdx); } template FORCE_INLINE Vector Vector::Shuffle() const { return Swizzle(); } FORCE_INLINE Vector Vector::Length2() const { Vector result; result = _mm_mul_ps(m_data, m_data); auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 1, 1, 1)); // m_x+m_y result = _mm_add_ss(result, vTemp); result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(0, 0, 0, 0)); result = _mm_sqrt_ps(result); return result; } FORCE_INLINE Vector Vector::Length3() const { Vector result; // Perform the dot product on m_x,m_y and m_z result = _mm_mul_ps(m_data, m_data); // vTemp has m_z and m_y auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 2, 1, 2)); // m_x+m_z, m_y result = _mm_add_ss(result, vTemp); // m_y,m_y,m_y,m_y vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1)); // m_x+m_z+m_y,??,??,?? result = _mm_add_ss(result, vTemp); // Splat the length squared result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(0, 0, 0, 0)); // Get the length result = _mm_sqrt_ps(result); return result; } FORCE_INLINE Vector Vector::Length4() const { Vector result; // Perform the dot product on m_x,m_y,m_z and m_w result = _mm_mul_ps(m_data, m_data); // vTemp has m_z and m_w auto vTemp = _mm_shuffle_ps(result, result, _MM_SHUFFLE(3, 2, 3, 2)); // m_x+m_z, m_y+m_w result = _mm_add_ps(result, vTemp); // m_x+m_z,m_x+m_z,m_x+m_z,m_y+m_w result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(1, 0, 0, 0)); // ??,??,m_y+m_w,m_y+m_w vTemp = _mm_shuffle_ps(vTemp, result, _MM_SHUFFLE(3, 3, 0, 0)); // ??,??,m_x+m_z+m_y+m_w,?? result = _mm_add_ps(result, vTemp); // Splat the length result = _mm_shuffle_ps(result, result, _MM_SHUFFLE(2, 2, 2, 2)); // Get the length result = _mm_sqrt_ps(result); return result; } FORCE_INLINE float Vector::GetLength2() const { return Length2().GetX(); } FORCE_INLINE float Vector::GetLength3() const { return Length3().GetX(); } FORCE_INLINE float Vector::GetLength4() const { return Length4().GetX(); } FORCE_INLINE Vector Vector::InverseLength2() const { // Perform the dot product on m_x and m_y auto vLengthSq = _mm_mul_ps(m_data, m_data); // vTemp has m_y splatted auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 1, 1, 1)); // m_x+m_y vLengthSq = _mm_add_ss(vLengthSq, vTemp); vLengthSq = _mm_sqrt_ss(vLengthSq); vLengthSq = _mm_div_ss(Vector::One, vLengthSq); vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(0, 0, 0, 0)); return vLengthSq; } FORCE_INLINE Vector Vector::InverseLength3() const { // Perform the dot product auto vDot = _mm_mul_ps(m_data, m_data); // m_x=Dot.m_y, m_y=Dot.m_z auto vTemp = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(2, 1, 2, 1)); // Result.m_x = m_x+m_y vDot = _mm_add_ss(vDot, vTemp); // m_x=Dot.m_z vTemp = _mm_shuffle_ps(vTemp, vTemp, _MM_SHUFFLE(1, 1, 1, 1)); // Result.m_x = (m_x+m_y)+m_z vDot = _mm_add_ss(vDot, vTemp); // Splat m_x vDot = _mm_shuffle_ps(vDot, vDot, _MM_SHUFFLE(0, 0, 0, 0)); // Get the reciprocal vDot = _mm_sqrt_ps(vDot); // Get the reciprocal vDot = _mm_div_ps(Vector::One, vDot); return vDot; } FORCE_INLINE Vector Vector::InverseLength4() const { // Perform the dot product on m_x,m_y,m_z and m_w auto vLengthSq = _mm_mul_ps(m_data, m_data); // vTemp has m_z and m_w auto vTemp = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(3, 2, 3, 2)); // m_x+m_z, m_y+m_w vLengthSq = _mm_add_ps(vLengthSq, vTemp); // m_x+m_z,m_x+m_z,m_x+m_z,m_y+m_w vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(1, 0, 0, 0)); // ??,??,m_y+m_w,m_y+m_w vTemp = _mm_shuffle_ps(vTemp, vLengthSq, _MM_SHUFFLE(3, 3, 0, 0)); // ??,??,m_x+m_z+m_y+m_w,?? vLengthSq = _mm_add_ps(vLengthSq, vTemp); // Splat the length vLengthSq = _mm_shuffle_ps(vLengthSq, vLengthSq, _MM_SHUFFLE(2, 2, 2, 2)); // Get the reciprocal vLengthSq = _mm_sqrt_ps(vLengthSq); // Accurate! vLengthSq = _mm_div_ps(Vector::One, vLengthSq); return vLengthSq; } FORCE_INLINE float Vector::GetInverseLength2() const { return InverseLength2().GetX(); } FORCE_INLINE float Vector::GetInverseLength3() const { return InverseLength3().GetX(); } FORCE_INLINE float Vector::GetInverseLength4() const { return InverseLength4().GetX(); } FORCE_INLINE Vector Vector::LengthSquared2() const { return Vector::Dot2(m_data, m_data); } FORCE_INLINE Vector Vector::LengthSquared3() const { return Vector::Dot3(m_data, m_data); } FORCE_INLINE Vector Vector::LengthSquared4() const { return Vector::Dot4(m_data, m_data); } FORCE_INLINE float Vector::GetLengthSquared2() const { return LengthSquared2().GetX(); } FORCE_INLINE float Vector::GetLengthSquared3() const { return LengthSquared3().GetX(); } FORCE_INLINE float Vector::GetLengthSquared4() const { return LengthSquared4().GetX(); } FORCE_INLINE Vector Vector::Distance2(const Vector& to) const { return (to - *this).Length2(); } FORCE_INLINE Vector Vector::Distance3(const Vector& to) const { return (to - *this).Length3(); } FORCE_INLINE Vector Vector::Distance4(const Vector& to) const { return (to - *this).Length4(); } FORCE_INLINE float Vector::GetDistance2(const Vector& to) const { return (to - *this).Length2().GetX(); } FORCE_INLINE float Vector::GetDistance3(const Vector& to) const { return (to - *this).Length3().GetX(); } FORCE_INLINE float Vector::GetDistance4(const Vector& to) const { return (to - *this).Length4().GetX(); } FORCE_INLINE Vector Vector::DistanceSquared2(const Vector& to) const { return (to - *this).LengthSquared2(); } FORCE_INLINE Vector Vector::DistanceSquared3(const Vector& to) const { return (to - *this).LengthSquared3(); } FORCE_INLINE Vector Vector::DistanceSquared4(const Vector& to) const { return (to - *this).LengthSquared4(); } FORCE_INLINE float Vector::GetDistanceSquared2(const Vector& to) const { return (to - *this).GetLengthSquared2(); } FORCE_INLINE float Vector::GetDistanceSquared3(const Vector& to) const { return (to - *this).GetLengthSquared3(); } FORCE_INLINE float Vector::GetDistanceSquared4(const Vector& to) const { return (to - *this).GetLengthSquared4(); } FORCE_INLINE bool Vector::IsNormalized2() const { return (LengthSquared2() - Vector::One).Abs().IsLessThanEqual4(Vector::NormalizeCheckThreshold); } FORCE_INLINE bool Vector::IsNormalized3() const { return (LengthSquared3() - Vector::One).Abs().IsLessThanEqual4(Vector::NormalizeCheckThreshold); } FORCE_INLINE bool Vector::IsNormalized4() const { return (LengthSquared4() - Vector::One).Abs().IsLessThanEqual4(Vector::NormalizeCheckThreshold); } FORCE_INLINE Vector Vector::InBounds(const Vector& bounds) const { // Test if less than or equal auto vTemp1 = _mm_cmple_ps(m_data, bounds); // Negate the bounds auto vTemp2 = _mm_mul_ps(bounds, Vector::NegativeOne); // Test if greater or equal (Reversed) vTemp2 = _mm_cmple_ps(vTemp2, m_data); // Blend answers vTemp1 = _mm_and_ps(vTemp1, vTemp2); return vTemp1; } FORCE_INLINE bool Vector::IsInBounds2(const Vector& bounds) const { return ((_mm_movemask_ps(InBounds(bounds)) & 0x3) == 0x3) != 0; } FORCE_INLINE bool Vector::IsInBounds3(const Vector& bounds) const { return ((_mm_movemask_ps(InBounds(bounds)) & 0x7) == 0x7) != 0; } FORCE_INLINE bool Vector::IsInBounds4(const Vector& bounds) const { return (_mm_movemask_ps(InBounds(bounds)) == 0x0f) != 0; } FORCE_INLINE Vector Vector::Equal(const Vector& v) const { return _mm_cmpeq_ps(*this, v); } FORCE_INLINE bool Vector::IsEqual2(const Vector& v) const { return (((_mm_movemask_ps(Equal(v)) & 3) == 3) != 0); } FORCE_INLINE bool Vector::IsEqual3(const Vector& v) const { return (((_mm_movemask_ps(Equal(v)) & 7) == 7) != 0); } FORCE_INLINE bool Vector::IsEqual4(const Vector& v) const { return ((_mm_movemask_ps(Equal(v)) == 0x0f) != 0); } FORCE_INLINE Vector Vector::NearEqual(const Vector& v, const Vector& epsilon) const { // Get the difference auto vDelta = _mm_sub_ps(m_data, v); // Get the absolute value of the difference auto vTemp = _mm_setzero_ps(); vTemp = _mm_sub_ps(vTemp, vDelta); vTemp = _mm_max_ps(vTemp, vDelta); vTemp = _mm_cmple_ps(vTemp, epsilon); return vTemp; } FORCE_INLINE bool Vector::IsNearEqual2(const Vector& v, float epsilon) const { return IsNearEqual2(v, Vector(epsilon)); } FORCE_INLINE bool Vector::IsNearEqual3(const Vector& v, float epsilon) const { return IsNearEqual3(v, Vector(epsilon)); } FORCE_INLINE bool Vector::IsNearEqual4(const Vector& v, float epsilon) const { return IsNearEqual4(v, Vector(epsilon)); } FORCE_INLINE bool Vector::IsNearEqual2(const Vector& v, const Vector& epsilon) const { return (((_mm_movemask_ps(NearEqual(v, epsilon)) & 3) == 0x3) != 0); } FORCE_INLINE bool Vector::IsNearEqual3(const Vector& v, const Vector& epsilon) const { return (((_mm_movemask_ps(NearEqual(v, epsilon)) & 7) == 0x7) != 0); } FORCE_INLINE bool Vector::IsNearEqual4(const Vector& v, const Vector& epsilon) const { return ((_mm_movemask_ps(NearEqual(v, epsilon)) == 0xf) != 0); } FORCE_INLINE Vector Vector::GreaterThan(const Vector& v) const { return _mm_cmpgt_ps(m_data, v); } FORCE_INLINE bool Vector::IsAnyGreaterThan(const Vector& v) const { return !GreaterThan(v).IsZero4(); } FORCE_INLINE bool Vector::IsGreaterThan2(const Vector& v) const { return (((_mm_movemask_ps(GreaterThan(v)) & 3) == 3) != 0); } FORCE_INLINE bool Vector::IsGreaterThan3(const Vector& v) const { return (((_mm_movemask_ps(GreaterThan(v)) & 7) == 7) != 0); } FORCE_INLINE bool Vector::IsGreaterThan4(const Vector& v) const { return ((_mm_movemask_ps(GreaterThan(v)) == 0x0f) != 0); } FORCE_INLINE Vector Vector::GreaterThanEqual(const Vector& v) const { return _mm_cmpge_ps(m_data, v); } FORCE_INLINE bool Vector::IsAnyGreaterThanEqual(const Vector& v) const { return !GreaterThanEqual(v).IsZero4(); } FORCE_INLINE bool Vector::IsGreaterThanEqual2(const Vector& v) const { return ((_mm_movemask_ps(GreaterThanEqual(v)) & 3) == 3) != 0; } FORCE_INLINE bool Vector::IsGreaterThanEqual3(const Vector& v) const { return ((_mm_movemask_ps(GreaterThanEqual(v)) & 7) == 7) != 0; } FORCE_INLINE bool Vector::IsGreaterThanEqual4(const Vector& v) const { return (_mm_movemask_ps(GreaterThanEqual(v)) == 0x0f) != 0; } FORCE_INLINE Vector Vector::LessThan(const Vector& v) const { return _mm_cmplt_ps(m_data, v); } FORCE_INLINE bool Vector::IsAnyLessThan(const Vector& v) const { return !LessThan(v).IsZero4(); } FORCE_INLINE bool Vector::IsLessThan2(const Vector& v) const { return (((_mm_movemask_ps(LessThan(v)) & 3) == 3) != 0); } FORCE_INLINE bool Vector::IsLessThan3(const Vector& v) const { return (((_mm_movemask_ps(LessThan(v)) & 7) == 7) != 0); } FORCE_INLINE bool Vector::IsLessThan4(const Vector& v) const { return ((_mm_movemask_ps(LessThan(v)) == 0x0f) != 0); } FORCE_INLINE Vector Vector::LessThanEqual(const Vector& v) const { return _mm_cmple_ps(m_data, v); } FORCE_INLINE bool Vector::IsAnyLessThanEqual(const Vector& v) const { return !LessThanEqual(v).IsZero4(); } FORCE_INLINE bool Vector::IsLessThanEqual2(const Vector& v) const { return (((_mm_movemask_ps(LessThanEqual(v)) & 3) == 3) != 0); } FORCE_INLINE bool Vector::IsLessThanEqual3(const Vector& v) const { return (((_mm_movemask_ps(LessThanEqual(v)) & 7) == 7) != 0); } FORCE_INLINE bool Vector::IsLessThanEqual4(const Vector& v) const { return ((_mm_movemask_ps(LessThanEqual(v)) == 0x0f) != 0); } FORCE_INLINE Vector Vector::EqualsZero() const { return Equal(Vector::Zero); } FORCE_INLINE bool Vector::IsAnyEqualToZero2() const { return !EqualsZero().IsZero2(); } FORCE_INLINE bool Vector::IsAnyEqualToZero3() const { return !EqualsZero().IsZero3(); } FORCE_INLINE bool Vector::IsAnyEqualToZero4() const { return !EqualsZero().IsZero4(); } FORCE_INLINE bool Vector::IsZero2() const { return IsEqual2(Vector::Zero); } FORCE_INLINE bool Vector::IsZero3() const { return IsEqual3(Vector::Zero); } FORCE_INLINE bool Vector::IsZero4() const { return IsEqual4(Vector::Zero); } FORCE_INLINE Vector Vector::NearEqualsZero(float epsilon) const { return NearEqual(Vector::Zero, Vector(epsilon)); } FORCE_INLINE bool Vector::IsNearZero2(float epsilon) const { return IsNearEqual2(Vector::Zero, Vector(epsilon)); } FORCE_INLINE bool Vector::IsNearZero3(float epsilon) const { return IsNearEqual3(Vector::Zero, Vector(epsilon)); } FORCE_INLINE bool Vector::IsNearZero4(float epsilon) const { return IsNearEqual4(Vector::Zero, Vector(epsilon)); } FORCE_INLINE Vector Vector::EqualsInfinity() const { __m128 vTemp = _mm_and_ps(m_data, SIMD::g_absMask); return _mm_cmpeq_ps(vTemp, Vector::Infinity); } FORCE_INLINE bool Vector::IsInfinite2() const { return (_mm_movemask_ps(EqualsInfinity()) & 3) != 0; } FORCE_INLINE bool Vector::IsInfinite3() const { return (_mm_movemask_ps(EqualsInfinity()) & 7) != 0; } FORCE_INLINE bool Vector::IsInfinite4() const { return (_mm_movemask_ps(EqualsInfinity()) != 0); } FORCE_INLINE Vector Vector::EqualsNaN() const { return _mm_cmpneq_ps(m_data, m_data); } FORCE_INLINE bool Vector::IsNaN2() const { return (_mm_movemask_ps(EqualsNaN()) & 3) != 0; } FORCE_INLINE bool Vector::IsNaN3() const { return (_mm_movemask_ps(EqualsNaN()) & 7) != 0; } FORCE_INLINE bool Vector::IsNaN4() const { return (_mm_movemask_ps(EqualsNaN()) != 0); } FORCE_INLINE bool Vector::IsParallelTo(const Vector& v) const { Vector const vAbsDot = Vector::Dot3(*this, v).GetAbs(); Vector const vAbsDelta = Vector::One - vAbsDot; return vAbsDelta.IsLessThanEqual4(Vector::Epsilon); } FORCE_INLINE void Vector::ToDirectionAndLength2(Vector& direction, float& length) const { Vector const vLength = Length2(); direction = Vector::Select(*this, Vector::Zero, Select0011); direction /= vLength; length = vLength.ToFloat(); } FORCE_INLINE void Vector::ToDirectionAndLength3(Vector& direction, float& length) const { Vector const vLength = Length3(); direction = Vector::Select(*this, Vector::Zero, Select0001); direction /= vLength; length = vLength.ToFloat(); } FORCE_INLINE bool Vector::operator==(const Vector& rhs) const { return IsEqual4(rhs); } FORCE_INLINE bool Vector::operator!=(const Vector& rhs) const { return !IsEqual4(rhs); } }