cs686's picture
Deploy Kimodo ZeroGPU motion API
a550c4e verified
Raw
History Blame Contribute Delete
74.2 kB
/*
* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <cstring>
#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<uint32_t PermuteX, uint32_t PermuteY, uint32_t PermuteZ, uint32_t PermuteW>
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<uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx>
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<const __m128i*>(&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<uint32_t xIdx, uint32_t yIdx, uint32_t zIdx, uint32_t wIdx>
FORCE_INLINE Vector Vector::Shuffle() const
{
return Swizzle<xIdx, yIdx, zIdx, wIdx>();
}
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);
}
}