Compare commits

...

3 Commits

Author SHA1 Message Date
4d66db4a8a Merge branch 'refactor/rewrite' 2026-05-30 19:35:38 +08:00
bf74d7163f Update currenct expressions list 2026-05-30 19:35:07 +08:00
df718217d2 Rewrite 2026-05-29 21:16:48 +08:00
49 changed files with 895 additions and 838 deletions

View File

@ -0,0 +1,34 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "concepts.h"
#include "traits.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
struct Add : Expr<Add<L, R>>
{
TNVX_INLINE
Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
tnvx_ref_or_value_t<L> _l;
tnvx_ref_or_value_t<L> _r;
};
template<vec_expr L, vec_expr R>
[[nodiscard]] TNVX_INLINE
Add<L, R> operator+(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
template<vec_expr L, vec_expr R>
inline constexpr bool is_vec_expr<Add<L, R>> = true;
}
#include "add_impl.hpp"

View File

@ -0,0 +1,30 @@
#pragma once
#include "add.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
TNVX_INLINE
Add<L, R>::Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<vec_expr L, vec_expr R>
[[nodiscard]] TNVX_INLINE
vf4 Add<L, R>::eval() const noexcept
{
return detail::add(_l.eval(), _r.eval());
}
template<vec_expr L, vec_expr R>
[[nodiscard]] TNVX_INLINE
Add<L, R> operator+(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
{
return { l, r };
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "traits.h"
#include "expression.h"
#include <concepts>
namespace tnvx
{
template<typename T>
concept expression = requires (T t)
{
{ t.eval() } -> std::same_as<vf4>;
{ t.self() } -> std::same_as<const T&>;
}
&& std::is_base_of_v<Expr<T>, T>;
template<typename T>
concept vec_expr = expression<T> && is_vec_expr<T>;
template<typename T>
concept scalar_expr = expression<T> && is_scalar_expr<T>;
}

View File

@ -0,0 +1,3 @@
#pragma once
#define HAS_SSE42 1

View File

@ -0,0 +1,88 @@
#pragma once
#include "config.h"
#include "defines.h"
#if defined(HAS_SSE42)
#include <smmintrin.h>
using vf4 = __m128;
#else
#endif
namespace tnvx::detail
{
[[nodiscard]] TNVX_INLINE
bool eq(vf4 a, vf4 b)
{
return _mm_movemask_ps(_mm_cmpeq_ps(a, b)) == 0b1111;
}
[[nodiscard]] TNVX_INLINE
bool approx_eq(vf4 a, vf4 b, float eps) noexcept
{
vf4 abs_diff = _mm_andnot_ps(_mm_set1_ps(-0.0f), _mm_sub_ps(a, b));
return _mm_movemask_ps(_mm_cmple_ps(abs_diff, _mm_set1_ps(eps))) == 0b1111;
}
[[nodiscard]] TNVX_INLINE
vf4 scalar(float value) noexcept
{
return _mm_set_ps1(value);
}
[[nodiscard]] TNVX_INLINE
vf4 add(vf4 l, vf4 r) noexcept
{
return _mm_add_ps(l, r);
}
[[nodiscard]] TNVX_INLINE
vf4 sub(vf4 l, vf4 r) noexcept
{
return _mm_sub_ps(l, r);
}
[[nodiscard]] TNVX_INLINE
vf4 mul(vf4 l, vf4 r) noexcept
{
return _mm_mul_ps(l, r);
}
[[nodiscard]] TNVX_INLINE
vf4 div(vf4 l, vf4 r) noexcept
{
return _mm_div_ps(l, r);
}
[[nodiscard]] TNVX_INLINE
vf4 dot3(vf4 l, vf4 r) noexcept
{
return _mm_dp_ps(l, r, 0b01111111);
}
[[nodiscard]] TNVX_INLINE
vf4 magnitude3(vf4 v) noexcept
{
return _mm_sqrt_ps(_mm_dp_ps(v, v, 0b01111111));
}
[[nodiscard]] TNVX_INLINE
vf4 norm3(vf4 v) noexcept
{
return _mm_blend_ps(_mm_div_ps(v, magnitude3(v)), v, 0b1000);
}
[[nodiscard]] TNVX_INLINE
vf4 cross3(vf4 l, vf4 r) noexcept
{
vf4 a_yzx = _mm_shuffle_ps(l, l, _MM_SHUFFLE(3, 0, 2, 1));
vf4 b_yzx = _mm_shuffle_ps(r, r, _MM_SHUFFLE(3, 0, 2, 1));
vf4 t = _mm_sub_ps(_mm_mul_ps(l, b_yzx),
_mm_mul_ps(a_yzx, r));
return _mm_shuffle_ps(t, t, _MM_SHUFFLE(3, 0, 2, 1));
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
struct Cross3 : Expr<Cross3<L, R>>
{
TNVX_INLINE
Cross3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
tnvx_ref_or_value_t<L> _l;
tnvx_ref_or_value_t<R> _r;
};
template<vec_expr L, vec_expr R>
[[nodiscard]] TNVX_INLINE
Cross3<L, R> cross3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
template<vec_expr L, vec_expr R> inline constexpr bool is_vec_expr<Cross3<L, R>> = true;
}
#include "cross3_impl.hpp"

View File

@ -0,0 +1,30 @@
#pragma once
#include "cross3.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
TNVX_INLINE
Cross3<L, R>::Cross3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<vec_expr L, vec_expr R>
TNVX_INLINE
vf4 Cross3<L, R>::eval() const noexcept
{
return detail::cross3(_l.eval(), _r.eval());
}
template<vec_expr L, vec_expr R>
TNVX_INLINE
Cross3<L, R> cross3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
{
return { l, r };
}
}

View File

@ -1,4 +1,4 @@
#pragma once #pragma once
#if defined(_MSC_VER) #if defined(_MSC_VER)
#define TNVX_INLINE __forceinline #define TNVX_INLINE __forceinline

View File

@ -0,0 +1,35 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
#include "scalar.h"
namespace tnvx
{
template<vec_expr L, scalar_expr R>
struct Div : Expr<Div<L, R>>
{
TNVX_INLINE
Div(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
tnvx_ref_or_value_t<L> _l;
tnvx_ref_or_value_t<R> _r;
};
template<vec_expr E>
[[nodiscard]] TNVX_INLINE
Div<E, Scalar> operator/(const E& TNVX_RESTRICT l, float r) noexcept;
template<vec_expr L, scalar_expr R> inline constexpr bool is_vec_expr<Div<L, R>> = true;
}
#include "div_impl.hpp"

View File

@ -0,0 +1,30 @@
#pragma once
#include "div.h"
namespace tnvx
{
template<vec_expr L, scalar_expr R>
TNVX_INLINE
tnvx::Div<L, R>::Div(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<vec_expr L, scalar_expr R>
TNVX_INLINE
vf4 Div<L, R>::eval() const noexcept
{
return detail::div(_l.eval(), _r.eval());
}
template<vec_expr E>
TNVX_INLINE
Div<E, Scalar> operator/(const E& TNVX_RESTRICT l, float r) noexcept
{
return { l, Scalar(r) };
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
struct Dot3 : Expr<Dot3<L, R>>
{
TNVX_INLINE
Dot3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
[[nodiscard]] TNVX_INLINE
operator float() const noexcept;
private:
tnvx_ref_or_value_t<L> _l;
tnvx_ref_or_value_t<R> _r;
};
template<vec_expr L, vec_expr R>
[[nodiscard]] TNVX_INLINE
Dot3<L, R> dot3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
template<vec_expr L, vec_expr R> inline constexpr bool is_scalar_expr<Dot3<L, R>> = true;
}
#include "dot3_impl.h"

View File

@ -0,0 +1,37 @@
#pragma once
#include "dot3.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
TNVX_INLINE
Dot3<L, R>::Dot3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<vec_expr L, vec_expr R>
TNVX_INLINE
vf4 Dot3<L, R>::eval() const noexcept
{
return detail::dot3(_l.eval(), _r.eval());
}
template<vec_expr L, vec_expr R>
TNVX_INLINE
Dot3<L, R>::operator float() const noexcept
{
return _mm_cvtss_f32(eval());
}
template<vec_expr L, vec_expr R>
TNVX_INLINE
Dot3<L, R> dot3(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
{
return { l, r };
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "core.h"
namespace tnvx
{
template<typename Derived>
struct Expr
{
[[nodiscard]] TNVX_INLINE
const Derived& self() const noexcept;
};
}
#include "expression_impl.hpp"

View File

@ -0,0 +1,14 @@
#include "defines.h"
#include "expression.h"
namespace tnvx
{
template<typename Derived>
TNVX_INLINE
const Derived& Expr<Derived>::self() const noexcept
{
return static_cast<const Derived&>(*this);
}
}

View File

@ -0,0 +1,35 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
namespace tnvx
{
template<vec_expr E>
struct Magn3 : Expr<Magn3<E>>
{
TNVX_INLINE
explicit Magn3(const E& TNVX_RESTRICT e) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
[[nodiscard]] TNVX_INLINE
operator float() const noexcept;
private:
tnvx_ref_or_value_t<E> _e;
};
template<vec_expr E>
TNVX_INLINE
Magn3<E> magnitude3(const E& TNVX_RESTRICT e) noexcept;
template<vec_expr E> inline constexpr bool is_scalar_expr<Magn3<E>> = true;
}
#include "magnitude3_impl.hpp"

View File

@ -0,0 +1,37 @@
#pragma once
#include "magnitude3.h"
#include "dot3.h"
namespace tnvx
{
template<vec_expr E>
TNVX_INLINE
Magn3<E>::Magn3(const E& TNVX_RESTRICT e) noexcept
: _e(e)
{
}
template<vec_expr E>
TNVX_INLINE
vf4 Magn3<E>::eval() const noexcept
{
return detail::magnitude3(_e.eval());
}
template<vec_expr E>
TNVX_INLINE
Magn3<E>::operator float() const noexcept
{
return _mm_cvtss_f32(eval());
}
template<vec_expr E>
TNVX_INLINE
Magn3<E> magnitude3(const E& TNVX_RESTRICT e) noexcept
{
return Magn3<E>(e);
}
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
#include "scalar.h"
namespace tnvx
{
template<expression L, expression R>
struct Mul : Expr<Mul<L, R>>
{
TNVX_INLINE
Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
tnvx_ref_or_value_t<L> _l;
tnvx_ref_or_value_t<R> _r;
};
template<vec_expr E>
[[nodiscard]] TNVX_INLINE
Mul<E, Scalar> operator*(const E& TNVX_RESTRICT l, float r) noexcept;
template<vec_expr E>
[[nodiscard]] TNVX_INLINE
Mul<Scalar, E> operator*(float l, const E& TNVX_RESTRICT r) noexcept;
template<scalar_expr L, scalar_expr R>
[[nodiscard]] TNVX_INLINE
Mul<L, R> operator*(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
template<vec_expr L, scalar_expr R> inline constexpr bool is_vec_expr<Mul<L, R>> = true;
template<scalar_expr L, vec_expr R> inline constexpr bool is_vec_expr<Mul<L, R>> = true;
template<scalar_expr L, scalar_expr R> inline constexpr bool is_scalar_expr<Mul<L, R>> = true;
}
#include "mul_impl.hpp"

View File

@ -0,0 +1,44 @@
#pragma once
#include "mul.h"
namespace tnvx
{
template<expression L, expression R>
TNVX_INLINE
Mul<L, R>::Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<expression L, expression R>
TNVX_INLINE
vf4 Mul<L, R>::eval() const noexcept
{
return _mm_mul_ps(_l.eval(), _r.eval());
}
template<vec_expr E>
TNVX_INLINE
Mul<E, Scalar> operator*(const E& TNVX_RESTRICT l, float r) noexcept
{
return { l, Scalar(r) };
}
template<vec_expr E>
TNVX_INLINE
Mul<Scalar, E> operator*(float l, const E& TNVX_RESTRICT r) noexcept
{
return { Scalar(l), r };
}
template<scalar_expr L, scalar_expr R>
TNVX_INLINE
Mul<L, R> operator*(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
{
return { l, r };
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
namespace tnvx
{
template<vec_expr E>
struct Norm3 : Expr<Norm3<E>>
{
TNVX_INLINE
explicit Norm3(const E& TNVX_RESTRICT e) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
tnvx_ref_or_value_t<E> _e;
};
template<vec_expr E>
[[nodiscard]] TNVX_INLINE
Norm3<E> norm3(const E& TNVX_RESTRICT e) noexcept;
template<vec_expr E> inline constexpr bool is_vec_expr<Norm3<E>> = true;
}
#include "norm3_impl.hpp"

View File

@ -0,0 +1,29 @@
#pragma once
#include "norm3.h"
namespace tnvx
{
template<vec_expr E>
TNVX_INLINE
Norm3<E>::Norm3(const E& TNVX_RESTRICT e) noexcept
: _e(e)
{
}
template<vec_expr E>
TNVX_INLINE
vf4 Norm3<E>::eval() const noexcept
{
return detail::norm3(_e.eval());
}
template<vec_expr E>
TNVX_INLINE
Norm3<E> norm3(const E& TNVX_RESTRICT e) noexcept
{
return Norm3<E>(e);
}
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
namespace tnvx
{
struct Scalar : Expr<Scalar>
{
TNVX_INLINE
explicit Scalar(float value) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
vf4 _value;
};
template<>
inline constexpr bool is_scalar_expr<Scalar> = true;
}
#include "scalar_impl.hpp"

View File

@ -0,0 +1,20 @@
#pragma once
#include "scalar.h"
namespace tnvx
{
TNVX_INLINE
Scalar::Scalar(float value) noexcept
{
_value = detail::scalar(value);
}
TNVX_INLINE
vf4 Scalar::eval() const noexcept
{
return _value;
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "concepts.h"
#include "traits.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
struct Sub : Expr<Sub<L, R>>
{
TNVX_INLINE
Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
tnvx_ref_or_value_t<L> _l;
tnvx_ref_or_value_t<R> _r;
};
template<vec_expr L, vec_expr R>
[[nodiscard]] TNVX_INLINE
Sub<L, R> operator-(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
template<vec_expr L, vec_expr R> inline constexpr bool is_vec_expr<Sub<L, R>> = true;
}
#include "sub_impl.hpp"

View File

@ -0,0 +1,30 @@
#pragma once
#include "sub.h"
namespace tnvx
{
template<vec_expr L, vec_expr R>
TNVX_INLINE
Sub<L, R>::Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<vec_expr L, vec_expr R>
TNVX_INLINE
vf4 Sub<L, R>::eval() const noexcept
{
return detail::sub(_l.eval(), _r.eval());
}
template<vec_expr L, vec_expr R>
TNVX_INLINE
Sub<L, R> operator-(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
{
return { l, r };
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <type_traits>
namespace tnvx
{
template<typename T>
inline constexpr bool is_stored_by_value_v = std::is_trivially_copyable_v<T>
&& std::is_trivially_destructible_v<T>
&& sizeof(T) <= 16;
template<typename T>
using tnvx_ref_or_value_t = std::conditional_t<is_stored_by_value_v<T>, T, const T&>;
template<typename>
inline constexpr bool is_scalar_expr = false;
template<typename>
inline constexpr bool is_vec_expr = false;
}

View File

@ -0,0 +1,40 @@
#pragma once
#include "core.h"
#include "expression.h"
#include "traits.h"
#include "concepts.h"
namespace tnvx
{
struct alignas(16) vec4 : Expr<vec4>
{
TNVX_INLINE
vec4() noexcept = default;
TNVX_INLINE
vec4(float x, float y, float z, float w = 0) noexcept;
template<vec_expr E>
TNVX_INLINE
vec4(const E& TNVX_RESTRICT e) noexcept;
[[nodiscard]] TNVX_INLINE
vf4 eval() const noexcept;
private:
vf4 _data;
};
template<> inline constexpr bool is_vec_expr<vec4> = true;
TNVX_INLINE
bool approx_eq(const vec4& lhs, const vec4& rhs, float eps = 1e-6f) noexcept;
TNVX_INLINE
bool operator==(const vec4& lhs, const vec4& rhs) noexcept;
}
#include "vec4_impl.hpp"

View File

@ -0,0 +1,39 @@
#pragma once
#include "vec4.h"
namespace tnvx
{
TNVX_INLINE
vec4::vec4(float x, float y, float z, float w) noexcept
: _data(_mm_set_ps(w, z, y, x))
{
}
TNVX_INLINE
__m128 vec4::eval() const noexcept
{
return _data;
}
template<vec_expr E>
TNVX_INLINE
vec4::vec4(const E& TNVX_RESTRICT e) noexcept
: _data(e.eval())
{
}
TNVX_INLINE
bool approx_eq(const vec4& lhs, const vec4& rhs, float eps) noexcept
{
return detail::approx_eq(lhs.eval(), rhs.eval(), eps);
}
TNVX_INLINE
bool tnvx::operator==(const vec4& lhs, const vec4& rhs) noexcept
{
return detail::eq(lhs.eval(), rhs.eval());
}
}

View File

@ -1,28 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
template<expression L, expression R>
struct Add : Expr<Add<L, R>>
{
inline
Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
private:
tnvx_type_storage<L> _l;
tnvx_type_storage<R> _r;
};
template<expression L, expression R>
[[nodiscard]] inline
Add<L, R> operator+(const Expr<L>& l, const Expr<R>& r) noexcept;
}
#include "add_impl.hpp"

View File

@ -1,30 +0,0 @@
#pragma once
#include "add.h"
namespace tnvx
{
template<expression L, expression R>
Add<L, R>::Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<expression L, expression R>
TNVX_INLINE
__m128 Add<L, R>::eval() const noexcept
{
return _mm_add_ps(_l.eval(), _r.eval());
}
template<expression L, expression R>
TNVX_INLINE
Add<L, R> operator+(const Expr<L>& l, const Expr<R>& r) noexcept
{
return { l.self(), r.self() };
}
} // namespace tnvx

View File

@ -1,28 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
template<vector_expression L, vector_expression R>
struct Cross : Expr<Cross<L, R>>
{
inline
Cross(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
private:
tnvx_type_storage<L> _l;
tnvx_type_storage<R> _r;
};
template<vector_expression L, vector_expression R>
[[nodiscard]] inline
Cross<L, R> cross(const Expr<L>& l, const Expr<L>& r) noexcept;
}
#include "cross_impl.hpp"

View File

@ -1,47 +0,0 @@
#pragma once
#include "cross.h"
namespace tnvx
{
template<vector_expression L, vector_expression R>
TNVX_INLINE
Cross<L, R>::Cross(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<vector_expression L, vector_expression R>
TNVX_INLINE
__m128 Cross<L, R>::eval() const noexcept
{
__m128 a = _l.eval();
__m128 b = _r.eval();
// [ay, az, ax, 0]
__m128 a_yzx = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1));
// [by, bz, bx, 0]
__m128 b_yzx = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1));
// [ay*bz, az*bx, ax*by, 0]
__m128 mul0 = _mm_mul_ps(a, b_yzx);
// [az*by, ax*bz, ay*bx, 0]
__m128 mul1 = _mm_mul_ps(a_yzx, b);
// [ay*bz - az*by, az*bx - ax*bz, ax*by - ay*bx, 0]
__m128 sub = _mm_sub_ps(mul0, mul1);
// shuffle обратно в [x, y, z, 0]
return _mm_shuffle_ps(sub, sub, _MM_SHUFFLE(3, 0, 2, 1));
}
template<vector_expression L, vector_expression R>
Cross<L, R> cross(const Expr<L>& l, const Expr<R>& r) noexcept
{
return { l.self(), r.self() };
}
}

View File

@ -1,39 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
template<vector_expression L, vector_expression R>
struct Dot : Expr<Dot<L, R>>
{
inline
Dot(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
[[nodiscard]] inline
operator float() const noexcept;
private:
tnvx_type_storage<L> _l;
tnvx_type_storage<R> _r;
};
template<vector_expression L, vector_expression R>
[[nodiscard]] inline
Dot<L, R> dot(const Expr<L>& l, const Expr<R>& r) noexcept;
template<vector_expression L, vector_expression R>
[[nodiscard]] inline
float operator*(const Dot<L, R>& l, float r) noexcept;
template<vector_expression L, vector_expression R>
[[nodiscard]] inline
float operator*(float l, const Dot<L, R>& r) noexcept;
}
#include "dot_impl.hpp"

View File

@ -1,60 +0,0 @@
#pragma once
#include "dot.h"
namespace tnvx
{
template<vector_expression L, vector_expression R>
TNVX_INLINE
Dot<L, R>::Dot(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<vector_expression L, vector_expression R>
TNVX_INLINE
__m128 Dot<L, R>::eval() const noexcept
{
__m128 a = _l.eval();
__m128 b = _r.eval();
__m128 mul = _mm_mul_ps(a, b);
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
__m128 sum = _mm_add_ps(mul, shuf);
shuf = _mm_movehl_ps(shuf, sum);
sum = _mm_add_ss(sum, shuf);
return _mm_shuffle_ps(sum, sum, 0);
}
template<vector_expression L, vector_expression R>
TNVX_INLINE
Dot<L, R>::operator float() const noexcept
{
return _mm_cvtss_f32(eval());
}
template<vector_expression L, vector_expression R>
TNVX_INLINE
Dot<L, R> dot(const Expr<L>& l, const Expr<R>& r) noexcept
{
return { l.self(), r.self() };
}
template<vector_expression L, vector_expression R>
TNVX_INLINE
float operator*(const Dot<L, R>& l, float r) noexcept
{
return static_cast<float>(l) * r;
}
template<vector_expression L, vector_expression R>
float operator*(float l, const Dot<L, R>& r) noexcept
{
return l * static_cast<float>(r);
}
}

View File

@ -1,47 +0,0 @@
#pragma once
#include "defines.h"
#include <immintrin.h>
#include <cstdint>
#include <concepts>
namespace tnvx
{
template<typename Derived>
struct Expr
{
[[nodiscard]] inline
const Derived& self() const noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
[[nodiscard]] inline
float operator[](size_t i) const noexcept;
};
template<typename T>
concept expression = requires(T a)
{
{ a.eval() } -> std::same_as<__m128>;
}
&& std::derived_from<T, Expr<T>>;
struct Scalar;
template<typename T>
concept vector_expression = expression<T> && !std::same_as<T, Scalar>;
template<typename T>
constexpr bool tnvx_store_by_value = std::is_trivially_copyable_v<T>
&& std::is_trivially_destructible_v<T>
&& sizeof(T) <= 16;
template<typename T>
using tnvx_type_storage = std::conditional_t<tnvx_store_by_value<T>, T, const T&>;
} // namespace tnvx
#include "expression_impl.hpp"

View File

@ -1,31 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
template<typename Derived>
TNVX_INLINE
const Derived& Expr<Derived>::self() const noexcept
{
return static_cast<const Derived&>(*this);
}
template<typename Derived>
TNVX_INLINE
__m128 Expr<Derived>::eval() const noexcept
{
return self().eval();
}
template<typename Derived>
TNVX_INLINE
float Expr<Derived>::operator[](size_t i) const noexcept
{
alignas(16) float tmp[4];
_mm_store_ps(tmp, eval());
return tmp[i];
}
} // namespace tnvx

View File

@ -1,30 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
template<vector_expression E>
struct Magnitude : Expr<Magnitude<E>>
{
inline
Magnitude(const E& TNVX_RESTRICT e) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
[[nodiscard]] inline
operator float() const noexcept;
private:
tnvx_type_storage<E> _e;
};
template<vector_expression E>
[[nodiscard]] inline
Magnitude<E> magnitude(const Expr<E>& e) noexcept;
}
#include "magnitude_impl.hpp"

View File

@ -1,37 +0,0 @@
#pragma once
#include "magnitude.h"
#include "dot.h"
namespace tnvx
{
template<vector_expression E>
TNVX_INLINE
Magnitude<E>::Magnitude(const E& TNVX_RESTRICT e) noexcept
: _e(e)
{
}
template<vector_expression E>
TNVX_INLINE
__m128 Magnitude<E>::eval() const noexcept
{
return _mm_sqrt_ss(Dot<E, E>(_e, _e).eval());
}
template<vector_expression E>
TNVX_INLINE
Magnitude<E>::operator float() const noexcept
{
return _mm_cvtss_f32(eval());
}
template<vector_expression E>
TNVX_INLINE
Magnitude<E> magnitude(const Expr<E>& e) noexcept
{
return { e.self() };
}
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "expression.h"
#include "scalar.h"
namespace tnvx
{
template<expression L, expression R>
struct Mul : Expr<Mul<L, R>>
{
inline
Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
private:
tnvx_type_storage<L> _l;
tnvx_type_storage<R> _r;
};
//template<vector_expression L, vector_expression R>
//[[nodiscard]] inline
//Mul<L, R> operator*(const Expr<L>& l, const Expr<R>& r) noexcept;
template<expression E>
[[nodiscard]] inline
Mul<E, Scalar> operator*(const Expr<E>& l, float r) noexcept;
template<expression E>
[[nodiscard]] inline
Mul<Scalar, E> operator*(float l, const Expr<E>& r) noexcept;
}
#include "mul_impl.hpp"

View File

@ -1,44 +0,0 @@
#pragma once
#include "mul.h"
namespace tnvx
{
template<expression L, expression R>
TNVX_INLINE
inline Mul<L, R>::Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l)
, _r(r)
{
}
template<expression L, expression R>
TNVX_INLINE
__m128 Mul<L, R>::eval() const noexcept
{
return _mm_mul_ps(_l.eval(), _r.eval());
}
//template<vector_expression L, vector_expression R>
//TNVX_INLINE
//Mul<L, R> operator*(const Expr<L>& l, const Expr<R>& r) noexcept
//{
// return { l.self(), r.self() };
//}
template<expression E>
TNVX_INLINE
Mul<E, Scalar> operator*(const Expr<E>& l, float r) noexcept
{
return { l.self(), Scalar(r) };
}
template<expression E>
TNVX_INLINE
Mul<Scalar, E> operator*(float l, const Expr<E>& r) noexcept
{
return { Scalar(l), r.self() };
}
}

View File

@ -1,27 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
template<vector_expression E>
struct Norm : Expr<Norm<E>>
{
inline
Norm(const E& TNVX_RESTRICT e) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
private:
tnvx_type_storage<E> _e;
};
template<vector_expression E>
[[nodiscard]] inline
Norm<E> norm(const Expr<E>& e) noexcept;
}
#include "norm_impl.hpp"

View File

@ -1,38 +0,0 @@
#pragma once
#include "norm.h"
#include "dot.h"
namespace tnvx
{
template<vector_expression E>
TNVX_INLINE
Norm<E>::Norm(const E& TNVX_RESTRICT e) noexcept
: _e(e)
{
}
template<vector_expression E>
TNVX_INLINE
__m128 Norm<E>::eval() const noexcept
{
__m128 data = _e.eval();
__m128 mul = _mm_mul_ps(data, data);
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
__m128 sum = _mm_add_ps(mul, shuf);
shuf = _mm_movehl_ps(shuf, sum);
sum = _mm_add_ss(sum, shuf);
__m128 len = _mm_shuffle_ps(sum, sum, 0);
__m128 sqrt_len = _mm_sqrt_ps(len);
return _mm_div_ps(data, sqrt_len);
}
template<vector_expression E>
TNVX_INLINE
Norm<E> norm(const Expr<E>& e) noexcept
{
return { e.self() };
}
}

View File

@ -1,25 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
struct Scalar : Expr<Scalar>
{
inline
Scalar(float value) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
[[nodiscard]] inline
float operator[](size_t) const noexcept;
private:
float _value;
};
}
#include "scalar_impl.hpp"

View File

@ -1,26 +0,0 @@
#pragma once
#include "scalar.h"
namespace tnvx
{
TNVX_INLINE
Scalar::Scalar(float value) noexcept
: _value(value)
{
}
[[nodiscard]] TNVX_INLINE
__m128 Scalar::eval() const noexcept
{
return _mm_set1_ps(_value);
}
[[nodiscard]] TNVX_INLINE
float Scalar::operator[](size_t) const noexcept
{
return _value;
}
}

View File

@ -1,28 +0,0 @@
#pragma once
#include "expression.h"
namespace tnvx
{
template<expression L, expression R>
struct Sub : Expr<Sub<L, R>>
{
inline
Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
private:
tnvx_type_storage<L> _l;
tnvx_type_storage<R> _r;
};
template<expression L, expression R>
[[nodiscard]] inline
Sub<L, R> operator-(const Expr<L>& l, const Expr<R>& r) noexcept;
}
#include "sub_impl.hpp"

View File

@ -1,28 +0,0 @@
#pragma once
#include "sub.h"
namespace tnvx
{
template<expression L, expression R>
tnvx::Sub<L, R>::Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
: _l(l), _r(r)
{
}
template<expression L, expression R>
TNVX_INLINE
__m128 Sub<L, R>::eval() const noexcept
{
return _mm_sub_ps(_l.eval(), _r.eval());
}
template<expression L, expression R>
TNVX_INLINE
Sub<L, R> operator-(const Expr<L>& l, const Expr<R>& r) noexcept
{
return { l.self(), r.self() };
}
}

View File

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "vec4.h" #include "expressions/add.h"
#include "expressions/sub.h"
#include "internal/add.h" #include "expressions/scalar.h"
#include "internal/sub.h" #include "expressions/mul.h"
#include "internal/mul.h" #include "expressions/div.h"
#include "internal/dot.h" #include "expressions/dot3.h"
#include "internal/cross.h" #include "expressions/magnitude3.h"
#include "internal/norm.h" #include "expressions/norm3.h"
#include "internal/magnitude.h" #include "expressions/cross3.h"
#include "internal/scalar.h" #include "expressions/vec4.h"

View File

@ -1,63 +0,0 @@
#pragma once
#include "internal/expression.h"
#include <immintrin.h>
#include <array>
namespace tnvx
{
struct alignas(16) vec4 : Expr<vec4>
{
vec4() = default;
inline
vec4(float x, float y, float z, float w) noexcept;
template<typename E>
inline
vec4(const Expr<E>& expr) noexcept;
template<typename E>
inline
vec4& operator=(const Expr<E>& expr) noexcept;
[[nodiscard]] inline
__m128 eval() const noexcept;
[[nodiscard]] inline
auto magnitude() const noexcept;
[[nodiscard]] inline
auto norm() const noexcept;
[[nodiscard]] inline
float x() const noexcept;
[[nodiscard]] inline
float y() const noexcept;
[[nodiscard]] inline
float z() const noexcept;
[[nodiscard]] inline
float w() const noexcept;
[[nodiscard]] inline
std::array<float, 4> data() const noexcept;
private:
__m128 _data;
};
inline
bool operator==(const vec4& a, const vec4& b) noexcept;
inline
bool approx_eq(const vec4& a, const vec4& b, float eps = 1e-3f) noexcept;
}
#include "vec4_impl.hpp"

View File

@ -1,108 +0,0 @@
#pragma once
#include "vec4.h"
#include "internal/magnitude.h"
#include "internal/norm.h"
namespace tnvx
{
template<typename E>
TNVX_INLINE
vec4::vec4(const Expr<E>& expr) noexcept
: _data(expr.eval())
{
}
template<typename E>
TNVX_INLINE
vec4& vec4::operator=(const Expr<E>& expr) noexcept
{
_data = expr.eval();
return *this;
}
TNVX_INLINE
vec4::vec4(float x, float y, float z, float w) noexcept
{
_data = _mm_set_ps(w, z, y, x);
}
TNVX_INLINE
__m128 vec4::eval() const noexcept
{
return _data;
}
TNVX_INLINE
auto vec4::magnitude() const noexcept
{
return Magnitude<vec4>(*this);
}
TNVX_INLINE
auto vec4::norm() const noexcept
{
return Norm<vec4>(*this);
}
//TNVX_INLINE
//Magnitude<vec4> vec4::magnitude() const noexcept
//{
// return { *this };
//}
//
//TNVX_INLINE
//Norm<vec4> vec4::norm() const noexcept
//{
// return { *this };
//}
TNVX_INLINE
float vec4::x() const noexcept
{
return _mm_cvtss_f32(_data);
}
TNVX_INLINE
float vec4::y() const noexcept
{
return _mm_cvtss_f32(_mm_shuffle_ps(_data, _data, _MM_SHUFFLE(1, 1, 1, 1)));
}
TNVX_INLINE
float vec4::z() const noexcept
{
return _mm_cvtss_f32(_mm_shuffle_ps(_data, _data, _MM_SHUFFLE(2, 2, 2, 2)));
}
TNVX_INLINE
float vec4::w() const noexcept
{
return _mm_cvtss_f32(_mm_shuffle_ps(_data, _data, _MM_SHUFFLE(3, 3, 3, 3)));
}
TNVX_INLINE
std::array<float, 4> vec4::data() const noexcept
{
std::array<float, 4> result;
_mm_storeu_ps(result.data(), _data);
return result;
}
TNVX_INLINE
bool operator==(const vec4& a, const vec4& b) noexcept
{
return _mm_movemask_ps(_mm_cmpeq_ps(a.eval(), b.eval())) == 0xF;
}
TNVX_INLINE
bool approx_eq(const vec4& a, const vec4& b, float eps) noexcept
{
__m128 epsilon = _mm_set1_ps(eps);
__m128 diff = _mm_sub_ps(a.eval(), b.eval());
__m128 abs_diff = _mm_andnot_ps(_mm_set1_ps(-0.0f), diff);
return _mm_movemask_ps(_mm_cmplt_ps(abs_diff, epsilon)) == 0xF;
}
}

View File

@ -9,7 +9,7 @@ TEST(vec4, add)
{ {
vec4 a = { 1.0f, 2.0f, 3.0f, 4.0f }; vec4 a = { 1.0f, 2.0f, 3.0f, 4.0f };
vec4 b = { 4.0f, 3.0f, 2.0f, 1.0f }; vec4 b = { 4.0f, 3.0f, 2.0f, 1.0f };
vec4 check{ 5.0f, 5.0f, 5.0f, 5.0f }; vec4 check = { 5.0f, 5.0f, 5.0f, 5.0f };
vec4 result = a + b; vec4 result = a + b;
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
@ -45,12 +45,21 @@ TEST(vec4, mult_b)
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
} }
TEST(vec4, dot) TEST(vec4, div)
{ {
vec4 a = { 1.0f, 0.0f, 3.0f, 0.0f }; vec4 a = { 3.0f, 6.0f, 0.0f, 0.0f };
vec4 b = { 1.0f, 2.0f, 3.0f, 1.0f }; float b = 3.0f;
vec4 check = { 1.0f, 2.0f, 0.0f, 0.0f };
vec4 result = a / b;
EXPECT_TRUE(approx_eq(check, result));
}
TEST(vec4, dot3)
{
vec4 a = { 1.0f, 0.0f, 3.0f, 1.0f };
vec4 b = { 1.0f, 2.0f, 3.0f, 4.0f };
float check = 10.0f; float check = 10.0f;
float result = dot(a, b); float result = dot3(a, b);
EXPECT_FLOAT_EQ(check, result); EXPECT_FLOAT_EQ(check, result);
} }
@ -60,25 +69,25 @@ TEST(vec4, cross)
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f }; vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f };
vec4 check = { 0.0f, 0.0f, 1.0f, 0.0f }; vec4 check = { 0.0f, 0.0f, 1.0f, 0.0f };
vec4 result = cross(a, b); vec4 result = cross3(a, b);
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
} }
TEST(vec4, norm_a) TEST(vec4, norm_a)
{ {
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 0.0f, 1.0f };
vec4 check = { 1.0f, 0.0f, 0.0f, 0.0f }; vec4 check = { 1.0f, 0.0f, 0.0f, 1.0f };
vec4 result = norm(a); vec4 result = norm3(a);
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
} }
TEST(vec4, norm_b) TEST(vec4, norm_b)
{ {
vec4 a = { 1.0f, 0.0f, 1.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 1.0f, 1.0f };
vec4 check = { 0.707106f, 0.0f, 0.707106f, 0.0f }; vec4 check = { 0.707106f, 0.0f, 0.707106f, 1.0f };
vec4 result = norm(a); vec4 result = norm3(a);
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
} }
@ -87,7 +96,7 @@ TEST(vec4, magnitude_a)
{ {
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
float check = 1.0f; float check = 1.0f;
float result = magnitude(a); float result = magnitude3(a);
EXPECT_FLOAT_EQ(check, result); EXPECT_FLOAT_EQ(check, result);
} }
@ -96,20 +105,20 @@ TEST(vec4, magnitude_b)
{ {
vec4 a = { 1.0f, 0.0f, 1.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 1.0f, 0.0f };
float check = 1.4142135f; float check = 1.4142135f;
float result = magnitude(a); float result = magnitude3(a);
EXPECT_FLOAT_EQ(check, result); EXPECT_FLOAT_EQ(check, result);
} }
TEST(vec4, expression_a) TEST(vec4, expression_a)
{ {
vec4 a = { 1.0f, 1.0f, 1.0f, 0.0f }; vec4 a = { 1.0f, 1.0f, 1.0f, 1.0f };
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f }; vec4 b = { 0.0f, 1.0f, 0.0f, 3.0f };
vec4 c = { 1.15f, 0.0f, 11.0f, 0.0f }; vec4 c = { 1.15f, 0.0f, 11.0f, 0.0f };
vec4 check = { 3.45f, 0.0f, 33.0f, 0.0f }; vec4 check = { 3.45f, 0.0f, 33.0f, 0.0f };
vec4 result = dot(a, b) * 3.0f * c; vec4 result = dot3(a, b) * 3.0f * c;
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
} }
@ -122,7 +131,7 @@ TEST(vec4, expression_b)
vec4 check = { 33.0f, 0.0f, -3.45f, 0.0f }; vec4 check = { 33.0f, 0.0f, -3.45f, 0.0f };
vec4 result = dot(a, b) * 3.0f * cross(b, c); vec4 result = dot3(a, b) * 3.0f * cross3(b, c);
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
} }
@ -133,9 +142,12 @@ TEST(vec4, expression_c)
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f }; vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
vec4 c = { 1.0f, 0.0f, 1.0f, 0.0f }; vec4 c = { 1.0f, 0.0f, 1.0f, 0.0f };
vec4 check = { 7.243f, 5.657f, 10.071f, 0.0f }; vec4 check = { 7.24264050f, 5.65685463f, 10.0710678f, 0.0f };
vec4 result = norm(a + b * 2.0f) * dot(b, c) + c * 3.0f; vec4 tmp1 = norm3(a + b * 2.0f) * dot3(b, c);
vec4 tmp2 = c * 3.0f;
vec4 result = tmp1 + tmp2;
EXPECT_TRUE(approx_eq(check, result)); EXPECT_TRUE(approx_eq(check, result));
} }
@ -144,27 +156,27 @@ TEST(vec4, cross_anticommutative)
{ {
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f }; vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f };
EXPECT_TRUE(approx_eq(cross(a, b), cross(b, a) * -1.0f)); EXPECT_TRUE(approx_eq(cross3(a, b), cross3(b, a) * -1.0f));
} }
TEST(vec4, dot_commutative) TEST(vec4, dot_commutative)
{ {
vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f }; vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f }; vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
EXPECT_FLOAT_EQ(dot(a, b), dot(b, a)); EXPECT_FLOAT_EQ(dot3(a, b), dot3(b, a));
} }
TEST(vec4, norm_magnitude) TEST(vec4, norm_magnitude)
{ {
vec4 a = { 3.0f, 1.0f, 4.0f, 0.0f }; vec4 a = { 3.0f, 1.0f, 4.0f, 0.0f };
EXPECT_NEAR(magnitude(norm(a)), 1.0f, 1e-5f); EXPECT_NEAR(magnitude3(norm3(a)), 1.0f, 1e-5f);
} }
TEST(vec4, dot_perpendicular) TEST(vec4, dot_perpendicular)
{ {
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f }; vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f };
EXPECT_FLOAT_EQ(dot(a, b), 0.0f); EXPECT_FLOAT_EQ(dot3(a, b), 0.0f);
} }
TEST(vec4, cross_parallel) TEST(vec4, cross_parallel)
@ -172,7 +184,7 @@ TEST(vec4, cross_parallel)
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f }; vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
vec4 b = { 2.0f, 0.0f, 0.0f, 0.0f }; vec4 b = { 2.0f, 0.0f, 0.0f, 0.0f };
vec4 check = { 0.0f, 0.0f, 0.0f, 0.0f }; vec4 check = { 0.0f, 0.0f, 0.0f, 0.0f };
EXPECT_TRUE(approx_eq(cross(a, b), check)); EXPECT_TRUE(approx_eq(cross3(a, b), check));
} }
} }