engine/engine/inc/uf/utils/math/vector/pod.inl

528 lines
20 KiB
C++

#if !__clang__ && __GNUC__
#pragma GCC push_options
#pragma GCC optimize ("unroll-loops")
#endif
#if UF_USE_SIMD
#include "simd.h"
#endif
#include "redundancy.inl"
// Equality checking
template<typename T>
pod::Vector1t<T> /*UF_API*/ uf::vector::create( T x ) { pod::Vector1t<T> vec; vec.x = x; return vec; }
template<typename T>
pod::Vector2t<T> /*UF_API*/ uf::vector::create( T x, T y ) { pod::Vector2t<T> vec; vec.x = x, vec.y = y; return vec; }
template<typename T>
pod::Vector3t<T> /*UF_API*/ uf::vector::create( T x, T y, T z ) { pod::Vector3t<T> vec; vec.x = x, vec.y = y, vec.z = z; return vec; }
template<typename T>
pod::Vector4t<T> /*UF_API*/ uf::vector::create( T x, T y, T z, T w ) { pod::Vector4t<T> vec; vec.x = x, vec.y = y, vec.z = z, vec.w = w; return vec; }
template<typename T, size_t N>
pod::Vector<T, N> /*UF_API*/ uf::vector::copy( const pod::Vector<T, N>& v ) { return v; }
template<typename T, size_t N, typename U>
pod::Vector<T, N> /*UF_API*/ uf::vector::cast( const U& from ) {
ALIGN16 pod::Vector<T, N> to;
#pragma unroll // GCC unroll N
for ( auto i = 0; i < N && i < U::size; ++i )
to[i] = from[i];
return to;
}
// Equality checking
template<typename T> // Equality check between two vectors (less than)
int /*UF_API*/ uf::vector::compareTo( const T& left, const T& right ) {
return memcmp( &left, &right, T::size );
}
template<typename T> // Equality check between two vectors (equals)
bool /*UF_API*/ uf::vector::equals( const T& left, const T& right ) {
// return uf::vector::compareTo(left, right) == 0;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
if ( left[i] != right[i] ) return false;
return true;
}
// Basic arithmetic
template<typename T> // Adds two vectors of same type and size together
T /*UF_API*/ uf::vector::add( const T& left, const T& right ) {
#if UF_USE_SIMD
return uf::simd::add( left, right );
#endif
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = left[i] + right[i];
return res;
}
template<typename T> // Multiplies this vector by a scalar
T /*UF_API*/ uf::vector::add( const T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return uf::simd::add( vector, scalar );
#endif
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = vector[i] + scalar;
return res;
}
template<typename T> // Subtracts two vectors of same type and size together
T /*UF_API*/ uf::vector::subtract( const T& left, const T& right ) {
#if UF_USE_SIMD
return uf::simd::sub( left, right );
#endif
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = left[i] - right[i];
return res;
}
template<typename T> // Multiplies this vector by a scalar
T /*UF_API*/ uf::vector::subtract( const T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return uf::simd::sub( vector, scalar );
#endif
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = vector[i] - scalar;
return res;
}
template<typename T> // Multiplies two vectors of same type and size together
T /*UF_API*/ uf::vector::multiply( const T& left, const T& right ) {
#if UF_USE_SIMD
return uf::simd::mul( left, right );
#endif
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = left[i] * right[i];
return res;
}
template<typename T> // Multiplies this vector by a scalar
T /*UF_API*/ uf::vector::multiply( const T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return uf::simd::mul( vector, scalar );
#endif
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = vector[i] * scalar;
return res;
}
template<typename T> // Divides two vectors of same type and size together
T /*UF_API*/ uf::vector::divide( const T& left, const T& right ) {
#if UF_USE_SIMD
return uf::simd::div( left, right );
#elif UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = MATH_Fast_Divide(left[i], right[i]);
return res;
#else
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = left[i] * right[i];
return res;
#endif
}
template<typename T> // Divides this vector by a scalar
T /*UF_API*/ uf::vector::divide( const T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return uf::simd::div( vector, scalar );
#elif UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = MATH_Fast_Divide(vector[i], scalar);
return res;
#else
ALIGN16 T res;
scalar = 1.0 / scalar;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = vector[i] * scalar;
return res;
#endif
}
template<typename T> // Compute the sum of all components
typename T::type_t /*UF_API*/ uf::vector::sum( const T& vector ) {
typename T::type_t res = 0;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res += vector[i];
return res;
}
template<typename T> // Compute the product of all components
typename T::type_t /*UF_API*/ uf::vector::product( const T& vector ) {
typename T::type_t res = 0;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res *= vector[i];
return res;
}
template<typename T> // Flip sign of all components
T /*UF_API*/ uf::vector::negate( const T& vector ) {
#if UF_USE_SIMD
return uf::simd::mul( vector, -1.f );
#endif
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = -vector[i];
return res;
}
template<typename T> //
T /*UF_API*/ uf::vector::abs( const T& vector ) {
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = abs(vector[i]);
return res;
}
// Writes to first value
template<typename T> // Adds two vectors of same type and size together
T& /*UF_API*/ uf::vector::add_( T& left, const T& right ) {
#if UF_USE_SIMD
return left = uf::vector::add( (const T&) left, right );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
left[i] += right[i];
return left;
}
template<typename T> // Multiplies this vector by a scalar
T& /*UF_API*/ uf::vector::add_( T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return vector = uf::vector::add( (const T&) vector, scalar );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
vector[i] += scalar;
return vector;
}
template<typename T> // Subtracts two vectors of same type and size together
T& /*UF_API*/ uf::vector::subtract_( T& left, const T& right ) {
#if UF_USE_SIMD
return left = uf::vector::subtract( (const T&) left, right );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
left[i] -= right[i];
return left;
}
template<typename T> // Multiplies this vector by a scalar
T& /*UF_API*/ uf::vector::subtract_( T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return vector = uf::vector::subtract( (const T&) vector, scalar );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
vector[i] -= scalar;
return vector;
}
template<typename T> // Multiplies two vectors of same type and size together
T& /*UF_API*/ uf::vector::multiply_( T& left, const T& right ) {
#if UF_USE_SIMD
return left = uf::vector::multiply( (const T&) left, right );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
left[i] *= right[i];
return left;
}
template<typename T> // Multiplies this vector by a scalar
T& /*UF_API*/ uf::vector::multiply_( T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return vector = uf::vector::multiply( (const T&) vector, scalar );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
vector[i] *= scalar;
return vector;
}
template<typename T> // Divides two vectors of same type and size together
T& /*UF_API*/ uf::vector::divide_( T& left, const T& right ) {
#if UF_USE_SIMD
return left = uf::vector::divide( (const T&) left, right );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
left[i] /= right[i];
return left;
}
template<typename T> // Divides this vector by a scalar
T& /*UF_API*/ uf::vector::divide_( T& vector, /*const typename T::type_t&*/ typename T::type_t scalar ) {
#if UF_USE_SIMD
return vector = uf::vector::divide( (const T&) vector, scalar );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
vector[i] /= scalar;
return vector;
}
template<typename T> // Flip sign of all components
T& /*UF_API*/ uf::vector::negate_( T& vector ) {
#if UF_USE_SIMD
return vector = uf::vector::negate( (const T&) vector );
#endif
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
vector[i] = -vector[i];
return vector;
}
template<typename T> // Normalizes a vector
T& /*UF_API*/ uf::vector::normalize_( T& vector ) {
typename T::type_t norm = uf::vector::norm(vector);
if ( norm == 0 ) return vector;
return vector = uf::vector::divide((const T&) vector, norm);
}
template<typename T> //
T /*UF_API*/ uf::vector::min( const T& left, const T& right ) {
T res = left;
for ( auto i = 0; i < T::size; ++i ) res[i] = std::min( left[i], right[i] );
return res;
}
template<typename T> //
T /*UF_API*/ uf::vector::max( const T& left, const T& right ) {
T res;
for ( auto i = 0; i < T::size; ++i ) res[i] = std::max( left[i], right[i] );
return res;
}
template<typename T> //
T /*UF_API*/ uf::vector::ceil( const T& vector ) {
T res;
for ( auto i = 0; i < T::size; ++i ) res[i] = std::ceil( vector[i] );
return res;
}
template<typename T> //
T /*UF_API*/ uf::vector::floor( const T& vector ) {
T res;
for ( auto i = 0; i < T::size; ++i ) res[i] = std::floor( vector[i] );
return res;
}
template<typename T> //
T /*UF_API*/ uf::vector::round( const T& vector ) {
T res;
for ( auto i = 0; i < T::size; ++i ) res[i] = ::round( vector[i] );
return res;
}
// Complex arithmetic
template<typename T> // Compute the dot product between two vectors
typename T::type_t /*UF_API*/ uf::vector::dot( const T& left, const T& right ) {
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
return MATH_fipr( UF_EZ_VEC4(left, T::size), UF_EZ_VEC4(right, T::size) );
#elif UF_USE_SIMD
return uf::simd::dot( left, right );
#endif
return uf::vector::sum(uf::vector::multiply(left, right));
}
template<typename T> // Compute the angle between two vectors
pod::Angle /*UF_API*/ uf::vector::angle( const T& a, const T& b ) {
return acos(uf::vector::dot(a, b));
}
template<typename T> // Linearly interpolate between two vectors
T /*UF_API*/ uf::vector::lerp( const T& from, const T& to, double delta, bool clamp ) {
delta = fmax( 0, fmin(1,delta) );
// from + ( ( to - from ) * delta )
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = MATH_Lerp( from[i], to[i], delta );
return res;
#elif UF_USE_SIMD
return uf::simd::add(from, uf::simd::mul( uf::simd::sub(to, from), (float) delta) );
#endif
return uf::vector::add(from, uf::vector::multiply( uf::vector::subtract(to, from), delta ) );
}
template<typename T> // Linearly interpolate between two vectors
T /*UF_API*/ uf::vector::lerp( const T& from, const T& to, const T& delta, bool clamp ) {
//delta = fmax( 0, fmin(1,delta) );
// from + ( ( to - from ) * delta )
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
ALIGN16 T res;
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < T::size; ++i )
res[i] = MATH_Lerp( from[i], to[i], delta[i] );
return res;
#elif UF_USE_SIMD
return uf::simd::add(from, uf::simd::mul( uf::simd::sub(to, from), delta) );
#endif
return uf::vector::add(from, uf::vector::multiply( uf::vector::subtract(to, from), delta ) );
}
template<typename T> // Spherically interpolate between two vectors
T /*UF_API*/ uf::vector::slerp( const T& from, const T& to, double delta, bool clamp ) {
if ( clamp ) delta = fmax( 0, fmin(1,delta) );
typename T::type_t dot = uf::vector::dot(from, to);
typename T::type_t theta = acos(dot);
typename T::type_t sTheta = sin(theta);
typename T::type_t w1 = sin((1.0f - delta) * theta / sTheta);
typename T::type_t w2 = sin( delta * theta / sTheta );
#if UF_USE_SIMD
return uf::simd::add( uf::simd::mul( from, w1 ), uf::simd::mul( to, w2 ) );
#endif
return uf::vector::add(uf::vector::multiply(from, w1), uf::vector::multiply(to, w2));
}
template<typename T> //
T /*UF_API*/ uf::vector::mix( const T& x, const T& y, double a, bool clamp ) {
if ( clamp ) a = fmax( 0, fmin(1,a) );
// x * (1.0 - a) + y * a
#if UF_USE_SIMD
return uf::simd::add( uf::simd::mul( x, 1.0f - (float) a ), uf::simd::mul( y, (float) a ) );
#endif
return uf::vector::add( uf::vector::multiply( x, 1 - a ), uf::vector::multiply( y, a ) );
}
template<typename T> // Compute the distance between two vectors (doesn't sqrt)
typename T::type_t /*UF_API*/ uf::vector::distanceSquared( const T& a, const T& b ) {
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
ALIGN16 T delta = uf::vector::subtract(b, a);
return MATH_Sum_of_Squares( UF_EZ_VEC4( delta, T::size ) );
#elif UF_USE_SIMD
uf::simd::value<typename T::type_t> delta = uf::simd::sub( b, a );
// return uf::vector::sum( uf::simd::vector( uf::simd::mul( delta, delta ) ) );
return uf::simd::dot( delta, delta );
#else
ALIGN16 T delta = uf::vector::subtract(b, a);
return uf::vector::dot( delta, delta );
/*
ALIGN16 T delta = uf::vector::subtract(b, a);
uf::vector::multiply( delta, delta );
return uf::vector::sum(delta);
*/
#endif
}
template<typename T> // Compute the distance between two vectors
typename T::type_t /*UF_API*/ uf::vector::distance( const T& a, const T& b ) {
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
return MATH_Fast_Sqrt(uf::vector::distanceSquared(a,b));
#endif
return sqrt(uf::vector::distanceSquared(a,b));
}
template<typename T> // Gets the magnitude of the vector
typename T::type_t /*UF_API*/ uf::vector::magnitude( const T& vector ) {
return uf::vector::dot(vector, vector);
}
template<typename T> // Compute the norm of the vector
typename T::type_t /*UF_API*/ uf::vector::norm( const T& vector ) {
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
return MATH_Fast_Sqrt( uf::vector::magnitude(vector) );
#endif
return sqrt( uf::vector::magnitude(vector) );
}
template<typename T> // Normalizes a vector
T /*UF_API*/ uf::vector::normalize( const T& vector ) {
typename T::type_t norm = uf::vector::norm(vector);
if ( norm == 0 ) return vector;
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
return uf::vector::multiply(vector, MATH_fsrra(norm));
#endif
return uf::vector::divide(vector, norm);
}
template<typename T> // Normalizes a vector
void /*UF_API*/ uf::vector::orthonormalize( T& normal, T& tangent ) {
normal = uf::vector::normalize( normal );
ALIGN16 T norm = normal;
ALIGN16 T tan = uf::vector::normalize( tangent );
tangent = uf::vector::subtract( tan, uf::vector::multiply( norm, uf::vector::dot( norm, tan ) ) );
tangent = uf::vector::normalize( tangent );
}
template<typename T> // Normalizes a vector
T /*UF_API*/ uf::vector::orthonormalize( const T& x, const T& y ) {
return uf::vector::normalize( uf::vector::subtract( x, uf::vector::multiply( y, uf::vector::dot( y, x ) ) ) );
}
template<typename T> // Normalizes a vector
T /*UF_API*/ uf::vector::cross( const T& a, const T& b ) {
#if UF_USE_SIMD
uf::simd::value<typename T::type_t> x = a;
uf::simd::value<typename T::type_t> y = b;
#if SSE_INSTR_SET >= 7
uf::simd::value<typename T::type_t> tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
uf::simd::value<typename T::type_t> tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
tmp1 = _mm_mul_ps(tmp1,y);
uf::simd::value<typename T::type_t> tmp2 = _mm_fmsub_ps( tmp0,x, tmp1 );
uf::simd::value<typename T::type_t> res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
return res;
#else
uf::simd::value<typename T::type_t> tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
uf::simd::value<typename T::type_t> tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
tmp0 = _mm_mul_ps(tmp0,x);
tmp1 = _mm_mul_ps(tmp1,y);
uf::simd::value<typename T::type_t> tmp2 = _mm_sub_ps(tmp0,tmp1);
uf::simd::value<typename T::type_t> res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
return res;
#endif
#elif UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
ALIGN16 auto res = MATH_Cross_Product( a.x, a.y, a.z, b.x, b.y, b.z );
return *((T*) &res);
#else
ALIGN16 T res{
a.y * b.z - b.y * a.z,
a.z * b.x - b.z * a.x,
a.x * b.y - b.x * a.y
};
return res;
#endif
}
template<typename T> // Normalizes a vector
uf::stl::string /*UF_API*/ uf::vector::toString( const T& v ) {
uint_fast8_t size = T::size;
uf::stl::stringstream ss;
ss << "Vector(";
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < size; ++i ) {
ss << v[i];
if ( i + 1 < size ) ss << ", ";
}
ss << ")";
return ss.str();
}
template<typename T, size_t N>
ext::json::Value /*UF_API*/ uf::vector::encode( const pod::Vector<T,N>& v, const ext::json::EncodingSettings& settings ) {
ext::json::Value json;
if ( settings.quantize )
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < N; ++i )
json[i] = uf::math::quantizeShort( v[i] );
else
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < N; ++i )
json[i] = v[i];
return json;
}
template<typename T, size_t N>
pod::Vector<T,N>& /*UF_API*/ uf::vector::decode( const ext::json::Value& json, pod::Vector<T,N>& v ) {
if ( ext::json::isArray(json) )
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < N; ++i )
v[i] = json[i].as<T>(v[i]);
else if ( ext::json::isObject(json) ) {
auto i = 0;
ext::json::forEach(json, [&](const ext::json::Value& c){
if ( i >= N ) return;
v[i] = uf::math::unquantize( c.as<T>(v[i]) );
++i;
});
}
return v;
}
template<typename T, size_t N>
pod::Vector<T,N> /*UF_API*/ uf::vector::decode( const ext::json::Value& json, const pod::Vector<T,N>& _v ) {
pod::Vector<T,N> v = _v;
if ( ext::json::isArray(json) )
#pragma unroll // GCC unroll T::size
for ( auto i = 0; i < N; ++i )
v[i] = json[i].as<T>(_v[i]);
else if ( ext::json::isObject(json) ) {
auto i = 0;
ext::json::forEach(json, [&](const ext::json::Value& c){
if ( i >= N ) return;
v[i] = c.as<T>(_v[i]);
++i;
});
}
return v;
}
#if !__clang__ && __GNUC__
#pragma GCC pop_options
#endif